//---------------------------------------------------------------------------- // ObjectWindows // (C) Copyright 1992, 1994 by Borland International, All Rights Reserved // // Implementation of class TDecoratedFrame //---------------------------------------------------------------------------- #pragma hdrignore SECTION #include #include #include DIAG_DECLARE_GROUP(OwlWin); // diagnostic group for windows #if !defined(SECTION) || SECTION == 1 DEFINE_RESPONSE_TABLE2(TDecoratedFrame, TFrameWindow, TLayoutWindow) EV_WM_ENTERIDLE, EV_WM_MENUSELECT, EV_WM_SIZE, // inline in decframe.h END_RESPONSE_TABLE; const uint BLANK_HELP = UINT_MAX; TDecoratedFrame::TDecoratedFrame(TWindow* parent, const char far* title, TWindow* clientWnd, bool trackMenuSelection, TModule* module) : TLayoutWindow(parent, title, module) { // // Initialize virtual bases, in case the derived-most used default ctor // TWindow::Init(parent, title, module); TFrameWindow::Init(clientWnd, false); TLayoutMetrics metrics; TrackMenuSelection = trackMenuSelection; MenuItemId = 0; SettingClient = false; if (!ClientWnd) ClientWnd = new TWindow(this, "\007"); // create dummy placeholder if (ClientWnd->Attr.Style & WS_BORDER) { metrics.X.SameAs(lmParent, lmLeft); metrics.Y.SameAs(lmParent, lmTop); metrics.Width.SameAs(lmParent, lmRight); metrics.Height.SameAs(lmParent, lmBottom); } else { metrics.X.Set(lmLeft, lmRightOf, lmParent, lmLeft); metrics.Y.Set(lmTop, lmBelow, lmParent, lmTop); metrics.Width.Set(lmRight, lmLeftOf, lmParent, lmRight); metrics.Height.Set(lmBottom, lmAbove, lmParent, lmBottom); } SetChildLayoutMetrics(*ClientWnd, metrics); } void TDecoratedFrame::SetupWindow() { TRect clientRect = GetClientRect(); TFrameWindow::SetupWindow(); // // size/position child windows. don't wait until we get the WM_SIZE event // because by then the windows will have been displayed on the screen // ClientSize.cx = clientRect.right; ClientSize.cy = clientRect.bottom; Layout(); } // // Handle SetClientWindow() here to manage fixing up the layout metrics of // all the children before & after the client is changed. // TWindow* TDecoratedFrame::SetClientWindow(TWindow* clientWnd) { TLayoutMetrics metrics; GetChildLayoutMetrics(*ClientWnd, metrics); if (!clientWnd) clientWnd = new TWindow(this, "\007"); // create dummy placeholder clientWnd->SetParent(this); SetChildLayoutMetrics(*clientWnd, metrics); TWindow* oldWnd = GetClientWindow(); // Make sure that all child metrics that were based on the old client window // get updated to the new client // TWindow* first = GetFirstChild(); if (first) { TWindow* child = first; do { if (GetChildLayoutMetrics(*child, metrics)) { if (metrics.X.RelWin == oldWnd) metrics.X.RelWin = clientWnd; if (metrics.Y.RelWin == oldWnd) metrics.Y.RelWin = clientWnd; if (metrics.Width.RelWin == oldWnd) metrics.Width.RelWin = clientWnd; if (metrics.Height.RelWin == oldWnd) metrics.Height.RelWin = clientWnd; SetChildLayoutMetrics(*child, metrics); } child = child->Next(); } while (child != first); } // Now let the TFrameWindow set the client. Then delete the old client if it // was our temporary place holder. Set a flag while the client is being set // so that RemoveChild() below knows that we are taking care of things. // SettingClient = true; oldWnd = TFrameWindow::SetClientWindow(clientWnd); SettingClient = false; if (HIWORD(oldWnd->Title) && oldWnd->Title[0] == 007) { oldWnd->Destroy(); delete oldWnd; oldWnd = 0; } // Relayout the children to get the new client sized right // Layout(); return oldWnd; } // // Make sure that both bases get a chance to see the child removed. TWindow's // will be called twice, but the second call will be ignored. If we are // removing the client, and we are not in the process of setting the client, // then call SetClientWindow to put in a placeholder. // void TDecoratedFrame::RemoveChild(TWindow* child) { if (child == ClientWnd && !SettingClient) SetClientWindow(0); TFrameWindow::RemoveChild(child); TLayoutWindow::RemoveChild(child); } bool TDecoratedFrame::PreProcessMsg(MSG& msg) { // // give the decorations an opportunity to do pre-processing. don't bother // checking the client window since it is typically in the focus chain and // will be given an opportunity to do pre-processing // TWindow* firstChild = GetFirstChild(); if (firstChild) { TWindow* child = firstChild; do { if (child != ClientWnd && child->HWindow && (child->GetWindowLong(GWL_STYLE) & WS_VISIBLE) && child->PreProcessMsg(msg)) return true; child = child->Next(); } while (child != firstChild); } return TFrameWindow::PreProcessMsg(msg); } // // Handle WM_MENUSELECT to provide hint text in the status bar based on the // menu item id. Treat popup items seperatly and ask them for their ids. // void TDecoratedFrame::EvMenuSelect(uint menuItemId, uint flags, HMENU hMenu) { if (TrackMenuSelection) { if (flags == 0xFFFF && hMenu == 0) { // menu closing TMessageBar* messageBar = (TMessageBar*)ChildWithId(IDW_STATUSBAR); CHECK(messageBar); messageBar->SetHintText(0); MenuItemId = 0; // restore status bar to normal look } else if (flags & MF_POPUP) { TMenu popupMenu(hMenu); int count = popupMenu.GetMenuItemCount(); for (int pos = 0; pos < count && popupMenu.GetSubMenu(pos) != HMENU(menuItemId); pos++) ; MenuItemId = popupMenu.GetMenuItemID(pos); } else if (flags & (MF_SEPARATOR | MF_MENUBREAK | MF_MENUBARBREAK) || (menuItemId >= IDW_FIRSTMDICHILD && menuItemId < IDW_FIRSTMDICHILD+9)) { MenuItemId = BLANK_HELP; // display an empty help message // could also restore bar at this point too } else { MenuItemId = menuItemId; // display a help message with this string Id } } } void TDecoratedFrame::EvEnterIdle(uint source, HWND hWndDlg) { if (source == MSGF_MENU && MenuItemId) { char buf[128]; TMessageBar* messageBar = (TMessageBar*)ChildWithId(IDW_STATUSBAR); CHECK(messageBar); if (MenuItemId != BLANK_HELP) { int numBytes = GetModule()->LoadString(MenuItemId, buf, sizeof(buf)); if (numBytes == 0 && MergeModule != 0) numBytes = MergeModule->LoadString(MenuItemId, buf, sizeof(buf)); WARNX(OwlWin, numBytes == 0, 0, "TDecoratedFrame::EvEnterIdle LoadString(" << *GetModule() << "," << MenuItemId << ") Failed"); } else *buf = 0; messageBar->SetHintText(buf); MenuItemId = 0; // Don't repaint on subsequent EvEnterIdle's } TFrameWindow::EvEnterIdle(source, hWndDlg); } // // Get or set location codes stashed in the style long for a decoration // inline TDecoratedFrame::TLocation GetLocation(TWindow& w) { return TDecoratedFrame::TLocation(uint16(w.Attr.Style) & 0x0F00); } inline void SetLocation(TWindow& w, TDecoratedFrame::TLocation loc) { w.Attr.Style = (w.Attr.Style&~0x0F00) | loc; } // // Find first sibling of a decoration in the same location // struct TSiblingSearch { TWindow* Decoration; TDecoratedFrame::TLocation Location; TWindow* Sibling; TSiblingSearch(TWindow* decoration, TDecoratedFrame::TLocation location) { Decoration = decoration; Sibling = 0; Location = location; } }; static bool FindSibling(TWindow* win, void* param) { TSiblingSearch* search = (TSiblingSearch*)param; if (win == search->Decoration) return true; else if (GetLocation(*win) == search->Location && win->IsWindowVisible()) search->Sibling = win; return false; } LRESULT TDecoratedFrame::EvCommand(uint id, HWND hWndCtl, uint notifyCode) { if (hWndCtl == 0) { TWindow* decoration = ChildWithId(id); if (decoration) { bool visible = decoration->IsWindowVisible(); TLocation location = GetLocation(*decoration); TSiblingSearch search(decoration, location); FirstThat(FindSibling, &search); if (!search.Sibling) search.Sibling = ClientWnd; // // toggle the window's visibility // if (visible) RemoveChildLayoutMetrics(*decoration); else switch (location) { case Top: InsertAtTop(*decoration, search.Sibling); break; case Bottom: InsertAtBottom(*decoration, search.Sibling); break; case Left: InsertAtLeft(*decoration, search.Sibling); break; case Right: InsertAtRight(*decoration, search.Sibling); break; } Layout(); decoration->ShowWindow(visible ? SW_HIDE : SW_SHOW); return 0; } } return TFrameWindow::EvCommand(id, hWndCtl, notifyCode); } void TDecoratedFrame::EvCommandEnable(TCommandEnabler& commandEnabler) { TWindow* decoration = ChildWithId(commandEnabler.Id); if (!decoration) TFrameWindow::EvCommandEnable(commandEnabler); else { commandEnabler.Enable(); commandEnabler.SetCheck(decoration->IsWindowVisible() ? TCommandEnabler::Checked : TCommandEnabler::Unchecked); } } void TDecoratedFrame::InsertAtTop(TWindow& decoration, TWindow* insertAbove) { TLayoutMetrics metrics; TWindow* insertBelow; // // get the layout metrics for "insertAbove" (the window the decoration is // going to be inserted above) // GetChildLayoutMetrics(*insertAbove, metrics); insertBelow = metrics.Y.RelWin; // // if "insertAbove" has a border then set its top edge to be the same as // the decoration's bottom edge; otherwise place its top edge below the // decoration's bottom edge // metrics.Y.Set(lmTop, insertAbove->Attr.Style & WS_BORDER ? lmSameAs : lmBelow, &decoration, lmBottom); SetChildLayoutMetrics(*insertAbove, metrics); // // now set the layout metrics for the decoration so its top edge is the same // as the bottom edge of "insertBelow"(i.e. they overlap) // if (!insertBelow) metrics.Y.SameAs(lmParent, lmTop); else metrics.Y.Set(lmTop, lmSameAs, insertBelow, lmBottom); metrics.Height.MyEdge = lmHeight; metrics.Height.Relationship = lmAsIs; metrics.X.Set(lmLeft, lmSameAs, lmParent, lmLeft); metrics.Width.Set(lmRight, lmSameAs, lmParent, lmRight); SetChildLayoutMetrics(decoration, metrics); } void TDecoratedFrame::InsertAtBottom(TWindow& decoration, TWindow* insertBelow) { TLayoutMetrics metrics; TWindow* insertAbove; // // get the layout metrics for "insertBelow"(the window the decoration is // going to be inserted below) // GetChildLayoutMetrics(*insertBelow, metrics); if (insertBelow == ClientWnd) { insertAbove = metrics.Height.RelWin; // // if the client window has a border then set the client window's bottom // to be the same as the top edge of the decoration; otherwise set the // client window's bottom edge to be above the decoration's top edge // metrics.Height.Set(lmBottom, ClientWnd->Attr.Style & WS_BORDER ? lmSameAs : lmAbove, &decoration, lmTop); } else { insertAbove = metrics.Y.RelWin; // // set the bottom edge of "insertBelow" to be the same as the top edge of // the decoration // metrics.Y.Set(lmBottom, lmSameAs, &decoration, lmTop); } SetChildLayoutMetrics(*insertBelow, metrics); // // now set the layout metrics for the decoration so its bottom edge is the // same as the top edge of "insertAbove" // if (!insertAbove) metrics.Y.SameAs(lmParent, lmBottom); else metrics.Y.Set(lmBottom, lmSameAs, insertAbove, lmTop); metrics.Height.MyEdge = lmHeight; metrics.Height.Relationship = lmAsIs; metrics.X.Set(lmLeft, lmSameAs, lmParent, lmLeft); metrics.Width.Set(lmRight, lmSameAs, lmParent, lmRight); SetChildLayoutMetrics(decoration, metrics); } void TDecoratedFrame::InsertAtLeft(TWindow& decoration, TWindow* insertLeftOf) { TLayoutMetrics metrics; TWindow* insertRightOf; // // get the layout metrics for "insertLeftOf"(the window the decoration is // going to be inserted to the left of) // GetChildLayoutMetrics(*insertLeftOf, metrics); insertRightOf = metrics.X.RelWin; // // if "insertLeftOf" has a border then set its left edge to be the same as // the decoration's right edge; otherwise place its left edge one pixel to // the right of the decoration's right edge // metrics.X.Set(lmLeft, insertLeftOf->Attr.Style & WS_BORDER ? lmSameAs : lmRightOf, &decoration, lmRight); SetChildLayoutMetrics(*insertLeftOf, metrics); // // now set the layout metrics for the decoration so it's left edge is the // same as the right edge of "insertRightOf" // if (!insertRightOf) metrics.X.SameAs(lmParent, lmLeft); else metrics.X.Set(lmLeft, lmSameAs, insertRightOf, lmRight); metrics.Width.MyEdge = lmWidth; metrics.Width.Relationship = lmAsIs; // // if the client window has a border then place the decoration so its // "y" and "bottom" are the same as the client windows; otherwise place // its "y" above the client window's "y" and its "bottom" below the client // window's "bottom" // // this way if there are top or bottom decorations they will be tiled // over/under the left/right decorations // if (ClientWnd->Attr.Style & WS_BORDER) { metrics.Y.SameAs(ClientWnd, lmTop); metrics.Height.SameAs(ClientWnd, lmBottom); } else { metrics.Y.Set(lmTop, lmAbove, ClientWnd, lmTop); metrics.Height.Set(lmBottom, lmBelow, ClientWnd, lmBottom); } SetChildLayoutMetrics(decoration, metrics); } void TDecoratedFrame::InsertAtRight(TWindow& decoration, TWindow* insertRightOf) { TLayoutMetrics metrics; TWindow* insertLeftOf; // // get the layout metrics for "insertRightOf"(the window the decoration is // going to be inserted to the right of) // GetChildLayoutMetrics(*insertRightOf, metrics); if (insertRightOf == ClientWnd) { insertLeftOf = metrics.Width.RelWin; // // if the client window has a border then set the client window's right // edge to be the same as the left edge of the decoration; otherwise set // the client window's right edge to be one pixel to the left of the // decoration's left edge // metrics.Width.Set(lmRight, ClientWnd->Attr.Style & WS_BORDER ? lmSameAs : lmLeftOf, &decoration, lmLeft); } else { insertLeftOf = metrics.X.RelWin; // // set the right edge of "insertRightOf" to be the same as the left edge of // the decoration // metrics.X.Set(lmRight, lmSameAs, &decoration, lmLeft); } SetChildLayoutMetrics(*insertRightOf, metrics); // // now set the layout metrics for the decoration so its right edge is the // same as the left edge of "insertLeftOf" // if (!insertLeftOf) metrics.X.Set(lmRight, lmSameAs, lmParent, lmRight); else metrics.X.Set(lmRight, lmSameAs, insertLeftOf, lmLeft); metrics.Width.MyEdge = lmWidth; metrics.Width.Relationship = lmAsIs; // // if the client window has a border then place the decoration so its // "y" and "bottom" are the same as the client windows; otherwise place // its "y" above the client window's "y" and its "bottom" below the client // window's "bottom" // // this way if there are top or bottom decorations they will be tiled // over/under the left/right decorations // if (ClientWnd->Attr.Style & WS_BORDER) { metrics.Y.SameAs(ClientWnd, lmTop); metrics.Height.SameAs(ClientWnd, lmBottom); } else { metrics.Y.Set(lmTop, lmAbove, ClientWnd, lmTop); metrics.Height.Set(lmBottom, lmBelow, ClientWnd, lmBottom); } SetChildLayoutMetrics(decoration, metrics); } // // Insert a decoration window into position at one of the four edges. // void TDecoratedFrame::Insert(TWindow& decoration, TLocation location) { SetLocation(decoration, location); // keep loc for removal & sibling inserts // Make sure the decoration has clipsiblings style, since our rearranging // causes corners to overlap sometimes. // decoration.Attr.Style |= WS_CLIPSIBLINGS; if (decoration.HWindow) decoration.SetWindowLong(GWL_STYLE, decoration.GetWindowLong(GWL_STYLE)|WS_CLIPSIBLINGS); decoration.SetParent(this); RemoveChildLayoutMetrics(decoration); // in case this is a re-insert if (decoration.Attr.Style & WS_VISIBLE) switch (location) { case Top: InsertAtTop(decoration, ClientWnd); break; case Bottom: InsertAtBottom(decoration, ClientWnd); break; case Left: InsertAtLeft(decoration, ClientWnd); break; case Right: InsertAtRight(decoration, ClientWnd); break; } } #endif #if !defined(SECTION) || SECTION == 2 IMPLEMENT_STREAMABLE3(TDecoratedFrame, TFrameWindow, TLayoutWindow, TWindow); void* TDecoratedFrame::Streamer::Read(ipstream& is, uint32 /*version*/) const { ReadVirtualBase((TFrameWindow*)GetObject(), is); ReadBaseObject((TLayoutWindow*)GetObject(), is); return GetObject(); } void TDecoratedFrame::Streamer::Write(opstream& os) const { WriteVirtualBase((TFrameWindow*)GetObject(), os); WriteBaseObject((TLayoutWindow*)GetObject(), os); } #endif