Click here to Skip to main content
15,880,854 members
Articles / Desktop Programming / WTL
Article

Ultima Online Treasure Hunter Tools

Rate me:
Please Sign up or sign in to vote.
4.76/5 (12 votes)
31 Jul 20027 min read 105.3K   2.8K   33   6
UOTH is collection of little bleeding edge UI techniques. Oh, and a tool for UO games players.

Image 1

Introduction

I bet you are asking yourself "What in the world is a stupid tool for an online game doing on Code Project?" This program is my entry in the New C++ Competition. UO Treasure Hunter Tools (UOTH) uses VC7, WTL7 and ATL7 as its core technology.

So what makes UOTH worthy of consideration for the new competition? Well, there isn't one thing in UOTH that stands out as great or innovative programming. However, what UOTH does contain is a diverse collection of smaller bleeding edge UI and programming techniques.

  • Extending the default toolbar customization to include icon size and text position combo boxes.
  • Explorer style customization of an "Save As" dialog including the repositioning of controls so that they line up with other controls in the dialog.
  • Hoisting the contents of a modal dialog as a child in another dialog.
  • Printing and page setup support. This includes the printing of graphics.
  • Toolbars on dialogs
  • Dynamically adjusting the size of a dialog and painting graphics to the background of the dialog.
  • Example of display a dialog as either modal or modeless. The modeless dialog is also adjusted to allow the user to minimize it.
  • "Stay on top" example code.
  • An example of owner drawn button and dropdown menu.
  • Masked image painting.
  • Alpha blending monochrome images.
  • Stretching of bordered UI graphics without distorting the image.
  • Dropdown menu from toolbar.
  • Uses EXPAT XML parser for the parsing of configuration files.
  • Uses ZLIB and the included zip file support to package the static program data files.

Following are short descriptions of some of the programming highlights contained in UOTH.

Fancy Toolbar Customization

Toolbars these days are much more complex than just the simple 16 color, 16x15 bitmap images. As programmers we have to deal with large and small image sets. We have to deal with the display of text under the button or to the right. We also have to deal with allowing the user to configure which style of toolbar he likes best.

Luckily for the programmer, the common controls provide us with complete support for the advance toolbar color and text options common in today's application. However, a few minor points of interest are left out.

If you right click on an IE6 toolbar and select "customize" you will be presented with the standard toolbar customization dialog. However, this dialog includes two extra combo boxes at the bottom that allow the user to specify the text and icon options. At first glance, it would be reasonable to assume that IE6 includes their own private customization dialog, but that turns out not to be the case.

If you look at the method CMainWnd::OnCustomizeToolbar file MainWnd.cpp, it contains the notification handler for the toolbar. Specifically, the handling of the TBN_INITCUSTOMIZE notification contains a hack used to get the window of the toolbar customization dialog.

//
// Ok, this is an UNDOCUMENTED hack.  The initialize message
// actually contains the handle of the dialog for customization.
//

typedef struct hack_tagNMTOOLBARINIT
{
  NMHDR   hdr;
  HWND  hWndDialog;
} hack_NMTOOLBARINIT;

hack_NMTOOLBARINIT *pNMHack = (hack_NMTOOLBARINIT *) pnmh;
HWND hDlg = pNMHack ->hWndDialog;

As well noted in the code, the TBN_INITCUSTOMIZE notification structure actually contains the handle of the customization dialog. As with any undocumented feature, it is always a risky proposition to utilize it. However, given that IE6 uses this, I doubt it is going away any time soon.

Once we have the handle to the customization dialog, we can create a child dialog inside this dialog. The source code shows exactly how to do this.

Explorer Style Customization of the "Save As" Dialog.

Customizing the "Save As" dialog is very common these days. However, many developers just place their controls in their customization dialogs without regard to how they align with the other controls inside the "Save As" dialog. The sad part is, this is trivial. The CUOAMExportDlg class contains an example of customizing the "Save As" dialog. We are going to look specifically at what is required to properly reposition controls. This example assumes our customization dialog will appear below the rest of the "Save As" dialog.

Every control on the "Save As" dialog has specific control IDs that we can depend on being constant. These IDs are defined in the system include file "dlgs.h". We can use these control IDs to locate controls on the "Save As" dialog and reposition our controls relative to their positions.

In UOTH, the repositioning of the controls is handled by a routine named RepositionControl (and to think, I am someone who claims self documenting code is a myth). In this example, since our customization dialog is at the bottom, all we are concerned about is the horizontal position and size of our controls. Their vertical position and size is dictated by their position and size in our dialog.

//--------------------------------------------------------------------------
//
// @mfunc Reposition a control
//
// @parm CWindow & | wnd | Control to be reposition
//
// @parm UINT | nID | ID of the control used for positioning
//
// @parm bool | fSize | If true, adjust the width of the control
//
// @rdesc None.
//
//--------------------------------------------------------------------------

void CUOAMExportDlg::RepositionControl (CWindow &wnd, UINT nID, bool fSize)
{

  //
  // Get the window rect in the client area of the 
  // control we are interested in.
  //

  CWindow wndParent = GetParent ();
  CWindow wndAnchor = wndParent .GetDlgItem (nID);
  CRect rectAnchor;
  wndAnchor .GetWindowRect (&rectAnchor);
  wndParent .ScreenToClient (&rectAnchor);

  //
  // Reposition the control
  //

  DWORD dwSWFlags = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE;
  CRect rectCtrl;
  wnd .GetWindowRect (&rectCtrl);
  ScreenToClient (&rectCtrl);
  rectCtrl .OffsetRect (rectAnchor .left - rectCtrl .left, 0);
  if (fSize)
  {
    rectCtrl .right = rectCtrl .left + rectAnchor .Width ();
    dwSWFlags &= ~SWP_NOSIZE;
  }
  wnd .SetWindowPos (NULL, rectCtrl .left, rectCtrl .top,
    rectCtrl .Width (), rectCtrl .Height (), dwSWFlags);
  return;
}

Supplied to this routine is the window of the control to be repositioned, the ID of the "Save As" dialog control, and a flag stating if we wish for our control to be resized. As you can see from the code, it is actually a simple process. This routine is invoked during dialog initialization and resize.

One other final note. If you have a control, such as another button that needs to be placed below the "Ok" button, you might run into problems with the resize grip in the lower right corner of the dialog. The third block of code in the OnInitDialog method takes care of this problem.

Hoisting one Dialog Into Another

In many applications, you might find the need to place a common dialog elements in multiple locations. In UOTH, the filter dialog is not only used as it's own independent dialog, but also appears in the "Print..." dialog and the "UOAM Export..." dialog. A normal programmer's natural reaction would be to duplicate the code for the dialog in multiple places. However, with some simple adjustments a modal framed dialog can act like a child dialog.

The first thing to consider when a dialog is to act as both a modal framed dialog and a child dialog is that in the case of a child dialog, a WM_COMMAND message for the IDOK and IDCANCEL buttons are never received. Since the programmer is in total control of the dialog, this is a trivial problem to resolve. Instead of placing all the code to save the dialog settings in an OnOK routine, place the code in another routine that will be invoked by OnOK and the dialog that will using this dialog as a child dialog. For users of DDX, this would be very trivial. In my case, I don't use DDX since I have always found it to be more of a hassle than benefit.

Handling how data is saved when the user presses the "Ok" button is only half the battle. In order to use the dialog as a child window, you have to invoke the Create method on the dialog instead of DoModal. However, the dialog resource is still setup for the dialog to be displayed as a popup with a dialog frame. Luckily, with a little bit of code, this can be fixed dynamically. The following is the CFilterDlg::Create method.

//--------------------------------------------------------------------------
//
// @mfunc Create a modeless dialog
//
// @parm HWND | hWndParent | Parent window
//
// @parm LPARAM | dwInitParam | Initialization param
//
// @rdesc Window handle
//
//---------------------------------------------------------------------------

