//---------------------------------------------------------------------------- // ObjectWindows // (C) Copyright 1992, 1994 by Borland International, All Rights Reserved // // Implementation of class TGadgetWindow. //---------------------------------------------------------------------------- #include #include #include DEFINE_RESPONSE_TABLE1(TGadgetWindow, TWindow) EV_WM_CTLCOLOR, EV_WM_LBUTTONDOWN, EV_WM_LBUTTONUP, EV_WM_LBUTTONDBLCLK, EV_WM_MOUSEMOVE, EV_WM_SIZE, EV_WM_SYSCOLORCHANGE, END_RESPONSE_TABLE; static inline int HeightInPixels(int pointSize) { TScreenDC dc; return MulDiv(-pointSize, dc.GetDeviceCaps(LOGPIXELSY), 72); } TGadgetWindowFont::TGadgetWindowFont(int pointSize, bool bold, bool italic) : TFont("MS Sans Serif", HeightInPixels(pointSize), 0, 0, 0, bold ? FW_BOLD : FW_NORMAL, uint8(VARIABLE_PITCH | FF_SWISS), uint8(italic)) { } TGadgetWindow::TGadgetWindow(TWindow* parent, TTileDirection direction, TFont* font, TModule* module) : TWindow(parent, 0, module) { PRECONDITION(font); Attr.Style |= WS_BORDER; Capture = 0; AtMouse = 0; Font = font; Direction = direction; if (Direction == Horizontal) { ShrinkWrapWidth = false; ShrinkWrapHeight = true; } else { ShrinkWrapWidth = true; ShrinkWrapHeight = false; } HintMode = PressHints; WideAsPossible = 0; // number of gadgets with "WideAsPossible" set DirtyLayout = true; Gadgets = 0; NumGadgets = 0; // // compute the font height // TScreenDC dc; TEXTMETRIC metrics; dc.SelectObject(*Font); dc.GetTextMetrics(metrics); FontHeight = metrics.tmHeight + metrics.tmExternalLeading; // // choose a default height based on the height of the font plus room // for a top and bottom border // Attr.H = FontHeight; if (Attr.Style & WS_BORDER) Attr.H += 2 * GetSystemMetrics(SM_CYBORDER); SetBkgndColor(GetSysColor(COLOR_BTNFACE)); BkgndBrush = new TBrush(GetSysColor(COLOR_BTNFACE)); } TGadgetWindow::~TGadgetWindow() { TGadget* gadget = Gadgets; while (gadget) { TGadget* tmp = gadget; gadget = gadget->Next; delete tmp; } delete Font; delete BkgndBrush; } void TGadgetWindow::EvSysColorChange() { for (TGadget* g = Gadgets; g; g = g->NextGadget()) g->SysColorChange(); SetBkgndColor(GetSysColor(COLOR_BTNFACE)); delete BkgndBrush; BkgndBrush = new TBrush(GetSysColor(COLOR_BTNFACE)); } bool TGadgetWindow::IdleAction(long idleCount) { if (idleCount == 0) { // See if we missed a mouse move & still need to send a MouseLeave to a // gadget // if (AtMouse) { TPoint crsPoint; GetCursorPos(crsPoint); if (WindowFromPoint(crsPoint) != HWindow) HandleMessage(WM_MOUSEMOVE, 0, MAKELPARAM(-1,-1)); // nowhere } // Let the gadgets do command enabling if they need to // for (TGadget* g = Gadgets; g; g = g->NextGadget()) g->CommandEnable(); } return TWindow::IdleAction(idleCount); } TGadget* TGadgetWindow::GadgetWithId(int id) const { for (TGadget* g = Gadgets; g; g = g->NextGadget()) if (g->GetId() == id) return g; return 0; } void TGadgetWindow::SetShrinkWrap(bool shrinkWrapWidth, bool shrinkWrapHeight) { ShrinkWrapWidth = shrinkWrapWidth; ShrinkWrapHeight = shrinkWrapHeight; } bool TGadgetWindow::GadgetSetCapture(TGadget& gadget) { if (Capture) { return false; } else { Capture = &gadget; SetCapture(); return true; } } void TGadgetWindow::GadgetReleaseCapture(TGadget& gadget) { if (&gadget == Capture) { Capture = 0; ReleaseCapture(); } } // // Simulate menu select & idle messages to our parent so that it can display // hint text the same way it does for menu commands // void TGadgetWindow::SetHintCommand(int id) { if (Parent) { if (id > 0) Parent->HandleMessage(WM_MENUSELECT, id, 0); else // Send a menuselect w/ flags==0xFFFF and hMenu==0 to indicate end // #if defined(BI_PLAT_WIN32) Parent->HandleMessage(WM_MENUSELECT, 0xFFFF0000, 0); // flags+item, hmenu #else Parent->HandleMessage(WM_MENUSELECT, 0, 0x0000FFFFL);// item, hmenu+flags #endif Parent->HandleMessage(WM_ENTERIDLE, MSGF_MENU); } } void TGadgetWindow::SetMargins(TMargins& margins) { Margins = margins; LayoutSession(); } int TGadgetWindow::LayoutUnitsToPixels(int units) { const long UnitsPerEM = 8; return int((long(units) * FontHeight + UnitsPerEM / 2) / UnitsPerEM); } void TGadgetWindow::GetMargins(TMargins& margins, int& left, int& right, int& top, int& bottom) { switch (margins.Units) { case TMargins::Pixels: left = margins.Left; top = margins.Top; right = margins.Right; bottom = margins.Bottom; break; case TMargins::LayoutUnits: left = LayoutUnitsToPixels(margins.Left); top = LayoutUnitsToPixels(margins.Top); right = LayoutUnitsToPixels(margins.Right); bottom = LayoutUnitsToPixels(margins.Bottom); break; case TMargins::BorderUnits: int cxBorder = GetSystemMetrics(SM_CXBORDER); int cyBorder = GetSystemMetrics(SM_CYBORDER); left = margins.Left * cxBorder; top = margins.Top * cyBorder; right = margins.Right * cxBorder; bottom = margins.Bottom * cyBorder; break; } } void TGadgetWindow::LayoutSession() { if (HWindow) InvalidateRect(TileGadgets()); } void TGadgetWindow::GetDesiredSize(TSize& size) { int left, right, top, bottom; int maxWidth = 0, maxHeight = 0; TGadget* lastGadget = 0; GetMargins(Margins, left, right, top, bottom); if (ShrinkWrapWidth || ShrinkWrapHeight) { for (TGadget* gadget = Gadgets; gadget; gadget = gadget->Next) { TSize size; gadget->GetDesiredSize(size); if (size.cx > maxWidth) maxWidth = size.cx; if (size.cy > maxHeight) maxHeight = size.cy; } // // in some cases we need to actually tile the gadgets in order to tell how // much room is needed // if (Direction == Horizontal && ShrinkWrapWidth || Direction == Vertical && ShrinkWrapHeight) { TileGadgets(); for (lastGadget = Gadgets; lastGadget && lastGadget->Next; lastGadget = lastGadget->Next) ; } } if (!ShrinkWrapWidth) size.cx = Attr.W; else if (Direction == Horizontal) { size.cx = right; if (lastGadget) size.cx += lastGadget->GetBounds().right; } else { size.cx = left + right + maxWidth; if (Attr.Style & WS_BORDER) size.cx += 2 * GetSystemMetrics(SM_CXBORDER); } if (!ShrinkWrapHeight) size.cy = Attr.H; else if (Direction == Vertical) { size.cx = bottom; if (lastGadget) size.cx += lastGadget->GetBounds().bottom; } else { size.cy = top + bottom + maxHeight; if (Attr.Style & WS_BORDER) size.cy += 2 * GetSystemMetrics(SM_CYBORDER); } } bool TGadgetWindow::Create() { TSize size; GetDesiredSize(size); if (ShrinkWrapWidth) Attr.W = size.cx; if (ShrinkWrapHeight) Attr.H = size.cy; bool r = TWindow::Create(); GetDesiredSize(size); if ((ShrinkWrapWidth && Attr.W != size.cx) || (ShrinkWrapHeight && Attr.H != size.cy)) { if (ShrinkWrapWidth) Attr.W = size.cx; if (ShrinkWrapHeight) Attr.H = size.cy; LayoutSession(); } return r; } // // Set direction, & default shrink wrap flags. If already created, then also // adjust shrink wrap dimension & re-layout // void TGadgetWindow::SetDirection(TTileDirection direction) { if (Direction != direction) { Direction = direction; if (Direction == Horizontal) { ShrinkWrapWidth = false; ShrinkWrapHeight = true; } else { ShrinkWrapWidth = true; ShrinkWrapHeight = false; } // // Swap margin's X & Y axis // int t = Margins.Left; Margins.Left = Margins.Top; Margins.Top = t; t = Margins.Right; Margins.Right = Margins.Bottom; Margins.Bottom = t; if (HWindow) { TSize size; GetDesiredSize(size); if (ShrinkWrapWidth) Attr.W = size.cx; if (ShrinkWrapHeight) Attr.H = size.cy; LayoutSession(); } } } void TGadgetWindow::GetInnerRect(TRect& rect) { int left, right, top, bottom; GetMargins(Margins, left, right, top, bottom); rect.left = left; rect.right = Attr.W - right; rect.top = top; rect.bottom = Attr.H - bottom; if (Attr.Style & WS_BORDER) { rect.right -= 2 * GetSystemMetrics(SM_CYBORDER); rect.bottom -= 2 * GetSystemMetrics(SM_CXBORDER); } } void TGadgetWindow::PositionGadget(TGadget*, TGadget*, TPoint&) { } TRect TGadgetWindow::TileHorizontally() { TRect innerRect; TRect invalidRect; int width = 0; int x; GetInnerRect(innerRect); invalidRect.SetEmpty(); // // first pass tally the width of all gadgets that don't have "WideAsPossible" // set // // NOTE: we must also take into account any adjustments to the gadget spacing // for (TGadget* gadget = Gadgets; gadget; gadget = gadget->Next) { if (!gadget->WideAsPossible) { TSize desiredSize; gadget->GetDesiredSize(desiredSize); width += desiredSize.cx; } if (gadget->Next) { TPoint spacing(0, 0); PositionGadget(gadget, gadget->Next, spacing); width += spacing.x; } } // // now tile all the gadgets // x = innerRect.left; for (gadget = Gadgets; gadget; gadget = gadget->Next) { TRect bounds = gadget->Bounds; TRect originalBounds(bounds); TSize desiredSize; bounds.left = x; gadget->GetDesiredSize(desiredSize); bounds.top = innerRect.top + (innerRect.Height() - desiredSize.cy) / 2; bounds.bottom = bounds.top + desiredSize.cy; if (gadget->WideAsPossible) bounds.right = bounds.left + (innerRect.Width() - width) / WideAsPossible; else bounds.right = bounds.left + desiredSize.cx; if (bounds != originalBounds) { gadget->SetBounds(bounds); invalidRect |= bounds; if (originalBounds.TopLeft() != TPoint(0, 0)) invalidRect |= originalBounds; } x += bounds.Width(); if (gadget->Next) { TPoint origin(x, 0); PositionGadget(gadget, gadget->Next, origin); x = origin.x; } } return invalidRect; } TRect TGadgetWindow::TileVertically() { TRect innerRect; TRect invalidRect; int y; GetInnerRect(innerRect); invalidRect.SetEmpty(); // // now tile all the gadgets // y = innerRect.top; for (TGadget* gadget = Gadgets; gadget; gadget = gadget->Next) { TRect bounds = gadget->Bounds; TRect originalBounds(bounds); TSize desiredSize; gadget->GetDesiredSize(desiredSize); bounds.top = y; bounds.bottom = bounds.top + desiredSize.cy; bounds.left = innerRect.left + (innerRect.Width() - desiredSize.cx) / 2; bounds.right = bounds.left + desiredSize.cx; if (bounds != originalBounds) { gadget->SetBounds(bounds); invalidRect |= bounds; if (originalBounds.TopLeft () != TPoint (0, 0)) invalidRect |= originalBounds; } y += bounds.Height(); if (gadget->Next) { TPoint origin(0, y); PositionGadget(gadget, gadget->Next, origin); y = origin.y; } } return invalidRect; } TRect TGadgetWindow::TileGadgets() { return (Direction == Horizontal) ? TileHorizontally() : TileVertically(); } void TGadgetWindow::GadgetChangedSize(TGadget&) { LayoutSession(); } void TGadgetWindow::Insert(TGadget& gadget, TPlacement placement, TGadget* sibling) { PRECONDITION(!gadget.Window); TGadget** g = &Gadgets; if (sibling || placement == After) { while (*g && *g != sibling) g = &(*g)->Next; CHECK(*g == sibling); } if (placement == After && *g) g = &(*g)->Next; gadget.Next = *g; *g = &gadget; gadget.Window = this; NumGadgets++; gadget.Inserted(); if (gadget.WideAsPossible) WideAsPossible++; } // // remove a gadget from the gadget window's list // TGadget* TGadgetWindow::Remove(TGadget& gadget) { if (gadget.Window != this || !Gadgets) return 0; // // handle head-of-list case // if (&gadget == Gadgets) { Gadgets = Gadgets->Next; } // // scan for gadget, looking one link ahead // else { TGadget* g = Gadgets; while (g->Next && g->Next != &gadget) g = g->Next; if (!g->Next) // not found return 0; g->Next = g->Next->Next; } NumGadgets--; // // re-adjust gadget now that it doesn't live in a window // if (gadget.WideAsPossible) WideAsPossible--; if (&gadget == AtMouse) AtMouse = 0; GadgetReleaseCapture(gadget); gadget.Removed(); gadget.Window = 0; gadget.Next = 0; gadget.GetBounds() -= gadget.GetBounds().TopLeft(); return &gadget; } void TGadgetWindow::EvSize(uint sizeType, TSize& size) { TWindow::EvSize(sizeType, size); if (DirtyLayout || WideAsPossible > 0) { DirtyLayout = false; TileGadgets(); Invalidate(); } } // // Iterate thru the gadget list and paint each one. // void TGadgetWindow::PaintGadgets(TDC& dc, bool, TRect& rect) { TPoint viewport = dc.GetViewportOrg(); for (TGadget* gadget = Gadgets; gadget; gadget = gadget->Next) { if (gadget->Bounds.Touches(rect)) { dc.SetViewportOrg((TPoint&)gadget->Bounds); if (gadget->Clip) { dc.SaveDC(); dc.IntersectClipRect(gadget->Bounds); } gadget->Paint(dc); if (gadget->Clip) dc.RestoreDC(); } } dc.SetViewportOrg(viewport); } void TGadgetWindow::Paint(TDC& dc, bool erase, TRect& rect) { dc.SelectObject(*Font); PaintGadgets(dc, erase, rect); } // // Return gadget that a given window-relative point is in, 0 if none // TGadget* TGadgetWindow::GadgetFromPoint(TPoint& point) const { for (TGadget* gadget = Gadgets; gadget; gadget = gadget->Next) if (gadget->PtIn(point - *(TSize*)&gadget->Bounds.TopLeft())) break; return gadget; } HBRUSH TGadgetWindow::EvCtlColor(HDC hDC, HWND /*hWndChild*/, uint /*ctlType*/) { ::SetBkColor(hDC, BkgndColor); return HBRUSH(*BkgndBrush); } // // Determine if the mouse down is in an enabled gadget, & if so pass it to // the gadget. // void TGadgetWindow::EvLButtonDown(uint modKeys, TPoint& point) { TGadget* gadget = Capture ? Capture : GadgetFromPoint(point); if (gadget && (gadget->GetEnabled() || Capture)) gadget->LButtonDown(modKeys, point - *(TSize*)&gadget->Bounds.TopLeft()); TWindow::EvLButtonDown(modKeys, point); } // // Forward mouse ups to the gadget that has captures the mouse, if any, or // to the gadget at the mouse pos // void TGadgetWindow::EvLButtonUp(uint modKeys, TPoint& point) { TGadget* gadget = Capture ? Capture : GadgetFromPoint(point); if (gadget && (gadget->GetEnabled() || Capture)) gadget->LButtonUp(modKeys, TPoint(point.x - gadget->Bounds.left, point.y - gadget->Bounds.top)); TWindow::EvLButtonUp(modKeys, point); } // // Pass double clicks thru as if they were just a second click, finish the // first click, & begin the second. Dn + Dbl + Up -> Dn + Up+Dn + Up // void TGadgetWindow::EvLButtonDblClk(uint modKeys, TPoint& point) { EvLButtonUp(modKeys, point); EvLButtonDown(modKeys, point); } // // Forward mouse moves to the gadget that has captures the mouse, if any. // Otherwise checks for mouse entering & leaving gadgets // // Could enhance this by delaying mouse enter messages until mouse has been // in the same area for a while, or looking ahead in queue for mouse msgs. // void TGadgetWindow::EvMouseMove(uint modKeys, TPoint& point) { if (Capture) { Capture->MouseMove(modKeys, TPoint(point.x - Capture->Bounds.left, point.y - Capture->Bounds.top)); } else { TGadget* gadget = GadgetFromPoint(point); if (gadget != AtMouse) { if (AtMouse) AtMouse->MouseLeave(modKeys, TPoint(point.x - AtMouse->Bounds.left, point.y - AtMouse->Bounds.top)); AtMouse = gadget; if (AtMouse) AtMouse->MouseEnter(modKeys, TPoint(point.x - AtMouse->Bounds.left, point.y - AtMouse->Bounds.top)); } } TWindow::EvMouseMove(modKeys, point); }