Click here to Skip to main content
15,885,818 members
Articles / Desktop Programming / MFC
Article

EnsureRectangleOnDisplay

Rate me:
Please Sign up or sign in to vote.
4.23/5 (8 votes)
5 Mar 20062 min read 36.4K   547   20   3
Ensure a window rectangle is fully on one screen.

Popup half on window

Popup fully on window

Introduction

I had a problem with one of my software projects yesterday. On one of my dialogs, I had a small button which popped up a small Options dialog. This would have been a popup menu, but after looking at some of the articles on Jensen Harris' blog, I wanted a richer menu. So I went with a small popup dialog.

But as the main dialog is often parked near the edge of the screen, the popup dialog would fall off. I could adjust the popup rectangle to be in the main screen, but I wanted it to be multi-monitor aware.

The Message Board

Given a blank brain, I went and asked wiser people than me on the CP Visual C++ discussion board. James R Twine and Peterchen gave me 90% of the answers. Peterchen's answer pointed to Nish's article which I'd seen before, and was niggling in my brain.

I'd have left it there, but Ryan Binns pointed out a nitpicky problem with the above solutions - the popup dialog could be spilt across more than one screen. Being a nit-picker myself (what programmer isn't?), I had to roll my own function which wouldn't have that vulnerability.

The Code

Thanks to Occam, I found out I'd reinvented the wheel. The hard part of my snippet has already been done by Microsoft in <multimon.h>. The code has been dramatically reduced. But as I invented a decent wheel, I've kept it below. Feel free to use either implementation of the function. Hans Dietrich also pointed out the redundant fFlags parameter, which I've removed. Here's the new code, without further ado...

// Only use this definition once - it creates the stubs
#define COMPILE_MULTIMON_STUBS
#include <multimon.h>

// Take coords, and shift them so all corners
// (if possible) appear on the nearest screen.
BOOL EnsureRectangleOnDisplay (RECT *prc)
{
    HMONITOR hMonitor;
    MONITORINFO mi;
    memset (&mi, 0, sizeof (mi));
    mi.cbSize = sizeof(mi);
    
    if (!prc)
        return FALSE;
   
    hMonitor = MonitorFromRect (prc, 
               MONITOR_DEFAULTTONEAREST);
    if (hMonitor)
        GetMonitorInfo (hMonitor, &mi);

    // Now we have a clipping rectangle,
    // bring our input rectangle into sight.
    if (prc->right > mi.rcWork.right)
    {
        prc->left -= prc->right - mi.rcWork.right;
        prc->right = mi.rcWork.right;
    }
    if (prc->bottom > mi.rcWork.bottom)
    {
        prc->top -= prc->bottom - mi.rcWork.bottom;
        prc->bottom = mi.rcWork.bottom;
    }
    if (prc->left < mi.rcWork.left)
    {
        prc->right += mi.rcWork.left - prc->left;
        prc->left = mi.rcWork.left;
    }
    if (prc->top < mi.rcWork.top)
    {
        prc->bottom += mi.rcWork.top - prc->top;
        prc->top = mi.rcWork.top;
    }
   
    return TRUE;
}

The Old Code!

I had problems with linking on my Visual Studio 6 machine, so I dynamically loaded user32.dll and the two functions GetMonitorInfo and MonitorFromRect. If you can statically link them, feel free to cut my code down to size. The LoadLib class was lightly adapted from its original home in Owner Drawn Menu with Icons, Titles and Shading by Bruno Podetti.

EnsureRectangleOnDisplay function

typedef struct tagMONITORINFO
{
    DWORD   cbSize;
    RECT    rcMonitor;
    RECT    rcWork;
    DWORD   dwFlags;
} MONITORINFO, *LPMONITORINFO;
         
#define MONITOR_DEFAULTTONULL       0x00000000
#define MONITOR_DEFAULTTOPRIMARY    0x00000001
#define MONITOR_DEFAULTTONEAREST    0x00000002
         
typedef BOOL (WINAPI* GetMonitorInfo)(HMONITOR 
                  hMonitor, LPMONITORINFO lpmi);
typedef HMONITOR (WINAPI* MonitorFromRect)
             (LPCRECT lprc, DWORD dwFlags);
         
