Source/displays.c

/*************************************************************************************
#
#       displays.c
#
#       This segment handles the Display Manager notification and
#       repositions application windows in response.
#
#       Author(s):  Michael Marinkovich
#                   marink@apple.com
#
#       Modification History: 
#
#           10/12/95    MWM     Initial coding                   
#           10/05/96    MWM     Removed the fixed window title bar height and
#                               replaced it with a func that calculates the
#                               proper height.
#
#
#       Copyright © 1992-96 Apple Computer, Inc., All Rights Reserved
#
#
#       You may incorporate this sample code into your applications without
#       restriction, though the sample code has been provided "AS IS" and the
#       responsibility for its operation is 100% yours.  However, what you are
#       not permitted to do is to redistribute the source as "DSC Sample Code"
#       after having made changes. If you're going to re-distribute the source,
#       we require that you make it clear in the source that the code was
#       descended from Apple Sample Code, but that you've made changes.
#
*************************************************************************************/
 
#include <Events.h>
#include <ToolUtils.h>
#include <Gestalt.h>
#include <LowMem.h>
#include <OSUtils.h>
#include <QuickDraw.h>
 
#include "App.h"
#include "Proto.h"
 
 
//----------------------------------------------------------------------
//
//  InstallAEDMNotification - tell DM that we want to be notified by AE.
//                    
//                    
//----------------------------------------------------------------------
 
OSErr InstallAEDMNotification(void)
{
    OSErr           err = noErr;
    
    err = AEInstallEventHandler(kCoreEventClass, kAESystemConfigNotice,
                                NewAEEventHandlerProc(WorldChangedProc),
                                0L, false);
    return err;
 
}
 
 
 
//----------------------------------------------------------------------
//
//  WorldChangedProc - Display Manager calls this proc when a depth or  
//                     mode change is made. Your application should
//                     handle window repositioning here. 
//----------------------------------------------------------------------
 
pascal OSErr WorldChangedProc(AppleEvent event, AppleEvent reply, long refCon)
{
    #pragma unused (refCon)
    OSErr           err = noErr;
 
    err = HandleNotification(&event);
    
    return noErr;
    
}
 
 
//----------------------------------------------------------------------
//
//  HandleNotification - handle the AppleEvent returned by the 
//                       AppleEvent procedure.
//                    
//----------------------------------------------------------------------
 
OSErr HandleNotification(AppleEvent *event)
{
    OSErr                   err = noErr;
    GrafPtr                 oldPort;
    AEDescList              displayList;
    AEDescList              aDisplay;
    AERecord                oldConfig,newConfig;
    AEKeyword               tempWord;
    DisplayIDType           displayID;
    unsigned long           returnType;
    long                    count;
    Rect                    oldRect, newRect;
    
    GetPort(&oldPort);
 
    // Get a list of the displays from the Display Notice AppleEvent.
    err = AEGetParamDesc(event,kAEDisplayNotice,typeWildCard,&displayList);
    // How many items in the list
    err = AECountItems(&displayList,&count);
    
    while (count > 0)        // Loop through the list.
    {
        err = AEGetNthDesc(&displayList, count, typeWildCard, &tempWord, 
                                    &aDisplay);
        
        // Get the Old Rect.            
        err = AEGetNthDesc(&aDisplay, 1, typeWildCard, &tempWord, 
                           &oldConfig);
        err = AEGetKeyPtr(&oldConfig, keyDeviceRect, typeWildCard, 
                          &returnType, &oldRect, 8, nil);
        
        // Get the DisplayID so we can get the GDevice later.               
        err = AEGetKeyPtr(&oldConfig, keyDisplayID, typeWildCard, 
                          &returnType, &displayID, 8, nil);
 
        // Get the New Rect.                
        err = AEGetNthDesc(&aDisplay, 2, typeWildCard, &tempWord, 
                           &newConfig);
        err = AEGetKeyPtr(&newConfig, keyDeviceRect, typeWildCard, 
                          &returnType, &newRect, 8, nil);
        
        // If the New and Old rects are not the same then we can assume
        // the GDevice has changed and we need to rearrange the windows.
        if (err == noErr && !EqualRect(&newRect, &oldRect))
            HandleDeviceChange(displayID, &newRect);
 
        count--;
        err = AEDisposeDesc(&aDisplay);
        err = AEDisposeDesc(&oldConfig);
        err = AEDisposeDesc(&newConfig);
 
    }
    
    err = AEDisposeDesc(&displayList);
    SetPort(oldPort);
    
    return err;
    
}
 
 
//----------------------------------------------------------------------
//
//  HandleDeviceChange - called when the oldconfig is different from 
//                       newconfig. Will check all windows on effected 
//                       device and move if needed.
//----------------------------------------------------------------------
 
