iBinx Corp.
contact usshisen-sho

Source listing: . Return to the main page

/* -*- C++ -*-
 *******************************************************************
 *
 *
 * wndcore.cpp - core window class implementations
 *
 *
 *******************************************************************
 *
 * General purpose window classes for Win32
 *
 *******************************************************************
 *
 * Created 2001-02-20 by Jim Mason <jmason@sirius.com>
 *
 * Copyright (C) 2001 Jim Mason.
 *
 *******************************************************************
 *
 * This file is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This file is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this file; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 *******************************************************************
 */

#include "stdafx.h"
#include "wndcore.h"

// External functions
#include "shisen.h"
HINSTANCE App::GetHInstance();   // returns the HINSTANCE of the application
HWND App::GetMainHWnd();         // returns the HWND of the main application window

/////////////////////////////////////////////////////////////////////////////
// Window superclass

Window::Window() {
    m_hWnd = NULL;
    m_oldWndProc = NULL;
}

Window::~Window() {
    if(m_hWnd && m_oldWndProc) {
        // The window is not gone, but our C++ class instance is
        // being deleted; it is likely that someone has incorrectly
        // coded to explicitly delete the C++ instance, rather than
        // destroying the window.  We'll assert in debug mode so the
        // programmer will know:
        ASSERT(FALSE);

        // For all C++ objects which are allocated on the heap and
        // require explicit deletion, you should override PostNcDestroy
        // in your derived class and do a "delete this" there; then
        // when you do a DestroyWindow on the window, both the window
        // and the C++ class instance will be destroyed.

        // If we did a DestroyWindow at this point, the subclass's
        // OnDestroy, PostNcDestroy, etc., would never be run, as the
        // derived class(es) have already been destructed, and the C++
        // vtbl is no longer setup correctly.
        //
        // We will just unhook our wndproc from the window.
        SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)m_oldWndProc);
        SetWindowLong(m_hWnd, GWL_USERDATA, 0L);
    }
}

BOOL Window::Create(DWORD dwStyleEx, LPCTSTR lpszClassName, LPCTSTR lpszWindowName,
                    DWORD dwStyle, const RECT& rect, HWND hParentWnd, UINT nID) {
    CREATESTRUCT cs;
    cs.dwExStyle = dwStyleEx;
    cs.lpszClass = lpszClassName;
    cs.lpszName = lpszWindowName;
    cs.style = dwStyle;
    cs.x = rect.left;
    cs.y = rect.top;
    cs.cx = rect.right - rect.left;
    cs.cy = rect.bottom - rect.top;
    cs.hwndParent = hParentWnd;
    cs.hMenu = (HMENU)nID;

    if(PreCreateWindow(cs)) {
        m_hWnd = CreateWindowEx(cs.dwExStyle, cs.lpszClass, cs.lpszName, cs.style,
                                  cs.x, cs.y, cs.cx, cs.cy, cs.hwndParent, cs.hMenu,
                                  App::GetHInstance(), this);
        if(m_hWnd && (WNDPROC)GetWindowLong(m_hWnd, GWL_WNDPROC) != StaticWndProc) {
            // If our window proc is not registered as the window proc, do so now.
            SubclassWindow(m_hWnd);

            // We have missed the creation messages; at least run OnCreate.
            if(OnCreate(&cs) == -1) {
                DestroyWindow(m_hWnd);
                return FALSE;
            }
        }
    }

    return m_hWnd != NULL;
}

void Window::SubclassWindow(HWND hWnd) {
    if(m_oldWndProc == NULL) {
        // Let PreSubclassWindow have a go first
        PreSubclassWindow();

        // Hook in our window proc
        m_hWnd = hWnd;
        SetWindowLong(m_hWnd, GWL_USERDATA, (LONG)this);
        m_oldWndProc = (WNDPROC)GetWindowLong(m_hWnd, GWL_WNDPROC);
        SetWindowLong(m_hWnd, GWL_WNDPROC, (LONG)StaticWndProc);
    }
}

