TweetFollow Us on Twitter

Sprocket
Volume Number:10
Issue Number:10
Column Tag:Foundation Technology
Related Topics: Window Manager Process Manager

Sprocket: A Small 7.5-Adept Framework

Introducing the MacTech Magazine tiny application framework!

By Dave Falkenburg, Apple Computer, Inc.

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

This month we bring you yet another framework. This one, though, offers a little less than you’re probably used to. That’s right, less. It’s just about as small a framework as you can have and still have some interesting features. This one, written by Apple’s Dave Falkenburg, has support for many of the more recent Apple system features, yet comes in well under 33K for a 68K executable.

We plan to use Sprocket as the basis for many of the articles in the coming months. This will allow us to focus on what’s being taught (the new code) rather than what you’ve seen before.

In addition, Dave plans to continue development of Sprocket, so we hope to see more articles detailing how he’s making things work, as well as covering new framework features.

We don’t have room in this issue to list all of the code, so we’re only listing what fits. The complete set of sources will be available on the source code disk and our online sites (see page 2 for info).

Let us hear what you think of this. Do you like the way Sprocket is implemented? Do you have a better way to do something? Is something unclear? Did you find a bug? Is there something you’d like to see added? Let us know; we’re aiming to serve your needs - Ed stb

1994 Isn’t 1984

With the advent of Apple’s System 7.5 release, many of the system extensions developed over the last few years will actually be delivered to the customer in a form they can understand: a single box.

AppleGuide, a faster disk cache, QuickTime 2.0, PC Exchange with Macintosh Easy Open, Macintosh Drag & Drop, and the Thread Manager are some of the enhancements which come along for free with 7.5. Unsuspecting customers also receive a copy of PowerTalk 1.1 and QuickDraw GX 1.01 which they can also install.

It’s probably about time to think about adding support for these new features in your own applications. To help you along, here’s Sprocket.

Sprocket sports a minimalist design, with just enough to crank out small applications which do their work inside windows. It has plenty of hooks to aid in supporting new Apple technologies, and in many cases has all the necessary support code. It isn’t a full-blown class library. In fact, it’s only use of C++ objects is to do a very standard Macintosh thing: managing windows.

No! No! No! The Journey is the Journey -
The Reward is the Reward.

People not interested in the folklore of how Sprocket came to be can skip ahead to the next section.

Sprocket is the outgrowth of one programmer trying to make sense of all the interesting new things that have been added to Macintosh OS in the past few years. It leverages heavily on the ideas presented in recent develop magazine articles by Dean Yu, Steve Falkenburg, and Dave Hershey, as well as insights provided by SmartFriends™ all over.

After a dissatisfying experience with C++ and bulky application frameworks, I decided that there was something wrong with creating superfluous objects for things that wouldn’t get reused - all it seemed to do was make code less readable to the average programmer, and make things tougher on the compiler.

Like many other folks, I had decided that my application skeleton couldn’t be written in C++. Most of my motivation was slow compile times, and the bloated code which came out of CFront at the time. Still wanting flexibility, a decided to use ad hoc “object-oriented C” for my application skeleton. This meant making big structs full of function pointers that get jammed into the refCon of windows, etc. Lots of explicit initialization, and lots of opportunity to type in bugs.

Thankfully, Symantec finally shipped a real C++ compiler and I decided to bite the bullet. C++ was going to be the language I would use.

As it turns out, I just happened to be overhauling my Macintosh skeleton application about the time Dean Yu was working on his first develop article about making floating windows without defiling the Window Manager. As a result, I took an early version of his floating window code and “methodized” it. Similarly, Dean’s ideas about smartzooming on comp.sys.mac.programmer were incorporated into my skeleton. After a while, I shelved the project, because I was off to Apple to lend a hand on the PowerMac System Software project.

Maybe it was seeing developers porting their applications to PowerPC, or maybe it was the need to write new code instead of hacking other peoples code, but around the end of the 7.1.2 project I began hacking on my application at home after work. At the time (and until very recently) Icalled it App. You’ll see references to it all through the code (and maybe even here and there in this article).

Around the same time, some folks around here were talking about some Canadian company with PowerPC development tools in their pocket. It turned out that the rumors were true, and that a little 12 person startup had decided to do what two Fortune 500 companies seemingly deemed “impossible”.

Metrowerks Code Warrior is an excellent, rapid turnaround environment for building both 68K and PowerPC applications for Macintosh - and it’s a hell of a lot cheaper than buying an RS/6000 and trying to weasel an unreleased C compiler from the local Big Blue rep. With new tools in hand, I started adding Mixed Mode Universal ProcPtrs everywhere I needed them, and got it to run on a friend’s 6100/60.

About the time I was getting ready to add support for PowerTalk mail and QuickDraw GX, Scott gave me a call and convinced me that this would be a cool thing to share with the rest of the world, especially as a framework for other articles to use as a default basis for what they're teaching.

Dealing with Macintosh Windows - A Few Techniques

Folks familiar with C++ Macintosh programming can skip ahead to the section about the design philosophy employed while writing Sprocket.

A very common technique used by Macintosh programmers is to use the refCon field of the WindowRecord to indicate which routines to call when handling activate, update, mouse clicks, and other events.

One popular technique is to use the windowKind field as an indicator of what routine to call for each possible thing you need to do for a window:


/* 1 */
void
MyHandleUpdateEvent(WindowPeek theWindowToUpdate)
 { 
 GetPort(&oldPort);
 SetPort(theWindowToUpdate);
 BeginUpdate(theWindowToUpdate);

 kindOfWindow = theWindowToUpdate->windowKind;
 switch (kindOfWindow)
 { 
 case kDocumentWindow:
 MyDrawingWindowDrawProc();
 break;

 case kDrawingToolsPaletteWindow:
 MyDrawingToolsDrawProc();
 break;

 //... even more explicit window types follow
 } 
 EndUpdate(aWindow);
 SetPort(oldPort);
 } 

One drawback is the need to maintain a ton of switch statements everywhere in your code. Another less obvious one is that a new type of window cannot be added to the application without recompiling the application. Wouldn’t it be nice to let plug-in modules put up their own “first class” windows?

To avoid the problems here, clever programmers have begun to use the refCon field of the WindowRecord to store a table of function pointers. Programs that use this technique include NewsWatcher and CollaboDraw. In this way, all the switch statements can be removed, potentially saving code throughout the file:


/* 2 */
void
MyHandleUpdateEvent(WindowPtr theWindowToUpdate)
 { 
 GrafPtroldPort;
 WindowProcs*procTable =(WindowProcs*)GetWindowRefCon(theWindow);

 GetPort(&oldPort);
 SetPort(theWindowToUpdate);
 BeginUpdate(theWindowToUpdate);
 if (procTable->UpdateProc != NULL)// call the proc if it
 (*(procTable->DrawProc)());// isn’t null.
 EndUpdate(aWindow);
 SetPort(oldPort);
 } 

This is flexible, but leads to the need to initialize and maintain these procTables manually, which means a lot of initialization routines like:


/* 3 */
WindowPtr
MakeMyWindow()
 { 
 WindowProcs *myProcTable = NewHandleClear(sizeof(WindowProcs));
 myProcTable->DrawProc = MyDrawingWindowDrawProc;
 // ... and many more things you need to type in.
 } 

Once you’ve seen the light, and switch over to using ProcPtrs like this, you’ve started down the road to object oriented programming in C - object weenies call this stuff “polymorphism”. Your event loop only needs to know how to deal with generic windows - no switch statements anywhere. Any new type of window with a different set of WindowProcs can be thought of as a subclass of the base window.

Why Do I Need To Type All This Stuff In?

I don’t know about you, but I hate typing. Cut and paste can help here, but it also seems like that’s where a lot of bugs get introduced in large programs. Just when you might say: “Gee I can write a tool to automatically generate the initialization code for the window proc tables”, you’ll notice that there is no need. A C++ compiler can do that for you.

It’s simple to create a C++ object whose pointer can be stored in a window’s refCon. Other than the “function pointer magic” that the compiler does for you, this method leads to very similar coding as used with the function pointer case:


/* 4 */
void  HandleUpdate(EventRecord * anEvent)
 { 
 GrafPtroldPort;
 WindowPtraWindow = (WindowPtr) anEvent->message;  
 TWindow *wobj;
 
 GetPort(&oldPort);
 SetPort(aWindow);
 BeginUpdate(aWindow);
 if ((wobj = GetWindowObject(aWindow)) != nil)
 wobj->Draw();
 EndUpdate(aWindow);
 SetPort(oldPort);
 } 

Besides the desire to dispatch events to windows in a fast and flexible manner, there are other things which “good” Macintosh programs should do. Smart zooming, and patchless floating windows are two of these things.

Both of these things involve overriding the normal behavior of Macintosh windows, and hence are natural for rolling into the the base window methods. If you look at the source, you can see that Dean Yu’s advice in develop 15 and 17 has been taken to heart. (It’s a good thing he wrote those articles before starting to forget about all this Macintosh OS stuff)

Philosophy

The basic approaches used in developing Sprocket were:

• Use C++ objects instead of initializing tables of function pointers. Let the tools do the work - I spent many days debugging stupid “cut and paste” errors in my older code. Now that stable C++ compilers exist for Macintosh, many “nuisance” reasons to avoid C++ are gone. In many environments C++ code compiles just in the same amount of time as C code. There are at least 5 compilers I’ve used in the last year - MPW C++, Symantec C++, Metrowerks, PPCC, and xlC under AIX on an RS/6000.

