Click here to Skip to main content
Click here to Skip to main content

EnsureRectangleOnDisplay

, 5 Mar 2006
Rate this:
Please Sign up or sign in to vote.
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

Share

About the Author

Iain Clarke, Warrior Programmer
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 Pinmemberoccam28-Feb-06 4:56 
GeneralExcellent! PinmemberHans Dietrich24-Feb-06 23:25 
GeneralRe: Excellent! PinmemberIain Clarke25-Feb-06 1:49 

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

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

| Advertise | Privacy | Mobile
Web04 | 2.8.140827.1 | Last Updated 6 Mar 2006
Article Copyright 2006 by Iain Clarke, Warrior Programmer
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid