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