HWND CFilterDlg::Create (HWND hWndParent, LPARAM dwInitParam)
{

  //
  // Find the region
  //

  HRSRC hRsrc = FindResource (_Module .GetResourceInstance (),
        MAKEINTRESOURCE (IDD), RT_DIALOG);
  if (hRsrc == NULL)
    return NULL;

  //
  // Get the size of the resource
  //

  DWORD dwSize = ::SizeofResource (_Module .GetResourceInstance (), hRsrc);

  //
  // Allocate the global memory to contain the regions
  //

  HGLOBAL hTemplate = ::GlobalAlloc (GPTR, dwSize);
  if (hTemplate == NULL)
    return NULL;
  DLGTEMPLATE *pTemplate = (DLGTEMPLATE *) ::GlobalLock (hTemplate);
  DLGTEMPLATEEX *pTemplateEx = (DLGTEMPLATEEX *) pTemplate;

  //
  // Load and lock the resource
  //

  HGLOBAL hSource = ::LoadResource (_Module .GetResourceInstance (), hRsrc);
  LPVOID pSource = ::LockResource (hSource);
  memcpy (pTemplate, pSource, dwSize);
  UnlockResource (hSource);
  ::FreeResource (hSource);

  //
  // Adjust the flags 
  //

  DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_BORDER | WS_DLGFRAME |
    DS_3DLOOK | DS_FIXEDSYS | DS_SETFONT | DS_CONTROL;
  DWORD dwExStyle = 0;
  if (pTemplateEx ->signature == 0xFFFF)
  {
    pTemplateEx ->exStyle = dwExStyle;
    pTemplateEx ->style = dwStyle;
  }
  else
  {
    pTemplate ->dwExtendedStyle = dwExStyle;
    pTemplate ->style = dwStyle;
  }

  //
  // Create the window
  //

  ATLASSERT (m_hWnd == NULL);
  _AtlWinModule .AddCreateWndData (&m_thunk.cd, 
    (CDialogImplBaseT <CWindow> *) this);
#ifdef _DEBUG
  m_bModal = false;
#endif //_DEBUG
  HWND hWnd = ::CreateDialogIndirectParam (
    _AtlBaseModule.GetResourceInstance(), 
    pTemplate, hWndParent, StartDialogProc,
    dwInitParam);
  ATLASSERT (m_hWnd == hWnd);

  //
  // If we created the window, delete OK and CANCEL :)
  //

  if (m_hWnd)
  {
    ::DestroyWindow (GetDlgItem (IDOK));
    ::DestroyWindow (GetDlgItem (IDCANCEL));
  }

  //
  // Unlock the globals
  //

  ::GlobalUnlock (hTemplate);
  ::GlobalFree (hTemplate);
  return hWnd;
}

The first thing that must be done is to load the dialog resource into volatile memory. This allows us to modify the create parameters. Next, the style and extended style flags are changed to force the dialog to display as a child window with no border. Finally, after the dialog is created, the OK and CANCEL buttons are removed.

This code will create the dialog at the default window position. The dialog will need to be repositioned to the proper place by the parent window. This is usually done in the WM_SIZE message handler.

Other Code of Note (The Brown Nose Section)

  • This program wouldn't have been doable without the addition of code from other people here at CodeProject.
  • The color button was based on work by Maunder, Alexander Bischofberger and James White (link, link).
  • The bitmap mask drawing was based on work by Raja Segar and Chris Becke (link, link).
  • The alpha drawing was based on work by Christian Graus (link).

About the Author

Tim has been a professional programmer for way too long. He currently works at a company he co-founded that specializes in data acquisition software for industrial automation.

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
Web Developer
Canada Canada
Tim has been a professional programmer for way too long. He currently works in the tools department at BioWare Corp..

Comments and Discussions

 
GeneralAdded demo download... Pin
Tim Smith1-Aug-02 9:29
Tim Smith1-Aug-02 9:29 
QuestionHow Ironic Pin
Rick York24-Jul-02 21:15
mveRick York24-Jul-02 21:15 
AnswerRe: How Ironic Pin
Chris Maunder25-Jul-02 2:03
cofounderChris Maunder25-Jul-02 2:03 
GeneralRe: How Ironic Pin
Jeremy Falcon1-Aug-02 10:11
professionalJeremy Falcon1-Aug-02 10:11 
AnswerRe: How Ironic Pin
Tim Smith25-Jul-02 2:27
Tim Smith25-Jul-02 2:27 
GeneralKool................!!!! Pin
Saty24-Jul-02 16:56
Saty24-Jul-02 16:56 

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.