home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Otherware
/
Otherware_1_SB_Development.iso
/
mac
/
util
/
comm
/
news102.sit
/
NewsWatcher
/
source
/
userint.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-04-03
|
60KB
|
2,457 lines
/*----------------------------------------------------------
#
# NewsWatcher - Macintosh NNTP Client Application
#
# Written by Steven Falkenburg
# ⌐1990 Apple Computer, Inc.
#
#-----------------------------------------------------------
#
# userint.c
#
# This code segment contains the main program which controls
# the user interface for NewsWatcher.
#
#-----------------------------------------------------------*/
#pragma segment userint
#include "compat.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 <StdLib.h>
#include <CType.h>
#include <Script.h>
#include <Printing.h>
#include <Traps.h>
#include <SegLoad.h>
#endif
#include <String.h>
#include "nntp.h"
#include "commands.h"
#include "userint.h"
#include "newsprocess.h"
#include "ScrollStuff.h"
#include "netstuff.h"
#include "miscstuff.h"
#include "printstuff.h"
#include "NNTPLow.h"
#ifndef _Unimplemented
#define _Unimplemented 0xA89F
#define _WaitNextEvent 0xA860
#endif
/* globals */
Handle gLifeBoat; /* lifeboat memory -- de-allocated when memory gets low */
Boolean gSinking = true; /* flag set after lifeboat has been jettisoned */
Boolean gOutOfMemory = false; /* flag set when out of memory - and luck */
Boolean gDone = true; /* flag set true upon program termination */
Boolean gCancel = false; /* flag set when user cancels an action */
short gMoveModalProc; /* proc ID for movable modal dialog */
Boolean gInBackground = false; /* background/foreground flag */
Rect desktopExtent;
Boolean gHasColorQD;
Boolean gAuthenticated = false; /* true when user has been authenticated */
char gPass[256]; /* password */
TEHandle gFrontTE;
Boolean gHasWaitNextEvent = false; /* true if WaitNextEvent available */
TPrefRec gPrefs; /* preferences */
Cursor gIBeamCurs,gWatchCurs;
static MenuHandle gTheMenu[kNumMenus]; /* pull-down menu storage */
static Rect gDragRect,gWindLimits; /* drag/resize limit rectangles */
static ListHandle gTheList;
static Boolean gClicked = false;
static Boolean gScrapDirty,gScrapChanged;
static short gScrapCompare;
static WindowPtr gGroupWindow;
/* main program - calls other things */
void main(void)
{
MoreMasters();
MoreMasters();
MoreMasters();
MoreMasters();
MoreMasters();
InitStuff();
MainEvent();
ExitToShell(); /* Yes, you really do need to do this! */
}
/* InitStuff: initializes macintosh managers */
void InitStuff(void)
{
EventRecord ev;
SysEnvRec theEnv;
short tNumber;
gLifeBoat = NewHandle(kLifeBoatSize);
if (MemError() == noErr)
gSinking = false;
MoveHHi(gLifeBoat);
HLock(gLifeBoat);
InitGraf(&QDTHEPORT);
InitFonts();
InitWindows();
InitMenus();
TEInit();
InitDialogs(nil);
InitCursor();
InitCursorCtl(nil);
InitPrint();
FlushEvents(everyEvent,0);
EventAvail(everyEvent,&ev);
desktopExtent = (**GetGrayRgn()).rgnBBox;
gMoveModalProc = 2053;
if (SysEnvirons(1,&theEnv) == noErr && theEnv.systemVersion >= 1792)
gMoveModalProc = 5;
gHasColorQD = theEnv.hasColorQD;
if (( theEnv.machineType > envMachUnknown ) &&
( theEnv.machineType < envMacII ) ) { /* it's a 512KE, Plus, or SE */
tNumber = _WaitNextEvent & 0x03FF;
if ( tNumber > 0x01FF ) /* which means the tool traps */
tNumber = _Unimplemented; /* only go to 0x01FF */
}
gHasWaitNextEvent = NGetTrapAddress(tNumber, ToolTrap) != GetTrapAddress(_Unimplemented);
SetRect(&gDragRect,4,24,desktopExtent.right-4,desktopExtent.bottom-4);
SetRect(&gWindLimits,50,50,6000,6000);
gIBeamCurs = **(GetCursor(iBeamCursor));
gWatchCurs = **(GetCursor(watchCursor));
ReadPrefs(&gPrefs);
SetUpMenus();
StartNNTP();
gScrapCompare = InfoScrap()->scrapCount + 1;
gScrapDirty = false;
ReadDeskScrap();
CloseStatusWindow();
SetUpWindows();
CheckGroups();
CloseStatusWindow();
DoStartUp();
gDone = false;
}
/* SetUpMenus: draws the pull-down menus onto the screen */
void SetUpMenus(void)
{
short i;
for (i=0; i<kNumMenus; i++)
gTheMenu[i] = GetMenu(i+kMenuOffset);
AddResMenu(gTheMenu[kAppleMenu],'DRVR');
AddResMenu(gTheMenu[kFontMenu],'FONT');
for (i=0; i<kNumMenus; i++)
InsertMenu(gTheMenu[i],0);
DrawMenuBar();
}
/* SetUpWindows: creates an initial, empty window */
void SetUpWindows(void)
{
WindowPtr theWind;
TwindowInfo *theInfo;
GrafPtr savePort;
extern short gNumGroups;
extern TGroup *gGroupList;
Point firstOffset;
Point thePt;
SetPt(&firstOffset,kOffLeft,kOffTop);
theInfo = (TwindowInfo *) GetWRefCon(gGroupWindow = theWind = MakeNewWindow(cGroup,false,firstOffset,"\pNewsgroups"));
theInfo->numGroups = gNumGroups;
theInfo->data2 = (unsigned long) gGroupList;
theInfo->parentGroup = nil;
theInfo->parentWindow = nil;
theInfo->childList = nil;
MakeGroupList((ListHandle)theInfo->data,gNumGroups,gGroupList);
SetPt(&thePt,0,0);
LSetSelect(true,thePt,(ListHandle)theInfo->data);
GetPort(&savePort);
SetPort(theWind);
SizeWindow(theWind,gPrefs.groupWindowSize.right-gPrefs.groupWindowSize.left,
gPrefs.groupWindowSize.bottom-gPrefs.groupWindowSize.top,true);
SizeContents(gPrefs.groupWindowSize.right-gPrefs.groupWindowSize.left,
gPrefs.groupWindowSize.bottom-gPrefs.groupWindowSize.top,theWind);
MoveWindow(theWind,gPrefs.groupWindowSize.left,gPrefs.groupWindowSize.top,false);
CloseStatusWindow();
if (gPrefs.openWindowsZoomed)
DoZoom(theWind,inZoomOut);
if (!gPrefs.groupWindowVisible) {
gPrefs.groupWindowVisible = !gPrefs.groupWindowVisible;
HideShowGroups();
}
else
ShowWindow(theWind);
InvalRect(&theWind->portRect);
SetPort(savePort);
gFrontTE = nil;
}
/* NewGroupWindow creates a new user group window */
WindowPtr NewGroupWindow(char *groupName)
{
WindowPtr theWind;
TwindowInfo *theInfo;
Point firstOffset;
SetPt(&firstOffset,kOffLeft,kOffTop);
theInfo = (TwindowInfo *) GetWRefCon(theWind = MakeNewWindow(cUserGroup,true,firstOffset,groupName));
BlockMove(groupName,theInfo->diskFile,groupName[0]+1);
theInfo->changed = false;
theInfo->numGroups = 0;
theInfo->saved = false;
theInfo->data2 = nil;
theInfo->childList = nil;
theInfo->parentWindow = nil;
ShowWindow(theWind);
return theWind;
}
/* MyOffSet offsets a window so no two windows are at the same origin and the
windows do not hang off of the screen.
*/
Rect *MyOffSet(Rect *theRect)
{
WindowPtr wind;
Rect firstRect;
firstRect = *theRect;
wind = FrontWindow();
while (wind) {
if ((theRect->top == (-wind->portBits.bounds.top)) && (theRect->left == (-wind->portBits.bounds.left))) {
OffsetRect(theRect,gPrefs.windowOffset.h,gPrefs.windowOffset.v);
if (theRect->right > desktopExtent.right) {
OffsetRect(&firstRect,0,gPrefs.windowOffset.v+2);
*theRect = firstRect;
}
if (theRect->bottom > desktopExtent.bottom) {
OffsetRect(&firstRect,gPrefs.windowOffset.h+2,0);
*theRect = firstRect;
}
wind = FrontWindow();
}
else
wind = (WindowPtr) ((WindowPeek) wind)->nextWindow;
}
if (theRect->top > desktopExtent.bottom)
theRect->top = desktopExtent.bottom - 10;
if (theRect->left > desktopExtent.right)
theRect->left = desktopExtent.right - 10;
if (theRect->bottom > desktopExtent.bottom && theRect->top < (desktopExtent.bottom-64))
theRect->bottom = desktopExtent.bottom;
if (theRect->right > desktopExtent.right && theRect->left < (desktopExtent.right-64))
theRect->right = desktopExtent.right;
return(theRect);
}
/* MakeNewWindow is the low-level procedure to make a new NewsWatcher window.
Depending on what type is needed, the window will have different
features.
*/
WindowPtr MakeNewWindow(short type,Boolean hasClose,Point topLeft,char *title)
{
WindowPtr theWindow;
TwindowInfo *theInfo;
Rect bounds;
short rightMark;
if (!gPrefs.parentWindows)
SetPt(&topLeft,kOffLeft,kOffTop);
switch (type) {
case cGroup:
case cNewGroup:
case cUserGroup:
rightMark = topLeft.h+300;
break;
case cSubject:
rightMark = topLeft.h+400;
break;
case cMessage:
case cMiscMessage:
case cSendMessage:
case cPostMessage:
rightMark = topLeft.h+495;
break;
}
SetRect(&bounds,topLeft.h,topLeft.v,rightMark,kHeightWindow+topLeft.v);
theWindow = NewWindow(nil,MyOffSet(&bounds),"\pUntitled",false,zoomDocProc,(WindowPtr)-1,hasClose,nil);
theInfo = (TwindowInfo *) MyNewPtr(sizeof(TwindowInfo));
if (MyMemErr() != noErr)
return nil;
theInfo->kind = type;
switch (type) {
case cGroup:
case cNewGroup:
theInfo->filter[0] = '\0';
theInfo->filterLen = 0;
theInfo->data = (Handle)NewList(theWindow);
(**((ListHandle)theInfo->data)).listFlags = 0;
break;
case cUserGroup:
case cSubject:
theInfo->filter[0] = '\0';
theInfo->filterLen = 0;
theInfo->data = (Handle)NewList(theWindow);
LDoDraw(true,(ListHandle)theInfo->data);
break;
case cMessage:
case cMiscMessage:
case cPostMessage:
case cSendMessage:
theInfo->data = (Handle)NewText(theWindow);
MakeScrollers(theWindow);
break;
}
if (type == cGroup || type == cNewGroup || type == cUserGroup)
(**((ListHandle)theInfo->data)).lClikLoop = (ProcPtr) DoClikLoop;
SetWRefCon(theWindow,(long)theInfo);
SetWTitle(theWindow,(StringPtr) title);
AddWindowsMenu(theWindow);
return theWindow;
}
/* MakeScrollers adds scroll bars to textedit windows */
void MakeScrollers(WindowPtr theWindow)
{
Rect windowRect,hScrollRect,vScrollRect;
windowRect = theWindow->portRect;
SetRect(&hScrollRect,-1,(windowRect.bottom-windowRect.top - 15),
(windowRect.right-windowRect.left - 14),
(windowRect.bottom-windowRect.top + 1));
SetRect(&vScrollRect,(windowRect.right-windowRect.left - 15),-1,
(windowRect.right-windowRect.left+1),
(windowRect.bottom-windowRect.top-14));
SetCRefCon(NewControl(theWindow,&vScrollRect,"\p",true,0,0,0,16,0),kVRef);
SetCRefCon(NewControl(theWindow,&hScrollRect,"\p",true,0,0,kMaxColumns,16,1),kHRef);
}
/* NewList makes a new list manager list for list windows */
ListHandle NewList(WindowPtr theWindow)
{
ListHandle theList;
Point thePt;
Rect listRect,sizeRect;
GrafPtr savePort;
short fontNum;
SetPt(&thePt,0,0);
SetRect(&sizeRect,0,0,1,0);
SetRect(&listRect,theWindow->portRect.left,theWindow->portRect.top,theWindow->portRect.right-15,theWindow->portRect.bottom-15);
GetPort(&savePort);
SetPort(theWindow);
GetFNum(gPrefs.listFont,&fontNum);
TextFont(fontNum);
TextSize(gPrefs.listSize);
theList = LNew(&listRect,&sizeRect,thePt,kLDEFProc,theWindow,false,true,false,true);
(**theList).selFlags |= lNoNilHilite;
SetPort(savePort);
return theList;
}
/* DoClikLoop is the click loop routine for list windows in newswatcher. It
handles subscribing to and re-ordering of groups.
*/
pascal Boolean DoClikLoop(void)
{
Rect cellRect;
Cell lastClicked;
Point thePt;
static Point firstPt;
long theClick;
if (!gClicked) {
gClicked = true;
GetMouse(&firstPt);
return true;
}
#ifdef THINK_C
theClick = LLastClick(gTheList);
lastClicked = * ((Cell *) &theClick);
#else
#pragma unused (theClick)
lastClicked = LLastClick(gTheList);
#endif
LRect(&cellRect,lastClicked,gTheList);
GetMouse(&thePt);
if (PtInRect(thePt,&cellRect)) {
return true;
}
else {
TrackClick(firstPt);
return false;
}
}
/* TrackClick is called by the clickLoop routine when the mouse goes outside
the boundaries of the cell clicked in.
*/
void TrackClick(Point thePt)
{
RgnHandle dragRgn;
Rect cellRect;
Cell curCell,firstCell,lastCell,countCell;
Point curPt;
GrafPtr fullPort,savePort;
WindowPtr theWindow,startWindow;
TwindowInfo *info;
short part,cellHeight,diff;
char cellData[512];
short dataLen;
TGroup *curGroup,*curPrev,*firstGroup,*firstPrev,*lastGroup;
startWindow = FrontWindow();
info = (TwindowInfo *)GetWRefCon(startWindow);
dragRgn = NewRgn();
SetPt(&curCell,0,0);
SetPt(&firstCell,0,0);
SetPt(&lastCell,0,0);
OpenRgn();
LGetSelect(true,&firstCell,gTheList);
while (LGetSelect(true,&curCell,gTheList)) {
LRect(&cellRect,curCell,gTheList);
FrameRect(&cellRect);
lastCell = curCell;
curCell.v++;
}
CloseRgn(dragRgn);
GetMouse(&curPt);
SubPt(curPt,&thePt);
OffsetRgn(dragRgn,-thePt.h,-thePt.v);
LocalToGlobal(&curPt);
SetPt(&thePt,0,0);
LocalToGlobal(&thePt);
OffsetRgn(dragRgn,thePt.h,thePt.v);
fullPort = (GrafPtr) MyNewPtr(sizeof(GrafPort));
if (MyMemErr() != noErr)
return;
GetPort(&savePort);
OpenPort(fullPort);
CopyRgn(GetGrayRgn(),fullPort->visRgn);
fullPort->portRect = desktopExtent;
DragGrayRgn(dragRgn,curPt,&desktopExtent,&desktopExtent,noConstraint,nil);
DisposeRgn(dragRgn);
GetMouse(&curPt);
LocalToGlobal(&curPt);
SetPort(savePort);
ClosePort(fullPort);
MyDisposPtr((Ptr)fullPort);
part = FindWindow(curPt,&theWindow);
switch (info->kind) {
case cGroup:
case cNewGroup:
if (part == inContent) {
if (((TwindowInfo *)GetWRefCon(theWindow))->kind == cUserGroup) {
SubscribeSelected((TwindowInfo *)GetWRefCon(startWindow),gTheList,theWindow);
}
else
SysBeep(1);
}
break;
case cUserGroup:
if (theWindow == startWindow && part == inContent) {
LDoDraw(false,gTheList);
GlobalToLocal(&curPt);
cellHeight = cellRect.bottom - cellRect.top;
if (cellHeight == 0)
cellHeight=1;
curCell.v = (**gTheList).visible.top + (curPt.v / cellHeight);
curCell.h = 0;
if (curCell.v < firstCell.v || curCell.v > lastCell.v) {
curCell.v = LAddRow((lastCell.v - firstCell.v + 1),curCell.v,gTheList);
/* re-order linked list */
if (curCell.v > 0) {
curPrev = NthGroup((TGroup *)info->data2,curCell.v-1);
curGroup = curPrev->next;
}
else {
curPrev = nil;
curGroup = (TGroup *)info->data2;
}
if (firstCell.v > 0) {
firstPrev = NthGroup((TGroup *)info->data2,firstCell.v-1);
firstGroup = firstPrev->next;
}
else {
firstPrev = nil;
firstGroup = (TGroup *)info->data2;
}
lastGroup = NthGroup((TGroup *)info->data2,lastCell.v);
if (!firstGroup || !lastGroup) {
SysBeep(1);
return;
}
if (curPrev)
curPrev->next = firstGroup;
else
info->data2 = (unsigned long) firstGroup;
if (firstPrev)
firstPrev->next = lastGroup->next;
else
info->data2 = (unsigned long) (lastGroup->next);
lastGroup->next = curGroup;
if (curCell.v < firstCell.v) {
diff = (lastCell.v - firstCell.v + 1);
firstCell.v += diff;
lastCell.v += diff;
}
countCell.h = 0;
for (countCell.v = firstCell.v; countCell.v <= lastCell.v; countCell.v++,curCell.v++) {
dataLen = 512;
LGetCell(cellData,&dataLen,countCell,gTheList);
LSetCell(cellData,dataLen,curCell,gTheList);
}
LDelRow((lastCell.v - firstCell.v + 1),firstCell.v,gTheList);
}
else
SysBeep(1);
InvalRect(&startWindow->portRect);
LDoDraw(true,gTheList);
}
else
SysBeep(1);
break;
default:
SysBeep(1);
break;
}
}
/* NthGroup gets the nth group out of the user group linked list.
*/
TGroup *NthGroup(TGroup *head,short index)
{
short i;
TGroup *curGroup;
for (i=0,curGroup = head;
i<index && curGroup;
i++,curGroup = curGroup->next)
;
return curGroup;
}
/* NewText creates a new textedit record for message windows.
*/
TEHandle NewText(WindowPtr theWindow)
{
TEHandle theTE;
Rect theRect;
short fontNum;
SetPort(theWindow);
GetFNum(gPrefs.textFont,&fontNum);
TextFont(fontNum);
TextSize(gPrefs.textSize);
SetRect(&theRect,theWindow->portRect.left,theWindow->portRect.top,theWindow->portRect.right-15,
theWindow->portRect.bottom-15);
InsetRect(&theRect,kTextMargin,kTextMargin);
theTE = TENew(&theRect,&theRect);
(**theTE).crOnly = -1;
SetClikLoop(AutoScroll,theTE);
return theTE;
}
/* DoCloseWindow removes a window from the screen */
Boolean DoCloseWindow(WindowPtr theWind)
{
TwindowInfo *info;
TGroup *tmpPtr,*tmpPtr2;
TReadRec *tmpPtr3,*tmpPtr4;
TSubject *tmpSubject;
WindowPtr childWindows;
Handle tmpHandle;
Point offPt;
GrafPtr savePort;
RemoveWindowsMenu(theWind);
DoActivate(theWind,false,false);
info = (TwindowInfo *)GetWRefCon(theWind);
switch (info->kind) {
case cGroup:
/* don't dispose data if main group window -- need to save that later */
LDispose((ListHandle)info->data);
gPrefs.groupWindowSize = theWind->portRect;
SetPt(&offPt,0,0);
GetPort(&savePort);
SetPort(theWind);
LocalToGlobal(&offPt);
SetPort(savePort);
OffsetRect(&gPrefs.groupWindowSize,offPt.h,offPt.v);
DisposeWindow(theWind);
MyDisposPtr((Ptr)info);
break;
case cNewGroup:
LDispose((ListHandle)info->data);
tmpHandle = RecoverHandle((Ptr)info->data2);
if (tmpHandle && MyMemErr()==noErr) {
HUnlock(tmpHandle);
MyDisposHandle(tmpHandle);
}
DisposeWindow(theWind);
MyDisposPtr((Ptr)info);
break;
case cSubject:
if (info->parentWindow)
RemoveChild(info->parentWindow,theWind);
if (info->parentGroup)
MarkReadMsgs(info);
HLock((Handle)info->data2);
tmpSubject = (TSubject *) *((Handle)(info->data2));
HUnlock((Handle)info->data2);
MyDisposHandle((Handle)info->data2);
LDispose((ListHandle)info->data);
MyDisposPtr((Ptr)info);
/* remove references within message windows */
childWindows = (WindowPtr)(((WindowPeek)theWind)->nextWindow);
while (childWindows!=nil) {
info = (TwindowInfo *)GetWRefCon(childWindows);
if (info->kind == cMessage && info->parentWindow == theWind)
info->parentWindow = nil;
childWindows = (WindowPtr)(((WindowPeek)childWindows)->nextWindow);
}
DisposeWindow(theWind);
break;
case cUserGroup:
while (info->childList)
DoCloseWindow(info->childList->childWindow);
if (info->changed && !(CheckForSave(theWind)))
return false;
LDispose((ListHandle)info->data);
for (tmpPtr = (TGroup *)info->data2; tmpPtr!=nil;) {
tmpPtr2 = tmpPtr;
tmpPtr = tmpPtr->next;
for (tmpPtr3 = tmpPtr2->read; tmpPtr3!=nil;) {
tmpPtr4 = tmpPtr3;
tmpPtr3 = tmpPtr3->next;
MyDisposPtr((Ptr)tmpPtr4);
}
MyDisposPtr((Ptr)tmpPtr2);
}
MyDisposPtr((Ptr)info);
DisposeWindow(theWind);
break;
case cPostMessage:
case cSendMessage:
if (info->changed && !CheckForSend(theWind))
return false;
case cMessage:
case cMiscMessage:
TEDispose((TEHandle)info->data);
MyDisposPtr((Ptr)info);
DisposeWindow(theWind);
gFrontTE = nil;
break;
}
return true;
}
/* ShowAbout: shows the about box */
void ShowAbout(void)
{
DialogPtr theDlg;
short item;
theDlg = GetNewDialog(kAboutID,nil,(WindowPtr)-1L);
OutlineOK(theDlg);
do
ModalDialog(CmdKeyFilter,&item);
while (item != okButton);
DisposDialog(theDlg);
}
/* MainEvent: main event loop- dispatches to other handlers */
void MainEvent(void)
{
EventRecord ev;
RgnHandle cursorRgn;
Boolean gotEvt;
cursorRgn = NewRgn();
do {
gCancel = false;
if (IsMovableModal(FrontWindow()))
CloseStatusWindow();
if (gHasWaitNextEvent)
gotEvt = WaitNextEvent(everyEvent,&ev,kSleepTime,cursorRgn);
else {
gotEvt = GetNextEvent(everyEvent,&ev);
SystemTask();
}
FixCursor(ev.where,cursorRgn);
if (gotEvt) {
switch (ev.what) {
case mouseDown:
HandleMouseDowns(&ev);
break;
case keyDown:
case autoKey:
HandleKeyDowns(&ev);
break;
case activateEvt:
HandleActivates(&ev);
break;
case updateEvt:
HandleUpdates((WindowPtr)(ev.message));
break;
case app4Evt:
HandleSREvt(ev.message);
break;
}
FixCursor(ev.where,cursorRgn);
}
if (gFrontTE)
TEIdle(gFrontTE);
}
while (!gDone);
/* clear private user info before quitting */
Logout();
DisposeRgn(cursorRgn);
CloseNewsConnection();
WritePrefs(&gPrefs);
WriteGroups();
}
/* IsAppWindow returns true if the window belongs to the application
*/
Boolean IsAppWindow(WindowPtr window)
{
short windowKind;
if (window == nil)
return(false);
else {
windowKind = ((WindowPeek) window)->windowKind;
return ((windowKind >= userKind) || (windowKind == dialogKind));
}
}
/* IsDAWindow returns true if the window is a DA window
*/
Boolean IsDAWindow(WindowPtr window)
{
if ( window == nil )
return(false);
else /* DA windows have negative windowKinds */
return (((WindowPeek) window)->windowKind < 0);
}
/* IsMovableModal returns true if the window is a movable modal
(this will be the status window).
*/
Boolean IsMovableModal(WindowPtr window)
{
return (IsAppWindow(window) && ((TwindowInfo *)GetWRefCon(window))->kind==cMoveModal);
}
/* FixCursor sets up the type of cursor needed, depending on where the mouse
pointer is located. This routine is called in conjunction with WaitNextEvent.
*/
void FixCursor(Point mouse,RgnHandle region)
{
WindowPtr frontMost;
RgnHandle arrowRgn,iBeamRgn;
Rect iBeamRect;
Point topLeftPt,botRightPt;
frontMost = FrontWindow();
if ((!gInBackground) && (!IsDAWindow(frontMost))) {
arrowRgn = NewRgn();
iBeamRgn = NewRgn();
SetRectRgn(arrowRgn,-32768,-32768,32767,32767);
if (IsAppWindow(frontMost) && gFrontTE ) {
iBeamRect = (**gFrontTE).viewRect;
SetPort(frontMost);
SetPt(&topLeftPt,iBeamRect.left,iBeamRect.top);
SetPt(&botRightPt,iBeamRect.right,iBeamRect.bottom);
LocalToGlobal(&topLeftPt);
LocalToGlobal(&botRightPt);
iBeamRect.left = topLeftPt.h;
iBeamRect.top = topLeftPt.v;
iBeamRect.right = botRightPt.h;
iBeamRect.bottom = botRightPt.v;
RectRgn(iBeamRgn,&iBeamRect);
SetOrigin(-frontMost->portBits.bounds.left, -frontMost->portBits.bounds.top);
SectRgn(iBeamRgn, frontMost->visRgn, iBeamRgn);
SetOrigin(0,0);
}
DiffRgn(arrowRgn,iBeamRgn,arrowRgn);
if (PtInRgn(mouse,iBeamRgn)) {
SetCursor(&gIBeamCurs);
CopyRgn(iBeamRgn,region);
}
else {
SetCursor(&QDARROW);
CopyRgn(arrowRgn,region);
}
DisposeRgn(arrowRgn);
DisposeRgn(iBeamRgn);
}
}
/* HandleSREvt: Handles suspend/resume events- activates main window */
void HandleSREvt(long message)
{
SetCursor(&QDARROW);
if ((message >> 24) == kSRMess)
if ((message & 1) != 0) {
gInBackground = false;
DoActivate(FrontWindow(),true,true);
}
else {
gInBackground = true;
DoActivate(FrontWindow(),false,true);
}
}
/* HandleActivates: Handles activate events */
void HandleActivates(EventRecord *ev)
{
DoActivate((WindowPtr)ev->message,((ev->modifiers & activeFlag) != 0),((ev->modifiers & 0x0002) != 0));
}
/* DoActivate activates/deactivates windows */
void DoActivate(WindowPtr theWind,Boolean actFlag,Boolean chFlag)
{
TwindowInfo *theInfo;
GrafPtr savePort;
Rect growBoxRect;
short fontNum;
if (IsAppWindow(theWind)) {
theInfo = (TwindowInfo *)GetWRefCon(theWind);
switch (theInfo->kind) {
case cGroup:
case cNewGroup:
case cUserGroup:
case cSubject:
LActivate(actFlag,(ListHandle)theInfo->data);
if (actFlag) {
GetFNum(gPrefs.listFont,&fontNum);
SwitchFont(fontNum,gPrefs.listSize);
gTheList = (ListHandle)theInfo->data;
}
break;
case cMessage:
case cMiscMessage:
case cPostMessage:
case cSendMessage:
if (actFlag) {
GetFNum(gPrefs.textFont,&fontNum);
SwitchFont(fontNum,gPrefs.textSize);
ShowControl( ((WindowPeek) theWind)->controlList );
ShowControl( (*(((WindowPeek) theWind)->controlList))->nextControl);
TEActivate((TEHandle)theInfo->data);
gFrontTE = (TEHandle)theInfo->data;
}
else {
HideControl( ((WindowPeek) theWind)->controlList );
HideControl( (*(((WindowPeek) theWind)->controlList))->nextControl);
TEDeactivate((TEHandle)theInfo->data);
gFrontTE = nil;
}
break;
case cMoveModal:
break;
}
GetPort(&savePort);
SetPort(theWind);
SetRect(&growBoxRect,theWind->portRect.right-15,theWind->portRect.top,
theWind->portRect.right,theWind->portRect.bottom);
InvalRect(&growBoxRect);
SetPort(savePort);
if (chFlag)
if (actFlag) {
ReadDeskScrap();
DisableItem(gTheMenu[kEditMenu],kUndoItem);
DisableItem(gTheMenu[kEditMenu],kCutItem);
DisableItem(gTheMenu[kEditMenu],kCopyItem);
DisableItem(gTheMenu[kEditMenu],kPasteItem);
DisableItem(gTheMenu[kEditMenu],kClearItem);
}
else {
WriteDeskScrap();
EnableItem(gTheMenu[kEditMenu],kUndoItem);
EnableItem(gTheMenu[kEditMenu],kCutItem);
EnableItem(gTheMenu[kEditMenu],kCopyItem);
EnableItem(gTheMenu[kEditMenu],kPasteItem);
EnableItem(gTheMenu[kEditMenu],kClearItem);
}
EnableItems(theInfo->kind,actFlag);
DisableItems(theInfo->kind,actFlag);
}
}
/* EnableItems enables menu items, depending on whether or not
the type of window being activated is of a certain type
*/
void EnableItems(short kind,Boolean actFlag)
{
switch (kind) {
case cGroup:
if (!actFlag) {
EnableItem(gTheMenu[kSpecialMenu],kNextItem);
EnableItem(gTheMenu[kFileMenu],kCloseItem);
EnableItem(gTheMenu[kFileMenu],kSaveItem);
EnableItem(gTheMenu[kFileMenu],kSaveAsItem);
}
else {
EnableItem(gTheMenu[kSpecialMenu],kSubscribeItem);
EnableItem(gTheMenu[kSpecialMenu],kReadItem);
EnableItem(gTheMenu[kEditMenu],kSearchItem);
}
break;
case cNewGroup:
if (!actFlag) {
EnableItem(gTheMenu[kSpecialMenu],kNextItem);
EnableItem(gTheMenu[kFileMenu],kSaveItem);
EnableItem(gTheMenu[kFileMenu],kSaveAsItem);
}
else {
EnableItem(gTheMenu[kSpecialMenu],kSubscribeItem);
EnableItem(gTheMenu[kSpecialMenu],kReadItem);
EnableItem(gTheMenu[kEditMenu],kSearchItem);
}
break;
case cUserGroup:
if (actFlag) {
EnableItem(gTheMenu[kSpecialMenu],kNextItem);
EnableItem(gTheMenu[kFileMenu],kCloseItem);
EnableItem(gTheMenu[kFileMenu],kSaveItem);
EnableItem(gTheMenu[kFileMenu],kSaveAsItem);
EnableItem(gTheMenu[kSpecialMenu],kMarkReadItem);
EnableItem(gTheMenu[kSpecialMenu],kMarkUnreadItem);
EnableItem(gTheMenu[kNetMenu],kPutNetItem);
EnableItem(gTheMenu[kSpecialMenu],kUnsubscribeItem);
EnableItem(gTheMenu[kSpecialMenu],kReadItem);
EnableItem(gTheMenu[kEditMenu],kSearchItem);
}
break;
case cSubject:
if (actFlag) {
EnableItem(gTheMenu[kSpecialMenu],kMarkReadItem);
EnableItem(gTheMenu[kSpecialMenu],kMarkUnreadItem);
EnableItem(gTheMenu[kSpecialMenu],kReadItem);
}
else {
EnableItem(gTheMenu[kFileMenu],kSaveItem);
EnableItem(gTheMenu[kFileMenu],kSaveAsItem);
}
break;
case cMessage:
if (actFlag) {
EnableItem(gTheMenu[kEditMenu],kCopyItem);
EnableItem(gTheMenu[kEditMenu],kRotItem);
EnableItem(gTheMenu[kSpecialMenu],kFollowUpItem);
EnableItem(gTheMenu[kSpecialMenu],kRespondItem);
EnableItem(gTheMenu[kSpecialMenu],kViewRefItem);
EnableItem(gTheMenu[kFileMenu],kPrintItem);
}
else {
EnableItem(gTheMenu[kEditMenu],kSearchItem);
EnableItem(gTheMenu[kEditMenu],kNarrowItem);
}
break;
case cMiscMessage:
if (actFlag) {
EnableItem(gTheMenu[kEditMenu],kCopyItem);
EnableItem(gTheMenu[kFileMenu],kPrintItem);
EnableItem(gTheMenu[kEditMenu],kRotItem);
}
else {
EnableItem(gTheMenu[kSpecialMenu],kNextItem);
EnableItem(gTheMenu[kEditMenu],kSearchItem);
EnableItem(gTheMenu[kEditMenu],kNarrowItem);
}
break;
case cSendMessage:
case cPostMessage:
if (actFlag) {
EnableItem(gTheMenu[kEditMenu],kCopyItem);
EnableItem(gTheMenu[kEditMenu],kCutItem);
EnableItem(gTheMenu[kEditMenu],kClearItem);
EnableItem(gTheMenu[kEditMenu],kPasteItem);
EnableItem(gTheMenu[kEditMenu],kRotItem);
EnableItem(gTheMenu[kSpecialMenu],kSendItem);
}
else {
EnableItem(gTheMenu[kEditMenu],kSearchItem);
EnableItem(gTheMenu[kEditMenu],kNarrowItem);
}
break;
}
}
/* DisableItems disables items which are appropriate to the window which
is being enabled/disabled.
*/
void DisableItems(short kind,Boolean actFlag)
{
switch (kind) {
case cGroup:
if (actFlag) {
DisableItem(gTheMenu[kSpecialMenu],kNextItem);
DisableItem(gTheMenu[kFileMenu],kCloseItem);
DisableItem(gTheMenu[kFileMenu],kSaveItem);
DisableItem(gTheMenu[kFileMenu],kSaveAsItem);
}
else {
DisableItem(gTheMenu[kSpecialMenu],kSubscribeItem);
DisableItem(gTheMenu[kSpecialMenu],kReadItem);
DisableItem(gTheMenu[kEditMenu],kSearchItem);
}
break;
case cNewGroup:
if (actFlag) {
DisableItem(gTheMenu[kSpecialMenu],kNextItem);
DisableItem(gTheMenu[kFileMenu],kSaveItem);
DisableItem(gTheMenu[kFileMenu],kSaveAsItem);
}
else {
DisableItem(gTheMenu[kSpecialMenu],kSubscribeItem);
DisableItem(gTheMenu[kSpecialMenu],kReadItem);
DisableItem(gTheMenu[kEditMenu],kSearchItem);
}
break;
case cUserGroup:
if (!actFlag) {
DisableItem(gTheMenu[kSpecialMenu],kMarkReadItem);
DisableItem(gTheMenu[kSpecialMenu],kMarkUnreadItem);
DisableItem(gTheMenu[kNetMenu],kPutNetItem);
DisableItem(gTheMenu[kSpecialMenu],kUnsubscribeItem);
DisableItem(gTheMenu[kSpecialMenu],kReadItem);
DisableItem(gTheMenu[kEditMenu],kSearchItem);
}
break;
case cSubject:
if (!actFlag) {
DisableItem(gTheMenu[kSpecialMenu],kMarkReadItem);
DisableItem(gTheMenu[kSpecialMenu],kMarkUnreadItem);
DisableItem(gTheMenu[kSpecialMenu],kReadItem);
}
else {
DisableItem(gTheMenu[kFileMenu],kSaveItem);
DisableItem(gTheMenu[kFileMenu],kSaveAsItem);
}
break;
case cMessage:
if (!actFlag) {
DisableItem(gTheMenu[kEditMenu],kRotItem);
DisableItem(gTheMenu[kSpecialMenu],kFollowUpItem);
DisableItem(gTheMenu[kSpecialMenu],kRespondItem);
DisableItem(gTheMenu[kSpecialMenu],kViewRefItem);
DisableItem(gTheMenu[kFileMenu],kPrintItem);
}
else {
DisableItem(gTheMenu[kEditMenu],kSearchItem);
DisableItem(gTheMenu[kEditMenu],kNarrowItem);
}
break;
case cMiscMessage:
if (!actFlag) {
DisableItem(gTheMenu[kEditMenu],kRotItem);
DisableItem(gTheMenu[kEditMenu],kCopyItem);
DisableItem(gTheMenu[kFileMenu],kPrintItem);
}
else {
DisableItem(gTheMenu[kSpecialMenu],kNextItem);
DisableItem(gTheMenu[kEditMenu],kSearchItem);
DisableItem(gTheMenu[kEditMenu],kNarrowItem);
}
break;
case cSendMessage:
case cPostMessage:
if (!actFlag) {
DisableItem(gTheMenu[kEditMenu],kCopyItem);
DisableItem(gTheMenu[kEditMenu],kCutItem);
DisableItem(gTheMenu[kEditMenu],kClearItem);
DisableItem(gTheMenu[kEditMenu],kPasteItem);
DisableItem(gTheMenu[kEditMenu],kRotItem);
DisableItem(gTheMenu[kSpecialMenu],kSendItem);
}
else {
DisableItem(gTheMenu[kEditMenu],kSearchItem);
DisableItem(gTheMenu[kEditMenu],kNarrowItem);
}
break;
}
}
/* ReadDeskScrap reads the clipboard into the TE scrap.
*/
void ReadDeskScrap(void)
{
long scrapLength,ignore;
OSErr result;
if (gScrapCompare != InfoScrap()->scrapCount) {
if ((scrapLength = GetScrap(nil,'TEXT',&ignore))>0)
if ((result = TEFromScrap()) != noErr)
scrapLength = result;
if (scrapLength > 0)
EnableItem(gTheMenu[kEditMenu],kPasteItem);
else {
TESetScrapLen(0);
DisableItem(gTheMenu[kEditMenu],kPasteItem);
}
gScrapCompare = InfoScrap()->scrapCount;
}
}
/* WriteDeskScrap writes TE scrap to the clipboard
*/
void WriteDeskScrap(void)
{
OSErr result;
if (gScrapDirty) {
gScrapCompare = ZeroScrap();
result = TEToScrap();
gScrapDirty = false;
}
}
/* HandleUpdates: Handles update events */
void HandleUpdates(WindowPtr wind)
{
GrafPtr savePort;
GetPort(&savePort);
SetPort(wind);
BeginUpdate(wind);
if (!IsMovableModal(wind)) {
EraseRect(&wind->portRect);
DrawMainWindow(wind);
}
else
UpdateStatus();
EndUpdate(wind);
SetPort(savePort);
}
/* HandleMouseDown: Handles mouse down events */
void HandleMouseDowns(EventRecord *ev)
{
WindowPtr theWindow;
short part;
part = FindWindow(ev->where,&theWindow);
if (!(IsMovableModal(FrontWindow()) && !IsMovableModal(theWindow)))
switch (part) {
case inMenuBar:
if (!IsMovableModal(FrontWindow()))
DoCommand(MenuSelect(ev->where));
break;
case inSysWindow:
SystemClick(ev,theWindow);
break;
case inDrag:
DoDrag(theWindow,ev->where);
break;
case inGrow:
DoGrow(theWindow,ev->where);
break;
case inGoAway:
if (TrackGoAway(theWindow,ev->where)) {
DoCloseWindow(FrontWindow());
}
break;
case inZoomIn:
case inZoomOut:
if (TrackBox(theWindow,ev->where,FindWindow(ev->where,&theWindow)))
DoZoom(theWindow,FindWindow(ev->where,&theWindow));
break;
case inContent:
HandleContent(theWindow,ev);
break;
}
}
/* DoDrag: Handles drag window events */
void DoDrag(WindowPtr window,Point globMouse)
{
if (window != FrontWindow())
SelectWindow(window);
DragWindow(window,globMouse,&gDragRect);
SetPort(window);
}
/* DoGrow: Handles grow window events */
void DoGrow(WindowPtr window,Point globMouse)
{
long newSize;
Rect tmpRect;
GrafPtr tempPort;
if ((newSize = GrowWindow(window,globMouse,&gWindLimits)) != 0) {
GetPort(&tempPort);
SetPort(window);
SetRect(&tmpRect,window->portRect.right-15-kTextMargin,window->portRect.top,
window->portRect.right,window->portRect.bottom);
InvalRect(&tmpRect);
SetRect(&tmpRect,window->portRect.left,window->portRect.bottom-15-kTextMargin,
window->portRect.right-16,window->portRect.bottom);
InvalRect(&tmpRect);
SizeWindow(window,LoWord(newSize),HiWord(newSize),true);
SizeContents(LoWord(newSize),HiWord(newSize),window);
SetRect(&tmpRect,window->portRect.right-15,window->portRect.top,
window->portRect.right,window->portRect.bottom);
InvalRect(&tmpRect);
SetRect(&tmpRect,window->portRect.left,window->portRect.bottom-15,
window->portRect.right-15,window->portRect.bottom);
InvalRect(&tmpRect);
SetPort(tempPort);
}
}
/* Re-sizes the window's contents, handling all types of windows
*/
void SizeContents(short width,short height,WindowPtr wind)
{
TwindowInfo *theInfo;
TEHandle theTE;
Rect tmpRect;
short lineHeight;
Point tmpPt;
theInfo = (TwindowInfo *) GetWRefCon(wind);
switch (theInfo->kind) {
case cGroup:
case cNewGroup:
case cUserGroup:
case cSubject:
LSize(width-15,height-15,(ListHandle)theInfo->data);
SetPt(&tmpPt,0,0);
LRect(&tmpRect,tmpPt,(ListHandle)theInfo->data);
SetPt(&tmpPt,width-15,tmpRect.bottom-tmpRect.top);
LCellSize(tmpPt,(ListHandle)theInfo->data);
InvalRect(&wind->portRect);
break;
case cMessage:
case cMiscMessage:
case cPostMessage:
case cSendMessage:
theTE = (TEHandle)(theInfo->data);
if ((**theTE).viewRect.bottom < (wind->portRect.bottom - 15)) {
tmpRect = (**theTE).viewRect;
tmpRect.top = tmpRect.bottom;
tmpRect.bottom = wind->portRect.bottom - 15;
InvalRect(&tmpRect);
}
lineHeight = (**theTE).lineHeight;
tmpRect = wind->portRect;
SetRect(&tmpRect,wind->portRect.left,wind->portRect.top,
wind->portRect.right-15,wind->portRect.bottom-15);
tmpRect.top = (((tmpRect.bottom - tmpRect.top) /
lineHeight) * lineHeight) + tmpRect.top - lineHeight - kTextMargin;
InvalRect(&tmpRect);
RedoControls(wind);
FixText(wind);
InvalRect(&wind->portRect);
break;
}
}
/* ToggleZoom toggles the window zoomed state of the active window.
The window will only be grown as big as it needs to be to contain
all of the data. Also, whichever monitor contains the majority of
the window will be the destination for the window.
*/
void ToggleZoom(WindowPtr theWindow)
{
WStateData **stateHndl;
Rect windRect;
Point locToGlobPt;
GrafPtr savePort;
if (!IsAppWindow(theWindow))
return;
/* get frontwindow rect in global coords */
GetPort(&savePort);
SetPort(theWindow);
windRect = theWindow->portRect;
SetPt(&locToGlobPt,windRect.left,windRect.top);
LocalToGlobal(&locToGlobPt);
windRect.left = locToGlobPt.h;
windRect.top = locToGlobPt.v;
SetPt(&locToGlobPt,windRect.right,windRect.bottom);
LocalToGlobal(&locToGlobPt);
windRect.right = locToGlobPt.h;
windRect.bottom = locToGlobPt.v;
SetPort(savePort);
stateHndl = (WStateData **) ((WindowPeek)theWindow)->dataHandle;
if (EqualRect(&(**stateHndl).userState,&windRect))
DoZoom(theWindow,inZoomOut);
else
DoZoom(theWindow,inZoomIn);
}
/* DoZoom: Handles zoom-window events -- from Apple Technical Note #??? */
void DoZoom(WindowPtr theWindow,short zoomDir)
{
Rect windRect, theSect, zoomRect, tmpRect;
GDHandle nthDevice,dominantGDevice;
long sectArea,greatestArea;
short bias;
Boolean sectFlag;
GrafPtr savePort;
Point locToGlobPt;
WStateData **stateHndl;
Rect stdRect,userRect;
GetPort(&savePort);
SetPort(theWindow);
EraseRect(&theWindow->portRect);
stateHndl = (WStateData **) ((WindowPeek)theWindow)->dataHandle;
if (zoomDir == inZoomOut && gHasColorQD) {
windRect = theWindow->portRect;
SetPt(&locToGlobPt,windRect.left,windRect.top);
LocalToGlobal(&locToGlobPt);
windRect.left = locToGlobPt.h;
windRect.top = locToGlobPt.v;
SetPt(&locToGlobPt,windRect.right,windRect.bottom);
LocalToGlobal(&locToGlobPt);
windRect.right = locToGlobPt.h;
windRect.bottom = locToGlobPt.v;
/* calc height of title bar */
bias = windRect.top - 23;
windRect.top -= bias;
nthDevice = GetDeviceList();
dominantGDevice = 0;
greatestArea = 0;
while (nthDevice != nil)
if (TestDeviceAttribute(nthDevice,screenDevice) &&
TestDeviceAttribute(nthDevice,screenActive)) {
sectFlag = SectRect(&windRect,&((**nthDevice).gdRect),&theSect);
sectArea = (long)(theSect.right - theSect.left) * (long)(theSect.bottom - theSect.top);
if (sectArea > greatestArea) {
greatestArea = sectArea;
dominantGDevice = nthDevice;
}
nthDevice = GetNextDevice(nthDevice);
}
if (dominantGDevice == GetMainDevice())
bias += GetMBarHeight();
if (dominantGDevice)
tmpRect = (**dominantGDevice).gdRect;
else
tmpRect = desktopExtent;
SetRect(&zoomRect,tmpRect.left+3,tmpRect.top+bias+3,tmpRect.right-3,tmpRect.bottom-3);
(**stateHndl).stdState = zoomRect;
}
stdRect = (**stateHndl).stdState;
userRect = (**stateHndl).userState;
CalcZoom(theWindow,&stdRect,&userRect);
(**stateHndl).stdState = stdRect;
(**stateHndl).userState = userRect;
ZoomWindow(theWindow,zoomDir,true);
InvalRect(&theWindow->portRect);
SizeContents(theWindow->portRect.right-theWindow->portRect.left,theWindow->portRect.bottom-theWindow->portRect.top,theWindow);
SetRect(&tmpRect,theWindow->portRect.left,theWindow->portRect.bottom-16,
theWindow->portRect.right,theWindow->portRect.bottom);
InvalRect(&tmpRect);
SetPort(savePort);
}
/* CalcZoom Calculates the maximum needed size for the window.
*/
void CalcZoom(WindowPtr window,Rect *zoomRect,Rect *userRect)
{
TwindowInfo *info;
ListHandle theList;
TEHandle theTE;
long width,height,tmpWidth;
Rect finalRect;
short i;
short offset,length;
Cell theCell;
info = (TwindowInfo *)GetWRefCon(window);
finalRect = *zoomRect;
switch (info->kind) {
case cGroup:
case cNewGroup:
case cUserGroup:
case cSubject:
theList = (ListHandle) ((TwindowInfo *)GetWRefCon(window))->data;
height = 20 + ((**theList).dataBounds.bottom - (**theList).dataBounds.top) * (**theList).cellSize.v;
SetPt(&theCell,0,0);
HLock((Handle)(**theList).cells);
(**theList).port->txFace = bold;
for (width=0,theCell.v=(**theList).dataBounds.top; theCell.v< (**theList).dataBounds.bottom; theCell.v++) {
LFind(&offset,&length,theCell,theList);
tmpWidth = TextWidth((Ptr)*((**theList).cells),offset,length);
if (tmpWidth > width)
width = tmpWidth;
}
width += 25;
HUnlock((Handle)(**theList).cells);
break;
case cMessage:
case cMiscMessage:
case cSendMessage:
case cPostMessage:
theTE = (TEHandle) ((TwindowInfo *)GetWRefCon(window))->data;
height = 36 + (**theTE).nLines * (**theTE).lineHeight;
HLock((Handle)(**theTE).hText);
for (width=0,i=0; i<((**theTE).nLines-1); i++) {
tmpWidth = TextWidth((Ptr)(*(**theTE).hText)+(**theTE).lineStarts[i],0,(**theTE).lineStarts[i+1] - (**theTE).lineStarts[i]);
if ( tmpWidth > width )
width = tmpWidth;
}
HUnlock((Handle)(**theTE).hText);
width += (2*kTextMargin) + 18;
break;
}
if (height < 64)
height = 64;
if (width < 64)
width = 64;
if ( (finalRect.right-finalRect.left) > width ) {
finalRect.right = userRect->left+width;
finalRect.left = userRect->left;
}
if ( (finalRect.bottom-finalRect.top) > height ) {
finalRect.bottom = userRect->top+height;
finalRect.top = userRect->top;
}
if (finalRect.bottom > zoomRect->bottom) {
OffsetRect(&finalRect,0,-(finalRect.bottom-zoomRect->bottom));
}
if (finalRect.right > zoomRect->right) {
OffsetRect(&finalRect,-(finalRect.right-zoomRect->right),0);
}
if (finalRect.top < zoomRect->top) {
OffsetRect(&finalRect,0,zoomRect->top - finalRect.top);
if (finalRect.bottom > zoomRect->bottom)
finalRect.bottom = zoomRect->bottom;
}
if (finalRect.left < zoomRect->left) {
OffsetRect(&finalRect,zoomRect->left - finalRect.left,0);
if (finalRect.right > zoomRect->right)
finalRect.right = zoomRect->right;
}
*zoomRect = finalRect;
}
/* HandleKeyDowns: handles keypress events */
void HandleKeyDowns(EventRecord *ev)
{
short theChar;
WindowPtr theWindow;
theWindow = FrontWindow();
theChar = ev->message & charCodeMask;
if ((ev->modifiers & cmdKey) != 0)
DoCommand(MenuKey(theChar));
else if (IsAppWindow(theWindow))
switch (((TwindowInfo *)GetWRefCon(theWindow))->kind) {
case cGroup:
case cNewGroup:
case cUserGroup:
case cSubject:
HandleListKey(theChar);
break;
case cMessage:
case cMiscMessage:
if (theChar >= 0x1C && theChar <= 0x1F) {
TEKey(theChar,(TEHandle)((TwindowInfo *)GetWRefCon(theWindow))->data);
CheckInsertion(theWindow);
}
break;
case cPostMessage:
case cSendMessage:
TEKey(theChar,(TEHandle)((TwindowInfo *)GetWRefCon(theWindow))->data);
AdjustScrollBar(theWindow);
CheckInsertion(theWindow);
break;
default:
SysBeep(1);
break;
}
}
/* HandleListKey handles keydown events in list manager windows. The
current selection is changed to one matching the typed substring
*/
void HandleListKey(char theChar)
{
ListHandle theList;
static long lastDown = 0;
static short numChars = 0;
static char searchStr[256];
Cell theCell,tmpCell;
theList = (ListHandle) ((TwindowInfo *)GetWRefCon(FrontWindow()))->data;
switch (theChar) {
case CR:
ReadMessage(FrontWindow());
return;
case 0x1C:
case 0x1E:
SetPt(&tmpCell,0,0);
while (LGetSelect(true,&tmpCell,theList)) {
LSetSelect(false,tmpCell,theList);
tmpCell.v++;
}
if (tmpCell.v > 1)
tmpCell.v -= 2;
else
tmpCell.v--;
LSetSelect(true,tmpCell,theList);
LAutoScroll(theList);
break;
case 0x1D:
case 0x1F:
SetPt(&tmpCell,0,0);
while (LGetSelect(true,&tmpCell,theList)) {
LSetSelect(false,tmpCell,theList);
tmpCell.v++;
}
if (tmpCell.v >= (**theList).dataBounds.bottom)
tmpCell.v--;
LSetSelect(true,tmpCell,theList);
LAutoScroll(theList);
break;
default:
SetPt(&theCell,0,0);
if ((TickCount()-lastDown) > (2*GetDblTime()))
numChars = 0;
lastDown = TickCount();
searchStr[numChars++] = theChar;
if (LSearch(searchStr,numChars,CompareStart,&theCell,theList)) {
SetPt(&tmpCell,0,0);
while (LGetSelect(true,&tmpCell,theList)) {
LSetSelect(false,tmpCell,theList);
tmpCell.v++;
}
LSetSelect(true,theCell,theList);
LAutoScroll(theList);
}
break;
}
}
/* DoCommand: Process pull-down menu requests */
void DoCommand(long mResult)
{
int selItem,selMenu,temp;
Str255 name;
GrafPtr tempPort;
WindowPtr theWindow;
theWindow = FrontWindow();
if (IsMovableModal(theWindow))
return;
selItem = LoWord(mResult);
selMenu = HiWord(mResult);
switch (selMenu) {
case kAppleMenu+kMenuOffset:
if (selItem>2) {
GetPort(&tempPort);
SetCursor(&QDARROW);
GetItem(gTheMenu[kAppleMenu],selItem,&name);
temp = OpenDeskAcc(&name);
SetPort(tempPort);
}
else ShowAbout();
break;
case kFileMenu+kMenuOffset:
switch (selItem) {
case kNewItem:
NewGroupWindow("\pUntitled Groups");
break;
case kCloseItem:
if (theWindow)
DoCloseWindow(theWindow);
break;
case kOpenItem:
DoOpenFile();
break;
case kSaveItem:
if (theWindow)
switch (((TwindowInfo *)GetWRefCon(theWindow))->kind) {
case cMessage:
case cMiscMessage:
DoSaveMessage((TwindowInfo *)GetWRefCon(theWindow));
break;
case cPostMessage:
case cSendMessage:
DoSaveWindow(theWindow);
break;
default:
DoSaveFile(theWindow);
break;
}
break;
case kSaveAsItem:
if (theWindow)
switch (((TwindowInfo *)GetWRefCon(theWindow))->kind) {
case cMessage:
case cMiscMessage:
DoSaveMessage((TwindowInfo *)GetWRefCon(theWindow));
break;
case cPostMessage:
case cSendMessage:
DoSaveWindow(theWindow);
break;
default:
DoSaveAsFile(theWindow);
break;
}
break;
case kPageSetupItem:
DoPageSetup();
break;
case kPrintItem:
DoPrint();
break;
case kPrefItem:
SetPrefs(&gPrefs);
break;
case kQuitItem:
if (DoQuitStuff())
gDone = true;
break;
}
break;
case kEditMenu+kMenuOffset:
if (!(SystemEdit(selItem-1)))
switch (selItem) {
case kUndoItem:
break;
case kCutItem:
if (IsAppWindow(theWindow))
switch (((TwindowInfo *)GetWRefCon(theWindow))->kind) {
case cPostMessage:
case cSendMessage:
TECut(gFrontTE);
AdjustScrollBar(theWindow);
CheckInsertion(theWindow);
gScrapDirty = true;
break;
}
break;
case kClearItem:
if (IsAppWindow(theWindow))
switch (((TwindowInfo *)GetWRefCon(theWindow))->kind) {
case cPostMessage:
case cSendMessage:
TEDelete(gFrontTE);
AdjustScrollBar(theWindow);
CheckInsertion(theWindow);
break;
}
break;
case kPasteItem:
if (IsAppWindow(theWindow))
switch (((TwindowInfo *)GetWRefCon(theWindow))->kind) {
case cPostMessage:
case cSendMessage:
TEPaste(gFrontTE);
AdjustScrollBar(theWindow);
CheckInsertion(theWindow);
break;
}
break;
case kCopyItem:
if (gFrontTE) {
TECopy(gFrontTE);
gScrapDirty = true;
EnableItem(gTheMenu[kEditMenu],kPasteItem);
}
break;
case kRotItem:
DoRot();
break;
case kNarrowItem:
DoNarrowSelection();
break;
case kSearchItem:
DoSearch();
break;
case kSelectAllItem:
DoSelectAll();
break;
}
break;
case kSpecialMenu+kMenuOffset:
switch (selItem) {
case kMarkReadItem:
case kMarkUnreadItem:
if (theWindow)
switch (((TwindowInfo *)GetWRefCon(theWindow))->kind) {
case cSubject:
DoMarkArticleRead((selItem == kMarkReadItem));
break;
case cUserGroup:
DoMarkGroupRead((selItem == kMarkReadItem));
break;
}
break;
case kReadItem:
ReadMessage(theWindow);
break;
case kNextItem:
DoNextMessage();
break;
case kViewRefItem:
OpenReferences();
break;
case kSendItem:
if (theWindow && DoSendMsg((TwindowInfo *)GetWRefCon(theWindow)))
DoCloseWindow(theWindow);
break;
case kFollowUpItem:
MakeFollowUp();
break;
case kRespondItem:
MakeRespond();
break;
case kPostItem:
MakePost();
break;
case kSubscribeItem:
HandleSubscribe();
break;
case kUnsubscribeItem:
HandleUnsubscribe();
break;
}
break;
case kNetMenu+kMenuOffset:
switch (selItem) {
case kGetNetItem:
GetFromNet();
break;
case kPutNetItem:
SendToNet();
break;
case kSetNNTPItem:
ChangeServerAddress(true);
break;
case kSetSMTPItem:
ChangeServerAddress(false);
break;
}
break;
case kFontMenu+kMenuOffset:
ChangeFont(selItem);
break;
case kSizeMenu+kMenuOffset:
ChangeSize(selItem);
break;
case kWindowsMenu+kMenuOffset:
switch (selItem) {
case kHideShowWind:
HideShowGroups();
break;
case kSendToBack:
if (IsAppWindow(theWindow))
SendBehind(theWindow,nil);
break;
case kZoomWindow:
if (theWindow)
ToggleZoom(theWindow);
break;
default:
SelectWindowsMenu(selItem);
break;
}
}
HiliteMenu(0);
}
/* HandleContent: Handles mouse-downs within content of window */
void HandleContent(WindowPtr theWindow,EventRecord *ev)
{
GrafPtr savePort;
TwindowInfo *theInfo;
short part;
Boolean extendClick;
TEHandle theTE;
ControlHandle control;
Cell theCell;
if (IsMovableModal(FrontWindow()))
return;
if (theWindow != FrontWindow()) {
SelectWindow(theWindow);
return;
}
GetPort(&savePort);
SetPort(theWindow);
GlobalToLocal(&ev->where);
theInfo = (TwindowInfo *) GetWRefCon(theWindow);
switch (theInfo->kind) {
case cGroup:
case cNewGroup:
if (LClick(ev->where,ev->modifiers,(ListHandle)theInfo->data)) {
SetPt(&theCell,0,0);
while (LGetSelect(true,&theCell,(ListHandle)theInfo->data)) {
HandleGroupSelect(theCell,theWindow);
theCell.v++;
}
}
break;
case cUserGroup:
if (LClick(ev->where,ev->modifiers,(ListHandle)theInfo->data)) {
SetPt(&theCell,0,0);
while (LGetSelect(true,&theCell,(ListHandle)theInfo->data)) {
HandleUserGroupSelect(theCell,theWindow);
theCell.v++;
}
}
break;
case cSubject:
if (LClick(ev->where,ev->modifiers,(ListHandle)theInfo->data)) {
SetPt(&theCell,0,0);
while (LGetSelect(true,&theCell,(ListHandle)theInfo->data)) {
HandleSubjectSelect(theCell,theWindow);
theCell.v++;
}
}
break;
case cMessage:
case cMiscMessage:
case cPostMessage:
case cSendMessage:
theTE = (TEHandle)((TwindowInfo *)GetWRefCon(theWindow))->data;
if ((part = FindControl(ev->where,theWindow,&control)) != 0)
DoScrollers(control,part,ev->where);
else {
if (PtInRect(ev->where,&((**theTE).viewRect))) {
extendClick = ((ev->modifiers & shiftKey) != 0);
TEClick(ev->where,extendClick,theTE);
}
}
break;
}
gClicked = false;
SetPort(savePort);
}
/* DrawMainWindow is called to re-draw window contents in response
to update events.
*/
void DrawMainWindow(WindowPtr wind)
{
TwindowInfo *theInfo;
DrawGrowIcon(wind);
theInfo = (TwindowInfo *) GetWRefCon(wind);
switch (theInfo->kind) {
case cGroup:
case cNewGroup:
case cUserGroup:
case cSubject:
LUpdate(wind->visRgn,(ListHandle)theInfo->data);
break;
case cMessage:
case cMiscMessage:
case cPostMessage:
case cSendMessage:
UpdtControl(wind,wind->visRgn);
TEUpdate(&wind->portRect,(TEHandle)theInfo->data);
break;
}
}
/* RedoControls is called when a window is re-sized and the controls
for the window need to be moved/resized
*/
void RedoControls(WindowPtr window)
{
HideControl(vScrollCont(window));
HideControl(vScrollCont(window));
MoveControl(vScrollCont(window),window->portRect.right-15,window->portRect.top-1);
SizeControl(vScrollCont(window),16,window->portRect.bottom - window->portRect.top - 13);
MoveControl(hScrollCont(window),window->portRect.left-1,window->portRect.bottom-15);
SizeControl(hScrollCont(window),window->portRect.right - window->portRect.left - 13,16);
ShowControl(vScrollCont(window));
ShowControl(hScrollCont(window));
}
/* FixText is called when a textedit window has been re-sized and the text
needs to be re-flowed.
*/
void FixText(WindowPtr window)
{
short controlLine,charPos;
Rect viewRect;
short lineHeight;
TEHandle theTE;
theTE = (TEHandle)(((TwindowInfo *)GetWRefCon(window))->data);
lineHeight = (**theTE).lineHeight;
viewRect = window->portRect;
viewRect.top = viewRect.top;
viewRect.right = viewRect.right - 15;
viewRect.bottom = viewRect.bottom - 15;
InsetRect(&viewRect,kTextMargin,kTextMargin);
viewRect.bottom = (((viewRect.bottom - viewRect.top) /
lineHeight) * lineHeight) + viewRect.top;
(**theTE).destRect = viewRect;
(**theTE).viewRect = viewRect;
TECalText(theTE);
controlLine = GetCtlValue(vScrollCont(window));
charPos = (**theTE).lineStarts[controlLine];
AdjustScrollBar(window);
ScrollChar(window,charPos,false);
}
/* DoStartUp: processes start-up events (printing/opening) */
Boolean DoStartUp(void)
{
short theMessage,nDocs,thisDoc;
AppFile docInfo;
Boolean result;
CountAppFiles(&theMessage,&nDocs);
if (nDocs != 0) {
for (thisDoc=1; thisDoc<=nDocs; thisDoc++) {
GetAppFiles(thisDoc,&docInfo);
if (docInfo.fType=='NEWS') {
if (result = LoOpenFile(docInfo.fName,docInfo.vRefNum))
return true;
ClrAppFiles(thisDoc);
if (theMessage == appPrint) {
gDone = true;
return false;
}
}
else {
SysBeep(1);
return false;
}
}
return true;
}
else return false;
}
/* DoQuitStuff handles events prior to the quitting of the application.
All windows must be closed, and the scrap must be written.
*/
Boolean DoQuitStuff(void)
{
WindowPtr curWindow,tmpWindow;
Boolean stillQuit = true;
curWindow = FrontWindow();
while (curWindow!=nil && stillQuit) {
while (curWindow && !(IsAppWindow(curWindow)))
curWindow = (WindowPtr) ((WindowPeek)curWindow)->nextWindow;
if (curWindow) {
tmpWindow = curWindow;
curWindow = (WindowPtr) ((WindowPeek)curWindow)->nextWindow;
stillQuit &= DoCloseWindow(tmpWindow);
}
}
WriteDeskScrap();
return stillQuit;
}
/* HideShowGroups hides/shows the main groups window.
*/
void HideShowGroups(void)
{
extern TPrefRec gPrefs;
if (gPrefs.groupWindowVisible) {
HideWindow(gGroupWindow);
RemoveWindowsMenu(gGroupWindow);
SetItem(gTheMenu[kWindowsMenu],kHideShowWind,kShowText);
}
else {
ShowWindow(gGroupWindow);
AddWindowsMenu(gGroupWindow);
SetItem(gTheMenu[kWindowsMenu],kHideShowWind,kHideText);
}
gPrefs.groupWindowVisible = !gPrefs.groupWindowVisible;
}
/* ChangeFont is executed when the user selects a different font from the menu */
void ChangeFont(short item)
{
short i,fontNum;
TwindowInfo *info;
Str255 fontName;
GrafPtr savePort;
short theSize;
WindowPtr theWindow;
theWindow = FrontWindow();
for (i=1; i<=CountMItems(gTheMenu[kFontMenu]); i++)
CheckItem(gTheMenu[kFontMenu],i,false);
CheckItem(gTheMenu[kFontMenu],item,true);
GetItem(gTheMenu[kFontMenu],item,fontName);
GetFNum(fontName,&fontNum);
if (theWindow)
info = (TwindowInfo *)GetWRefCon(theWindow);
else
return;
if (info->kind <= cSubject) {
theSize = gPrefs.listSize;
BlockMove(fontName,gPrefs.listFont,fontName[0]+1);
}
else {
BlockMove(fontName,gPrefs.textFont,fontName[0]+1);
(**(TEHandle)info->data).txFont = fontNum;
theSize = gPrefs.textSize;
}
GetPort(&savePort);
SetPort(theWindow);
TextFont(fontNum);
UpdateFontStuff(theWindow);
InvalRect(&theWindow->portRect);
SetPort(savePort);
UpdateSizeMenu(fontNum,theSize);
}
/* SwitchFont is executed on activate events to update font menu */
void SwitchFont(short fontNumber,short size)
{
short i,numItems;
Str255 fontName,itemName;
numItems = CountMItems(gTheMenu[kFontMenu]);
for (i=1; i<=numItems; i++)
CheckItem(gTheMenu[kFontMenu],i,false);
GetFontName(fontNumber,fontName);
for (i=1; i<=numItems; i++) {
GetItem(gTheMenu[kFontMenu],i,itemName);
if (EqualString(itemName,fontName,true,true))
CheckItem(gTheMenu[kFontMenu],i,true);
}
UpdateSizeMenu(fontNumber,size);
}
/* UpdateSizeMenu is executed on activate events to update size menu */
void UpdateSizeMenu(short fontNumber,short theSize)
{
Str255 sizeStr;
long size;
short i,j;
for (i=1; i<=CountMItems(gTheMenu[kSizeMenu]); i++) {
GetItem(gTheMenu[kSizeMenu],i,sizeStr);
for (j=1; j<=sizeStr[0]; j++)
if (sizeStr[j] == ' ')
sizeStr[0] = j-1;
StringToNum(sizeStr,&size);
CheckItem(gTheMenu[kSizeMenu],i,false);
if (RealFont(fontNumber,(short)size))
SetItemStyle(gTheMenu[kSizeMenu],i,outline);
else
SetItemStyle(gTheMenu[kSizeMenu],i,0);
if (size == theSize)
CheckItem(gTheMenu[kSizeMenu],i,true);
}
}
/* ChangeSize is called when the user changes the font size */
void ChangeSize(short item)
{
Str255 sizeStr;
long size;
short i;
GrafPtr savePort;
TwindowInfo *info;
short theFont;
WindowPtr theWindow;
theWindow = FrontWindow();
GetItem(gTheMenu[kSizeMenu],item,sizeStr);
for (i=1; i<=sizeStr[0]; i++)
if (sizeStr[i] == ' ')
sizeStr[0] = i-1;
StringToNum(sizeStr,&size);
if (theWindow)
info = (TwindowInfo *)GetWRefCon(theWindow);
else
return;
if (info->kind <= cSubject) {
GetFNum(gPrefs.listFont,&theFont);
gPrefs.listSize = size;
}
else {
GetFNum(gPrefs.textFont,&theFont);
gPrefs.textSize = size;
}
GetPort(&savePort);
SetPort(theWindow);
TextSize(size);
UpdateSizeMenu(theFont,size);
UpdateFontStuff(theWindow);
InvalRect(&theWindow->portRect);
SetPort(savePort);
}
/* UpdateFontStuff updates the font information for a window */
void UpdateFontStuff(WindowPtr window)
{
FontInfo fontInfo;
TwindowInfo *info;
ListHandle theList;
TEHandle theTE;
Point newSize;
if (!window)
return;
GetFontInfo(&fontInfo);
info = (TwindowInfo *)GetWRefCon(window);
switch (info->kind) {
case cGroup:
case cNewGroup:
case cUserGroup:
case cSubject:
theList = (ListHandle)info->data;
newSize.h = (**theList).cellSize.h;
newSize.v = fontInfo.ascent+fontInfo.descent+fontInfo.leading;
LCellSize(newSize,theList);
break;
case cMessage:
case cMiscMessage:
case cSendMessage:
case cPostMessage:
theTE = (TEHandle)info->data;
(**theTE).txFont = window->txFont;
(**theTE).txFace = window->txFace;
(**theTE).txMode = window->txMode;
(**theTE).txSize = window->txSize;
(**theTE).lineHeight = fontInfo.ascent+fontInfo.descent+fontInfo.leading;
(**theTE).fontAscent = fontInfo.ascent;
break;
default:
SysBeep(1);
}
}
/* RemoveWindowsMenu removes a window's title from the windows menu when the
window is closed, etc...
*/
void RemoveWindowsMenu(WindowPtr wind)
{
short item;
Str255 name,itemString;
if (!wind)
return;
GetWTitle(wind,name);
for (item=kFirstWindOffset; item<=CountMItems(gTheMenu[kWindowsMenu]); item++) {
GetItem(gTheMenu[kWindowsMenu],item,itemString);
if (EqualString(name,itemString,true,true)) {
DelMenuItem(gTheMenu[kWindowsMenu],item);
return;
}
}
}
/* AddWindowsMenu adds a window's title to the windows menu.
*/
void AddWindowsMenu(WindowPtr wind)
{
Str255 name;
if (!wind)
return;
GetWTitle(wind,name);
InsMenuItem(gTheMenu[kWindowsMenu],"\pnew item",kFirstWindOffset-1);
SetItem(gTheMenu[kWindowsMenu],kFirstWindOffset,name);
}
/* SelectWindowsMenu is called when a user selects an item from the windows
menu. The selected window is brought to the front.
*/
void SelectWindowsMenu(short item)
{
Str255 name,itemString;
WindowPtr window;
GetItem(gTheMenu[kWindowsMenu],item,itemString);
TitleFilter(itemString);
for (window = FrontWindow(); window != nil; window = (WindowPtr) ((WindowPeek)window)->nextWindow) {
GetWTitle(window,name);
if (EqualString(name,itemString,true,true)) {
SelectWindow(window);
return;
}
}
}