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

OpenOffice 4.1.7 - Free and open-source...
OpenOffice.org is both an Open Source product and a project. The product is a multi-platform office productivity suite. It includes the key desktop applications, such as a word processor, spreadsheet... Read more
Backup and Sync 3.46 - File backup and s...
Backup and Sync (was Google Drive) is a place where you can create, share, collaborate, and keep all of your stuff. Whether you're working with a friend on a joint research project, planning a... Read more
iClock 5.5 - Customizable menu bar clock...
iClock replaces the old Apple's default menu bar clock with more features, customization and increases your productivity. Features: Have your Apple or Google calendar instantly available from the... Read more
Garmin Express 6.18.0.0 - Manage your Ga...
Garmin Express is your essential tool for managing your Garmin devices. Update maps, golf courses and device software. You can even register your device. Update maps Update software Register your... Read more
MarsEdit 4.3.5 - Quick and convenient bl...
MarsEdit is a blog editor for OS X that makes editing your blog like writing email, with spell-checking, drafts, multiple windows, and even AppleScript support. It works with with most blog services... Read more
Xcode 11.0 - Integrated development envi...
Xcode includes everything developers need to create great applications for Mac, iPhone, iPad, and Apple Watch. Xcode provides developers a unified workflow for user interface design, coding, testing... Read more
DaisyDisk 4.8 - $9.99
DaisyDisk allows you to visualize your disk usage and free up disk space by quickly finding and deleting big unused files. The program scans your disk and displays its content as a sector diagram... Read more
VMware Fusion 11.5.0 - Run Windows apps...
VMware Fusion and Fusion Pro - virtualization software for running Windows, Linux, and other systems on a Mac without rebooting. The latest version includes full support for Windows 10, macOS Mojave... Read more
Apple Configurator 2.10 - Configure and...
Apple Configurator makes it easy to deploy iPad, iPhone, iPod touch, and Apple TV devices in your school or business. Use Apple Configurator to quickly configure large numbers of devices connected to... Read more
Spotify 1.1.15.448. - Stream music, crea...
Spotify is a streaming music service that gives you on-demand access to millions of songs. Whether you like driving rock, silky R&B, or grandiose classical music, Spotify's massive catalogue puts... Read more

Latest Forum Discussions

See All

