//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1991, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Implementation of class TFrameWindow, a TWindow with additional features
/// for frames, such as client window, menus, icons, etc.
//----------------------------------------------------------------------------
#include <owl/pch.h>
#include <stdio.h>
#include <owl/docking.h>
#include <owl/framewin.h>
#include <owl/applicat.h>
#include <owl/menu.h>
#include <owl/uimetric.h>
#include <owl/bardescr.h>
#include <algorithm>
using namespace std;
namespace owl {
#if !defined(WM_SETICON)
# define WM_SETICON 0x0080
#endif
OWL_DIAGINFO;
DIAG_DECLARE_GROUP(OwlCmd);
DEFINE_RESPONSE_TABLE1(TFrameWindow, TWindow)
EV_WM_QUERYDRAGICON,
EV_WM_INITMENUPOPUP,
EV_WM_SETFOCUS,
EV_WM_SIZE,
EV_WM_PARENTNOTIFY,
EV_WM_QUERYNEWPALETTE,
EV_WM_PALETTECHANGED,
END_RESPONSE_TABLE;
//
/// Overrides TCommandEnable::Enable. Enables or disables the menu options that
/// control the appearance of the corresponding menu item.
//
void
TMenuItemEnabler::Enable(bool enable)
{
TCommandEnabler::Enable(enable);
::EnableMenuItem(HMenu, Position,
MF_BYPOSITION | (enable ? MF_ENABLED : MF_GRAYED));
}
//
/// Overrides TCommandEnable::SetText. Changes the text of the corresponding menu
//
void
TMenuItemEnabler::SetText(LPCTSTR str)
{
::ModifyMenu(HMenu, Position, MF_BYPOSITION | MF_STRING, Id, str);
}
//
/// Overrides TCommandEnable::SetCheck. Checks or unchecks the corresponding menu
/// item. The state parameter reflects the menu item's state, which can be checked,
/// unchecked, or indeterminate.
//
void
TMenuItemEnabler::SetCheck(int state)
{
::CheckMenuItem(HMenu, Position,
MF_BYPOSITION | (state == Checked ? MF_CHECKED : MF_UNCHECKED));
}
//----------------------------------------------------------------------------
//
/// Constructs a window object with the parent window supplied in parent, which is
/// zero if this is the main window. title, which by default is zero, contains the
/// title displayed in the window's caption bar. clientWnd is the client window for
/// this frame window or zero if none exists. shrinkToClient controls whether the
/// client window will size to fit the frame or the frame window will fit the
/// client. Note that this parameter only affects the size of the main window. When
/// a client window is used in a frame window that doesn't have shrinktoClient set,
/// the client window resizes to fit the frame window. When a client window is used
/// in a frame window that has the shrinktoClient set, the frame window shrinks to
/// fit the size of the client window.
//
TFrameWindow::TFrameWindow(TWindow* parent,
LPCTSTR title,
TWindow* clientWnd,
bool shrinkToClient,
TModule* module)
{
// Initialize virtual base, in case the derived-most used default ctor
//
TWindow::Init(parent, title, module);
IconResId = 0; // remember that we still need to init
Init(clientWnd, shrinkToClient);
}
//
/// String-aware overload
//
TFrameWindow::TFrameWindow(
TWindow* parent,
const tstring& title,
TWindow* client,
bool shrinkToClient,
TModule* module)
{
TWindow::Init(parent, title, module);
IconResId = 0;
Init(client, shrinkToClient);
}
//
/// Smart-pointer-aware overload.
//
TFrameWindow::TFrameWindow(TWindow* parent, const tstring& title, unique_ptr<TWindow> client, bool shrinkToClient, TModule* module)
{
TWindow::Init(parent, title, module);
IconResId = nullptr;
Init(client.get(), shrinkToClient);
client.release(); // We took ownership;
}
//
/// Constructor for a TFrameWindow that is being used as an alias for a
/// non-ObjectWindows window. hWnd is the handle to the existing window object that
/// TFrameWindow controls; module contains the module passed to the base class's
/// contructor.
///
/// This constructor is generally not used by derived
/// classes, only as generic alias to a framewindow-like HWND
//
TFrameWindow::TFrameWindow(HWND THandle, TModule* module)
:
TWindow(THandle, module)
{
Init(0);
}
//
/// Protected constructor for use by immediate virtually derived classes.
/// Immediate derivitives must call Init() before constructions are done.
//
TFrameWindow::TFrameWindow()
{
IconResId = 0; // Zero this member to remember that we still need to init
}
//
/// Normal initialization of a default constructed TFrameWindow. Is ignored
/// if called more than once.
///
/// This initialize function is for use with virtually derived classes, which must
/// call Init before construction is completed. This procedure provides necessary
/// data to virtually derived classes and takes care of providing the data in the
/// appropriate sequence.
//
void
TFrameWindow::Init(TWindow* clientWnd, bool shrinkToClient)
{
if (!IconResId) {
Attr.Style = WS_OVERLAPPEDWINDOW | WS_VISIBLE;
Attr.X = Attr.W = CW_USEDEFAULT;
if (clientWnd)
Attr.Style |= WS_CLIPCHILDREN;
if (shrinkToClient)
SetFlag(wfShrinkToClient);
Init(clientWnd);
}
}
//
// Private initializer does a bulk of the common frame window initialization
//
void
TFrameWindow::Init(TWindow* clientWnd)
{
HWndRestoreFocus = 0;
KeyboardHandling = false;
ClientWnd = clientWnd;
MenuDescr = 0;
BarDescr = 0;
DeleteBar = false;
MergeModule = 0;
CurIcon = 0;
CurIconSm = 0;
IconModule = 0;
IconSmModule = 0;
SetIcon(&GetGlobalModule(), IDI_OWLAPP);
#if !defined( MAINWIN )
SetIconSm(&GetGlobalModule(), IDI_OWLAPPSM);
#endif
MinimizedPos = TPoint(-1,-1); // Windows convention for never minimized
if (ClientWnd) {
ClientWnd->SetParent(this);
ClientWnd->EnableAutoCreate(); // in case client is a dialog
SetBkgndColor(NoErase); // no need to erase client area
}
}
//
/// Destructor for a TFrameWindow
//
/// Deletes any associated menu descriptor.
//
TFrameWindow::~TFrameWindow()
{
if (IsFlagSet(wfMainWindow))
if (GetApplication()->GetMainWindow() == this)
GetApplication()->ClearMainWindow();
delete MenuDescr;
if(DeleteBar)
delete BarDescr;
}
//
// Return a state mask representing the enabled menu items (up to 32)
//
static uint32
GetMenuStateBits(HMENU hmenu, int count)
{
uint32 bit = 1;
uint32 result = 0;
for (int i = 0; i < count; i++) {
uint state = GetMenuState(hmenu, i, MF_BYPOSITION);
if (state != (uint)-1) {
if (!(state & (MF_DISABLED | MF_GRAYED))) {
result |= bit;
}
}
bit <<= 1;
}
return result;
}
//
/// Responds to WM_INITMENUPOPUP by performing a command enable run on each
/// of the menu items in the popup menu
///
/// Sent before a pop-up menu is displayed, EvInitMenuPopup lets an application
/// change the items on the menu before the menu is displayed. EvInitMenuPopup
/// controls whether the items on the pop-up menu are enabled or disabled, checked
/// or unchecked, or strings. HMENU indicates the menu handle. index is the index of
/// the pop-up menu. sysMenu indicates if the pop-up menu is the system menu.
//
void
TFrameWindow::EvInitMenuPopup(HMENU hPopupMenu, uint index, bool isSysMenu)
{
if (!isSysMenu && hPopupMenu) {
const int count = ::GetMenuItemCount(hPopupMenu);
// Save current state of visible top level menus
//
uint32 preState = 0;
if (hPopupMenu == GetMenu())
preState = GetMenuStateBits(hPopupMenu, count);
TWindow::EvInitMenuPopup(hPopupMenu, index, isSysMenu);
// If the top level menu state changes, redraw the menu bar
//
if (hPopupMenu == GetMenu())
if (GetMenuStateBits(hPopupMenu, count) != preState)
DrawMenuBar();
}
}
//
/// Overrides TWindow's virtual function. TApplication calls the main window's
/// IdleAction when no messages are waiting to be processed. TFrameWindow uses this
/// idle time to perform command enabling for the menu bar. It also forwards
/// IdleAction to each of its children. IdleAction can be overridden to do
/// background processing.
//
bool
TFrameWindow::IdleAction(long idleCount)
{
if (idleCount == 0) {
// do command enabling for the menu bar if this is the active task
//
if (GetFocus()) {
long style = ::GetWindowLong(*this, GWL_STYLE);
if (!(style & WS_CHILD)) {
if (IsWindow()) {
HMENU hMenu = ::GetMenu(*this);
if (IsMenu(hMenu)) {
HandleMessage(WM_INITMENUPOPUP, TParam1(hMenu));
HandleMessage(WM_UNINITMENUPOPUP, TParam1(hMenu));
}
}
}
}
}
// give child windows an opportunity to do any idle processing
//
return TWindow::IdleAction(idleCount);
}
//
/// Locates and returns the child window that is the target of the command and
/// command enable messages. If the current application does not have focus or if
/// the focus is within a toolbar in the application, GetCommandTarget returns the
/// most recently active child window.
///
/// If an alternative form of command processing is desired, a user's main window
/// class can override this function. TFrameWindow's EvCommand and EvCommandEnable
/// functions use GetCommandTarget to find the command target window. This member is
/// not available under Presentation Manager.
//
HWND
TFrameWindow::GetCommandTarget()
{
// Retrieve the focus window and our client
//
HWND hFocus = ::GetFocus();
TWindow* client = GetClientWindow();
// 1. The first candidate is a focus window that's a child of our client
//
if (hFocus && client && client->IsChild(hFocus)) {
TRACEX(OwlCmd, 1, "TFrameWindow::GetCommandTarget - focus, "\
"child of client: " << static_cast<void*>(hFocus));
return hFocus;
}
// 2. The next option is our client window itself
//
if (client) {
TRACEX(OwlCmd, 1, "TFrameWindow::GetCommandTarget - client: " << *client);
return *client;
}
// 3. The next option is a focus window that's a child of ours
//
if (hFocus && IsChild(hFocus)) {
TRACEX(OwlCmd, 1, "TFrameWindow::GetCommandTarget - focus, "\
<< static_cast<void*>(hFocus));
return hFocus;
}
// 4. If all of the above fail, resort to the last focus child of ours
//
if (HWndRestoreFocus) {
#if defined(__TRACE) || defined(__WARN)
TWindow* win = GetWindowPtr(HWndRestoreFocus);
if (!win) {
TRACEX(OwlCmd, 1, "TFrameWindow::GetCommandTarget - HwndRestoreFocus, "\
<< static_cast<void*>(HWndRestoreFocus));
} else {
TRACEX(OwlCmd, 1, "TFrameWindow::GetCommandTarget - HwndRestoreFocus, "\
<< *win);
}
#endif
return HWndRestoreFocus;
}
// 5. When all else fails, send ourselves in
//
TRACEX(OwlCmd, 1, "TFrameWindow::GetCommandTarget - self, " << *this);
return *this;
}
//
/// Handle WM_COMMAND to provide extra processing for commands:
/// Extra processing for commands: starts with the command target window
/// (usually the focus window) and gives it and its parent windows an
/// opportunity to handle the command.
//
TResult
TFrameWindow::EvCommand(uint id, HWND hCtl, uint notifyCode)
{
TRACEX(OwlCmd, 1, "TFrameWindow::EvCommand - id(" << id << "), ctl(" <<
static_cast<void*>(hCtl) << "), code(" << notifyCode << ")");
// Walk the command chain from the command target back up to us or until
// we hit a child that is an owl window. Delegate to owl-child or forward to
// our base if no child is found.
//
if (hCtl == 0) {
HWND hCmdTarget = GetCommandTarget();
// Check owl parentage too in case the HWNDs were reparented
//
while (hCmdTarget && hCmdTarget != GetHandle()) {
TWindow* cmdTarget = GetWindowPtr(hCmdTarget);
if (cmdTarget)
return cmdTarget->EvCommand(id, hCtl, notifyCode);
hCmdTarget = ::GetParent(hCmdTarget);
}
}
return TWindow::EvCommand(id, hCtl, notifyCode);
}
//
/// Handle WM_COMMAND_ENABLE to provide command enable distribution and default
/// support for windows without command enable handlers.
///
/// Handles checking and unchecking of the frame window's menu items.
/// EvCommandEnable uses TWindow's RouteCommandEnable member function to perform the
/// majority of this command enabling work.
//
void
TFrameWindow::EvCommandEnable(TCommandEnabler& commandEnabler)
{
// Don't process for windows out of our window tree (esp. other apps)
//
RouteCommandEnable(GetCommandTarget(), commandEnabler);
}
//
/// Overrides TWindow's virtual function. Performs preprocessing of window messages.
/// If the child window has requested keyboard navigation, PreProcessMsg handles any
/// accelerator key messages and then processes any other keyboard messages.
//
bool
TFrameWindow::PreProcessMsg(MSG& msg)
{
if (TWindow::PreProcessMsg(msg))
return true; // Processed accelerators
if (KeyboardHandling && msg.message >= WM_KEYFIRST &&
msg.message <= WM_KEYLAST)
{
HWND parent = ::GetParent(msg.hwnd);
// Retrieve the COMBO handle if we're in the EDIT ctrl parented to the
// combobox
//
tchar szClassName[0x10];
::GetClassName(parent, szClassName, COUNTOF(szClassName));
if (!_tcsicmp(szClassName, _T("COMBOBOX")))
parent = ::GetParent(parent);
if (parent && ::IsDialogMessage(parent, &msg))
return true;
}
return false;
}
//
/// Overrides TWindow's non-virtual SetMenu function, thus allowing derived classes
/// the opportunity to implement this function differently from TWindow. SetMenu
/// sets the window's menu to the menu indicated by newMenu. If newMenu is 0, the
/// window's current menu is removed. SetMenu returns 0 if the menu remains
/// unchanged; otherwise, it returns a nonzero value.
///
/// It also calls the application's PreProcessMenu() if it is the main window
/// to let it make any changes just before setting.
//
bool
TFrameWindow::SetMenu(HMENU newMenu)
{
if (IsFlagSet(wfMainWindow))
GetApplication()->PreProcessMenu(newMenu);
return TWindow::SetMenu(newMenu);
}
//
/// Perform a high-level menu assignment either before or after the HWND for the
/// window has been created.
///
/// Sets Attr.Menu to the supplied menuResId and frees any previous strings pointed
/// to by Attr.Menu. If HWindow is nonzero, loads and sets the menu of the window,
/// destroying any previously existing menu.
///
/// Returns true if successful; false otherwise
//
bool
TFrameWindow::AssignMenu(TResId menuResId)
{
if (menuResId != Attr.Menu) {
if (Attr.Menu.IsString())
delete[] Attr.Menu.GetString();
Attr.Menu = menuResId.IsString() ? TResId(strnewdup(menuResId)) : menuResId;
}
// If the window has been created then load and set the new menu and destroy
// the old menu
//
if (!GetHandle())
return true;
HMENU curMenu = GetMenu();
HMENU newMenu = LoadMenu(Attr.Menu);
if (!SetMenu(newMenu))
return false;
if (curMenu)
::DestroyMenu(curMenu);
return true;
}
//
/// Sets the icon in the module specified in iconModule to the resource ID specified
/// in iconResId. See the sample file BMPVIEW.CPP for an example of painting an icon
/// from a bitmap. You can set the iconResId to one of these predefined values as
/// well as user-defined values:
/// - \c \b IDI_APPLICATION Default icon used for applications
/// - \c \b IDI_ASTERISK Asterisk used for an informative message
/// - \c \b IDI_EXCLAMATION Exclamation mark used for a warning message
/// - \c \b IDI_HAND Hand used for warning messages
/// - \c \b IDI_QUESTION Question mark used for prompting a response
//
bool
TFrameWindow::SetIcon(TModule* module, TResId resId)
{
// Delete old icon if not system icon
//
if (CurIcon && IconModule) {
TUser::DestroyIcon(CurIcon);
CurIcon = 0;
}
IconModule = module;
IconResId = resId;
HINSTANCE hInstance = IconModule ? HINSTANCE(*IconModule) : HINSTANCE(0);
if (IconResId != 0)
CurIcon = TUser::LoadIcon(hInstance, IconResId);
if (CurIcon && IsWindow())
SendMessage(WM_SETICON, true, (LPARAM)(HICON)CurIcon);
return true;
}
//
/// Set the Small Icon (16 x 16)
//
bool
TFrameWindow::SetIconSm(TModule* module, TResId resId)
{
// Delete old small icon
//
if (CurIconSm && IconSmModule) {
TUser::DestroyIcon(CurIconSm);
CurIconSm = 0;
}
IconSmModule = module;
IconSmResId = resId;
HINSTANCE hInstance = IconSmModule ? HINSTANCE(*IconSmModule) : HINSTANCE(0);
if (IconSmResId != 0) {
CurIconSm = (HICON)::LoadImage(hInstance, IconSmResId, IMAGE_ICON,
TUIMetric::CxSmIcon, TUIMetric::CySmIcon,
LR_DEFAULTCOLOR);
if (!CurIconSm)
CurIconSm = TUser::LoadIcon(hInstance, IconSmResId);
}
if (CurIconSm && IsWindow())
SendMessage(WM_SETICON, false, (LPARAM)(HICON)CurIconSm);
return true;
}
//
/// Returns a pointer to the client window. If you are trying to access a
/// window-based object in a TMDIChild (which is a frame window), you can use this
/// function.
//
TWindow*
TFrameWindow::GetClientWindow()
{
return ClientWnd;
}
//
/// Sets the client window to the specified window. Users are responsible for
/// destroying the old client window if they want to eliminate it.
///
/// Assume clientWnd was parented to us.
//
TWindow*
TFrameWindow::SetClientWindow(TWindow* clientWnd)
{
TWindow* oldClientWnd = ClientWnd;
HWND oldWnd = oldClientWnd ? oldClientWnd->GetHandle() : (HWND)0;
RemoveChild(ClientWnd);
if (HWndRestoreFocus == oldWnd)
HWndRestoreFocus = 0;
ClientWnd = clientWnd;
if (ClientWnd) {
ClientWnd->SetParent(this);
if (GetHandle()) {
if (!ClientWnd->GetHandle())
ClientWnd->Create();
ClientWnd->ShowWindow(SW_SHOWNOACTIVATE);
}
SetBkgndColor(NoErase); // no need to erase client area
ResizeClientWindow(true); // !CQ defer repaint?
}
else
SetBkgndColor(NoColor); // will need to erase client area
// Pass the focus to the new client, but only if we have it currently
//
if (ClientWnd && ClientWnd->GetHandle() && GetFocus() == GetHandle()) {
ClientWnd->SetFocus();
HWndRestoreFocus = ClientWnd->GetHandle();
}
return oldClientWnd;
}
//
/// Smart-pointer-aware overload.
//
auto TFrameWindow::SetClientWindow(unique_ptr<TWindow> c) -> unique_ptr<TWindow>
{
auto old = unique_ptr<TWindow>{SetClientWindow(c.get())};
c.release(); // We took ownership.
return old;
}
//
/// If someone removes our client with a RemoveChild() call, update our client
/// and restore focus ptrs.
//
void
TFrameWindow::RemoveChild(TWindow* child)
{
TWindow::RemoveChild(child);
if (child) {
if (child == ClientWnd)
ClientWnd = 0;
if (child->GetHandle() == HWndRestoreFocus) {
HWndRestoreFocus = 0;
}
}
}
//
/// Overrides TWindow's virtual function. Pastes the number of the view into the
/// caption and then shows the number on the screen. This function can be overridden
/// if you don't want to use the default implementation, which displays a number on
/// the screen. That is, you might want to write "Two" instead of ":2" on the
/// screen. For an example of the behavior of this function, see step 12 of the
/// ObjectWindows tutorial, which renumbers the views if one of them is closed.
///
/// Generates a composite title based on the caption, docname, and index
/// if it is > 0.
/// \code
/// [<Title> - ]<docname>[:<index>]
/// \endcode
//
bool
TFrameWindow::SetDocTitle(LPCTSTR docname, int index)
{
if (index >= 0) {
tstring title;
LPCTSTR c = GetCaption();
if (c && *c) {
title = c;
title += _T(" - ");
}
if (docname)
title += docname;
if (index > 0) {
title += _T(":");
tchar num[10];
_stprintf(num, _T("%d"), index );
title += num;
}
SetWindowText(title.c_str());
}// else if index negative, simply acknowledge that title will display
return true;
}
//
// Obtain the real windows application icon. The IDI_APPLICATION icon is an
// ugly black & white box, but when a class is registered with this icon it
// gets substituted with a better windows icon. Worse case we end up with the
// ugly box icon.
//
static HICON
getAppIcon()
{
static HICON hRealAppIcon = 0;
if (!hRealAppIcon) {
WNDCLASS wndClass;
static tchar className[] = _T("IconSnarfer");
memset(&wndClass, 0, sizeof wndClass);
wndClass.hInstance = GetGlobalModule().GetHandle();
wndClass.hIcon = ::LoadIcon(0, IDI_APPLICATION);
wndClass.lpszClassName = className;
wndClass.lpfnWndProc = ::DefWindowProc;
::RegisterClass(&wndClass);
::GetClassInfo(GetGlobalModule().GetHandle(), className, &wndClass);
hRealAppIcon = wndClass.hIcon;
::UnregisterClass(className, GetGlobalModule().GetHandle());
}
return hRealAppIcon ? hRealAppIcon : ::LoadIcon(0, IDI_APPLICATION);
}
//
/// Responds to a WM_QUERYDRAGICON message sent to a minimized (iconic) window that
/// is going to be dragged. Instead of the default icon, EvQueryDragIcon uses the
/// icon that was set using SetIcon.
/// This member is not available under Presentation Manager.
//
HICON
TFrameWindow::EvQueryDragIcon()
{
// !JK Consider the following problems:
// !JK (1) If a derived class sets CurIcon (instantiated with something other
// !JK than a module & res id), this function will ignore it! Why do a
// !JK ::LoadIcon again? It won't actually load again anyway (see MS
// !JK Win16/Win32 doc). It doesn't reference count either.
// !JK
// !JK (2) If IconResId is non-zero but bad (i.e., ::LoadIcon fails), getAppIcon()
// !JK is returned; but if IconResId is zero, TWindow::EvQueryDragIcon() is
// !JK returned. What is the rationale behind returning one icon in the case
// !JK of a bad res id and a different icon in the case of a zero res id?
// !JK
// !JK This function body should be:
// !JK return (CurIcon)? CurIcon: TWindow::EvQueryDragIcon();
// !JK -or-
// !JK return (CurIcon)? CurIcon: getAppIcon();
if (IconResId) {
HINSTANCE hInstance = IconModule ? HINSTANCE(*IconModule) : HINSTANCE(0);
HICON hIcon = TUser::LoadIcon(hInstance, IconResId);
return hIcon ? hIcon : getAppIcon();
// !CQ This LoadIcon() may be causing a resource leak. May need to keep icon
// !CQ We are keeping it!!! CurIcon!!!
}
else
return TWindow::EvQueryDragIcon();
}
namespace {
auto IsEnabledVisibleChild_(long style) -> bool
{
return (style & (WS_CHILD | WS_VISIBLE | WS_DISABLED)) == (WS_CHILD | WS_VISIBLE);
}
auto SearchForChildWithTab_(TWindow* win) -> TWindow*
{
for (auto& child : win->GetChildren())
{
if (!child.GetHandle()) continue;
long style = child.GetWindowLong(GWL_STYLE);
if (IsEnabledVisibleChild_(style))
{
if (style & WS_TABSTOP)
return &child;
else if (const auto result = SearchForChildWithTab_(&child))
return result;
}
}
return nullptr;
}
} // namespace
//
// If the receiver doesn't have any children then returns 0. Otherwise
// we search for the first child with WS_TABSTOP; If no child has WS_TABSTOP
// then we return the first enabled visible child
//
// Does a depth-first search of nested child windows
//
// NOTE: we stop at the first child with WS_TABSTOP and do not search its
// children...
//
TWindow*
TFrameWindow::FirstChildWithTab()
{
const auto win = SearchForChildWithTab_(this);
if (win) return win;
auto c = GetChildren();
const auto i = find_if(c.begin(), c.end(), [](TWindow& w)
{ return w.GetHandle() && IsEnabledVisibleChild_(w.GetWindowLong(GWL_STYLE)); });
return (i != c.end()) ? &(*i) : nullptr;
}
//
/// Overrides TWindow's virtual function. Responds to a request by a child window to
/// hold its HWND when it is losing focus. Stores the child's HWND in
/// HwndRestoreFocus.
///
/// return true if caller can stop searching for a window to hold its handle.
//
bool
TFrameWindow::HoldFocusHWnd(HWND hWndLose, HWND hWndGain)
{
if (IsChild(hWndLose)) {
if (!hWndGain || !IsChild(hWndGain))
HWndRestoreFocus = hWndLose;
return true;
}
return hWndLose == GetHandle();
}
//
/// Restores the focus to the active window. hWndLostFocus contains the handle for
/// the window that lost focus.
///
/// Handle WM_SETFOCUS to return focus back to the child that had it most
/// recently, or find the best one to give it to otherwise.
//
void
TFrameWindow::EvSetFocus(HWND hWndLostFocus)
{
TWindow::EvSetFocus(hWndLostFocus);
if (!HWndRestoreFocus) {
HWND cmdTgt = GetCommandTarget();
if (cmdTgt && IsChild(cmdTgt))
HWndRestoreFocus = cmdTgt;
}
// if (HWndRestoreFocus == hWndLostFocus)
// HWndRestoreFocus = GetHandle();
if (HWndRestoreFocus) {
// Set focus to the saved HWND as long as it is still a valid window handle
//
if (::IsWindow(HWndRestoreFocus))
::SetFocus(HWndRestoreFocus);
else
HWndRestoreFocus = 0;
}
}
//
/// Responds to a message to notify the parent window that a given event has
/// occurred. If the client window is destroyed, closes the parent window. If
/// shrinkToClient is set and the child window has changed size, the frame is
/// adjusted.
/// When a TFrameWindow's client window is destroyed, the TFrameWindow object sees
/// the WM_PARENTNOTIFY message and posts a close message to itself. Without this
/// message, an empty frame would remain and the client window would then have to
/// determine how to destroy the frame. If you don't want this to happen, you can
/// derive from the frame window and have your application handle the EvParentNotify
/// or EvClose messages.
//
void
TFrameWindow::EvParentNotify(const TParentNotify& n)
{
switch (n.Event)
{
case WM_DESTROY:
{
const TParentNotifyChildInfo& i = static_cast<const TParentNotifyChildInfo&>(n);
if (ClientWnd && ClientWnd->GetHandle() == i.Child)
PostMessage(WM_CLOSE); // Using ShutDownWindow() has side effects.
TWindow* c = GetWindowPtr(i.Child);
if (c)
c->ClearFlag(wfFullyCreated);
}
break;
case WM_SIZE: // TODO: This is not a documented event for the WM_PARENTNOTIFY message. Investigate.
{
const TParentNotifyChildInfo& i = static_cast<const TParentNotifyChildInfo&>(n);
if (IsFlagSet(wfShrinkToClient) && ClientWnd && ClientWnd->GetHandle() == i.Child && !IsIconic())
ResizeClientWindow(true);
}
break;
}
DefaultProcessing();
}
//
/// Forwards the WM_QUERYNEWPALETTE message to the client window.
//
bool
TFrameWindow::EvQueryNewPalette()
{
if (GetClientWindow())
return GetClientWindow()->ForwardMessage();
else
return TWindow::EvQueryNewPalette();
}
//
/// Forwards the WM_PALETTECHANGED message to the client window.
//
void
TFrameWindow::EvPaletteChanged(HWND hWndPalChg)
{
if (GetClientWindow())
GetClientWindow()->ForwardMessage();
else
TWindow::EvPaletteChanged(hWndPalChg);
}
//
// Resize & reposition the client window to fit in this frames client area
// or resize the frame to fit around the client's client area if
// wfShrinkToClient
// Return true if a client was actualy resized.
// Adjust clients styles & make sure they get set.
//
bool
TFrameWindow::ResizeClientWindow(bool repaint)
{
/// bool hasThickFrame = Attr.Style & WS_THICKFRAME;
/// Attr.Style |= WS_THICKFRAME;
// Nothing to resize if there's not Client window
//
if (!ClientWnd)
return false;
// Prevent recursion during resize by ignore calls from EvParentNotify and
// EvSize when we have already been called.
// Do this by disabling notifications while resizing using the
// wfShrinkToClient flag as a semaphore on the client
//
if (ClientWnd->IsFlagSet(wfShrinkToClient))
return true;
ClientWnd->SetFlag(wfShrinkToClient);
bool clientResized = false;
TSize clientAreaSize = GetClientRect().Size();
TSize childSize = ClientWnd->GetWindowRect().Size();
// First time through, strip client window of thick borders.
// If shrink-to-client, then must measure the client size first
// If the client has scrolls bars, we must hide them to obtain the correct
// size.
// Border style is left on & dealt with by hand below
//
const uint32 badClientStyles = WS_DLGFRAME | WS_THICKFRAME | // bad borders
WS_POPUP | WS_OVERLAPPED; // bad parenting
const uint32 badClientExStyles = WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE |
WS_EX_STATICEDGE; // more bad borders
if ((ClientWnd->GetStyle() & badClientStyles) ||
(ClientWnd->GetExStyle() & badClientExStyles)) {
if (IsFlagSet(wfShrinkToClient)) {
TSize tstSize = ClientWnd->GetClientRect().Size();
ClientWnd->ShowScrollBar(SB_BOTH, false);
childSize = ClientWnd->GetClientRect().Size();
if (childSize != tstSize) {
int restore = SB_BOTH;
if (childSize.cx == tstSize.cx)
restore = SB_HORZ;
if (childSize.cy == tstSize.cy)
restore = SB_VERT;
ClientWnd->ShowScrollBar(restore, true);
}
}
if (ClientWnd->GetStyle() & badClientStyles) {
bool reparent = (ClientWnd->GetStyle() & (WS_POPUP|WS_OVERLAPPED)) != 0;
uint32 style = ClientWnd->GetStyle();
style &= ~badClientStyles;
style |= WS_CHILD | WS_BORDER | WS_VISIBLE;
ClientWnd->SetStyle( style );
if (reparent)
::SetParent(*ClientWnd, *this);
}
if (ClientWnd->GetExStyle() & badClientExStyles) {
uint32 exStyle = ClientWnd->GetExStyle();
exStyle &= ~badClientExStyles;
ClientWnd->SetExStyle( exStyle );
}
}
if (ClientWnd->GetStyle() & WS_BORDER) {
childSize = ClientWnd->GetClientRect().Size();
}
if (childSize != clientAreaSize) {
if (IsFlagSet(wfShrinkToClient)) {
TRect outside = GetWindowRect();
TSize border = outside.Size() - clientAreaSize;
SetWindowPos(0, 0, 0,
childSize.cx + border.cx, childSize.cy + border.cy,
SWP_NOACTIVATE|SWP_NOMOVE| (repaint ? 0 : SWP_NOREDRAW));
clientAreaSize = childSize; // Must move client, will not cause an EvSize
}
else {
clientResized = true; // Client will get resized
}
}
// If frame is sizeable, turn off flag so that user can then resize
// after initial setup
//
if (Attr.Style & WS_THICKFRAME && !TYPESAFE_DOWNCAST(this, TFloatingSlip))
ClearFlag(wfShrinkToClient);
/// if (!hasThickFrame) {
/// Attr.Style &= ~WS_THICKFRAME;
/// }
// Handle simple border style by shoving the client's borders under the frame
// This code MUST not resize the client if shrinkToClient
// !CQ use SetWindowPos() to get at SWP_NOSIZE?
//
if (ClientWnd->GetStyle() & WS_BORDER) {
int bx = TUIMetric::CxBorder;
int by = TUIMetric::CyBorder;
ClientWnd->MoveWindow(-bx, -by, clientAreaSize.cx+bx+bx,
clientAreaSize.cy+by+by, repaint);
}
else
ClientWnd->MoveWindow(0, 0, clientAreaSize.cx, clientAreaSize.cy, repaint);
// Turn off semaphore bit
//
ClientWnd->ClearFlag(wfShrinkToClient);
return clientResized;
}
//
/// Calls TWindow::SetUpWindow to create windows in a child list. SetupWindow
/// performs the initial adjustment of the client window if one exists, assigns the
/// frame's menu based on the menu descriptor, and initializes HwndRestoreFocus.
//
void
TFrameWindow::SetupWindow()
{
// Create windows in child list (this includes the client window)
//
TWindow::SetupWindow();
ResizeClientWindow(true); // !CQ defer repaint?
if (MinimizedPos != TPoint(-1,-1)) {
auto windata = GetWindowPlacement();
windata.flags = WPF_SETMINPOSITION;
windata.showCmd = SW_SHOWNA;
windata.ptMinPosition = MinimizedPos;
SetWindowPlacement(windata);
}
// If SetMenuDescr() was called before window created, update the menu now
//
if (IsFlagSet(wfMainWindow) && MenuDescr) {
HMENU curMenu = GetMenu();
TMenu newMenu(*MenuDescr, NoAutoDelete);
if (SetMenu(newMenu)) {
if (curMenu)
::DestroyMenu(curMenu);
}
else
::DestroyMenu(newMenu);
}
// If we haven't set THandleRestoreFocus then pick the first child with tabstop
//
if (!HWndRestoreFocus) {
///TH Previous version would be to search for first child with tabstop.
///TH Why not use CommandTarget?
// !BB Because it breaks when GetCommandTarget returns a non-child window
// !BB Please, leave this code AS IS?
#if 1
TWindow* win = FirstChildWithTab();
HWndRestoreFocus = win ? win->GetHandle() : GetHandle();
#else
HWND cmdTgt = GetCommandTarget();
if (cmdTgt && IsChild(cmdTgt))
HWndRestoreFocus = cmdTgt;
#endif
}
if (CurIcon)
SendMessage(WM_SETICON, true, (LPARAM)(HICON)CurIcon);
if (CurIconSm)
SendMessage(WM_SETICON, false, (LPARAM)(HICON)CurIconSm);
}
//
/// Cleans up any associated icons.
//
void
TFrameWindow::CleanupWindow()
{
// icon cleanup
//
SetIcon(0, 0);
SetIconSm(0, 0);
TWindow::CleanupWindow();
}
//
/// Tell child windows frame has minimized/maximized/restored
/// (They may want to change enabled state or release/restore resources)
//
void
TFrameWindow::BroadcastResizeToChildren(uint sizeType, const TSize& size)
{
if (sizeType == SIZE_MINIMIZED
|| sizeType == SIZE_MAXIMIZED
|| sizeType == SIZE_RESTORED)
{
ChildBroadcastMessage(WM_OWLFRAMESIZE, sizeType, reinterpret_cast<TParam2>(&size));
}
}
//
/// Response method for an incoming WM_SIZE message
///
/// Resizes the client window' so that it is equivalent to the client rectangle's
/// size. Calls TWindow::EvSize() in response to an incoming WM_SIZE message.
///
/// If no WM_SIZE sent, forwards WM_SIZE message to client so it can recalc.
//
void
TFrameWindow::EvSize(uint sizeType, const TSize& size)
{
TWindow::EvSize(sizeType, size);
TSize newSize = size;
if (ClientWnd) {
bool sizeSent = false;
if (sizeType != SIZE_MINIMIZED) {
sizeSent = ResizeClientWindow(true); // !CQ defer repaint?
newSize = ClientWnd->GetClientRect().Size();
}
if (!sizeSent)
ClientWnd->ForwardMessage();
}
BroadcastResizeToChildren(sizeType, newSize);
}
//
/// Sets the menu descriptor to the new menu descriptor.
//
void
TFrameWindow::SetMenuDescr(const TMenuDescr& menuDescr)
{
delete MenuDescr;
MenuDescr = new TMenuDescr(menuDescr);
if (IsFlagSet(wfMainWindow) && GetHandle()) {
HMENU curMenu = GetMenu();
TMenu newMenu(*MenuDescr, NoAutoDelete);
if (SetMenu(newMenu))
::DestroyMenu(curMenu);
else
::DestroyMenu(newMenu);
}
}
//
/// Sets the control bar descriptor to the new bar descriptor.
//
void
TFrameWindow::SetBarDescr(TBarDescr* barDescr, TAutoDelete delonClose)
{
if(DeleteBar)
delete BarDescr;
BarDescr = barDescr;
DeleteBar = delonClose == AutoDelete;
if (IsFlagSet(wfMainWindow) && GetHandle())
RestoreBar();
}
//
/// Merges the given menu descriptor with this frame's own menu descriptor and
/// displays the resulting menu in this frame. See TMenuDescr for a description of
/// menu bar types that can be merged.
///
/// Optionally use an existing HMENU to merge into & set
//
bool
TFrameWindow::MergeMenu(const TMenuDescr& childMenuDescr)
{
if (!MenuDescr || !GetHandle())
return false;
MergeModule = childMenuDescr.GetModule();
TMenu curMenu(*this, NoAutoDelete);
TMenu newMenu(NoAutoDelete);
MenuDescr->Merge(childMenuDescr, newMenu);
if (IsFlagSet(wfMainWindow))
GetApplication()->PreProcessMenu(newMenu);
if (SetMenu(newMenu)) {
::DestroyMenu(curMenu);
return true;
}
else {
::DestroyMenu(newMenu);
return false;
}
}
//
// Restores this frame window's menu to the one described by our menu
// descriptor
//
bool
TFrameWindow::RestoreMenu()
{
if (!MenuDescr)
return false;
HMENU curMenu = GetMenu();
TMenu newMenu(*MenuDescr, NoAutoDelete);
if (SetMenu(newMenu)) {
MergeModule = 0;
::DestroyMenu(curMenu);
}
else
::DestroyMenu(newMenu);
return true;
}
IMPLEMENT_STREAMABLE1(TFrameWindow, TWindow);
#if OWL_PERSISTENT_STREAMS
//
// Reads data of the uninitialized TFrameWindow from the passed ipstream
//
void*
TFrameWindow::Streamer::Read(ipstream& is, uint32 version) const
{
TFrameWindow* o = GetObject();
ReadVirtualBase((TWindow*)o, is);
if (o->IsFlagSet(wfMainWindow))
return o;
is >> o->ClientWnd;
is >> o->KeyboardHandling;
o->HWndRestoreFocus = 0;
bool hasMenuDescr = is.readByte();
if (hasMenuDescr) {
o->MenuDescr = new TMenuDescr;
is >> *o->MenuDescr;
}
else
o->MenuDescr = 0;
o->BarDescr = 0;
if(version > 2){
bool hasBarDescr = is.readByte();
if (hasBarDescr) {
o->BarDescr = new TBarDescr;
is >> *o->BarDescr;
}
}
// stream in window icon information
//
is >> o->IconModule;
is >> o->IconResId;
if (version > 1) {
is >> o->IconSmModule;
is >> o->IconSmResId;
}
else {
o->IconSmModule = 0;
o->IconSmResId = 0;
}
// load the window's icons
//
o->CurIcon = 0;
o->CurIconSm = 0;
o->SetIcon(o->IconModule, o->IconResId);
o->SetIconSm(o->IconSmModule, o->IconSmResId);
is >> o->MergeModule;
is >> o->MinimizedPos;
return o;
}
//
// Writes data of the TFrameWindow to the passed opstream
//
void
TFrameWindow::Streamer::Write(opstream& os) const
{
TFrameWindow* o = GetObject();
WriteVirtualBase((TWindow*)o, os);
if (o->IsFlagSet(wfMainWindow))
return;
os << o->ClientWnd;
os << o->KeyboardHandling;
os.writeByte(uint8(o->MenuDescr ? 1 : 0));
if (o->MenuDescr)
os << *o->MenuDescr;
// added in stream ver 3
os.writeByte(uint8(o->BarDescr ? 1 : 0));
if (o->BarDescr)
os << *o->BarDescr;
os << o->IconModule;
os << o->IconResId;
os << o->IconSmModule; // added in stream ver 2
os << o->IconSmResId; // added in stream ver 2
os << o->MergeModule;
auto windata = o->GetWindowPlacement();
os << TPoint(windata.ptMinPosition);
}
#endif
} // OWL namespace
↑ V601 The 'true' value is implicitly cast to the integer type. Inspect the second argument.
↑ V601 The 'false' value is implicitly cast to the integer type. Inspect the second argument.
↑ V601 The 'true' value is implicitly cast to the integer type. Inspect the second argument.
↑ V601 The 'false' value is implicitly cast to the integer type. Inspect the second argument.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: KeyboardHandling, HWndRestoreFocus, ClientWnd, DocTitleIndex, MergeModule, MenuDescr, ...
↑ V821 Decreased performance. The 'curMenu' variable can be constructed in a lower level scope.