void Window::SubclassDlgItem(UINT nID, Window* pParent) {
    SubclassWindow(pParent->GetDlgItem(nID));
}

ATOM Window::RegisterClass(WNDCLASS* pwc) {
    // Setup our window proc as the class wndproc
    pwc->lpfnWndProc = (WNDPROC)StaticWndProc;
    return ::RegisterClass(pwc);
}

ATOM Window::RegisterClassEx(WNDCLASSEX* pwcex) {
    // Setup our window proc as the class wndproc
    pwcex->lpfnWndProc = (WNDPROC)StaticWndProc;
    return ::RegisterClassEx(pwcex);
}

LRESULT Window::StaticWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam) {
    Window* pThis;
    switch(nMsg) {
    case WM_CREATE:
        pThis = (Window*)((LPCREATESTRUCT)lParam)->lpCreateParams;
        ASSERT(pThis);
        pThis->m_hWnd = hWnd;
        SetWindowLong(hWnd, GWL_USERDATA, (long)pThis);
        break;
    case WM_INITDIALOG:
        pThis = (Window*)lParam;
        ASSERT(pThis);
        pThis->m_hWnd = hWnd;
        SetWindowLong(hWnd, GWL_USERDATA, lParam);
        break;
    case WM_NCDESTROY:
        pThis = (Window*)GetWindowLong(hWnd, GWL_USERDATA);
        ASSERT(pThis);
        if(pThis->m_oldWndProc) {
            // Unhook our window proc...
            SetWindowLong(hWnd, GWL_WNDPROC, (LONG)pThis->m_oldWndProc);
            // ...call the old wndproc...
            (*pThis->m_oldWndProc)(hWnd, nMsg, wParam, lParam);
            pThis->m_oldWndProc = NULL;
        }
        // ...then finally call PostNcDestroy
        pThis->m_hWnd = NULL;
        pThis->PostNcDestroy();
        return 0L;
    default:
        pThis = (Window*)GetWindowLong(hWnd, GWL_USERDATA);
        break;
    }

    // Dispatch to the instance WndProc
    return pThis?pThis->WndProc(nMsg, wParam, lParam):
                    DefWindowProc(hWnd, nMsg, wParam, lParam);
}

LRESULT Window::WndProc(UINT nMsg, WPARAM wParam, LPARAM lParam) {
    // Called to handle a message
    //
    // The default implementation handles OnCreate, OnDestroy,
    // and OnCommand; it passes all other messages onto the
    // default handler (either the old window proc or DefWindowProc).
    //
    // You may override in your derived class to handle additional
    // messages.

    switch (nMsg) {
    case WM_CREATE:
        return OnCreate((LPCREATESTRUCT)lParam);
    case WM_DESTROY:
        OnDestroy();
        return 0L;
    case WM_COMMAND:
        return OnCommand(LOWORD(wParam), HIWORD(wParam), (HWND)lParam);
    }

    // Default behaviour is to hand off to the original window proc
    return m_oldWndProc?(*m_oldWndProc)(m_hWnd, nMsg, wParam, lParam):
            DefWindowProc(m_hWnd, nMsg, wParam, lParam);
}

BOOL Window::PreCreateWindow(CREATESTRUCT &cs) {
    // Called just prior to creation of a new window
    //
    // The default implementation does nothing.
    //
    // You can override this in your derived class to change
    // window styles, register new window classes, etc.
    //
    // Return FALSE to abort window creation.

    return TRUE;
}

void Window::PreSubclassWindow() {
    // Called just prior to subclassing an existing window
    //
    // The default implementation does nothing.
    //
    // You can override in your derived class to register new
    // window classes, etc.
}

void Window::PostNcDestroy() {
    // This is called after WM_NCDESTROY has been handled.  The
    // window should now be considered destroyed; this is the last
    // message the window will ever see.
    //
    // The default implementation does nothing.
    //
    // You can override in your derived class to do final cleanup, such
    // as doing "delete this" on objects that reqire explicit deletion. 
}