OSErr HandleDeviceChange(DisplayIDType displayID, Rect *newRect)
{
    OSErr           err;
    GDHandle        gd;
    GDHandle        onGD;
    WindowRef       window;
    
    // Get the GDevice from the DisplayID.
    err = DMGetGDeviceByDisplayID((DisplayIDType) displayID, &gd, false);
 
    if (err == noErr && gd != nil) 
    {
        window = LMGetWindowList();
        
        while (nil != window) 
        {
            SetPort(window); 
            // which device holds the greatest portion of the window
            onGD = GetGreatestDevice(window);
            
            // If the window is not 50% or greater on
            // the desired device then pass it up.
            if (onGD == gd) 
            { 
                if (OutOfBoundsRect(window->portRect, *newRect)) 
                {
                    MoveInbounds(window, gd, *newRect); 
                    if (OutOfBoundsRect(window->portRect, *newRect)) 
                    {
                        ResizeInbounds(window, gd, *newRect);
                        
                        // If it is one of our document windows then we need
                        // to reset the std state and the scroll bars.
                        if (GetIsAppWindow(window))
                            AdjustScrollbars(window, true);
                    }       
                }
                ResetStdState(window);
 
            }   
            window = (WindowRef)(((WindowPeek)window)->nextWindow);
        }
    }
    
    return err;
    
}
 
    
//----------------------------------------------------------------------
//
//  OutOfBoundsRect -  check to see if the window is out of the device
//                     rect.
//                    
//----------------------------------------------------------------------
 
Boolean OutOfBoundsRect(Rect windRect, Rect screenRect)
{
    Boolean     out = false;
    
 
    GlobalToLocal(&TopLeft(screenRect));
    GlobalToLocal(&BotRight(screenRect));
        
    if ((windRect.right > screenRect.right) || (windRect.bottom > screenRect.bottom))
        out = true;
 
    if ((windRect.left < screenRect.left) || (windRect.top < screenRect.top))
        out = true;
        
    return out;
    
}
    
    
//----------------------------------------------------------------------
//
//  MoveInbounds -  Move window on to desired device
//                      
//                    
//----------------------------------------------------------------------
 
void MoveInbounds(WindowRef window, GDHandle gd, Rect screenRect)
{
    Rect        bounds;
    short       hGlobal;
    short       vGlobal;
    
    bounds = window->portRect;
    
    LocalToGlobal(&TopLeft(bounds));
    LocalToGlobal(&BotRight(bounds));
    
    hGlobal = bounds.left;
    vGlobal = bounds.top;
    
    // we want to make the left top a priority so adjust it first
    // as to override the bottom, right movements. This is so we
    // can resize the window later. No need to adjust the top 
    // because the top coordinates don't change.
    
    if (((bounds.right - bounds.left) > (screenRect.right - screenRect.left)) ||
        ((bounds.bottom - bounds.top) > (screenRect.bottom - screenRect.top))) 
    {
        
        // adjust left
        if (bounds.left < screenRect.left)
            hGlobal = screenRect.left + 4;
        
        vGlobal = screenRect.top;
        if (gd == GetMainDevice())
            vGlobal += (GetMBarHeight() + GetWTitleHeight(window));
 
    }   
    else 
    {
        // adjust left
        if (bounds.left < screenRect.left)
            hGlobal = screenRect.left + 4;
 
        if ((bounds.top - 100 < screenRect.top) && (gd == GetMainDevice()))
            vGlobal = screenRect.top + (GetMBarHeight() + GetWTitleHeight(window));
 
        // adjust right
        if (bounds.right > screenRect.right)
            hGlobal = (screenRect.right - (bounds.right - bounds.left)) - 4;
        
        // adjust bottom
        if (bounds.bottom > screenRect.bottom)
            vGlobal = (screenRect.bottom - (bounds.bottom - bounds.top)) - 4;
    }
    
    MoveWindow(window, hGlobal, vGlobal,false);
        
}
    
 
//----------------------------------------------------------------------
//
//  ResizeInbounds -  resize the window to fit in the graphics device
//                      
//                    
//----------------------------------------------------------------------
 
