I have a question about detecting mouse-over event management. There's no MacTrap function destined for it so you have to poll the mouse location with GetMouse (gets you local coordinates) and compare it to something related to the GrafPort you're using (window, whole screen, etc.). My question is, how can I not make it so dang awkward! The only way I found forces me to do a lot of prep and I wonder if I'm just missing a useful helper function or two here.
Problem #1:
Can't seem to shake off a few lines of the bottom right window whenever you switch ink color, despite calling a FillRect to get rid of the old stuff
Problem #2:
You have to slow down your drawing, otherwise you only get a bunch of separated squares. I wish it would mimick mac paint more and produce continuous lines/curves.
Problem #3:
In order to detect a mouse-over a particular window, I have to:
1) GetMouse the mouse coordinates in local coordinates
2) Transform them into global coordinates
3) SetPort into an individual window where I want to detect a mouse-over situation
4) transform the mouse coordinates into that local coordinates
5) Use PtInRegion with the local->global->second local mouse Point with the (WindowPtr->visRgn) region
No matter what I tried (portRect, GrafPort.portBits.bounds or others, all of these retangles seem to work in local coordinates, forcing me to wrangle my info to the specific local coordinates I wanted to check. I'd love to have a way to de-convert a local rect to global rect.
I didn't want to add to the week 1 thread of the THINK C programming club here https://tinkerdifferent.com/threads/study-group-1-drawing-on-the-macintosh.1811/
since I tried my own thing with multiple windows, attempting and mostly succeeding at:
-Load and display 4 small windows from a resource file
-Deal with mouse-over events (this has to be done manually by comparing an old mouse position that's polled frequently and seeing if the mouse's Point data is inside a region with the PtInRegion function
-Allow to paint with the mouse in one window
-Do the FlyingLines demo in one window
-Allow pen color changes by simply mousing over the remaining two other windows
Here it is in action:
See attached .SIT file for Symantec THINK C v6 project, source code and executable.
Here's the full code source
Problem #1:
Can't seem to shake off a few lines of the bottom right window whenever you switch ink color, despite calling a FillRect to get rid of the old stuff
Problem #2:
You have to slow down your drawing, otherwise you only get a bunch of separated squares. I wish it would mimick mac paint more and produce continuous lines/curves.
Problem #3:
In order to detect a mouse-over a particular window, I have to:
1) GetMouse the mouse coordinates in local coordinates
2) Transform them into global coordinates
3) SetPort into an individual window where I want to detect a mouse-over situation
4) transform the mouse coordinates into that local coordinates
5) Use PtInRegion with the local->global->second local mouse Point with the (WindowPtr->visRgn) region
No matter what I tried (portRect, GrafPort.portBits.bounds or others, all of these retangles seem to work in local coordinates, forcing me to wrangle my info to the specific local coordinates I wanted to check. I'd love to have a way to de-convert a local rect to global rect.
I didn't want to add to the week 1 thread of the THINK C programming club here https://tinkerdifferent.com/threads/study-group-1-drawing-on-the-macintosh.1811/
since I tried my own thing with multiple windows, attempting and mostly succeeding at:
-Load and display 4 small windows from a resource file
-Deal with mouse-over events (this has to be done manually by comparing an old mouse position that's polled frequently and seeing if the mouse's Point data is inside a region with the PtInRegion function
-Allow to paint with the mouse in one window
-Do the FlyingLines demo in one window
-Allow pen color changes by simply mousing over the remaining two other windows
Here it is in action:
See attached .SIT file for Symantec THINK C v6 project, source code and executable.
Here's the full code source
C:
#define kBaseResID 128
#define kTopLeftWin kBaseResID
#define kTopRighttWin kBaseResID+1
#define kBottomLeftWin kBaseResID+2
#define kBottomRighttWin kBaseResID+3
#define kNumLines 20 // used in the flyingLines window
#define kMoveToFront (WindowPtr)-1L // used when spawning the windows
#define kRandomUpperLimit 32768 // used in the flyingLines window
#define kEmptyString "\p" // used in every Pascal-type string
#define kEmptyTitle kEmptyString // used in windows without a title
#define kVisible true // used when spawning the windows
#define kNoGoAway false // used for windows without a close button
#define kNilRefCon (long)nil // used when spawning windows, no record wanted
#define mFile kBaseResID
#define iQuit 1
// GLOBALS
Rect gLines[kNumLines]; // used in the flyingLines window
short gDeltaTop = 3, gDeltaBottom = 3; // used in the flyingLines window
short gDeltaLeft = 2, gDeltaRight = 6; // used in the flyingLines window
WindowPtr gTLwin, gTRwin, gBLwin, gBRwin; // tracks all 4 window pointers for later detections
short gCurrentInk; // current Pattern for the pen
PatPtr gOldPattern; // detects color changes for single triggers during changes
//FUNCTIONS
void ToolBoxInit(void); // runs once at the start, initializes Mac managers
void WindowInit(void); // runs once at the start, initializes all 4 windows
void LinesInit(void); // runs once at the start, initializes the lines for flyingLines
void MenuBarInit(void); // runs once at the start, initializes the menu
void HandleMenuChoice(long menuChoice); // handles menu choices
void HandleFileChoice(short item); // handles file menu choice
void HandleMouseDown(EventRecord *eventPtr); // handles mouse down event
void MainLoop(void); // runs constantly until quitting, main loop
void UpdateLinesInLoop(void); // runs once per loop to do the lines
void DetectInkChangeInLoop(WindowPtr theWindow, short tMode, PatPtr color); // runs once per loop to see if a new ink has to be chosen
void DetectPenDrag(void); // runs once per loop to see if a pen drag has to be drawn
void RandomRect(Rect *rectPtr); // is used by flyingLines
short Randomize(short range); // is used by flyingLines
void RecalcLine(short i); // is used by flyingLines
void DrawLine(short i); // is used by flyingLines
void DetectPainting(Point *oldMousePoint, Point *newMousePoint); //runs once per loop to see if painting should occur in the top right window
// TOOLBOXINIT
void ToolBoxInit()
{
InitGraf(&thePort);
InitFonts();
InitWindows();
InitMenus();
TEInit();
InitDialogs(nil);
InitCursor();
}
// MENUBARINIT
void MenuBarInit()
{
Handle menuBar;
MenuHandle menu;
menuBar = GetNewMBar( kBaseResID);
SetMenuBar(menuBar);
DrawMenuBar();
}
//HANDLEMENUCHOICE
void HandleMenuChoice(long menuChoice)
{
short menu;
short item;
if(menuChoice != 0)
{
menu = HiWord(menuChoice);
item = LoWord(menuChoice);
switch(menu)
{
case mFile:
HandleFileChoice(item);
break;
}
HiliteMenu(0);
}
}
// HANDLEFILECHOICE
void HandleFileChoice(short item)
{
switch(item)
{
case iQuit:
ExitToShell();
break;
}
}
// HANDLEMOUSEDOWN
void HandleMouseDown(EventRecord *eventPtr)
{
WindowPtr whichWindow;
short thePart;
long menuChoice;
thePart = FindWindow(eventPtr->where, &whichWindow);
switch(thePart)
{
case inMenuBar:
menuChoice = MenuSelect(eventPtr->where);
HandleMenuChoice(menuChoice);
break;
}
}
// WINDOWINIT
// Will initialize all 4 windows and keep the pointers for future updating
// TOP LEFT: BLACK INK - if you mouse over it, the pattern will change to black
// TOP RIGHT: CANVAS - if you mouse over it, it will draw a pen over the window in the chosen ink
// BOTTOM LEFT: WHITE INK - if you mouse over it, the pattern will change to white
// BOTTOM RIGHT: FLYING LINES - a screensaver like random line pattern is continually updated in the loop
void WindowInit(void)
{
WindowPtr window;
//TOP LEFT
gTLwin = GetNewWindow(kTopLeftWin, nil, kMoveToFront);
if(gTLwin == nil)
{
SysBeep(10); //can't load window resource
ExitToShell();
}
ShowWindow(gTLwin);
SetPort(gTLwin);
PenPat(white);
FillRect(&(gTLwin->portRect),black);
//TOP RIGHT
gTRwin = GetNewWindow(kTopRighttWin, nil, kMoveToFront);
if(gTRwin == nil)
{
SysBeep(10); //can't load window resource
ExitToShell();
}
ShowWindow(gTRwin);
SetPort(gTRwin);
PenPat(black);
PenSize(5,5);
FillRect(&(gTRwin->portRect),white);
//BOTTOM LEFT
gBLwin = GetNewWindow(kBottomLeftWin, nil, kMoveToFront);
if(gBLwin == nil)
{
SysBeep(10); //can't load window resource
ExitToShell();
}
ShowWindow(gBLwin);
SetPort(gBLwin);
PenPat(black);
//BOTTOM RIGHT
gBRwin = GetNewWindow(kBottomRighttWin, nil, kMoveToFront);
if(gBRwin == nil)
{
SysBeep(10); //can't load window resource
ExitToShell();
}
ShowWindow(gBRwin);
SetPort(gBRwin);
PenMode(patXor);
}
// LINESINIT
void LinesInit(void)
{
short i;
WindowPtr currentWin;
GetPort(¤tWin);
HideCursor();
SetPort(gTLwin);
GetDateTime((unsigned long *)(&randSeed));
SetPort(gTRwin);
GetDateTime((unsigned long *)(&randSeed));
SetPort(gBLwin);
GetDateTime((unsigned long *)(&randSeed));
SetPort(gBRwin);
GetDateTime((unsigned long *)(&randSeed));
RandomRect(&(gLines[0]));
DrawLine(0);
for(i=1; i<kNumLines; i++)
{
gLines[i] = gLines[i-1];
RecalcLine(i);
DrawLine(i);
}
ShowCursor();
SetPort(currentWin);
}
//MAIN
void main(void)
{
ToolBoxInit();
MenuBarInit();
WindowInit();
LinesInit();
MainLoop();
}
// MAINLOOP
void MainLoop(void)
{
EventRecord wutup;
Boolean exitFlag = false;
Point oldMousePoint;
GetMouse(&oldMousePoint);
LocalToGlobal(&oldMousePoint);
while(exitFlag == false) //exit condition as part of the while loop
{
Point newMousePoint;
GetMouse(&newMousePoint);
LocalToGlobal(&newMousePoint);
GetNextEvent(everyEvent, &wutup);
UpdateLinesInLoop(); //updates the flying lines
DetectInkChangeInLoop(gTLwin, srcBic, &black);
DetectInkChangeInLoop(gBLwin, srcOr, &white);
DetectPainting(&oldMousePoint, &newMousePoint);
if(wutup.what==keyDown)
{
char theChar;
theChar = wutup.message & charCodeMask;
if((wutup.modifiers & cmdKey) != 0)
{
HandleMenuChoice(MenuKey(theChar));
}
}
if(wutup.what==mouseDown)
{
HandleMouseDown(&wutup);
}
GetMouse(&oldMousePoint);
LocalToGlobal(&oldMousePoint);
}
}
// MOUSE PAINTING
void DetectPainting(Point *oldMousePoint, Point *newMousePoint)
{
WindowPtr currentWin;
GetPort(¤tWin);
SetPort(gTRwin);
GlobalToLocal(newMousePoint);
GlobalToLocal(oldMousePoint);
if(PtInRgn(*newMousePoint, gTRwin->visRgn) && Button())
{
MoveTo(newMousePoint->h, newMousePoint->v);
LineTo(oldMousePoint->h, oldMousePoint->v);
}
LocalToGlobal(newMousePoint);
LocalToGlobal(oldMousePoint);
SetPort(currentWin);
}
// INK CHANGING SECTION
void DetectInkChangeInLoop(WindowPtr theWindow, short tMode, PatPtr color)
{
Point currentMousePoint;
WindowPtr currentWin;
GetPort(¤tWin);
GetMouse(¤tMousePoint);
LocalToGlobal(¤tMousePoint);
SetPort(theWindow);
GlobalToLocal(¤tMousePoint);
if(PtInRgn(currentMousePoint, theWindow->visRgn))
{
TextMode(tMode);
MoveTo(theWindow->portRect.left, (theWindow->portRect.bottom-theWindow->portRect.top)/2);
DrawString("\p Your pen is now this color");
if(*color==black && *gOldPattern != black)
{
SetPort(gBRwin);
FillRect(&(gBRwin->portRect),white);
PenMode(patXor);
PenPat(*color);
gOldPattern = color;
}
else if(*color == white && *gOldPattern != white)
{
SetPort(gBRwin);
FillRect(&(gBRwin->portRect),black);
PenMode(notPatXor);
PenPat(*color);
gOldPattern = color;
}
SetPort(gTRwin);
PenPat(*color);
}
else
{
FillRect(&(theWindow->portRect),*color);
}
SetPort(currentWin);
}
// FLYING LINES SECTION
// UPDATELINESINLOOP
void UpdateLinesInLoop(void)
{
WindowPtr currentWin; //temporary keepsake of the active window so it can update the other ones
short currentPenMode; //temporary keepsake of the penMode
short i;
GetPort(¤tWin); //backup the current window
currentPenMode = thePort -> pnMode;
SetPort(gBRwin); //bottom right, update flying lines
DrawLine(kNumLines - 1);
for(i=kNumLines-1; i>0; i--)
gLines[i] = gLines[i-1];
RecalcLine(0);
DrawLine(0);
SetPort(currentWin); //go back to previous active window
PenMode(currentPenMode); //go back to previous pen mode
}
// RANDOMRECT
void RandomRect(Rect *rectPtr)
{
rectPtr->left = Randomize(gBRwin->portRect.right - gBRwin->portRect.left);
rectPtr->right = Randomize(gBRwin->portRect.right - gBRwin->portRect.left);
rectPtr->top = Randomize(gBRwin->portRect.bottom - gBRwin->portRect.top);
rectPtr->bottom = Randomize(gBRwin->portRect.bottom - gBRwin->portRect.top);
}
// RANDOMIZE
short Randomize(short range)
{
long randomNumber;
randomNumber = Random();
if(randomNumber < 0) randomNumber *=-1;
return((randomNumber * range) / kRandomUpperLimit);
}
// RECALCLINE
void RecalcLine(short i)
{
gLines[i].top += gDeltaTop;
if( (gLines[i].top < gBRwin->portRect.top) ||
(gLines[i].top > gBRwin->portRect.bottom) )
{
gDeltaTop *= -1;
gLines[i].top += 2*gDeltaTop;
}
gLines[i].bottom += gDeltaBottom;
if( (gLines[i].bottom < gBRwin->portRect.top) ||
(gLines[i].bottom > gBRwin->portRect.bottom))
{
gDeltaBottom *= -1;
gLines[i].bottom += 2*gDeltaBottom;
}
gLines[i].left += gDeltaLeft;
if( (gLines[i].left < gBRwin->portRect.left) ||
(gLines[i].left > gBRwin->portRect.right))
{
gDeltaLeft *= -1;
gLines[i].left += 2*gDeltaLeft;
}
gLines[i].right += gDeltaRight;
if( (gLines[i].right < gBRwin->portRect.left) ||
(gLines[i].right > gBRwin->portRect.right))
{
gDeltaRight *= -1;
gLines[i].right += 2* gDeltaRight;
}
}
// DRAWLINE
void DrawLine(short i) // no need to set the port here, it's only called by UpdateLinesInLoop and the port is set to the bottom right window
{
MoveTo(gLines[i].left, gLines[i].top);
LineTo(gLines[i].right,gLines[i].bottom);
}