int Window::OnCreate(LPCREATESTRUCT lpCreateStruct) {
    // This is called just after window creation.
    //
    // Note that for Dialogs, this method is never called; see
    // Dialog::OnInitDialog.
    //
    // The default implementation calls the DefWindowProc.
    //
    // Return 0 if successful; to abort window creation, return -1.

    return DefWindowProc(m_hWnd, WM_CREATE, 0, (LPARAM)lpCreateStruct);
}

void Window::OnDestroy() {
    // This is called just as the window is being destroyed.
    //
    // The default implementation calls the DefWindowProc.
    //
    // You can override in your derived class to release any resources
    // or do cleanup as needed.

    DefWindowProc(m_hWnd, WM_DESTROY, 0, 0L);
}

BOOL Window::OnCommand(UINT nID, UINT nEvent, HWND hSource) {
    // This is called upon receipt of a WM_COMMAND message.
    //
    // You can override in your derived class to handle any commands
    // your window expects to receive.
    //
    // Return TRUE if you handle the message; FALSE otherwise.
    return FALSE;
}


/////////////////////////////////////////////////////////////////////////////
// Dialog superclass

Dialog::Dialog(LPCTSTR szTemplateName, Window* pParent) {
    m_szTemplateName = szTemplateName;
    m_pParent = pParent;
}

Dialog::Dialog(UINT nTempateID, Window* pParent) {
    m_szTemplateName = MAKEINTRESOURCE(nTempateID);
    m_pParent = pParent;
}

int Dialog::DoModal() {
    return DialogBoxParam(App::GetHInstance(), m_szTemplateName,
                            m_pParent?m_pParent->GetHWnd():App::GetMainHWnd(), 
                            (DLGPROC)StaticWndProc, (LPARAM)this);
}

LRESULT Dialog::WndProc(UINT nMsg, WPARAM wParam, LPARAM lParam) {
    // Called to handle a message
    //
    // The default implementation handles OnInitDialog and OnCommand; it
    // lets the dialog manager handle all other messages.
    //
    // You may override in your derived class to handle additional
    // messages.

    switch (nMsg) {
    case WM_INITDIALOG:
        return OnInitDialog();
    case WM_COMMAND:
        return OnCommand(LOWORD(wParam), HIWORD(wParam), (HWND)lParam);
    }

    // Unlike Window::WndProc, we don't want to call DefWindowProc here,
    // as the dialog manager will handle un-handled messages for us.
    return 0L;
}

BOOL Dialog::OnInitDialog() {
    // This is called just after dialog creation.  The dialog resources
    // have been created, but are not yet visible.
    //
    // The default implementation centres the dialog on the application.
    //
    // You can override in your derived class to initialize dialog
    // controls.
    //
    // Return TRUE unless you explicitly set the focus to a control.

    // Centre dialog in frame
    RECT rcDlg, rcFrame;
    GetWindowRect(&rcDlg);
    if(m_pParent)
        m_pParent->GetWindowRect(&rcFrame);
    else
        ::GetWindowRect(App::GetMainHWnd(), &rcFrame);
    OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top);
    OffsetRect(&rcDlg, (rcFrame.right - rcFrame.left - rcDlg.right) / 2,
                        (rcFrame.bottom - rcFrame.top - rcDlg.bottom) / 3);
    OffsetRect(&rcDlg, rcFrame.left, rcFrame.top);
    MoveWindow(rcDlg, FALSE);

    // Return TRUE, as we have not set focus.
    return TRUE;
}

BOOL Dialog::OnCommand(UINT nID, UINT nEvent, HWND hSource) {
    // This is called upon receipt of a WM_COMMAND message.
    //
    // You can override in your derived class to handle any commands
    // your dialog expects to receive.
    //
    // Return TRUE if you handle the message; FALSE otherwise.

    // Handle OK and CANCEL button actions
    if (nID == IDOK || nID == IDCANCEL) { 
        EndDialog(m_hWnd, nID);
        return TRUE;
    }

    return Window::OnCommand(nID, nEvent, hSource);
}

<< Back to Shisen-Sho