void ResizeInbounds(WindowRef window, GDHandle gd, Rect screenRect)
{
    Rect        windRect;
    short       h;
    short       v;
 
    windRect = window->portRect;
    
    // make the window bounds the size of the gdRect
    // less the fudge factor.
    h = windRect.right - windRect.left;
    v = windRect.bottom - windRect.top;
 
    if (h > screenRect.right - screenRect.left)
        h = (screenRect.right - screenRect.left) - 8;
    
    if (v > screenRect.bottom - screenRect.top) 
    {
        v = (screenRect.bottom - screenRect.top) - 8;
        
        // If we are on the main device then subtract the mBar
        // height. Also subtract the height of the title
        // bar on the window. 
        
        if (gd == GetMainDevice())
            v -= (LMGetMBarHeight() + GetWTitleHeight(window));
    }       
 
        
    SizeWindow(window, h, v, true);
 
}
    
 
//----------------------------------------------------------------------
//
//  GetGreatestDevice - find thw device that holds the greatest area 
//                      of the window.
//                    
//----------------------------------------------------------------------
 
GDHandle GetGreatestDevice(WindowRef window)
{
    GDHandle    gd;
    GDHandle    savedGD;
    Rect        gdRect;
    Rect        foundRect;
    long        size;
    long        greatest = nil;
 
    gd = DMGetFirstScreenDevice(dmOnlyActiveDisplays);
    savedGD = gd;
    
    // Loop through the device list
    while (gd != nil) 
    {   
        gdRect = (**gd).gdRect;
        
        GlobalToLocal(&TopLeft(gdRect));
        GlobalToLocal(&BotRight(gdRect));
        
        if (SectRect(&window->portRect, &gdRect, &foundRect)) 
        {
            size = ((long)(foundRect.right - foundRect.left) * 
                   (long)(foundRect.bottom - foundRect.top));
            
            if (size > greatest) 
            {
                greatest = size;
                savedGD = gd;       // save the greatest device
            }   
        }
        gd = DMGetNextScreenDevice(gd, dmOnlyActiveDisplays);
    }
    
    return savedGD;
    
}
 
 
//----------------------------------------------------------------------
//
//  ResetStdState - since we are now on a different size screen we need
//                  to change the stdState window size so our zooming
//                  will work properly.  
//----------------------------------------------------------------------
 
void ResetStdState(WindowRef window)
{
    Rect        screenRect;
    
    screenRect = window->portRect;
 
    LocalToGlobal(&TopLeft(screenRect));
    LocalToGlobal(&BotRight(screenRect));
    
    SetWindowStandardState(window, &screenRect);
    
}
    
    
//----------------------------------------------------------------------
//
//  GetWTitleHeight - return the height of the titlebar for a given
//                    window. If the window is nil a zero height will
//                    be returned.
//----------------------------------------------------------------------
    
short GetWTitleHeight(WindowRef window)
{
    short           tHeight = 0;
    
    if (nil != window)              
        tHeight = (**((WindowPeek)window)->contRgn).rgnBBox.top - 
                  (**((WindowPeek)window)->strucRgn).rgnBBox.top;
    
    return tHeight;
 
}