BOOL EnsureRectangleOnDisplay (RECT *prc, UINT fFlags)
{
    HMONITOR hMonitor;
    MONITORINFO mi;
    mi.cbSize = sizeof(mi);
    
    if (!prc)
        return FALSE;
   
    // Load the library once
    static CImcLoadLib user32 (_T("USER32.DLL"));
   
    // Fail to single monitor stuff
    SystemParametersInfo (SPI_GETWORKAREA, 0, &mi.rcWork, FALSE); 
   
    // It would be odd for the program to work
    // with user32.dll missing...
    if (user32.m_hModule) {
        MonitorFromRect mfr = (MonitorFromRect) 
                 user32.GetProcAddress ("MonitorFromRect");
#ifdef UNICODE
        // why there's two versions, I have *no* idea.
        GetMonitorInfo gmi = (GetMonitorInfo) user32.GetProcAddress 
                             ("GetMonitorInfoW");
#else
        // Maybe they'll extend the struct...
        GetMonitorInfo gmi = (GetMonitorInfo) 
                              user32.GetProcAddress ("GetMonitorInfoA");
#endif
    
    // were both of the functions valid?
    if (mfr && gmi)
        {
       // get the nearest monitor to the passed rect.
       hMonitor = mfr (prc, MONITOR_DEFAULTTONEAREST);
       if (hMonitor)
           gmi (hMonitor, &mi);
      }
    }
   
    // Now we have a clipping rectangle,
    // bring our input rectangle into sight.
    if (prc->right > mi.rcWork.right)
    {
        prc->left -= prc->right - mi.rcWork.right;
        prc->right = mi.rcWork.right;
     }
    if (prc->bottom > mi.rcWork.bottom)
    {
        prc->top -= prc->bottom - mi.rcWork.bottom;
        prc->bottom = mi.rcWork.bottom;
    }
   
    if (prc->left < mi.rcWork.left)
    {
        prc->right += mi.rcWork.left - prc->left;
        prc->left = mi.rcWork.left;
    }
    if (prc->top < mi.rcWork.top)
    {
        prc->bottom += mi.rcWork.top - prc->top;
        prc->top = mi.rcWork.top;
    }
   
 return TRUE;
}

The borrowed from elsewhere bit!

////////////////////////////////////////////////////////
// Originally by Bruno Podetti, lifted from
// Owner Drawn Menu with Icons, Titles and Shading 
// <A href="http://www.codeproject.com/menu/newmenuxpstyle.asp">http://www.codeproject.com/menu/newmenuxpstyle.asp</A>
// Adapted by me.

class CImcLoadLib
{
public:
 HMODULE m_hModule;
   
 CImcLoadLib (LPCTSTR pModuleName)
 {
  m_hModule = LoadLibrary(pModuleName);
 }
   
 ~CImcLoadLib ()
 {
  if(m_hModule)
  {
   FreeLibrary(m_hModule);
   m_hModule = NULL;
  }
 }
  
 FARPROC GetProcAddress(LPCSTR lpProcName)
 {
  return ::GetProcAddress (m_hModule, lpProcName);
 }
};

Using the code...

BOOL CHistogramPeakClipSelector::OnInitDialog ()
{
    // Make us look like a popup menu, but fancy.
    CRect rc;
    GetWindowRect (&rc);
    rc.OffsetRect (-rc.TopLeft ());
    rc.OffsetRect (m_ptCorner);

    if (EnsureRectangleOnDisplay (rc, 0))
        SetWindowPos (NULL, rc.left, rc.top, 0,0, SWP_NOSIZE | SWP_NOZORDER);
    else
        SendMessage (DM_REPOSITION);
   
 ....

m_ptCorner is where I want the top left corner of the popup to be. But if that would make it fall off the screen, adjust the rectangle appropriately. If for some reason, my glorious helper function failed, fall through to using Peterchen / Nish's idea.

History

  • 2006 March 6th: Improved and reduced, thanks to Occam and Hans Dietrich.
  • 2006 February 24th: I just wrote it - of course, every line is a bug!

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
Sweden Sweden
I have now moved to Sweden for love, and recently married a lovely Swede.


-----------------
I started programming on BBC micros (6502) when I was six and never quite stopped even while I was supposed to be studying physics and uni.

I've been working for ~13 years writing software for machine control and data analysis. I now work on financial transaction transformation software, for a Software company in Gamlastan, Stockholm.
Look at my articles to see my excellent coding skills. I'm modest too!

Comments and Discussions

 
GeneralDynamic Loading Pin
occam28-Feb-06 4:56
occam28-Feb-06 4:56 
GeneralExcellent! Pin
Hans Dietrich24-Feb-06 23:25
mentorHans Dietrich24-Feb-06 23:25 
I think this will be very useful.

One question: Why go to the trouble of loading USER32.DLL, and then getting the address for GetMonitorInfo()? Windows won't run without USER32.DLL, and GetMonitorInfo() and MonitorFromRect() are included in every version of Windows since 98. Are you concerned with Windows 95?

BTW, in the code in your article, if either GMI or MFR fail to load, the code will then use the data in mi, even though it has not been initialized.

Finally, the function BOOL EnsureRectangleOnDisplay (RECT *prc, UINT fFlags) does not reference the input parameter fFlags. If you can do so, I recommend using Warning Level 4.

Best wishes,
Hans
GeneralRe: Excellent! Pin
Iain Clarke, Warrior Programmer25-Feb-06 1:49
Iain Clarke, Warrior Programmer25-Feb-06 1:49 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.