• Ignore System 6. If you have a friend with a Macintosh Classic, SE, LC, etc. get them to buy a PowerMac - they’ll like it. It isn’t that hard to add support back into Sprocket, but it probably isn’t worth the added hassles.

• Keep the RAM footprint small for the base functionality so that we don’t have to worry about heap fragmentation in the future or possible VM thrashing.

• Fold code snippets used by several subroutines into reusable chunks. Once again - reuse code where it makes sense to keep the code size down. This also tends to improve locality of reference for instruction cache accesses.

• Keep use of C++ objects down to only areas where there will typically be a lot of shared code or reuse. MacApp’s Nothing application used to be 300K! We don’t have a TApplication - there’s usually only ONE application anyway, why carry around base-class code you might not use just because C++ insists on creating references to it in the __vtbls?

• Sprocket is not a class library full of everything you ever had to do in CS101: If you want one of those, go buy it - it’ll have code written by genuine computer scientists that know more about algorithms than the average programmer. Lots of folks like the Booch class libraries.

• Use the Thread Manager in lieu of lots of IdleProcs. We still have Idle handlers for windows, but they really aren’t needed if the application can rely on using the Thread Manager.

A Quick Look At The Sources

The source directory is broken down into three folders:

:Interfaces: Definitions for Sprocket-supplied objects and

utility functions.

:Lib: Implementation of Sprocket’s core routines and objects.

:AppSpecific: Example code for building your own application using Sprocket.

As a rule of thumb, you shouldn’t need to change things inside :Interfaces: or :Lib:, while :AppSpecific: contains code designed for tweaking/replacement.