Marvel Strike Force is adding Agent Coul...
Marvel Strike Force, the popular squad-based RPG, is set to receive a bunch of new content over the next few weeks. [Read more] | Read more »
Lots of premium games are going free (so...
You may have seen over the past couple weeks a that a bunch of premium games have suddenly become free. This isn’t a mistake, nor is it some last hurrah before Apple Arcade hits, and it’s important to know that these games aren’t actually becoming... | Read more »
Yoozoo Games launches Saint Seiya Awaken...
If you’re into your anime, you’ve probably seen or heard of Saint Seiya. Based on a shonen manga by Masami Kurumada, the series was massively popular in the 1980s – especially in its native Japan. Since then, it’s grown into a franchise of all... | Read more »
Five Nights at Freddy's AR: Special...
Five Nights at Freddy's AR: Special Delivery is a terrifying new nightmare from developer Illumix. Last week, FNAF fans were sent into a frenzy by a short teaser for what we now know to be Special Delivery. Those in the comments were quick to... | Read more »
Rush Rally 3's new live events are...
Last week, Rush Rally 3 got updated with live events, and it’s one of the best things to happen to racing games on mobile. Prior to this update, the game already had multiplayer, but live events are more convenient in the sense that it’s somewhat... | Read more »
Why your free-to-play racer sucks
It’s been this way for a while now, but playing Hot Wheels Infinite Loop really highlights a big issue with free-to-play mobile racing games: They suck. It doesn’t matter if you’re trying going for realism, cart racing, or arcade nonsense, they’re... | Read more »
Steam Link Spotlight - The Banner Saga 3
Steam Link Spotlight is a new feature where we take a look at PC games that play exceptionally well using the Steam Link app. Our last entry talked about Terry Cavanaugh’s incredible Dicey Dungeons. Read about how it’s a great mobile experience... | Read more »
Combo Quest (Games)
Combo Quest 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: Combo Quest is an epic, time tap role-playing adventure. In this unique masterpiece, you are a knight on a heroic quest to retrieve... | Read more »
Hero Emblems (Games)
Hero Emblems 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: ** 25% OFF for a limited time to celebrate the release ** ** Note for iPhone 6 user: If it doesn't run fullscreen on your device... | Read more »
Puzzle Blitz (Games)
Puzzle Blitz 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Puzzle Blitz is a frantic puzzle solving race against the clock! Solve as many puzzles as you can, before time runs out! You have... | Read more »

Price Scanner via MacPrices.net

11″ WiFi iPad Pros on sale today for up to $2...
Amazon has new 2018 Apple 11″ WiFi iPad Pros in stock today and on sale for up to $200 off Apple’s MSRP. These are the same iPad Pros sold by Apple in its retail and online stores. Be sure to select... Read more
Select 12″ iPad Pros on sale for $200 off App...
Amazon has select 2018 Apple 12″ iPad Pros in stock today and on sale for $200 off Apple’s MSRP. These are the same iPad Pros sold by Apple in its retail and online stores. Be sure to select Amazon... Read more
Get one of Apple’s new 2019 iPhone 11 models...
Boost Mobile is offering the new 2019 Apple iPhone 11, iPhone 11 Pro, and 11 Pro Max for $100 off MSRP. Their discount reduces the cost of an iPhone 11 to $599 for the 64GB models, $899 for the 64GB... Read more
13″ 1.4GHz Silver MacBook Pros on sale for $1...
B&H Photo has new 2019 13″ 1.4GHz 4-Core Touch Bar Silver MacBook Pros on sale for $100 off Apple’s MSRP. Overnight shipping is free to many addresses in the US. These are the same MacBook Pros... Read more
4-core and 6-core 2018 Mac minis available at...
Apple has Certified Refurbished 2018 Mac minis available on their online store for $120-$170 off the cost of new models. Each mini comes with a new outer case plus a standard Apple one-year warranty... Read more
$250 prepaid Visa card with any Apple iPhone,...
Xfinity Mobile will include a free $250 prepaid Visa card with the purchase of any new iPhone, new line activation, and transfer of phone number to Xfinity Mobile. Offer is valid through October 27,... Read more
Sprint is offering the 64GB Apple iPhone 11 P...
Sprint has the new 64GB iPhone 11 Pro available for $12.50 per month for new customers with an eligible trade-in in of iPhone 7 or newer. That’s down from their standard monthly lease of $41.67. The... Read more
Final week: Apple’s 2019 Back to School Promo...
Purchase a new Mac using Apple’s Education discount, and take up to $400 off MSRP. All teachers, students, and staff of any educational institution with a .edu email address qualify for the discount... Read more
Save $30 on Apple’s AirPods at these reseller...
Amazon is offering discounts on new 2019 Apple AirPods ranging up to $30 off MSRP as part of their Labor Day sale. Shipping is free: – AirPods with Charging Case: $144.95 $15 off MSRP – AirPods with... Read more
Preorder your Apple Watch Series 5 today at A...
Amazon has Apple Watch Series 5 GPS models available for preorder and on sale today for $15 off Apple’s MSRP. Shipping is free and starts on September 20th: – 40mm Apple Watch Series 5 GPS: $384.99 $... Read more

Jobs Board

Systems Analyst ( *Apple* & Android) (Jo...
Systems Analyst ( Apple & Android) (Job ID: 572513) + 11751 Meadowville Ln, Chester, VA 23836, USA + Full-time Company Description Computer Consultants International, Read more
*Apple* Mobile App Developer - eiWorkflow So...
…eiWorkflow Solutions, LLC is currently looking for a consultant for the following role. Apple Mobile App Developer Tasks the role will be performing: ? Mobile App Read more
Essbase Developer - *Apple* - Theorem, LLC...
Job Summary Apple is seeking an experienced, detail-minded Essbase developer to join our worldwide business development and strategy team. If you are someone who Read more
Student Employment (Blue *Apple* Cafe) Spri...
Student Employment (Blue Apple Cafe) Spring 2019 Penn State University Campus/Location: Penn State Brandywine Campus City: Media, PA Date Announced: 12/20/2018 Date Read more
Best Buy *Apple* Computing Master - Best Bu...
**732093BR** **Job Title:** Best Buy Apple Computing Master **Job Category:** Store Associates **Location Number:** 001441-Beaumont-Store **Job Description:** The Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.