home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Otherware
/
Otherware_1_SB_Development.iso
/
mac
/
util
/
comm
/
news102.sit
/
NewsWatcher
/
source
/
commands.c
next >
Wrap
Text File
|
1991-04-03
|
32KB
|
1,354 lines
/*----------------------------------------------------------
#
# NewsWatcher - Macintosh NNTP Client Application
#
# Written by Steven Falkenburg
# ⌐1990 Apple Computer, Inc.
#
#-----------------------------------------------------------
#
# commands.c
#
# This file contains user interface routines which are
# called in response to user actions recorded in userint.c
#
#-----------------------------------------------------------*/
#pragma segment userint
#include "compat.h"
#include <CType.h>
#ifdef PROTOS
#include <Types.h>
#include <QuickDraw.h>
#include <Fonts.h>
#include <Windows.h>
#include <Menus.h>
#include <TextEdit.h>
#include <Dialogs.h>
#include <OSUtils.h>
#include <Desk.h>
#include <ToolUtils.h>
#include <OSEvents.h>
#include <Lists.h>
#include <CursorCtl.h>
#include <Packages.h>
#include <Scrap.h>
#include <Resources.h>
#include <Script.h>
#include <Printing.h>
#include <Strings.h>
#endif
#include <StdLib.h>
#include <String.h>
#include "nntp.h"
#include "userint.h"
#include "newsprocess.h"
#include "ScrollStuff.h"
#include "netstuff.h"
#include "miscstuff.h"
#include "printstuff.h"
#include "commands.h"
#ifdef NNTPNEWS
#include "NNTPLow.h"
#else
#include "HFSNTPLow.h"
#endif
/* adds a child window to the windowlist of a parent
Windows with associated child windows will close
their children when closed
*/
void AddChild(WindowPtr parent,WindowPtr child)
{
TwindowInfo *parentInfo;
TWList *newChild;
parentInfo = (TwindowInfo *) GetWRefCon(parent);
newChild = (TWList *) MyNewPtr(sizeof(TWList));
if (MyMemErr() != noErr)
return;
newChild->childWindow = child;
newChild->next = parentInfo->childList;
parentInfo->childList = newChild;
}
/* Removes a child window from the windowlist of a parent
*/
void RemoveChild(WindowPtr parent,WindowPtr child)
{
TwindowInfo *parentInfo;
TWList *current,*prev;
parentInfo = (TwindowInfo *) GetWRefCon(parent);
for (current = prev = parentInfo->childList;
current != nil && current->childWindow!=child;
prev = current,current = current->next)
;
if (current) {
prev->next = current->next;
if (prev == current)
parentInfo->childList = current->next;
MyDisposPtr((Ptr)current);
}
}
/* MakeGroupList takes a list of groups and a List Manager
handle. The groups are sorted and added to the list.
*/
void MakeGroupList(ListHandle theList,short numGroups,TGroup *groupList)
{
long i;
Cell theCell;
char theName[256];
short cellLen;
StatusWindow("Sorting Groups...",-1);
qsort(groupList, numGroups, sizeof(TGroup), MyCompare);
StatusWindow("Building List...",0);
GiveTime(0);
LDoDraw(false,theList);
LAddRow(numGroups,0,theList);
for (i=0; i<numGroups; i++) {
strcpy(theName,groupList[i].name);
if ((groupList[i].lastMess - groupList[i].firstMess) <= 0) {
cellLen = strlen(theName)+2;
theName[cellLen-1] = 0xff;
}
else
cellLen = strlen(theName)+1;
SetPt(&theCell,0,i);
LSetCell(theName,cellLen,theCell,theList);
StatusWindow("Building List...",(short)(((float)(i+1)/(float)numGroups)*100)+1);
GiveTime(0);
}
StatusWindow("Building List...",100);
GiveTime(0);
LDoDraw(true,theList);
SetCursor(&QDARROW);
}
/* MyCompare is a comparison routine used in the call to
qsort() above. It does a simple string compare, and
gives time to background applications.
*/
int MyCompare(TGroup *one,TGroup *two)
{
GiveTime(0);
return strcmp(one->name,two->name);
}
/* HandleGroupSelect is called in response to a mouse
down event in a main group window (not a user group
window). It opens a subject window containing the
names of all articles in that group.
*/
void HandleGroupSelect(Cell theCell,WindowPtr window)
{
TwindowInfo *theInfo;
WindowPtr theWind;
Str255 tmpStr,tmpTitle;
TGroup *groupList,*theGroup;
GrafPtr savePort;
Cell thePt;
Point firstOffset;
WindowPtr tmpWindow;
char groupStr[256];
short groupLen;
extern TPrefRec gPrefs;
SetPt(&firstOffset,0,0);
GetPort(&savePort);
SetPort(window);
LocalToGlobal(&firstOffset);
SetPort(savePort);
theInfo = (TwindowInfo *) GetWRefCon(window);
groupList = (TGroup *) theInfo->data2;
groupLen = 256;
LGetCell(groupStr,&groupLen,theCell,(ListHandle)theInfo->data);
if (!FindGroup(theInfo,groupStr,&theGroup)) {
SysBeep(1);
return;
}
strcpy((char *)tmpStr,theGroup->name);
c2pstr((char *)tmpStr);
for (tmpWindow = FrontWindow(); tmpWindow != nil; tmpWindow = (WindowPtr) ((WindowPeek)tmpWindow)->nextWindow) {
GetWTitle(tmpWindow,tmpTitle);
if (EqualString(tmpStr,tmpTitle,true,true)) {
SelectWindow(tmpWindow);
return;
}
}
if (theGroup->firstMess > 0 &&
theGroup->lastMess> 0 &&
theGroup->lastMess - theGroup->firstMess >= 0) {
theInfo = (TwindowInfo *) GetWRefCon(theWind = MakeNewWindow(cSubject,true,firstOffset,(char *)tmpStr));
theInfo->parentGroup = nil;
theInfo->childList = nil;
theInfo->parentWindow = nil;
InitSubjectList(theInfo);
StatusWindow("Getting Subject List...",0);
AddToSubjectList(theInfo,theGroup->name,
theGroup->firstMess,theGroup->lastMess);
StatusWindow("Getting Subject List...",100);
GiveTime(0);
CloseStatusWindow();
SetPt(&thePt,0,0);
LSetSelect(true,thePt,(ListHandle)theInfo->data);
if (gPrefs.openWindowsZoomed)
ToggleZoom(theWind);
ShowWindow(theWind);
GetPort(&savePort);
SetPort(theWind);
InvalRect(&theWind->portRect);
SetPort(savePort);
}
else
SysBeep(1);
}
/* HandleUserGroupSelect is called in response to a mouse
down in a user group window. It opens a window containing
the subjects of all unread articles in that group.
*/
void HandleUserGroupSelect(Cell theCell,WindowPtr window)
{
TwindowInfo *theInfo;
WindowPtr theWind;
Str255 tmpStr,tmpTitle;
TGroup *theGroup;
GrafPtr savePort;
ListHandle theList;
char groupName[256];
short groupLen = 255;
TReadRec *read;
WindowPtr parent;
Point firstOffset;
WindowPtr tmpWindow;
extern TPrefRec gPrefs;
SetPt(&firstOffset,0,0);
GetPort(&savePort);
SetPort(window);
LocalToGlobal(&firstOffset);
SetPort(savePort);
theInfo = (TwindowInfo *)GetWRefCon(window);
theInfo->changed = true;
theList = (ListHandle) theInfo->data;
LGetCell(groupName,&groupLen,theCell,theList);
strcpy((char *)tmpStr,groupName);
c2pstr((char *)tmpStr);
/* make sure group window is not already open */
for (tmpWindow = FrontWindow(); tmpWindow != nil; tmpWindow = (WindowPtr) ((WindowPeek)tmpWindow)->nextWindow) {
GetWTitle(tmpWindow,tmpTitle);
if (EqualString(tmpStr,tmpTitle,true,true)) {
SelectWindow(tmpWindow);
return;
}
}
/* search linked info list for group info */
if (!FindGroup(theInfo,groupName,&theGroup) || !theGroup->read) {
SysBeep(1);
return;
}
strcpy((char *)tmpStr,theGroup->name);
parent = window;
theInfo = (TwindowInfo *) GetWRefCon(theWind = MakeNewWindow(cSubject,true,firstOffset,(char *)c2pstr((char *)tmpStr)));
theInfo->parentGroup = theGroup;
theInfo->parentWindow = parent;
theInfo->childList = nil;
AddChild(parent,theWind);
InitSubjectList(theInfo);
StatusWindow("Getting Subject List...",0);
for (read = theGroup->read; read!=nil; read = read->next)
AddToSubjectList(theInfo,theGroup->name,read->firstRead,read->lastRead);
StatusWindow("Getting Subject List...",100);
GiveTime(0);
CloseStatusWindow();
SetPt(&theCell,0,0);
LSetSelect(true,theCell,(ListHandle)theInfo->data);
if (gPrefs.openWindowsZoomed)
ToggleZoom(theWind);
ShowWindow(theWind);
GetPort(&savePort);
SetPort(theWind);
InvalRect(&theWind->portRect);
SetPort(savePort);
}
/* FindGroup locates a user group record within the linked list
of user groups, given the name of the group.
*/
Boolean FindGroup(TwindowInfo *info,char *groupName,TGroup **theGroup)
{
*theGroup = nil;
if (info->kind == cGroup || info->kind == cNewGroup)
*theGroup = bsearch(groupName,(TGroup *)info->data2,info->numGroups,sizeof(TGroup),SearchCompare);
else if (info->kind == cUserGroup)
for (*theGroup = (TGroup *)info->data2;
*theGroup != nil && strcmp((*theGroup)->name,groupName)!=0;
*theGroup = (*theGroup)->next)
;
return (*theGroup != nil);
}
/* SearchCompare is used in the bsearch() call within FindGroup.
It is used to compare each instance of group name to the
name provided.
*/
int SearchCompare(char *key,TGroup *member)
{
return (strcmp(key,member->name));
}
/* HandleSubjectSelect is called in response to a mouse down in a
subject list window. In this case, the article whose subject
was selected is opened in an article window.
*/
void HandleSubjectSelect(Cell theCell,WindowPtr wind)
{
TwindowInfo *info,*articleInfo;
char newsGroup[256],numStr[256],title[256];
char *tmpStr,*tmpStr2;
long number;
short nameLen;
short i;
Str255 tmpTitle1,tmpTitle2;
WindowPtr tmpWindow;
info = (TwindowInfo *) GetWRefCon(wind);
GetWTitle(wind,newsGroup);
p2cstr(newsGroup);
nameLen = 256;
LGetCell(title,&nameLen,theCell,(ListHandle)info->data);
strcpy((char *)tmpTitle1,title);
c2pstr((char *)tmpTitle1);
for (tmpWindow = FrontWindow(); tmpWindow != nil; tmpWindow = (WindowPtr) ((WindowPeek)tmpWindow)->nextWindow) {
GetWTitle(tmpWindow,tmpTitle2);
if (EqualString(tmpTitle1,tmpTitle2,true,true)) {
SelectWindow(tmpWindow);
return;
}
}
for (tmpStr = numStr,tmpStr2 = title+1;
*tmpStr2 != ' ' && *tmpStr2 != '\0';
*tmpStr++ = *tmpStr2++)
;
*tmpStr = '\0';
c2pstr(numStr);
StringToNum((StringPtr)numStr,&number);
p2cstr(numStr);
if (info->parentGroup && title[0] != '├') {
nameLen = strlen(title)+2;
*title = '├';
LSetCell(title,nameLen,theCell,(ListHandle)info->data);
for (i=0; i<info->numSubjects && ((TSubject *)*((Handle)info->data2))[i].number != number; i++)
;
if (i<=info->numSubjects)
((TSubject *)*((Handle)info->data2))[i].read = true;
else
SysBeep(1);
}
OpenArticle(newsGroup,numStr,title,wind,cMessage);
if (info->parentGroup && info->parentWindow) {
info = (TwindowInfo *)GetWRefCon(info->parentWindow);
articleInfo = (TwindowInfo *)GetWRefCon(FrontWindow());
MarkXrefsRead((TEHandle)articleInfo->data,(TGroup *)info->data2);
}
}
/* OpenReferences opens up all articles which are referred to in
the article contained in the frontmost window. The "References:"
field is used to determine which articles should be fetched.
*/
void OpenReferences(void)
{
TwindowInfo *info;
Handle theText;
long refOffset,endHeader,startReference,endReference;
char title[256];
char mungeText1[256];
char mungeText2[256];
if (!FrontWindow()) {
SysBeep(1);
return;
}
strcpy(mungeText1,CRSTR);
strcat(mungeText1,"References:");
strcpy(mungeText2,CRSTR);
strcat(mungeText2,CRSTR);
info = (TwindowInfo *)GetWRefCon(FrontWindow());
if (info->kind != cMessage)
return;
theText = (Handle) TEGetText((TEHandle)info->data);
if ((refOffset = Munger(theText,0,mungeText1,12L,nil,0L))>=0 &&
refOffset < (endHeader = Munger(theText,0,mungeText2,2L,nil,0L))) {
do {
startReference = Munger(theText,refOffset,"<",1L,nil,0L);
endReference = Munger(theText,refOffset,">",1L,nil,0L);
if (startReference>=0 && endReference>=0 && endReference<endHeader) {
HLock(theText);
strncpy(title,(char *)((*theText)+startReference),endReference-startReference+1);
title[endReference-startReference+1] = '\0';
HUnlock(theText);
OpenArticle(nil,title,title,nil,cMiscMessage);
}
refOffset = endReference+1;
} while (startReference>=0 && endReference>=0 && endReference<endHeader);
}
}
/* OpenArticle gets the text of an article, given the newsgroup,
and article number or message-id. The article is fetched
and placed in a new window created by the procedure call.
*/
void OpenArticle(char *newsGroup,char *number,char *title,WindowPtr parent,short kind)
{
char *text;
long length;
WindowPtr theWindow;
TwindowInfo *newInfo;
GrafPtr savePort;
TEHandle theTE;
Point firstOffset;
extern TPrefRec gPrefs;
if (!FrontWindow()) {
SysBeep(1);
return;
}
SetPt(&firstOffset,0,0);
GetPort(&savePort);
if (parent == nil)
SetPort(FrontWindow());
else
SetPort(parent);
LocalToGlobal(&firstOffset);
SetPort(savePort);
GetPort(&savePort);
theWindow = MakeNewWindow(kind,true,firstOffset,(char *)c2pstr(title));
p2cstr(title);
newInfo = (TwindowInfo *) GetWRefCon(theWindow);
newInfo->parentWindow = parent;
newInfo->childList = nil;
SetPort(theWindow);
theTE = (TEHandle)((TwindowInfo *)GetWRefCon(theWindow))->data;
if (GetArticle(newsGroup,number,&text,&length,kMaxLength) == noErr) {
if (length > 32000)
length = 32000;
TESetText(text,length,theTE);
TESetSelect(0L,0L,theTE);
if (gPrefs.openWindowsZoomed)
DoZoom(theWindow,inZoomOut);
ShowWindow(theWindow);
RedoControls(theWindow);
FixText(theWindow);
InvalRect(&theWindow->portRect);
}
else
DoCloseWindow(theWindow);
SetPort(savePort);
MyDisposPtr(text);
}
/* DoSelectAll is called in response to a Select-All menu command.
If in a list manager window, all cells are hilited. If in a
textedit window, the entire range of a message is hilited.
*/
void DoSelectAll(void)
{
TwindowInfo *info;
Cell theCell;
ListHandle theList;
TEHandle theTE;
if (!FrontWindow()) {
SysBeep(1);
return;
}
info = (TwindowInfo *)GetWRefCon(FrontWindow());
if (info->kind < cMessage) {
theList = (ListHandle) info->data;
SetPt(&theCell,0,0);
do {
LSetSelect(true,theCell,theList);
} while (LNextCell(false,true,&theCell,theList));
}
else {
theTE = (TEHandle) info->data;
TESetSelect(0L,32767L,theTE);
}
}
/* DoSearch is called in response to a search menu command.
All currenly selected newsgroups are searched for a text
string which is prompted for below. A user group window
is created containing an entry for each of the selected
group which matched the search, and the articles are
placed in these groups.
*/
void DoSearch(void)
{
TwindowInfo *info,*newInfo;
ListHandle theList;
Cell theCell,newCell;
char cellData[256],headerTxt[256],searchTxt[256];
short dataLen;
TGroup *groupData,*prevGroup,*curGroup;
DialogPtr theDlg;
short item;
WindowPtr searchWindow;
short iType;
Handle iHndl;
Rect iRect;
char *current;
if (!FrontWindow()) {
SysBeep(1);
return;
}
/* display dialog asking what to search for */
theDlg = GetNewDialog(kSearchDlg,nil,(WindowPtr)-1);
OutlineOK(theDlg);
GetDItem(theDlg,5,&iType,&iHndl,&iRect);
SetDItem(theDlg,5,iType,(Handle) DrawPopUp,&iRect);
do ModalDialog(PopUpFilter,&item);
while (item != okButton && item != cancelButton);
GetDItem(theDlg,3,&iType,&iHndl,&iRect);
GetIText(iHndl,headerTxt);
p2cstr(headerTxt);
GetDItem(theDlg,4,&iType,&iHndl,&iRect);
GetIText(iHndl,searchTxt);
p2cstr(searchTxt);
for (current = searchTxt; *current != '\0'; current++)
*current = toupper(*current);
DisposDialog(theDlg);
if (item==cancelButton || *headerTxt=='\0' || *searchTxt=='\0')
return;
info = (TwindowInfo *) GetWRefCon(FrontWindow());
theList = (ListHandle) info->data;
searchWindow = NewGroupWindow("\pSearch Groups");
newInfo = (TwindowInfo *) GetWRefCon(searchWindow);
newInfo->changed = false;
newInfo->diskFile[0] = '\0';
SetPt(&theCell,0,0);
while (LGetSelect(true,&theCell,theList)) {
dataLen = 256;
LGetCell(cellData,&dataLen,theCell,theList);
if (FindGroup(info,cellData,&groupData)) {
groupData = Subscribe(groupData,searchWindow,&newCell);
if (groupData && groupData->read) {
MyDisposPtr((Ptr)groupData->read);
groupData->read = nil;
if (!SearchAdd(headerTxt,searchTxt,groupData)) {
LDelRow(1,newCell.v,(ListHandle)newInfo->data);
for (curGroup = prevGroup = (TGroup *)newInfo->data2;
curGroup && curGroup!=groupData;
prevGroup = curGroup, curGroup = curGroup->next)
;
if (curGroup == nil) {
SysBeep(1);
return;
}
prevGroup->next = nil;
if (prevGroup == curGroup)
newInfo->data2 = nil;
MyDisposPtr((Ptr)groupData);
}
}
theCell.v++;
}
}
}
/* DrawPopUp is the useritem procedure to draw a pop-up menu.
This routine is called in response to update events for the
pop-up menu area.
*/
pascal void DrawPopUp(DialogPtr theDlg,short theItem)
{
#pragma unused (theItem)
Rect iRect;
short iType;
Handle iHndl;
GetDItem(theDlg,5,&iType,&iHndl,&iRect);
/* draw the box and shadow */
FrameRect(&iRect);
MoveTo(iRect.right,iRect.top+2);
LineTo(iRect.right,iRect.bottom);
LineTo(iRect.left+2,iRect.bottom);
/* draw the downward triangle */
MoveTo(((iRect.left+iRect.right)/2)-5,((iRect.top+iRect.bottom)/2)-3);
Line(8,0);
Move(-1,1);
Line(-6,0);
Move(1,1);
Line(4,0);
Move(-1,1);
Line(-2,0);
Move(1,1);
Line(0,0);
}
/* This routine is the dialog filter for the dialog box containing
a pop-up menu.
*/
pascal Boolean PopUpFilter(DialogPtr theDlg,EventRecord *ev,short *item)
{
char keyPressed;
Point downLoc;
MenuHandle popMenu;
short iType;
Handle iHndl;
Rect iRect;
long chosen;
short choice;
Str255 iString;
*item = 0;
switch (ev->what) {
case keyDown:
case autoKey:
keyPressed = ev->message & charCodeMask;
if (keyPressed == CR || keyPressed == 03) {
*item = okButton;
return true;
}
break;
case mouseDown:
SetPort(theDlg);
downLoc = ev->where;
GlobalToLocal(&downLoc);
if (FindDItem(theDlg,downLoc)+1 == 5) {
GetDItem(theDlg,5,&iType,&iHndl,&iRect);
popMenu = GetMenu(kHeaderMenu);
InsertMenu(popMenu,-1);
downLoc.h = iRect.left;
downLoc.v = iRect.top;
LocalToGlobal(&downLoc);
CalcMenuSize(popMenu);
chosen = PopUpMenuSelect(popMenu,downLoc.v,downLoc.h,1);
if (chosen) {
choice = LoWord(chosen);
GetItem(popMenu,choice,iString);
GetDItem(theDlg,3,&iType,&iHndl,&iRect);
SetIText(iHndl,iString);
SelIText(theDlg,3,0,32767);
}
DisposeMenu(popMenu);
}
break;
}
return false;
}
/* SearchAdd is called once for each group being searched. It
searches for matching articles. It returns true if one
or more of the articles in the group matched the selection.
*/
Boolean SearchAdd(char *headerName,char *headerContents, TGroup *groupData)
{
TSubject subjects[64];
long numSubjects;
long index,rFirst,rLast,firstUnread = 0,lastUnread = 0;
OSErr err = noErr;
Boolean gotOne = false;
char statusStr[256];
for (rFirst = groupData->firstMess; rFirst <= groupData->lastMess && err == noErr; rFirst += 64) {
rLast = ((rFirst+63) > groupData->lastMess) ? groupData->lastMess : rFirst+63;
err = GetMessages(groupData->name,rFirst,rLast,subjects,&numSubjects,headerName);
strcpy(statusStr,"Searching group: ");
strcat(statusStr,groupData->name);
StatusWindow(statusStr,-1);
if (err==noErr)
for (index=0; index < numSubjects; index++) {
if (MySearch(subjects[index].name,headerContents,strlen(subjects[index].name),strlen(headerContents)) == 0) {
if (firstUnread == 0)
firstUnread = lastUnread = subjects[index].number;
else
lastUnread = subjects[index].number;
}
else {
if (firstUnread != 0) {
MarkRead(firstUnread,lastUnread,groupData);
gotOne = true;
firstUnread = 0;
}
}
MyDisposPtr(subjects[index].name);
}
}
if (firstUnread != 0) {
MarkRead(firstUnread,groupData->lastMess,groupData);
gotOne = true;
}
return gotOne;
}
/* DoNarrowSelection is called in response to the Narrow Selection
menu command. This routine displays a dialog box, where the
user enters a string which must be contained in all of the
entries in a group or subject list.
*/
void DoNarrowSelection(void)
{
DialogPtr theDlg;
short item;
short iType;
Handle iHndl;
Rect iRect;
Str255 theText;
TwindowInfo *info;
char filterCopy[256];
short filterCopyLen,index;
if (!FrontWindow()) {
SysBeep(1);
return;
}
info = (TwindowInfo *) GetWRefCon(FrontWindow());
filterCopyLen = info->filterLen;
strncpy(filterCopy,info->filter,filterCopyLen);
theDlg = GetNewDialog(kNarrowDlg,nil,(WindowPtr)-1);
OutlineOK(theDlg);
if (info->filterLen > 0) {
BlockMove(info->filter,theText+1,info->filterLen);
theText[0] = info->filterLen;
GetDItem(theDlg,3,&iType,&iHndl,&iRect);
SetIText(iHndl,theText);
SelIText(theDlg,3,32000,32000);
}
do {
ModalDialog(NarrowFilter,&item);
SetCursor(&QDARROW);
} while (item != okButton && item != cancelButton);
if (item == cancelButton) {
if (info->filterLen > 0)
EndSearch(info);
if (filterCopyLen > 0) {
NewSearch(info,filterCopy[0]);
for (index=1; index<filterCopyLen; index++)
NarrowSearch(info,filterCopy[index]);
}
}
DisposDialog(theDlg);
}
/* NarrowFilter is the dialog filter for the narrow selection
search dialog box.
*/
pascal Boolean NarrowFilter(DialogPtr theDialog,EventRecord *theEvent,short *itemHit)
{
TwindowInfo *info;
char theChar;
unsigned long saveBits;
if (theEvent->what == keyDown || theEvent->what == autoKey) {
saveBits = (theEvent->message & 0xFFFFFF00);
theChar = toupper(theEvent->message & charCodeMask);
theEvent->message = saveBits | theChar;
SelIText(theDialog,3,32000,32000);
info = (TwindowInfo *) GetWRefCon((WindowPtr)((WindowPeek)FrontWindow())->nextWindow); /* window after dlog */
switch (theChar) {
case CR:
*itemHit = 1;
return true;
break;
case 0x08:
if (info->filterLen > 0)
WidenSearch(info);
break;
default:
if (info->filterLen == 0)
NewSearch(info,theChar);
else
NarrowSearch(info,theChar);
break;
}
}
return false;
}
/* NewSearch is called by the narrow selection filter to start
a new narrowing search. It creates a secondary list manager
list which is visible over top of the main list. Entries
which match are added to this list.
*/
void NewSearch(TwindowInfo *info,char theChar)
{
Cell theCell,newCell;
char cellData[256];
short dataLen;
ListHandle newList;
WindowPtr theWindow;
theWindow = (WindowPtr)((WindowPeek)FrontWindow())->nextWindow;
info->narrowList = (ListHandle) info->data;
newList = NewList(theWindow);
info->data = (Handle) newList;
LActivate(false,info->narrowList);
(**newList).lClikLoop = (**(info->narrowList)).lClikLoop;
info->filter[info->filterLen++] = theChar;
SetPt(&theCell,0,0);
SetPt(&newCell,0,0);
while (PtInRect(theCell,&(**(info->narrowList)).dataBounds) &&
LSearch(info->filter,info->filterLen,MySearch,&theCell,info->narrowList)) {
LAddRow(1,newCell.v,newList);
dataLen = 256;
LGetCell(cellData,&dataLen,theCell,info->narrowList);
LSetCell(cellData,dataLen,newCell,newList);
theCell.v++;
newCell.v++;
}
LDoDraw(true,newList);
ForceUpdate();
}
/* WidenSearch is called when the backspace key is pressed in
the narrow search dialog box. It removes constraint on the
list and adds entries where necessary
*/
void WidenSearch(TwindowInfo *info)
{
Cell theCell,newCell;
char cellData[256];
short dataLen;
ListHandle theList;
info->filterLen--;
if (info->filterLen == 0)
EndSearch(info);
else {
/* rebuild list */
theList = (ListHandle) info->data;
LDoDraw(false,theList);
LDelRow(0,0,theList);
LAddRow(1,0,theList);
SetPt(&theCell,0,0);
SetPt(&newCell,0,0);
while (PtInRect(theCell,&(**(info->narrowList)).dataBounds) &&
LSearch(info->filter,info->filterLen,MySearch,&theCell,info->narrowList)) {
LAddRow(1,newCell.v,theList);
dataLen = 256;
LGetCell(cellData,&dataLen,theCell,info->narrowList);
LSetCell(cellData,dataLen,newCell,theList);
theCell.v++;
newCell.v++;
}
LDoDraw(true,theList);
ForceUpdate();
}
}
/* NarrowSearch is called by the narrowing search filter when
a non-backspace key is pressed in the dialog. It searches
the secondary list for entries which may be removed.
*/
void NarrowSearch(TwindowInfo *info,char theChar)
{
Cell theCell;
char data[256];
short dataLen;
Boolean notDone = true;
ListHandle theList;
theList = (ListHandle) info->data;
info->filter[info->filterLen++] = theChar;
LDoDraw(false,theList);
SetPt(&theCell,0,0);
do {
dataLen = 255;
LGetCell(data,&dataLen,theCell,theList);
if (PtInRect(theCell,&(**theList).dataBounds) &&
MySearch(data,info->filter,dataLen,info->filterLen) != 0) {
LDelRow(1,theCell.v,theList);
if (theCell.v != 0)
theCell.v--;
}
else notDone = LNextCell(false,true,&theCell,theList);
} while (notDone);
LDoDraw(true,theList);
ForceUpdate();
}
/* EndSearch is called when a narrowing search has been completed.
*/
void EndSearch(TwindowInfo *info)
{
ListHandle doneList;
WindowPtr theWindow;
GrafPtr savePort;
theWindow = (WindowPtr)((WindowPeek)FrontWindow())->nextWindow;
doneList = (ListHandle) info->data;
LActivate(true,info->narrowList);
LDispose(doneList);
info->data = (Handle) info->narrowList;
info->narrowList = nil;
info->filter[0] = '\0';
info->filterLen = 0;
GetPort(&savePort);
SetPort(theWindow);
SizeContents(theWindow->portRect.right-theWindow->portRect.left,theWindow->portRect.bottom-theWindow->portRect.top,theWindow);
SetPort(savePort);
ForceUpdate();
}
/* ForceUpdate is called by the narrowing search routines to
invalidate the list area, forcing an update event to be
generated for the area.
*/
void ForceUpdate(void)
{
WindowPtr theWindow;
GrafPtr savePort;
theWindow = (WindowPtr)((WindowPeek)FrontWindow())->nextWindow;
GetPort(&savePort);
SetPort(theWindow);
InvalRect(&theWindow->portRect);
SetPort(savePort);
HandleUpdates(theWindow);
}
/* MySearch is the search procedure used to match entries
for the narrowing search routines.
*/
pascal short MySearch(Ptr aPtr,Ptr bPtr,short aLen,short bLen)
{
short index;
char *aUpper;
GiveTime(0);
aUpper = (char *)NewPtr(aLen);
for (index = 0; index < aLen; index++)
aUpper[index] = toupper(aPtr[index]);
for (index = 0; index < aLen; index++) {
if ((aLen-index) < bLen) {
DisposPtr((Ptr)aUpper);
return 1;
}
if (strncmp(aUpper+index,bPtr,bLen) == 0) {
DisposPtr((Ptr)aUpper);
return 0;
}
}
DisposPtr((Ptr)aUpper);
return 1;
}
/* DoRot is called in response to the Do Rot-13 menu command. It
encrypts/decrypts an entire message, or just a selection using
the Rot-13 method common on USENET news groups.
*/
void DoRot(void)
{
GrafPtr theWindow;
TwindowInfo *info;
TEHandle theTE;
GrafPtr savePort;
short i,sStart,sEnd;
if (!FrontWindow()) {
SysBeep(1);
return;
}
info = (TwindowInfo *)GetWRefCon(theWindow = FrontWindow());
if (info->kind < cMessage)
return;
theTE = (TEHandle)info->data;
HLock((**theTE).hText);
sStart = (**theTE).selStart;
sEnd = (**theTE).selEnd;
if (sStart == sEnd) {
sStart = 0;
sEnd = (**theTE).teLength;
}
for (i=sStart; i<= sEnd; i++)
if (isalpha((*(**theTE).hText)[i]))
(*(**theTE).hText)[i] += (toupper((*(**theTE).hText)[i]) > 'M') ? -13 : 13;
HUnlock((**theTE).hText);
GetPort(&savePort);
SetPort(theWindow);
InvalRect(&theWindow->portRect);
SetPort(savePort);
}
/* DoNextMessage is called in response to the menu command of the same
name. It closes the current message window, and opens the next
message in the current group. If the group has no more messages,
the next group is opened, along with the first message in that
group. This is sort of a hack.
*/
void DoNextMessage(void)
{
TwindowInfo *info;
Cell theCell;
ListHandle theList;
WindowPtr parentWind;
Str255 cellData;
short dataLen;
if (!FrontWindow()) {
SysBeep(1);
return;
}
info = (TwindowInfo *) GetWRefCon(FrontWindow());
SetPt(&theCell,0,0);
switch (info->kind) {
case cUserGroup:
theList = (ListHandle) info->data;
if (LGetSelect(true,&theCell,theList)) {
dataLen = 256;
LGetCell(cellData,&dataLen,theCell,theList);
if ((unsigned char) cellData[dataLen-1] == 0xff) {
LSetSelect(false,theCell,theList);
if (LNextCell(false,true,&theCell,theList)) {
LSetSelect(true,theCell,theList);
LAutoScroll(theList);
}
DoNextMessage();
}
else {
info = (TwindowInfo *) GetWRefCon(FrontWindow());
if (info->kind == cUserGroup)
HandleUserGroupSelect(theCell,FrontWindow());
LSetSelect(false,theCell,theList);
if (LNextCell(false,true,&theCell,theList)) {
LSetSelect(true,theCell,theList);
LAutoScroll(theList);
}
}
}
else
SysBeep(1);
break;
case cSubject:
theList = (ListHandle) info->data;
if (LGetSelect(true,&theCell,theList)) {
HandleSubjectSelect(theCell,FrontWindow());
}
else {
if (parentWind = info->parentWindow) {
DoCloseWindow(FrontWindow());
BringToFront(parentWind);
DoNextMessage();
}
else SysBeep(1);
}
break;
case cMessage:
if (parentWind = info->parentWindow) {
DoCloseWindow(FrontWindow());
BringToFront(parentWind);
info = (TwindowInfo *) GetWRefCon(FrontWindow());
theList = (ListHandle) info->data;
LGetSelect(true,&theCell,theList);
LSetSelect(false,theCell,theList);
if (LNextCell(false,true,&theCell,theList))
LSetSelect(true,theCell,theList);
DoNextMessage();
}
else
SysBeep(1);
break;
default:
SysBeep(1);
}
}
/* ReadMessage is called in response to a mouse down in a list
manager window. This routine dispatches the open command
to the routine appropriate to the type of window.
*/
void ReadMessage(WindowPtr theWindow)
{
Cell theCell;
TwindowInfo *theInfo;
if (!theWindow) {
SysBeep(1);
return;
}
theInfo = (TwindowInfo *) GetWRefCon(theWindow);
SetPt(&theCell,0,0);
while (LGetSelect(true,&theCell,(ListHandle)theInfo->data)) {
switch (theInfo->kind) {
case cGroup:
case cNewGroup:
HandleGroupSelect(theCell,theWindow);
break;
case cUserGroup:
HandleUserGroupSelect(theCell,theWindow);
break;
case cSubject:
HandleSubjectSelect(theCell,theWindow);
break;
}
theCell.v++;
}
}
/* ToggleCheck toggles the state of a checkbox in the
active dialog box.
*/
void ToggleCheck(DialogPtr theDlg,short item)
{
Handle iHndl;
short iType;
Rect iRect;
short value;
GetDItem(theDlg,item,&iType,&iHndl,&iRect);
value = (GetCtlValue((ControlHandle)iHndl) == 1) ? 0 : 1;
SetCtlValue((ControlHandle)iHndl,value);
}
/* GetCheck gets the state of a checkbox in the active
dialog box.
*/
Boolean GetCheck(DialogPtr theDlg,short item)
{
Handle iHndl;
short iType;
Rect iRect;
GetDItem(theDlg,item,&iType,&iHndl,&iRect);
return (GetCtlValue((ControlHandle)iHndl) == 1);
}
/* SetValue sets the value of an editText dialog item
to an integer value.
*/
void SetValue(DialogPtr theDlg,short item,short value)
{
Handle iHndl;
short iType;
Rect iRect;
Str255 valStr;
NumToString(value,valStr);
GetDItem(theDlg,item,&iType,&iHndl,&iRect);
SetIText(iHndl,valStr);
}
/* GetValue gets the value of an editText dialog item
as an integer value.
*/
short GetValue(DialogPtr theDlg,short item)
{
Handle iHndl;
short iType;
Rect iRect;
Str255 valStr;
long value;
GetDItem(theDlg,item,&iType,&iHndl,&iRect);
GetIText(iHndl,valStr);
StringToNum(valStr,&value);
return (short)value;
}
/* SetPrefs allows the user to set several preferences for the
NewsWatcher program. The routine uses a dialog box to
display and let the user edit these values.
*/
void SetPrefs(TPrefRec *prefs)
{
DialogPtr theDlg;
short item;
theDlg = GetNewDialog(kPrefDlg,nil,(WindowPtr)-1);
OutlineOK(theDlg);
if (prefs->openWindowsZoomed)
ToggleCheck(theDlg,3);
if (prefs->parentWindows)
ToggleCheck(theDlg,4);
if (prefs->mostRecentFirst)
ToggleCheck(theDlg,5);
SetValue(theDlg,6,prefs->maxFetch);
SetValue(theDlg,7,prefs->windowOffset.h);
SetValue(theDlg,8,prefs->windowOffset.v);
do {
ModalDialog(CmdKeyFilter,&item);
if (item >= 3 && item <= 5)
ToggleCheck(theDlg,item);
}
while (item != okButton && item != cancelButton);
if (item == okButton) {
prefs->openWindowsZoomed = GetCheck(theDlg,3);
prefs->parentWindows = GetCheck(theDlg,4);
prefs->mostRecentFirst = GetCheck(theDlg,5);
prefs->maxFetch = GetValue(theDlg,6);
prefs->windowOffset.h = GetValue(theDlg,7);
prefs->windowOffset.v = GetValue(theDlg,8);
}
DisposDialog(theDlg);
}