[comp.sys.mac] MultiFInder+MultiVideo+MultiMachine

oster@dewey.soe.berkeley.edu (David Phillip Oster) (01/02/88)

This is a tutorial for programmers about how MultiFinder interacts
with:
1.) AppleShare
2.) big screens
3.) multiple machines
4.) multiple video cards.

The MultiFinder user interface guidlines state that a multifinder
friendly application will remember where the user put his windows,
so the next time the user starts the application the windows will
re-appear in the same place.

It is fairly easy to do this: the window template resources
('WIND' and 'DLOG') both start with a rectangle that is the position
of the window in global, i.e, desktop coordinates.  When the user
closes a window, or when the user quits, the program just compares
the topLeft of the window, in global coordinates, with the position
in the template, and writes the template if the position has changed.

interaction (1): with AppleShare: remember, your application might
be running in a network, multi-user environment. Therefore, you don't
want to ever write to your application. Your application should copy
the changed template into a special preferences, or stationery pad file.
(See Tech Note #115 for a discussion of stationery pads.)

interaction (2) with big screens. If your application uses a fairly
small window, and if the user can move it around, remember that there
are some big screens out there. SuperMac makes one that has 7 times the
area of a standard Mac screen, and on a Mac II, the user may have 6 of these
video cards in his machine. That is a desktop 42 times as big as a standard
Mac screen.  It would be perverse to let the user move a window around, but
have the programs alerts and dialogs always come up in the same place.
You should have your alerts and dialogs come up near the window they refer
to. I align mine near the upper left corner of the document windows, except:
2.(a) if you align your dialogs and alerts with the document windows, and the
document windows are near the edge of the screen, your dialogs and alerts
might go off the edge of the screen. Pretty frustrating for the user if he
can't see the button he is supposed to press.
I adjust my dialogs and alerts so they are entirely on the screen with the
upper left corner of the document window.


interaction (3): with multiple machines: when you create a window, you 
can't just call:
GetNewWindow(id, NIL, (WindowPtr) -1L)

because the user might have just moved his files from a Macintosh with
a big screen to one with a small screen, and the files might call for a
window that (3.a) has its title-bar off the screen, so the user can't
drag it around, and/or (3.b) has its growbox off the screen, so the user
can't make it small enough to fit. So, as you create a window, you have
to check that it is draggable, and small enough. This brings us to 
interaction 4.

interaction (4): Color QuickDraw supports multiple video cards. The Control
Panel desk accessory supplies a "Monitors" CDEV that lets the user tell
the system how the video screens are arranged.  Before you create a window,
you must check that it is, in fact, visible. If the machine does not
have color quickdraw, no problem, you just look at screenRect, as always.
If the machine does have color quickdraw, there is a problem. It is not enough
to compare against DeskRgn, since the actual topology of monitors may be
quite jagged, and if it doesn't fit, it'll give you know guidance on how
to fix things.


the only way to tell if a titlebar is visible is examine each video 
card in turn, and see if the titlebar is in-fact visible on that card.

The titlebar of a window is a Rect, here is how you get it:
SetPort(window);
tl = topLeft(window->portRect);
LocalToGlobal(&tl);
titlebar.top = tl.v - 20;
titlebar.bottom = tl.v;
titlebar.left = tl.h;
titlebar.right = tl.h + window->portRect.right - window->portRect.left;

Here is how you search the device list to see if the title bar is
sufficienty visible:

for(gd = GetFirstDevice(); gd != NIL; gd = GetNextDevice(gd)){
    if(SectRect(&titleBar, &(**gd).gdRect, &r) &&
	r.bottom - r.top > 4 &&	/* tall enough overlap */
	r.right - r.left > 12){	/* long enough overlap */
    		/* okay, we've found the monitor with the title bar. */
		/* now, make the window small enough to fit inside */
		/* (**gd).gdRect */
	return TRUE;
    }
}
return FALSE;

You must also check that the titlebar of the window isn't covered
by the menu bar (this can happen if you move a program from a system
with two small side by side monitors to one with one big one.)
If the device with the titlebar is equal to the MainDevice(), you must
check the titleBar Rect against the following:
checkRect = (**MainDevice()).gdRect;
checkRect.bottom = MBarHeight;

If the window isn't visible, then you must move it onto the mainDevice.
  if the mainDevice is too small for the window, you must shrink it to
  fit.

It is friendly to align it with the edge of mainDevice that
it is closest to: i.e,, if it was off to the left, make it appear on the
left side of mainDevice, off to the right, on the right side of
mainDevice.

Good luck, and happy Mac programming.
The current version of Calendar, Calendar 2.1, obeys these rules, and
uses color hiliting if color quickdraw is available.

--- David Phillip Oster            --A Sun 3/60 makes a poor Macintosh II.
Arpa: oster@dewey.soe.berkeley.edu --A Macintosh II makes a poor Sun 3/60.
Uucp: {uwvax,decvax,ihnp4}!ucbvax!oster%dewey.soe.berkeley.edu

dgold@apple.UUCP (David Goldsmith) (01/07/88)

Thanks to David Oster for his article on dealing with window restoration
on multiple monitors.  I just wanted to add one thing:

For any loop over the set of GDevices on a multiple-monitor machine,
it is necessary to test the GDevice for being a screen and being active
before you assume it is a monitor you can put a window on.  Non-screen
GDevices (such as printers or off-screen GDevices) can appear in the list,
and even screen devices can appear but be inactive.  The latter case occurs
when the user connects a new monitor but has not yet configured it via
the Monitors cdev.  QuickDraw will not normally draw on an inactive device.

So any loop you have (such as the one David gave) should contain the
following test:

if (((**gd).gdFlags & (1<<screenDevice)) &&
	(((**gd).gdFlags) & (1<<screenActive))) {
	... whatever you want ...
}

around whatever you want to do for each active monitor.

-- 
David Goldsmith
Apple Computer, Inc.

AppleLink: GOLDSMITH1
UUCP:  {nsc,dual,sun,voder,ucbvax!mtxinu}!apple!dgold
CSNET: dgold@apple.CSNET, dgold%apple@CSNET-RELAY
BIX: dgoldsmith

oster@dewey.soe.berkeley.edu (David Phillip Oster) (01/09/88)

In article <7138@apple.UUCP> dgold@apple.UUCP (David Goldsmith) writes:
>So any loop you have (such as the one David gave) should contain the
>following test:

>if (((**gd).gdFlags & (1<<screenDevice)) &&
>	(((**gd).gdFlags) & (1<<screenActive))) {
>	... whatever you want ...
>}

>around whatever you want to do for each active monitor.


Now I'm confused. David Goldsmith is quite right, Stew Rubenstein also
pointed this out, but on the same page of Inside Mac Vol5. where
GetDeviceList() and GetNextDevice() are described is the call:

TestDeviceAttribute()

which is an easy and clean way of testing the gdflags. Surely, it is
better to use the call than testing the bits directly.
--------------------------------------------
Bug report:

I keep my modal dialogs entirely on the CRT screen that holds the
window the modal dialog refers to.  I do this by scanning th device list
looking for the one that holds my window, then constraining my modal
dialog ot be entirely on that screen (and if it is the main screen, I
check to keep it out of the way of the menu.) Apple itself got this check
wrong in the default menu definition procedure: menus that are supposed to
be drawn on a second video screen don't get drawn.

dgold@apple.UUCP (David Goldsmith) (01/12/88)

In article <22461@ucbvax.BERKELEY.EDU> oster@dewey.soe.berkeley.edu.UUCP (David Phillip Oster) writes:
>
>>if (((**gd).gdFlags & (1<<screenDevice)) &&
>>	(((**gd).gdFlags) & (1<<screenActive))) {
>>	... whatever you want ...
>>}
>
>Now I'm confused. David Goldsmith is quite right, Stew Rubenstein also
>pointed this out, but on the same page of Inside Mac Vol5. where
>GetDeviceList() and GetNextDevice() are described is the call:
>
>TestDeviceAttribute()
>
>which is an easy and clean way of testing the gdflags. Surely, it is
>better to use the call than testing the bits directly.

Quite right.  That should have been:

if (TestDeviceAttribute(gd, screenDevice) &&
	TestDeviceAttribute(gd, screenActive))

Thanks.
-- 
David Goldsmith
Apple Computer, Inc.

AppleLink: GOLDSMITH1
UUCP:  {nsc,dual,sun,voder,ucbvax!mtxinu}!apple!dgold
CSNET: dgold@apple.CSNET, dgold%apple@CSNET-RELAY
BIX: dgoldsmith