The code has been successfully built using Metrowerks CodeWarrior 4 and tested on a Macintosh IIsi. It is in the process of being run through even more compilers - I had some trouble using PPCC, so there could be something wrong with the code [we'll be grateful for any corrections you readers send in - Ed stb].

The coding conventions are based on MacApp and the unofficial C++ style guide published in an early develop article. I haven’t gone crazy with const parameters and the like, because like most of us, I’m still learning things about Bjarne’s creation. The indented braces are a habit of mine, since the Process Manager (and Finder) were typed this way.

Now on to the files themselves - first the core Sprocket sources:

:Interfaces:AppConditionals.h

Conditional compilation flags kept in a header. MPW and Unix folk tend to keep these things in Makefiles, but that really means that you have to use make. In this manner, we can potentially build Sprocket in both Unix/MPW and THINK/MW environments. If you do source code control, it’s also handy to keep conditionals in a source file.

:Interfaces:StandardMenus.h

Constants defining some standard MENU IDs. It would be nice to rip out menu IDs and item numbers from application code to abstract menu choices from the position in the menu bar. Just about every other framework does. We may do that eventually.

:Interfaces:AppLib.h

Function prototypes and declarations for all the utility functions and useful global variables provided in the less object oriented parts of Sprocket.

:Lib:AppLib.cp

main() lives in here. The heart of the Macintosh application. Routines include toolbox initialization, the main event loop, and cleanup routines. Points of interest include the use of dynamic run and sleep values inside the main event loop.

:Interfaces:AppleEventHandling.h
:Lib:AppleEventHandling.cp

Code for handling the required Apple event suite (kAEOpenApplication, kAEOpenDocuments, kAEPrintDocuments, and kAEQuitApplication). Support for opening AOCE letters is also supported. Once we add OSA scripting support, expect this file to grow by leaps and bounds.

:Interfaces:DialogUtils.h
:Lib:DialogUtils.cp

Useful utility functions for using modal dialogs and alerts in the System 7 world. Contains an auto-resized error alert mechanism and lots of FilterProcs needed to prevent update events from blocking background applications. For more information about why we do all these things see the Macintosh Technote: Pending Update Perils. Support for calling the Thread Manager YieldToAnyThread inside the filterProcs is also provided.

:Lib:Preferences.cp

Routines for dealing opening and/or creating a preferences file. [Am I the only one who thought that develop article on preferences files was a bit of overkill? - drf ]

:Interfaces:Window.h
:Lib:Window.cp

These are especially interesting, and are listed at the end of the article for your reading enjoyment.

Our favorite C++ object (so far). TWindow is the base class for defining a Macintosh window. Every window created directly by Sprocket is built using this object. When a Macintosh window is created, the pointer to a corresponding C++ object is stashed in the refCon.

I’ve taken care to make creating windows from application code as simple as:

TWhizbangWindow * myWindow = new TWhizbangWindow(<WhizzyParameters>);

Creating new kinds of windows is as easy as overriding the MakeNewWindow method, and providing unique drawing & event handling methods.

Right now, both floating and normal windows are supported. Modal windows (e.g., windows that come above floaters and cause them to be deactivated) are next on the list for implementation.

Weird C++ ALERT: This isn’t as easy as it looks because C++ has a habit of not letting you call virtual methods in an inherited class from within a constructor. From within the bottom-most derived window class, we call back to an inherited method, CreateWindow, to do common window creation, instead of creating the window from within TWindow::TWindow().

:Interfaces:DialogWindow.h
:Lib:DialogWindow.cp

A class called TDialogWindow which allows for the trivial creation of Dialog Manager-based windows. It’s kinda skanky right now because it temporarily patches FrontWindow in order to fool IsDialogEvent and DialogSelect into working with Dean’s floater technique.

:Interfaces:SplashWindow.h
:Lib:SplashWindow.cp

Object-based implementation of the splash screen. Mostly gratuitous, but a fun example of subclassing TWindow.

Application Specific Stuff

:AppSpecific:App.cp

Recipe code for the application-specific things. It implements the following required functions, as well as an about box dialog:

OSErr   SetupApplication(void);
OSErr   TearDownApplication(void);
void    HandleMenu(TWindow * topWindowObj,long menuCode);
void    ConvertClipboard(void);
OSErr   OpenNewDocument(void);
OSErr   OpenDocument(LetterDescriptor *,void *);
OSErr   PrintDocument(LetterDescriptor *,void *);
Boolean QuitApplication(void);

:AppSpecific:DocWindow.cp
:AppSpecific:DocWindow.h

A window which spins arrows using threads and draws a grow box.

:AppSpecific:PreferencesDialogWindow.cp
:AppSpecific:PreferencesDialogWindow.h

A totally bogus example showing how to override TDialogWindow. Preferences dialogs that look like this are less than nice to the novice user. Cool applications derive default preferences from the user’s use of the application, not from clever imitations of the System 6 Control Panel.

:AppSpecific:ToolWindow.cp
:AppSpecific:ToolWindow.h

An empty floating tool palette. Eventually it’d be nice to define tool palette template resources, but for now we use the parameter as a WIND resource id.

Nothing against my brother or anything, but I just can’t read the code in CollaboDraw without getting hives. Here’s my first attempt at building some C++ window classes for dealing with AOCE:

:Unfinished AOCE Stuff:MailableWindow.h
:Unfinished AOCE Stuff:MailableWindow.cp

A base class for an AOCE mailable window. Intended to be placed within :Lib: once all the mailer commands and Undo support have been added to Sprocket.

:Unfinished AOCE Stuff:MailableDocWindow.cp
:Unfinished AOCE Stuff:MailableDocWindow.h

A simple subclass of TMailableWindow intended to be added to :AppSpecific: when it is more complete.

Have fun reading through the code. There should be a couple of even less obvious tips and tricks in there that I’ve since forgotten about that might be useful. Like I said, Sprocket has picked up a lot of ideas from other sample code through the years.

Speaking of other code, remember that Sprocket is not the be-all, end-all Macintosh framework. It’s really a minimalist shell that makes it easy to demonstrate cool things in the magazine or building little hacks.

Other Frameworks

There are a ton of other Macintosh specific and/or cross platform frameworks that can be of great help. We’ll be focusing on Sprocket for articles here in the magazine, but these other frameworks have a lot to offer for full-fledged applications. A number of them are advertised right here in the magazine. Here are a few more (in no particular order):

MacApp - The first framework for building applications supplied by Apple. It had it’s beginnings as Object Pascal framework, but has been converted C++ over the last few years, and can now be used to build Power Macintosh applications. MacApp 3.x started integrating System 7 functionality and began to embrace C++.

TCL - A THINK C Objects based framework. Since Symantec didn’t have “real C++” until a few years ago, the most popular compiler used by Mac developers was essentially locked out of MacApp. TCL is still a popular framework, and the 2.0 version has been ported to the PowerPC.

AppsToGo - Eric Soldan of Apple DTS has a C-based application framework. This framework is heavily built upon being able to dynamically instantiate user interface elements from templates. Because it sprang forth out of the DTS.Lib effort in the early 90’s, lots of utility functions for doing “useful things” in MacOS are also included. Some things built using AppsToGo include ClickBook and Kibitz, as well as a bunch of other commercial applications. You can find it at ftp://ftp.apple.com/dts/mac/sc/apps.to.go

QuickApp - Another MacApp-flavored application framework. This is probably a lot like Sprocket, but is probably better tested, and leverages off more experiences. Apps can be as small as 50K. $149, e-mail emergent@aol.com.

MetroWerks PowerPlant - A C++ class library for building Macintosh Applications. There is a lot of very cool stuff inside PowerPlant, including support for floating windows, resource-based user interfaces, robust exception handling, AppleEvent Object model support, and lots of very useful classes for using the Thread Manager. Uses multiple inheritance to get more code reuse. Also has it’s own coding conventions similar to the MacApp-style, but also containing hints for whether or not the object “pulls in lots of other stuff”, or it's a lightweight, reusable “L”ibrary class. Greg Dow, the original author of TCL, is the principal force behind PowerPlant.

Visual C++, MFC - Microsoft’s cross platform development strategy - don’t write Mac software, just write Windows code. Probably a good choice for people porting vertical market applications from Windows to Mac on a very tight schedule.

OpenDoc - A whole new way of building applications. Stop thinking about applications - parts is parts. The idea here is that you essentially write a C++ object that can be embedded inside any document. It is important to note that OpenDoc isn’t really a framework in the sense of providing default “do the right thing” methods for parts. Instead it provides the class hierarchy alone. A cross-platform parts framework (OPF, the OpenDoc Parts Framework, descended from BedRock) is also under development.

OLE - Microsoft’s component software strategy. Instead of eliminating the application altogether, this is kind of a super-plug in that lets Microsoft use your code within their Office Suite, and other OLE-enabled applications.

Taligent - Some really cool C++ stuff, but few details have yet been disclosed in a public forum.

What to expect in the future:

• Making Sprocket more robust with a clean, ARM-inspired exception model.

• More support for AppleScript, and hence more studly AppleGuide abilities.

• Adding QuickDraw GX-aware Printing, and possibly GX window classes.

• Changes to transparently adapt to new stuff coming from Apple.

• And, of course, cool demos and new articles based on Sprocket.

And now to a few selected listings. We’ll show Window.h, Window.cp, and AppLib.h. They’re especially interesting, and they filled up all of our available space for this month. To see the rest of the files, check out this month’s source disk or the online sites (see p. 2 for details).

Window.h

/*  Contains:  Definition of TWindow, a base class which provides a framework 
for building way-cool windows which even John Sullivan would be happy 
with. Floating windows and “smart zooming” algorithms are based on code 
samples provided by Dean Yu.  Written by: Dave Falkenburg, Dean Yu
Copyright:© 1993-94 by Dave Falkenburg, all rights reserved. */

/* 5 */
#ifndef _WINDOW_
#define _WINDOW_

#ifndef __TYPES__
#include<Types.h>
#endif
#ifndef __WINDOWS__
#include<Windows.h>
#endif
#ifndef __EVENTS__
#include<Events.h>
#endif
#ifndef __DRAG__
#include<Drag.h>
#endif

typedef short  WindowTemplateID;

class TWindow
 {
public:
 enum WindowType
 {
 kNormalWindow = 0,
 kFloatingWindow,
 kModalWindow
 };
 TWindow();
 virtual~TWindow();

 //Event routing methods
 virtualBoolean  EventFilter(EventRecord * theEvent);
 
 Methods you shouldn’t need to override, but might need to

 virtual void    CreateWindow(WindowType typeOfWindowToCreate 
 = kNormalWindow);
 virtualvoidSelect(void);
 virtualvoidDrag(Point startPoint);
 virtual void    Nudge( short horizontalDistance, 
 short verticalDistance);
 virtual void    Grow(Point startPoint);
 virtual void    Zoom(short zoomState);
 virtualvoidShowHide(Boolean showFlag);

 Methods which MUST be overridden:

 virtual WindowPtr MakeNewWindow(WindowPtr behindWindow) = 0;

 Methods which probably should be overridden

 virtualvoidAdjustCursor(EventRecord * anEvent);
 virtual void    Idle(EventRecord * anEvent);
 virtual void    Activate(Boolean activating);
 virtual void    Draw(void);
 virtual void    Click(EventRecord * anEvent);
 virtualvoidKeyDown(EventRecord * anEvent);
 virtual void    GetPerfectWindowSize(Rect * perfectSize);
 virtualvoidGetWindowSizeLimits(Rect * limits);
 virtual void    AdjustForNewWindowSize(     Rect * oldRect,
 Rect * newRect);
 //Window property accessor methods 
 // watch for new ones when we add scripting support     
 
 virtualBoolean  IsVisible(void);
 virtualBoolean  CanClose(void);   
 virtualBoolean  Close(void);
 virtualBoolean  DeleteAfterClose(void);
 virtualvoidDoEditMenu(short item);

 //Methods for use with the Drag Manager
 
 virtualOSErr  HandleDrag(DragTrackingMessage dragMessage, 
 DragReference theDrag);
 virtualOSErr    DragEnterWindow(DragReference theDrag);
 virtualOSErr    DragInWindow(DragReference theDrag);
 virtualOSErr    DragLeaveWindow(DragReference theDrag);
 virtualOSErr    HandleDrop(DragReference theDragRef);

protected:
 WindowPtrfWindow;
 WindowType fWindowType;

 BooleanfIsVisible;
 };

Utility Functions:
// Don’t you just wish you didn’t have to do this?
pascal WindowPtr GetNewColorOrBlackAndWhiteWindow(
 short windowID, void *wStorage, WindowPtr behind);
pascal WindowPtr NewColorOrBlackAndWhiteWindow(
 void *wStorage, const Rect *boundsRect,
 ConstStr255Param title, Boolean visible, 
 short theProc, WindowPtr behind, 
 Boolean goAwayFlag, long refCon);

TWindow * GetWindowObject(WindowPtr aWindow);
WindowPtr FrontModalWindow(void);
WindowPtr LastModalWindow(void);
WindowPtr FrontFloatingWindow(void);
WindowPtr LastFloatingWindow(void);
WindowPtr FrontNonFloatingWindow(void);
void    HiliteAndActivateWindow( WindowPtr aWindow,
 Boolean active);
void    SuspendResumeWindows(Boolean resuming);
void    HiliteWindowsForModalDialog(Boolean hiliting);

pascal OSErrCallWindowDragTrackingHandler( 
 DragTrackingMessage message,
 WindowPtr theWindow,
 void *handlerRefCon,
 DragReference theDragRef);
pascal OSErrCallWindowDragReceiveHandler(
 WindowPtr theWindow, 
 void *handlerRefCon,
 DragReference theDragRef);
#endif

Window.cp


/* 6 */
/* Contains:Implementation of TWindow, a base class which provides a 
framework for building way-cool windows which even John Sullivan would 
be happy with. Floating windows and “smart zooming” algorithms are based 
on code samples provided by Dean Yu. Tim Craycroft, the guy making the 
window manager do all this work for you has also been a great help.
 
Written by: Dave Falkenburg
Copyright:© 1993-94 by Dave Falkenburg, all rights reserved.

To Do:  Make sure invisible windows can be created & managed
 Handle modal windows as another class of windows
 Fix activate bugs when showing and hiding windows
 Window positioning methods (getters and setters)
 Display Manager support
 Changes to support AEObject model
 */

#include <Types.h>
#include <Windows.h>
#include <Errors.h>
#include <Script.h>//for GetMBarHeight()
#include <LowMem.h>//for LMGetWindowList()

#include "AppLib.h"
#include "Window.h"

const short kFloatingWindowKind    = 1000;
const short kNormalWindowKind = 1001;
const WindowPtr  kNoFloatingWindows= (WindowPtr) -1;
const short kScreenEdgeSlop = 4;
const short kSpaceForFinderIcons = 64;
const short kMinimumTitleBarHeight = 21;
const short kMinimumWindowSize= 32;

static void HiliteShowHideFloatingWindows(
 Boolean hiliting,Boolean hiding);

static void FindScreenRectWithLargestPartOfWindow(
 WindowPtr aWindow, Rect *theBestScreenRect, 
 GDHandle * theBestDevice);
static pascal void CalculateWindowAreaOnDevice(
 short depth, short deviceFlags, GDHandle targetDevice,        
 long userData);

struct  CalcWindowAreaDeviceLoopUserData
 {
 GDHandle fScreenWithLargestPartOfWindow;
 long   fLargestArea;
 Rect   fWindowBounds;
 };

TWindow::TWindow()
 {
 }
TWindow::~TWindow()
 {
 }
TWindow::CreateWindow

void TWindow::CreateWindow( WindowType typeOfWindowToCreate 
/* = kNormalWindow */)
 {
 WindowPtrbehindWindow,oldFrontMostWindow;
 
 if (typeOfWindowToCreate == kModalWindow)
 {
 DebugStr("\pModal windows aren’t supported yet");
 fWindowType = kFloatingWindow;
 return;
 }
 else if (typeOfWindowToCreate == kFloatingWindow)
 {
 behindWindow = (WindowPtr) -1;
 oldFrontMostWindow = FrontWindow();

 fWindowType = kFloatingWindow;
 }
 else if (typeOfWindowToCreate == kNormalWindow)
 {
 behindWindow = LastFloatingWindow();

 fWindowType = kNormalWindow;
 
 if (behindWindow == kNoFloatingWindows)
 oldFrontMostWindow = nil;
 else
 oldFrontMostWindow = (WindowPtr) 
 ((WindowPeek) behindWindow)->nextWindow;
 }

 fWindow = this->MakeNewWindow(behindWindow);
 fIsVisible = ((WindowPeek) fWindow)->visible;

 if (fWindow)
 {
 SetWRefCon(fWindow,(long) this);

 if (typeOfWindowToCreate == kModalWindow)
 {
 DebugStr("\pCan’t create Modal windows yet");
 }
 else if (typeOfWindowToCreate == kFloatingWindow)
 {
 ((WindowPeek) fWindow)->windowKind = kFloatingWindowKind;
 
 //make sure the other window stays hilited
 if (oldFrontMostWindow)
 HiliteAndActivateWindow(oldFrontMostWindow,true);
 }
 else if (typeOfWindowToCreate == kNormalWindow)
 {
 ((WindowPeek) fWindow)->windowKind = kNormalWindowKind;

 //unhighlight the old front window
 if (oldFrontMostWindow)
 HiliteAndActivateWindow(oldFrontMostWindow,false);

 //hilite the new window 
 HiliteAndActivateWindow(fWindow,true);
 }
 }
 }
TWindow::AdjustCursor
void
TWindow::AdjustCursor(EventRecord * /* anEvent */)
 {
 }
TWindow::Idle
void
TWindow::Idle(EventRecord * /* anEvent */)
 {
 }
TWindow::Activate
void
TWindow::Activate(Boolean /* activating */)
 {
 }
TWindow::Draw
void
TWindow::Draw(void)
 {
 }
TWindow::Click
void
TWindow::Click(EventRecord * /* anEvent */)
 {
 }
 TWindow::KeyDown
void
TWindow::KeyDown(EventRecord * /* anEvent */)
 {
 }
TWindow::Select
void
TWindow::Select(void)
 {
 WindowPtrcurrentFrontWindow;
 
 if (fWindowType == kFloatingWindow)
 currentFrontWindow = FrontWindow();
 else if (fWindowType == kNormalWindow)
 currentFrontWindow = FrontNonFloatingWindow();
 else
 {
 }

 if (currentFrontWindow != fWindow)
 {
 if (fWindowType == kFloatingWindow)
 BringToFront(fWindow);
 else
 {
 WindowPtrlastFloater = LastFloatingWindow();

 //If there are no floating windows, just call SelectWindow like the 
good ol’ days
 if (lastFloater == kNoFloatingWindows)
 SelectWindow(fWindow);
 else
 {
 // Deactivate the window currently in front.
 HiliteAndActivateWindow(currentFrontWindow,false);
 
// Bring it behind the last floating window and activate it.  Note that 
Inside Mac 1
// states that you need to call PaintOne() and CalcVis() on a window 
if you are using 
// SendBehind() to bring it closer to the front.  With Sys 7, this is 
no longer necessary.
 SendBehind(fWindow,lastFloater);
 HiliteAndActivateWindow(fWindow,true);
 }
 }
 }
 }
TWindow::Drag
void
TWindow::Drag(Point startPoint)
 {
 GrafPtrsavePort;
 KeyMap theKeyMap;
 BooleancommandKeyDown = false;
 RgnHandledraggingRegion;
 long   dragResult;
 WindowPeek windowAsWindowPeek = (WindowPeek) fWindow;
 
 if (WaitMouseUp())//de-bounce?
 {
 // Set up the Window Manager port.
 GetPort(&savePort);
 SetPort(gWindowManagerPort);
 SetClip(GetGrayRgn());

 // Check to see if the command key is down.
 GetKeys(theKeyMap);
 commandKeyDown = ((theKeyMap[1] & 0x8000) != 0);
 
 if (commandKeyDown)
 {
 //We’re not going to change window ordering, so make sure that we don’t 

 //  drag in front of other windows which may be in front of ours.
 ClipAbove(windowAsWindowPeek);
 }
 else if (fWindowType != kFloatingWindow)
 {
 //We’re dragging a normal window, so make sure that we don’t drag in 

 //front of any floating windows.
 ClipAbove((WindowPeek) FrontNonFloatingWindow());
 }
 
 //Drag an outline of the window around the desktop.
 //NOTE: DragGrayRgn destroys the region passed in, so make a copy
 draggingRegion = NewRgn();
 CopyRgn(windowAsWindowPeek->strucRgn,draggingRegion);
 dragResult = DragGrayRgn(draggingRegion, startPoint,
 &gDeskRectangle, &gDeskRectangle, noConstraint, nil);
 DisposeRgn(draggingRegion);
 SetPort(savePort);//Get back to old port

 if ((dragResult != 0) && (dragResult != 0x80008000))
 {
 this->Nudge((dragResult & 0xFFFF),(dragResult >> 16));
 }
 }
 if (!commandKeyDown)
 Select();
 }
TWindow::Nudge
void
TWindow::Nudge(short horizontalDistance, short verticalDistance)
 {
 WindowPeek windowAsWindowPeek = (WindowPeek) fWindow;
 short  newHorizontalPosition,newVerticalPosition;
 
 newHorizontalPosition = (short) (**windowAsWindowPeek
 ->contRgn).rgnBBox.left + horizontalDistance;
 newVerticalPosition = (short) (**windowAsWindowPeek
 ->contRgn).rgnBBox.top + verticalDistance;
 MoveWindow(fWindow,newHorizontalPosition,
 newVerticalPosition, false);
 }
TWindow::Grow
void
TWindow::Grow(Point startPoint)
 {
 GrafPtroldPort;
 long newSize;
 Rect oldWindowRect,resizeLimits;
 
 GetPort(&oldPort);
 GetWindowSizeLimits(&resizeLimits);
 newSize = GrowWindow(fWindow,startPoint,&resizeLimits);
 if (newSize)
 {
 oldWindowRect = fWindow->portRect;
 SizeWindow(fWindow,(short) newSize,
 (short) (newSize >> 16),true);
 SetPort(fWindow);
 this->AdjustForNewWindowSize(&oldWindowRect,
 &fWindow->portRect);
 } 
 SetPort(oldPort);
 }
TWindow::Zoom
void
TWindow::Zoom(short zoomState)
 {
 GrafPtroldPort;
 FontInfo systemFontInfo;
 short  titleBarHeight;
 Rect   bestScreenRect,perfectWindowRect,scratchRect;
 short  amountOffscreen;
 WindowPeek windowAsWindowPeek = (WindowPeek) fWindow;
 GDHandle bestDevice;
 
 GetPort(&oldPort);

 //Figure out the height of the title bar so we can properly position
 //a window. The algorithm is stolen from the System 7.x 'WDEF' (0)
 //This probably isn’t the best thing to do: A better way might be 
 //to diff the structure and content region rectangles?
 SetPort(gWindowManagerPort);
 GetFontInfo(&systemFontInfo);
 titleBarHeight = (short) (systemFontInfo.ascent + 
 systemFontInfo.descent + 4);
 if ((titleBarHeight % 2) == 1)
 titleBarHeight--;
 if (titleBarHeight < kMinimumTitleBarHeight)
 titleBarHeight = kMinimumTitleBarHeight;

 //Only do the voodoo magic if we are really “zooming” the window.
 if (zoomState == inZoomOut)
 {
 FindScreenRectWithLargestPartOfWindow( 
 fWindow,&bestScreenRect,&bestDevice);
 bestScreenRect.top += titleBarHeight;

 this->GetPerfectWindowSize(&perfectWindowRect);
 OffsetRect(&perfectWindowRect,-perfectWindowRect.left,
 -perfectWindowRect.top);

 //Take the zero-pined perfect window size and move it to
 //the top left of the  window’s content region.
 OffsetRect(&perfectWindowRect,
 (**windowAsWindowPeek->contRgn).rgnBBox.left,
 (**windowAsWindowPeek->contRgn).rgnBBox.top);
 
 //Does perfectWindowRect fit completely on the best screen?
   SectRect(&perfectWindowRect,&bestScreenRect,&scratchRect);
 if (!EqualRect(&perfectWindowRect, &scratchRect))
 {
 //SectRect sez perfectWindowRect doesn’t completely fit on the screen, 

 //   so bump the window so that more of it fits.
 //Make sure that the left edge of perfectWindowRect is forced onto the 
best 
 //  screen.  This is in case we are bumping the window to the right.
 amountOffscreen = bestScreenRect.left 
 - perfectWindowRect.left;
 if (amountOffscreen > 0)
 {
 perfectWindowRect.left += amountOffscreen;
 perfectWindowRect.right += amountOffscreen;
 }

 //Make sure that the left edge of perfectWindowRect is forced
 //onto the best screen.  This is in case we are bumping
 //the window downward to a new screen.
 amountOffscreen = bestScreenRect.top 
 - perfectWindowRect.top;
 if (amountOffscreen > 0)
 {
 perfectWindowRect.top += amountOffscreen;
 perfectWindowRect.bottom += amountOffscreen;
 }

 //If right edge of window falls off the screen,
 //Move window to the left until the right edge IS on the screen
 //OR the left edge is at bestScreenRect.left
 amountOffscreen = perfectWindowRect.right 
 - bestScreenRect.right;
 if (amountOffscreen > 0)
 {
 //Are we going to push the left edge offscreen? If so, change the
 //offset so we move the window all the way over to the left.
 if ((perfectWindowRect.left - amountOffscreen) 
 < bestScreenRect.left)
 amountOffscreen = perfectWindowRect.left 
 - bestScreenRect.left;
 perfectWindowRect.left -= amountOffscreen;
 perfectWindowRect.right -= amountOffscreen;
 }

 //If bottom edge of window falls off the screen,
 //Move window to up until the bottom edge IS on the screen
 //OR the top edge is at bestScreenRect.top
 amountOffscreen = perfectWindowRect.bottom 
 - bestScreenRect.bottom;
 if (amountOffscreen > 0)
 {
 //Are we going to push the top edge offscreen? If so, change the
 //offset so we move the window just to the top.
 if ((perfectWindowRect.top - amountOffscreen) 
 < bestScreenRect.top)
 amountOffscreen = perfectWindowRect.top 
 - bestScreenRect.top;
 perfectWindowRect.top -= amountOffscreen;
 perfectWindowRect.bottom -= amountOffscreen;
 }
 SectRect(&perfectWindowRect, &bestScreenRect, 
 &scratchRect);
 if (!EqualRect(&perfectWindowRect, &scratchRect))
 {
 //The edges of the window still fall offscreen,
 //so make the window smaller until it fits.
 if (perfectWindowRect.bottom > bestScreenRect.bottom)
 perfectWindowRect.bottom = bestScreenRect.bottom;

 //If the right edge is still falling off,
 //save space for Finder’s disk icons as well.
 if (perfectWindowRect.right > bestScreenRect.right)
 {
 perfectWindowRect.right = bestScreenRect.right;
 
 //If we were on the main screen, leave room for Finder icons, too.
 if (bestDevice == GetMainDevice())
 perfectWindowRect.right -= kSpaceForFinderIcons;
 }
 }
 }
 //Stash our new rectangle inside of the Window’s dataHandle
 //so that ZoomWindow does the right thing.
 (**((WStateDataHandle)
 (windowAsWindowPeek->dataHandle))).stdState 
 = perfectWindowRect;
 }
 //Don’t forget to set the port to the window being zoomed
 //Why, you ask? Because IM-IV-50 says to; otherwise you die
 SetPort(fWindow);

 Rect oldWindowRect = fWindow->portRect;
 
 ZoomWindow(fWindow,zoomState,false);
 this->AdjustForNewWindowSize(&oldWindowRect,
 &fWindow->portRect);
 SetPort(oldPort);
 }
TWindow::ShowHide
void
TWindow::ShowHide(Boolean showFlag)
 {
 //Here we need the “::” in front of ShowHide to indicate we are calling
 //the global function, and not the method ShowHide. Unintended recursion
 //can do bad things to the unsuspecting programmer.
 //Some C++ programmers would always prepend the “::” on function calls.
 ::ShowHide(fWindow,showFlag);
 fIsVisible = showFlag;
 } 
TWindow::EventFilter
Boolean
TWindow::EventFilter(EventRecord * /* theEvent */)
 {
 return false;
 }
TWindow::GetPerfectWindowSize
void
TWindow::GetPerfectWindowSize(Rect *perfectSize)
 {
 *perfectSize = qd.screenBits.bounds;
 }
TWindow::GetWindowSizeLimits
void
TWindow::GetWindowSizeLimits(Rect *limits)
 {
 limits->top = limits->left = kMinimumWindowSize;
 limits->right = gDeskRectangle.right - gDeskRectangle.left;
 limits->bottom = gDeskRectangle.bottom 
 - gDeskRectangle.top;
 }
TWindow::AdjustForNewWindowSize
void
TWindow::AdjustForNewWindowSize( Rect * /* oldRect */, 
 Rect * /* newSize */)
 {
 }
TWindow::IsVisible
Boolean
TWindow::IsVisible(void)
 {
 return fIsVisible;
 }
TWindow::CanClose
Boolean
TWindow::CanClose(void)
 {
 return true;
 }
TWindow::Close
Boolean
TWindow::Close(void)
 {
 WindowPtrnewFrontWindow = nil;
 
 if (FrontNonFloatingWindow() == fWindow)
 newFrontWindow = (WindowPtr) ((WindowPeek) fWindow)
 ->nextWindow;
 DisposeWindow(fWindow);
 if (newFrontWindow)
 HiliteAndActivateWindow(newFrontWindow,true);
 return true;
 }
TWindow::DeleteAfterClose
Boolean
TWindow::DeleteAfterClose(void)
 {
 return true;
 }
TWindow::DoEditMenu
void
TWindow::DoEditMenu(short /* menuCode */)
 {
 }
TWindow::HandleDrag
OSErr
TWindow::HandleDrag(DragTrackingMessage dragMessage, 
 DragReference theDrag)
 {
 OSErr  result = dragNotAcceptedErr; 
 switch (dragMessage)
 {
 case dragTrackingEnterWindow:
 result = this->DragEnterWindow(theDrag);
 break; 
 case dragTrackingInWindow:
 result = this->DragInWindow(theDrag);
 break; 
 case dragTrackingLeaveWindow:
 result = this->DragLeaveWindow(theDrag);
 break; 
 default:
 break;
 }
 return result;
 }
TWindow::DragEnterWindow
OSErr
TWindow::DragEnterWindow(DragReference /* theDrag */)
 {
 return dragNotAcceptedErr;
 }
TWindow::DragInWindow
OSErr
TWindow::DragInWindow(DragReference /* theDrag */)
 {
 return dragNotAcceptedErr;
 }
TWindow::DragLeaveWindow
OSErr
TWindow::DragLeaveWindow(DragReference /* theDrag */)
 {
 return dragNotAcceptedErr;
 }
TWindow::HandleDrop
OSErr
TWindow::HandleDrop(DragReference /* theDrag */)
 {
 return dragNotAcceptedErr;
 }
 Utility Functions used for floating windows

GetWindowObject
TWindow *
GetWindowObject(WindowPtr aWindow)
 {
 short  wKind;
 
 if (aWindow != nil)
 {
 wKind = ((WindowPeek) aWindow)->windowKind;
 if (wKind >= userKind)
 {
 //All windowKinds >= userKind are based upon TWindow
 return (TWindow *) GetWRefCon(aWindow);
 }
 }
 return (TWindow *) nil;
 }
Utility functions

GetNewColorOrBlackAndWhiteWindow
pascal WindowPtr
GetNewColorOrBlackAndWhiteWindow(short windowID, 
 void *wStorage, WindowPtr behind)
 {
 if (gHasColorQuickdraw)
 return GetNewCWindow(windowID,wStorage,behind);
 else
 return GetNewWindow(windowID,wStorage,behind);
 }
NewColorOrBlackAndWhiteWindow
pascal WindowPtr
NewColorOrBlackAndWhiteWindow( void *wStorage, 
 const Rect *boundsRect, ConstStr255Param title, 
 Boolean visible, short theProc, WindowPtr behind, 
 Boolean goAwayFlag, long refCon)
 {
 if (gHasColorQuickdraw)
 return NewCWindow(wStorage, boundsRect, title, visible,
 theProc, behind, goAwayFlag, refCon);
 else
 return NewWindow(wStorage, boundsRect, title, visible,
  theProc, behind, goAwayFlag,refCon);
 }
LastFloatingWindow
WindowPtr
LastFloatingWindow(void)
 {
 WindowPeek aWindow = (WindowPeek) FrontWindow();
 WindowPtrlastFloater = (WindowPtr) kNoFloatingWindows;
 
 while (aWindow && (aWindow->windowKind 
 == kFloatingWindowKind))
 {
 if (aWindow->visible)
 lastFloater = (WindowPtr) aWindow;

 aWindow = (WindowPeek) aWindow->nextWindow;
 }
 return(lastFloater);
 }
FrontNonFloatingWindow
WindowPtr
FrontNonFloatingWindow(void)
 {
 WindowPeek aWindow = (WindowPeek) LMGetWindowList();

 //Skip over floating windows
 while (aWindow && (aWindow->windowKind 
 == kFloatingWindowKind))
 aWindow = (WindowPeek) aWindow->nextWindow;

 //Skip over invisible, but otherwise normal windows
 while (aWindow && (aWindow->visible == 0))
 aWindow = (WindowPeek) aWindow->nextWindow;
 
 return (WindowPtr) aWindow;
 }
HiliteAndActivateWindow
void
HiliteAndActivateWindow(WindowPtr aWindow,Boolean active)
 {
 GrafPtroldPort;
 TWindow* wobj = GetWindowObject(aWindow);
 
 if (aWindow)
 {
 HiliteWindow(aWindow,active);
 if (wobj != nil)
 {
 GetPort(&oldPort);
 SetPort(aWindow);
 wobj->Activate(active);
 SetPort(oldPort);
 } 
 }
 }
SuspendResumeWindows
void
SuspendResumeWindows(Boolean resuming)
 {
 //When we suspend/resume, hide/show all the visible floaters
 HiliteShowHideFloatingWindows(resuming,true);
 }
HiliteWindowsForModalDialog
void
HiliteWindowsForModalDialog(Boolean hiliting)
 {
 //When we display a modal dialog, we need to unhighlight
 //all visible floaters. We also need to re-hilite them afterwards.
 HiliteShowHideFloatingWindows(hiliting,false);
 }
HiliteShowHideFloatingWindows
void
HiliteShowHideFloatingWindows(Boolean hiliting,Boolean dohiding)
 {
 WindowPeek aWindow;
 TWindow *wobj;
 
 HiliteAndActivateWindow(FrontNonFloatingWindow(),hiliting);
 aWindow = LMGetWindowList();
 while (aWindow && aWindow->windowKind 
 == kFloatingWindowKind)
 {
 wobj = GetWindowObject((WindowPtr) aWindow);
 
 //If we are hiding or showing, only hide/show windows that were 
 //  visible to begin with.
 //NOTE:We use our copy of the visible flag so we can
 //automatically show floaters on a resume event.
 //NOTE:Since this isn’t a method of TWindow, we don’t really need the 
“::” 
 //     on ShowHide, but as long as we’re trying to avoid ambiguity 
 if (dohiding && (wobj != nil) && (wobj->IsVisible()))
 ::ShowHide((WindowPtr) aWindow,hiliting);
 
 //All floaters are hilited if any floater is hilited
 HiliteWindow((WindowPtr) aWindow,hiliting);
 aWindow = (WindowPeek) aWindow->nextWindow;
 }
 }
Routines used for dealing with windows and multiple screens

CalculateWindowAreaOnDevice
pascal void
CalculateWindowAreaOnDevice(short /* depth */,
 short /* deviceFlags */,GDHandle targetDevice,long userData)
 {
 CalcWindowAreaDeviceLoopUserData *deviceLoopDataPtr;
 long   windowAreaOnThisScreen;
 Rect   windowRectOnThisScreen;
 
 deviceLoopDataPtr = (CalcWindowAreaDeviceLoopUserData *)
 userData;

 SectRect(&deviceLoopDataPtr->fWindowBounds, 
 &(**targetDevice).gdRect,&windowRectOnThisScreen);
 OffsetRect(&windowRectOnThisScreen,
 -windowRectOnThisScreen.left,
 -windowRectOnThisScreen.top);
 windowAreaOnThisScreen = windowRectOnThisScreen.right 
 * windowRectOnThisScreen.bottom;
 if (windowAreaOnThisScreen > deviceLoopDataPtr->fLargestArea)
 {
 deviceLoopDataPtr->fLargestArea = windowAreaOnThisScreen;
 deviceLoopDataPtr->fScreenWithLargestPartOfWindow 
 = targetDevice;
 }
 }

DeviceLoopDrawingUPP CallCalcWindowAreaOnDevice = 
 NewDeviceLoopDrawingProc(&CalculateWindowAreaOnDevice);

FindScreenRectWithLargestPartOfWindow
void
FindScreenRectWithLargestPartOfWindow(WindowPtr aWindow,
 Rect *theBestScreenRect,GDHandle * theBestDevice)
 {
 RgnHandlecopyOfWindowStrucRgn;
 CalcWindowAreaDeviceLoopUserData  deviceLoopData;

 //Use DeviceLoop to find out what GDevice contains the largest
 //portion of the supplied window.
 //NOTE:Assumes thePort == the Window Manager Port because we using
 //the window strucRgn, not contRgn.
 deviceLoopData.fScreenWithLargestPartOfWindow = nil;
 deviceLoopData.fLargestArea = -1;
 deviceLoopData.fWindowBounds = (**(((WindowPeek) aWindow)
 ->contRgn)).rgnBBox;
 copyOfWindowStrucRgn = NewRgn();
 CopyRgn(((WindowPeek) aWindow)->strucRgn,copyOfWindowStrucRgn);

 DeviceLoop(copyOfWindowStrucRgn, 
 CallCalcWindowAreaOnDevice, 
 (long) &deviceLoopData,singleDevices);

 DisposeRgn(copyOfWindowStrucRgn);
 
 *theBestDevice = 
 deviceLoopData.fScreenWithLargestPartOfWindow;
 *theBestScreenRect = 
 (**(deviceLoopData.fScreenWithLargestPartOfWindow)).gdRect;

 //Leave some space around the edges of the screen so window look good, 
AND
 //if the best device is the main screen, leave space for the Menubar
 InsetRect(theBestScreenRect,kScreenEdgeSlop,kScreenEdgeSlop);
 if (GetMainDevice() 
 == deviceLoopData.fScreenWithLargestPartOfWindow)
 theBestScreenRect->top += GetMBarHeight();
 }
Drag Manager callback routines which dispatch to a window’s method

CallWindowDragTrackingHandler
pascal OSErr
CallWindowDragTrackingHandler(DragTrackingMessage dragMessage, 
 WindowPtr theWindow,void * /* refCon */,DragReference theDrag)
 {
 TWindow *wobj = GetWindowObject(theWindow);
 
 if (wobj)
 return(wobj->HandleDrag(dragMessage,theDrag));
 else
 return dragNotAcceptedErr;
 }
CallWindowDragReceiveHandler
pascal OSErr
CallWindowDragReceiveHandler(WindowPtr theWindow,
 void * /* refCon */,DragReference theDrag)
 {
 TWindow *wobj = GetWindowObject(theWindow);
 
 if (wobj)
 return(wobj->HandleDrop(theDrag));
 else
 return dragNotAcceptedErr;
 }


AppLib.h


/* 7 */
/* Contains:Prototypes for the “guts” of a Macintosh application.
Copyright:© 1993 by Dave Falkenburg, all rights reserved. */

#ifndef _APPLIB_
#define _APPLIB_

#include"AppConditionals.h"
#include"Preferences.h"
#include<Types.h>
#include<Windows.h>
#include<Dialogs.h>
#include<Menus.h>
#include<Files.h>
#include<AppleEvents.h>
#include<StandardFile.h>
#include<OCEStandardMail.h>
#include"Window.h"

#ifqUseQuickDrawGX
#include<FixMath.h>//make sure we don’t use GX lame #define of “fixed1”
#include<graphics types.h>
#endif
useful macros
#ifqDebug
#define DebugMessage(x) DebugStr(x)
#else
#define DebugMessage(x)
#endif
Resource IDs
#define kErrorAlertID128
#define kStandardCloseAlertID 129
#define kStandardCloseWithNewPubsAlertID     130
#define kCoreErrorStrings 128
#define kUnsupportedSystemSoftware 1
#define kNeedsThreadManager 2
#define kStandardCloseStrings 129
#define kQuittingStr 1
#define kClosingStr2
#define kPreferencesFileStrings    130
#define kPreferencesFileName1
#define kSplashPictureID  128

#ifqUseQuickDrawGX
// When using GX, we want to create a 300K graphics heap
// NOTE: I have no idea what to use as a number here!
#define kGraphicsHeapSize (300 * 1024)
#endif
Useful functions provided by App:

void    HandleEvent(EventRecord *anEvent);
void    HandleClose(WindowPtr aWindow);
short   StandardAlert(short alertID, short defaultItem=ok,
 short cancelItem= 0,
 ModalFilterUPP customFilterProc = nil);
void    ErrorAlert(short stringList,short whichString);
void    FatalErrorAlert(short stringList,short whichString);

extern ModalFilterUPPStandardDialogFilter;
extern ModalFilterYDUPP StandardDialogFilterYD;
extern  voidPseudoClickInDialogItem(DialogPtr theDialog,
  short itemToClick);
enum  StandardCloseResult
 {
 kSaveDocument = 1,
 kCancelSaveDocument = 2,
 kDontSaveDocument = 3
 };

StandardCloseResultStandardCloseDocument(
 const StringPtr documentType,StringPtr documentName,
 Boolean hasNewEditions, Boolean quitting);

OSErr CheckAppleEventForMissingParams(
 AppleEvent *theAppleEvent);

short OpenPreferencesResFile(void);

// AOCE “FrontWindow”-equivalent routine for the Standard Mail package
extern FrontWindowUPPFrontWindowProcForAOCEUPP;

Globals
extern  Boolean  gDone;
extern  Boolean  gMenuBarNeedsUpdate;
extern  Boolean  gHasColorQuickdraw;
extern  Boolean  gHasThreadManager;
extern  Boolean  gHasDragManager;
extern  Boolean  gHasAOCE;
extern  Boolean  gHasDisplayManager;

#ifqInlineInputAware
extern  Boolean  gHasTextServices;
extern  Boolean  gHasTSMTE;
#endif

#ifqUseQuickDrawGX
extern  Boolean  gHasQuickDrawGX;
extern  longgQuickDrawGXVersion;
extern  longgQuickDrawGXPrintingVersion;
extern  gxGraphicsClient  gQuickDrawGXClient;
#endif

extern  GrafPtr  gWindowManagerPort;
extern  RectgDeskRectangle;
extern  RgnHandlegMouseRegion;

extern  short    gPreferencesRsrcRefNum;

Routines that the application MUST supply:

extern  OSErr  SetupApplication(void);
extern  voidTearDownApplication(void);
extern  voidHandleMenu(TWindow * topWindowObj,long menuCode);
extern  voidConvertClipboard(void);

extern  OSErr  OpenNewDocument(void);
extern  OSErr  OpenDocument(LetterDescriptor *,void *);
extern  OSErr  PrintDocument(LetterDescriptor *,void *);
extern  Boolean  QuitApplication(void);

#endif

AppLib.cp


/* 8 */
/* Contains:The “guts” of a Macintosh application. Written by Dave and 
many other SmartFriends™  Copyright: © 1993-94 by Dave Falkenburg, all 
rights reserved. */
#ifdef  SystemSevenOrLater
#undef  SystemSevenOrLater
#endif
#define SystemSevenOrLater1

#include <limits.h>//For LONG_MAX

#include <Types.h>
#include <Quickdraw.h>
#include <Fonts.h>
#include <Menus.h>
#include <Windows.h>
#include <Dialogs.h>
#include <Desk.h>
#include <Events.h>
#include <AppleEvents.h>
#include <DiskInit.h>

#if qUseETO15Interfaces
#include <Gestalt.h>
#include <CodeFragments.h>
#include <Devices.h>
#else
#include <GestaltEqu.h>
#include <FragLoad.h>
#endif

#include <ToolUtils.h>
#include <Traps.h>
#include <LowMem.h>

#include <Threads.h>
#include <Drag.h>
#include <Editions.h>
#include <OCEStandardMail.h>

#ifqInlineInputAware
#include <TextServices.h>
#include <TSMTE.h>
#endif

#include "AppLib.h"
#include "StandardMenus.h"
#include "Window.h"
#include "SplashWindow.h"
#include "MailableWindow.h"
#include "AppleEventHandling.h"

#ifqUseQuickDrawGX
#include <graphics macintosh.h>
#include <graphics routines.h>
#include <PrintingManager.h>
#endif
Function Prototypes
void  main(void);
void  MainEventLoop(void);
void  HandleMouseDown(TWindow * topWindowObj, EventRecord * anEvent);
void  HandleUpdate(EventRecord * anEvent);
void  HandleClose(WindowPtr aWindow);

Globals
Boolean gDone = false;
Boolean gMenuBarNeedsUpdate = true;

Boolean gHasColorQuickdraw = false;
Boolean gHasThreadManager = false;
Boolean gHasDragManager = false;
Boolean gHasAppleScript = false;
Boolean gHasAOCE = false;
Boolean gHasDisplayManager = false;

GrafPtr gWindowManagerPort;
Rect    gDeskRectangle;
RgnHandle gMouseRegion = nil;

short   gPreferencesRsrcRefNum;

#ifqInlineInputAware
Boolean gHasTextServices = false;
Boolean gHasTSMTE = false;
#endif

#ifqUseQuickDrawGX
Boolean gHasQuickDrawGX = false;
long    gQuickDrawGXVersion = 0;
long    gQuickDrawGXPrintingVersion = 0;
gxGraphicsClient gQuickDrawGXClient;

// PrintingEventOverride is our generic event handler for QuickDrawGX.
// It alows us to handle events while the QuickDrawGX movable modal
// printing dialogs are displayed.
// ¿ Really should move to a GX-specific place ?
PrintingEventOverrideForGX
OSErr
PrintingEventOverrideForGX(EventRecord *anEvent, 
  Boolean filterEvent)
 {
 if (!filterEvent)
 switch (anEvent->what)
 {
 case mouseDown:
 case keyDown:
 case autoKey:
 break;
 default:
 HandleEvent(anEvent);
 }
 return noErr;
 }
#endif

// Values that can be adjusted by other application code to change
// the behavior of the MainEventLoop.
//
// Rules of thumb:
// Increase gXXXRunQuantum (and decrease gXXXSleepQuantum) when:
// The application has many threads running that need time
// Decrease gXXXRunQuantum when:
// Sending AppleEvents to other applications
// Launching other applications
// Running in the background

unsigned long  gForegroundRunQuantum = 0;
unsigned long  gForegroundSleepQuantum = GetCaretTime();
unsigned long  gBackgroundRunQuantum = 0;
unsigned long  gBackgroundSleepQuantum = LONG_MAX;

// Globals used to “tune” the performance of MainEventLoop
// (assume we’ll be starting in the foreground)

static  unsigned longgRunQuantum = gForegroundRunQuantum;
static  unsigned longgSleepQuantum = gForegroundSleepQuantum;

#ifdef  powerc
#ifndef __MWERKS__
QDGlobals qd;
#endif
#endif
main
void
main(void)
 {
 long   feature;

 MaxApplZone();
 MoreMasters(); MoreMasters(); MoreMasters(); MoreMasters();

 InitGraf(&qd.thePort); 
 InitFonts();
 InitWindows();
 InitMenus();
 TEInit();
 InitDialogs(nil);

 if (GetToolTrapAddress(_Unimplemented) 
 == GetOSTrapAddress(_Gestalt))
 FatalErrorAlert(kCoreErrorStrings,
 kUnsupportedSystemSoftware);

 if (Gestalt(gestaltQuickdrawFeatures,&feature) == noErr)
 gHasColorQuickdraw = ((feature & (1 << gestaltHasColor)) != 0);

 TSplashWindow * splashWindow = new TSplashWindow;
 
 if ((Gestalt(gestaltAppleEventsAttr,&feature) == noErr) 
 && (feature & (1 << gestaltAppleEventsPresent)))
 {
 //Figure out if we need to do AppleEvent recording
 gHasAppleScript = (feature & (1 << gestaltScriptingSupport));
 }
 else
 FatalErrorAlert(kCoreErrorStrings,
 kUnsupportedSystemSoftware);

#ifqInlineInputAware
 if ((Gestalt(gestaltTSMgrVersion,&feature) == noErr) 
  && (feature >= 1))
 {
 gHasTextServices = true;
 if (Gestalt(gestaltTSMTEAttr, &feature) == noErr)
 gHasTSMTE = (feature & (1 << gestaltTSMTEPresent));
 }
#endif

 if (Gestalt(gestaltThreadMgrAttr,&feature) == noErr)
 {
#ifdef  powerc
 //If running on a PowerPC, make sure that we not only have the 68K Thread
 //Manager, but also the PowerPC shared library, too.  Because of the 
wonders
 //of weak linking and out of memory errors we need to also check to 
make
 //sure that an entrypoint in the library is there, too. 
 if ((Ptr) NewThread != kUnresolvedSymbolAddress)
 gHasThreadManager = ((feature 
 & ((1 << gestaltThreadMgrPresent) 
  | (1 << gestaltThreadsLibraryPresent))) != 0);
#else
 gHasThreadManager = ((feature & (1 << gestaltThreadMgrPresent)) != 0);
#endif
 }
 
 //Check for and install Drag Manager callbacks
 if (Gestalt(gestaltDragMgrAttr,&feature) == noErr)
 {
#ifdef  powerc
 //If running on a PowerPC, make sure that we not only have the
 //68K Drag Manager, but also the PowerPC shared library, too.
 if ((Ptr) NewDrag != kUnresolvedSymbolAddress)
 gHasDragManager = ((feature 
 & ((1 << gestaltDragMgrPresent) 
  | (1 << gestaltPPCDragLibPresent))) != 0);
#else
 gHasDragManager = ((feature & (1<<gestaltDragMgrPresent))!=0);
#endif

 if (gHasDragManager)
 {
 InstallTrackingHandler(  NewDragTrackingHandlerProc
 (CallWindowDragTrackingHandler),
 (WindowPtr) nil,nil);
 InstallReceiveHandler( NewDragReceiveHandlerProc
 (CallWindowDragReceiveHandler), 
 (WindowPtr) nil,nil);
 }
 }
 //Check for Display Manager
 if (Gestalt(gestaltDisplayMgrAttr,&feature) == noErr)
 gHasDisplayManager 
 = ((feature & (1 << gestaltDisplayMgrPresent)) != 0);
 
 //Check for and initialize AOCE Standard Mail package if it exists
 if ((Gestalt(gestaltSMPMailerVersion,&feature) == noErr) 
 && (feature != 0))
 {
#ifdef  powerc
 if ((Ptr) SMPInitMailer != kUnresolvedSymbolAddress)
 gHasAOCE = (SMPInitMailer(kSMPVersion) == noErr);
#else
 gHasAOCE = (SMPInitMailer(kSMPVersion) == noErr);
#endif
 }

#ifqUseQuickDrawGX
 //Check for and initialize QuickDrawGX
 if (Gestalt(gestaltGXVersion, &gQuickDrawGXVersion)==noErr)
 if (Gestalt(gestaltGXPrintingMgrVersion,
 &gQuickDrawGXPrintingVersion) == noErr)
#ifdef  powerc
 if ((Ptr) GXEnterGraphics != kUnresolvedSymbolAddress)
 gHasQuickDrawGX = true;
#else
 gHasQuickDrawGX = true;
#endif

 if (gHasQuickDrawGX)
 {
// gQuickDrawGXClient = GXNewGraphicsClient(nil, kGraphicsHeapSize, 
// (gxClientAttribute) 0);
 GXEnterGraphics();
 GXInitPrinting();
 }
#endif
 
 InstallAppleEventHandlers(); // Install our AppleEvent Handlers

 //Setup desktop rectangle for dragging windows around         
 
 GetWMgrPort(&gWindowManagerPort);
 gDeskRectangle = (**GetGrayRgn()).rgnBBox;

 //Get the default menubar
 SetMenuBar(GetNewMBar(rMenuBar));
 AddResMenu(GetMHandle(mApple),'DRVR');
 
 gPreferencesRsrcRefNum = OpenPreferencesResFile();
 
 if (SetupApplication() == noErr)
 {
 delete splashWindow;// get rid of the splash screen
 MainEventLoop();
 TearDownApplication();
 }
 
#ifqUseQuickDrawGX
 if (gHasQuickDrawGX)// Tear down QuickDrawGX
 {
 GXExitPrinting();
// GXDisposeGraphicsClient(gQuickDrawGXClient);    //    Dies a horrible 
death for me?
 GXExitGraphics();
 }
#endif
 }
MainEventLoop
void
MainEventLoop(void)
 {
 EventRecordanEvent;
 unsigned long nextTimeToCheckForEvents = 0;
 
 while (!gDone)
 {
 if (gMenuBarNeedsUpdate)
 {
 gMenuBarNeedsUpdate = false;
 DrawMenuBar();
 }
 if ((gRunQuantum == 0) ||
 (TickCount() > nextTimeToCheckForEvents))
 {
 nextTimeToCheckForEvents = TickCount() + gRunQuantum;
 (void) WaitNextEvent(everyEvent,&anEvent,
 gSleepQuantum,gMouseRegion);
 HandleEvent(&anEvent);
 }
 if (gHasThreadManager)
 YieldToAnyThread();
 }
 }
HandleEvent
void
HandleEvent(EventRecord *anEvent)
 {
 TWindow* wobj;
 
 if (anEvent->what != updateEvt)
 wobj = GetWindowObject(FrontNonFloatingWindow());
 else
 wobj = GetWindowObject((WindowPtr) anEvent->message);
 if (wobj != nil)
 wobj->AdjustCursor(anEvent);
 if ((wobj != nil) && wobj->EventFilter(anEvent))
 return;
 else switch (anEvent->what)
 {
 case nullEvent:
 if (wobj != nil)
 wobj->Idle(anEvent);
 break;
 case mouseDown:
 HandleMouseDown(wobj,anEvent);
 break;
 case keyDown:
 case autoKey:
 if (anEvent->modifiers & cmdKey)
 HandleMenu(wobj,MenuKey((short) anEvent->message 
 & charCodeMask));
 else if (wobj != nil)
 wobj->KeyDown(anEvent);
 break;
 case updateEvt:
 HandleUpdate(anEvent);
 break;
 case diskEvt:
 if (anEvent->message >> 16)
 {
 static Point  where = {50,50};
 (void) DIBadMount(where,anEvent->message);
 }
 break;
 case osEvt:
 switch ((anEvent->message & osEvtMessageMask) >> 24)
 {
 case mouseMovedMessage:
 break;
 case suspendResumeMessage: 
 if (anEvent->message & resumeFlag)
 {
 gRunQuantum = gForegroundRunQuantum;
 gSleepQuantum = gForegroundSleepQuantum;
 }
 else
 {
 gRunQuantum = gBackgroundRunQuantum;
 gSleepQuantum = gBackgroundSleepQuantum;
 }
 SuspendResumeWindows(
 (anEvent->message & resumeFlag) != 0);
 if (anEvent->message & convertClipboardFlag)
 ConvertClipboard();
 break;
 }
 break;
 case kHighLevelEvent:
 (void) AEProcessAppleEvent(anEvent);
 break;
 default:
 break;
 }
 }
HandleMouseDown
void
HandleMouseDown(TWindow * topWindowObj,EventRecord *anEvent)
 {
 WindowPtraWindow;
 short  partCode;
 TWindow*wobj;

 partCode = FindWindow(anEvent->where,&aWindow);
 wobj = GetWindowObject(aWindow);
 switch(partCode)
 {
 case inMenuBar:
 HandleMenu(topWindowObj,MenuSelect(anEvent->where));
 break;
 case inSysWindow:
 SystemClick(anEvent,aWindow);
 break;
 case inContent:
 if (wobj)
 {
 GrafPtroldPort;
 
 GetPort(&oldPort);
 SetPort(aWindow);
 GlobalToLocal(&anEvent->where);
 wobj->Click(anEvent);
 SetPort(aWindow);
 }
 break;
 case inDrag:
 if (wobj)
 wobj->Drag(anEvent->where);
 break;
 case inGrow:
 if (wobj)
 wobj->Grow(anEvent->where);
 break;
 case inGoAway:
 if (TrackGoAway(aWindow,anEvent->where))
 HandleClose(aWindow);
 break;
 case inZoomIn:
      caseinZoomOut:
        if (TrackBox(aWindow,anEvent->where,partCode) && (wobj))
 wobj->Zoom(partCode);
        break;
 default:
 break;
 }
 }
HandleUpdate
void  HandleUpdate(EventRecord * anEvent)
 {
 GrafPtroldPort;
 WindowPtraWindow = (WindowPtr) anEvent->message;  
 TWindow* wobj;
 
 GetPort(&oldPort);
 SetPort(aWindow);
 BeginUpdate(aWindow);
 if ((wobj = GetWindowObject(aWindow)) != nil)
 wobj->Draw();
 EndUpdate(aWindow);
 SetPort(oldPort);
 }
HandleClose
void
HandleClose(WindowPtr aWindow)
 {
 short  windowKind;
 TWindow*wobj;
 
 if (aWindow)
 {
 windowKind = ((WindowPeek) aWindow)->windowKind;
 if (windowKind < 0)
 {
 CloseDeskAcc(((WindowPeek)aWindow)->windowKind);
 }
 else if ( ((wobj = GetWindowObject(aWindow)) != nil) &&
 wobj->CanClose() && wobj->Close() 
 && wobj->DeleteAfterClose())
 {
 delete wobj;
 }
 }
 }







  
 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
Marvel Future Fight celebrates nine year...
Announced alongside an advertising image I can only assume was aimed squarely at myself with the prominent Deadpool and Odin featured on it, Netmarble has revealed their celebrations for the 9th anniversary of Marvel Future Fight. The Countdown... | Read more »
HoYoFair 2024 prepares to showcase over...
To say Genshin Impact took the world by storm when it was released would be an understatement. However, I think the most surprising part of the launch was just how much further it went than gaming. There have been concerts, art shows, massive... | Read more »

Price Scanner via MacPrices.net

Amazon is offering a $100 discount on every M...
Amazon is offering a $100 instant discount on each configuration of Apple’s new 13″ M3 MacBook Air, in Midnight, this weekend. These are the lowest prices currently available for new 13″ M3 MacBook... Read more
You can save $300-$480 on a 14-inch M3 Pro/Ma...
Apple has 14″ M3 Pro and M3 Max MacBook Pros in stock today and available, Certified Refurbished, starting at $1699 and ranging up to $480 off MSRP. Each model features a new outer case, shipping is... Read more
24-inch M1 iMacs available at Apple starting...
Apple has clearance M1 iMacs available in their Certified Refurbished store starting at $1049 and ranging up to $300 off original MSRP. Each iMac is in like-new condition and comes with Apple’s... Read more
Walmart continues to offer $699 13-inch M1 Ma...
Walmart continues to offer new Apple 13″ M1 MacBook Airs (8GB RAM, 256GB SSD) online for $699, $300 off original MSRP, in Space Gray, Silver, and Gold colors. These are new MacBook for sale by... Read more
B&H has 13-inch M2 MacBook Airs with 16GB...
B&H Photo has 13″ MacBook Airs with M2 CPUs, 16GB of memory, and 256GB of storage in stock and on sale for $1099, $100 off Apple’s MSRP for this configuration. Free 1-2 day delivery is available... Read more
14-inch M3 MacBook Pro with 16GB of RAM avail...
Apple has the 14″ M3 MacBook Pro with 16GB of RAM and 1TB of storage, Certified Refurbished, available for $300 off MSRP. Each MacBook Pro features a new outer case, shipping is free, and an Apple 1-... Read more
Apple M2 Mac minis on sale for up to $150 off...
Amazon has Apple’s M2-powered Mac minis in stock and on sale for $100-$150 off MSRP, each including free delivery: – Mac mini M2/256GB SSD: $499, save $100 – Mac mini M2/512GB SSD: $699, save $100 –... Read more
Amazon is offering a $200 discount on 14-inch...
Amazon has 14-inch M3 MacBook Pros in stock and on sale for $200 off MSRP. Shipping is free. Note that Amazon’s stock tends to come and go: – 14″ M3 MacBook Pro (8GB RAM/512GB SSD): $1399.99, $200... Read more
Sunday Sale: 13-inch M3 MacBook Air for $999,...
Several Apple retailers have the new 13″ MacBook Air with an M3 CPU in stock and on sale today for only $999 in Midnight. These are the lowest prices currently available for new 13″ M3 MacBook Airs... Read more
Multiple Apple retailers are offering 13-inch...
Several Apple retailers have 13″ MacBook Airs with M2 CPUs in stock and on sale this weekend starting at only $849 in Space Gray, Silver, Starlight, and Midnight colors. These are the lowest prices... Read more

Jobs Board

Relationship Banker - *Apple* Valley Financ...
Relationship Banker - Apple Valley Financial Center APPLE VALLEY, Minnesota **Job Description:** At Bank of America, we are guided by a common purpose to help Read more
IN6728 Optometrist- *Apple* Valley, CA- Tar...
Date: Apr 9, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92308 **Requisition ID:** 824398 At Target Optical, we help people see and look great - and Read more
Medical Assistant - Orthopedics *Apple* Hil...
Medical Assistant - Orthopedics Apple Hill York Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now Read more
*Apple* Systems Administrator - JAMF - Activ...
…**Public Trust/Other Required:** None **Job Family:** Systems Administration **Skills:** Apple Platforms,Computer Servers,Jamf Pro **Experience:** 3 + years of Read more
Liquor Stock Clerk - S. *Apple* St. - Idaho...
Liquor Stock Clerk - S. Apple St. Boise Posting Begin Date: 2023/10/10 Posting End Date: 2024/10/14 Category: Retail Sub Category: Customer Service Work Type: Part Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.