//---------------------------------------------------------------------------- // ObjectWindows // (C) Copyright 1992, 1994 by Borland International, All Rights Reserved // // Implementation of TTinyCaption //---------------------------------------------------------------------------- #pragma hdrignore SECTION #include #include #include #if !defined(SECTION) || SECTION == 1 // // We only want to search this mixin for events, so don't include any base // classes in Find() // DEFINE_RESPONSE_TABLE(TTinyCaption) EV_WM_NCHITTEST, EV_WM_NCPAINT, EV_WM_NCCALCSIZE, EV_WM_NCLBUTTONDOWN, EV_WM_MOUSEMOVE, EV_WM_LBUTTONUP, EV_WM_NCACTIVATE, EV_WM_SYSCOMMAND, END_RESPONSE_TABLE; // // Rely on TWindow's default ctor since we will always be mixed-in and another // window will perform Init() // TTinyCaption::TTinyCaption() { TCEnabled = false; CaptionFont = 0; } TTinyCaption::~TTinyCaption() { delete CaptionFont; } void TTinyCaption::EnableTinyCaption(int captionHeight, bool closeBox) { Border.cx = GetSystemMetrics(SM_CXBORDER); Border.cy = GetSystemMetrics(SM_CYBORDER); // // Get width of window borders, these will vary with type of window // if (Attr.Style & WS_THICKFRAME) { Frame.cx = GetSystemMetrics(SM_CXFRAME); Frame.cy = GetSystemMetrics(SM_CYFRAME); } else if (Attr.Style & WS_DLGFRAME) { Frame.cx = GetSystemMetrics(SM_CXDLGFRAME); Frame.cy = GetSystemMetrics(SM_CYDLGFRAME); } else { Attr.Style |= WS_BORDER; // must have at least a border Frame = Border; } // // Calculate the actual caption height including lower divider, but not top // border // CaptionHeight = (GetSystemMetrics(SM_CYCAPTION) * captionHeight) / 100 - Border.cy; CloseBox = closeBox; delete CaptionFont; CaptionFont = new TFont( "Arial", // facename. "Small Fonts" is another option -(CaptionHeight-2*Border.cy), // height in pixels, 0, 0, 0, FW_NORMAL, // width,esc,orientation,weight VARIABLE_PITCH | FF_SWISS, // Pitch and Family false, false, false, // Italic, Underline, Strikeout ANSI_CHARSET, // Charset OUT_CHARACTER_PRECIS, // Output precision CLIP_DEFAULT_PRECIS, // Clip precision PROOF_QUALITY // Quality ); TCEnabled = true; } // // Return where in the non client area we are. We only handle caption // bar area // uint TTinyCaption::EvNCHitTest(TPoint& screenPt) { uint er; if (DoNCHitTest(screenPt, er) == esComplete) return er; return TWindow::EvNCHitTest(screenPt); } TEventStatus TTinyCaption::DoNCHitTest(TPoint& screenPt, uint& evRes) { if (!TCEnabled) return esPartial; // // Check style bits to see what to paint // long style = GetWindowLong(GWL_STYLE); bool hasCaption, hasSysMenu, hasMaximize, hasMinimize; hasCaption = hasSysMenu = hasMaximize = hasMinimize = false; if (style & WS_CAPTION) { hasCaption = true; if (style & WS_SYSMENU) hasSysMenu = true; if (style & WS_MAXIMIZEBOX) hasMaximize = true; if (style & WS_MINIMIZEBOX) hasMinimize = true; } // // Convert to window coordinates // TPoint winPt = screenPt - GetWindowRect().TopLeft(); if ((hasSysMenu || CloseBox) && GetSysBoxRect().Contains(winPt)) { evRes = HTSYSMENU; return esComplete; } else if (hasMinimize && GetMinBoxRect().Contains(winPt)) { evRes = HTMINBUTTON; return esComplete; } else if (hasMaximize && GetMaxBoxRect().Contains(winPt)) { evRes = HTMAXBUTTON; return esComplete; } // // CaptionRect includes buttons so make sure it's last checked // Should modify this one to allow clicking in left, top, right thin // borders of caption // else if (hasCaption && GetCaptionRect().Contains(winPt)) { evRes = HTCAPTION; return esComplete; } else { evRes = 0; return esPartial; } } // // We only need to paint the caption. Someone else will paint the borders // void TTinyCaption::EvNCPaint() { TWindow::EvNCPaint(); // Default border painting DoNCPaint(); // Then our special caption painting } TEventStatus TTinyCaption::DoNCPaint() { if (!TCEnabled || IsIconic()) return esPartial; // We don't do anything special for an Icon // // If we have focus or our children have focus, then we're active // Note: We can't use GetFocus here because when we're being restored // from an icon, we're active, but don't yet have focus! // HWND focus = GetFocus(); PaintCaption(GetActiveWindow() == HWindow || focus == HWindow || IsChild(focus)); return esPartial; // Caller must call function to paint borders } // // Returns the size of our client area // uint TTinyCaption::EvNCCalcSize(bool calcValidRects, NCCALCSIZE_PARAMS far& calcSize) { uint er; if (DoNCCalcSize(calcValidRects, calcSize, er) == esComplete) return er; return TWindow::EvNCCalcSize(calcValidRects, calcSize); } // // Return the size of our client area, leaving room for caption bar // TEventStatus TTinyCaption::DoNCCalcSize(bool /*calcValidRects*/, NCCALCSIZE_PARAMS far& calcSize, uint& evRes) { if (!TCEnabled || IsIconic()) return esPartial; TRect captRect = GetCaptionRect(); calcSize.rgrc[0].left += Frame.cx; calcSize.rgrc[0].top += Frame.cy + captRect.Height(); calcSize.rgrc[0].right -= Frame.cx; calcSize.rgrc[0].bottom -= Frame.cy; evRes = 0; return esComplete; } void TTinyCaption::EvNCLButtonDown(uint hitTest, TPoint& screenPt) { // // Display system menu, invert min/max icons (not), etc // if (DoNCLButtonDown(hitTest,screenPt) == esPartial) TWindow::EvNCLButtonDown(hitTest, screenPt); } TEventStatus TTinyCaption::DoNCLButtonDown(uint hitTest, TPoint& screenPt) { if (!TCEnabled) return esPartial; switch (hitTest) { case HTSYSMENU: DownHit = HTSYSMENU; if (CloseBox) { IsPressed = true; SetCapture(); PaintCloseBox(TWindowDC(*this), GetSysBoxRect(), IsPressed); } else { TRect sysBoxRect = GetSysBoxRect().InflatedBy(-1,-1); sysBoxRect.right += 1; TWindowDC(*this).PatBlt(sysBoxRect, PATINVERT); // // Display sys menu on button down // Need to lock sys menu until user clicks outside // // // Set flag to indicate we're expecting a sys command, & then send // message to popup sys menu // WaitingForSysCmd = true; SendMessage(WM_SYSCOMMAND, SC_MOUSEMENU|HTSYSMENU, MAKELPARAM(screenPt.x,screenPt.y)); // // If we didn't execute a command, user released btn outside of menu // If it was released in sys menu box, then redisplay menu as if it // were brought up with a keystroke // if (WaitingForSysCmd) { uint hitTest; TPoint pt; GetCursorPos(pt); DoNCHitTest(pt, hitTest); if (hitTest == HTSYSMENU) { SendMessage(WM_SYSCOMMAND, SC_KEYMENU|HTSYSMENU); } } if (HWindow) TWindowDC(*this).PatBlt(sysBoxRect, PATINVERT); } return esComplete; case HTMINBUTTON: DownHit = HTMINBUTTON; IsPressed = true; SetCapture(); PaintMinBox(TWindowDC(*this), GetMinBoxRect(), IsPressed); return esComplete; case HTMAXBUTTON: DownHit = HTMAXBUTTON; IsPressed = true; SetCapture(); PaintMaxBox(TWindowDC(*this), GetMaxBoxRect(), IsPressed); return esComplete; } DownHit = HTNOWHERE; return esPartial; } void TTinyCaption::EvMouseMove(uint modKeys, TPoint& pt) { if (DoMouseMove(modKeys, pt) == esPartial) TWindow::EvMouseMove(modKeys, pt); } TEventStatus TTinyCaption::DoMouseMove(uint /*modKeys*/, TPoint& pt) { if (TCEnabled && DownHit != HTNOWHERE) { uint hitTest; TPoint screenPt = pt; ClientToScreen(screenPt); // Cvt to screen coord DoNCHitTest(screenPt, hitTest); bool isNowPressed = hitTest == DownHit; if (isNowPressed != IsPressed) { IsPressed = isNowPressed; switch (DownHit) { case HTSYSMENU: if (CloseBox) PaintCloseBox(TWindowDC(*this), GetSysBoxRect(), IsPressed); return esComplete; case HTMINBUTTON: PaintMinBox(TWindowDC(*this), GetMinBoxRect(), IsPressed); return esComplete; case HTMAXBUTTON: PaintMaxBox(TWindowDC(*this), GetMaxBoxRect(), IsPressed); return esComplete; } } } return esPartial; } void TTinyCaption::EvLButtonUp(uint modKeys, TPoint& pt) { // // If we're still in area where buton went down, then do it // if (DoLButtonUp(modKeys, pt) == esPartial) TWindow::EvLButtonUp(modKeys, pt); } TEventStatus TTinyCaption::DoLButtonUp(uint modKeys, TPoint& pt) { if (TCEnabled && DownHit != HTNOWHERE) { ReleaseCapture(); DoMouseMove(modKeys, pt); uint hitTest; TPoint screenPt = pt; ClientToScreen(screenPt); // Cvt to screen coord DoNCHitTest(screenPt, hitTest); if (hitTest == DownHit) { DownHit = HTNOWHERE; switch (hitTest) { case HTSYSMENU: if (CloseBox) PostMessage(WM_CLOSE); return esComplete; // // We have to handle these buttons also to prevent defproc from painting // the standard big min/max buttons when left mouse button is pressed // case HTMINBUTTON: HandleMessage(WM_SYSCOMMAND, SC_MINIMIZE); return esComplete; case HTMAXBUTTON: HandleMessage(WM_SYSCOMMAND, IsZoomed() ? SC_RESTORE : SC_MAXIMIZE); return esComplete; } } DownHit = HTNOWHERE; } return esPartial; } bool TTinyCaption::EvNCActivate(bool active) { bool er; if (DoNCActivate(active, er) == esComplete) return er; return TWindow::EvNCActivate(active); } TEventStatus TTinyCaption::DoNCActivate(bool active, bool& evRes) { if (!TCEnabled || IsIconic()) return esPartial; // Let default do it's thing PaintCaption(active); evRes = ToBool(!active); return esComplete; } LRESULT TTinyCaption::EvCommand(uint id, HWND hWndCtl, uint notifyCode) { LRESULT er; if (DoCommand(id, hWndCtl, notifyCode, er) == esComplete) return er; return TWindow::EvCommand(id, hWndCtl, notifyCode); } TEventStatus TTinyCaption::DoCommand(uint id, HWND /*hWndCtl*/, uint notifyCode, LRESULT& evRes) { // // We're displaying system menu using TrackPopup... // This will send us WM_COMMAND messages instead of WM_SYSCOMMAND msgs // If we get a system menu command then transform it into a WM_SYSCOMMAND // if (!TCEnabled) return esPartial; if (id >= 0xF000) { WaitingForSysCmd = false; // Let LButtonDown handler know that a command was sent evRes = HandleMessage(WM_SYSCOMMAND, id, notifyCode); return esComplete; } else { evRes = 0; return esPartial; } } void TTinyCaption::EvSysCommand(uint cmdType, TPoint& p) { if (DoSysCommand(cmdType,p) == esPartial) TWindow::EvSysCommand(cmdType, p); } // // // Handle WM_SYSCOMMAND to make sure that SC_KEYMENU and SC_MOUSEMENU bring up // our sys menu at the right coord w/ respect to the tiny sys box. // // If iconic, then let default windows processing deal with the menu // TEventStatus TTinyCaption::DoSysCommand(uint cmdType, TPoint&) { if (!TCEnabled) return esPartial; cmdType &= 0xFFF0; if ((cmdType == SC_KEYMENU || cmdType == SC_MOUSEMENU) && !IsIconic()) { DoSysMenu(); return esComplete; } return esPartial; } void TTinyCaption::PaintButton(TDC& dc, TRect& r, bool pressed) { // dc.OWLFastWindowFrame(TBrush(GetSysColor(COLOR_WINDOWFRAME)), r, 1, 1); dc.FrameRect(r, TBrush(GetSysColor(COLOR_WINDOWFRAME))); r.Inflate(-1,-1); dc.TextRect(r, TColor::LtGray); if (r.Width() > 4 && r.Height() > 4) { if (pressed) { dc.TextRect(r.left, r.top, r.right, r.top+1, TColor::Gray); dc.TextRect(r.left, r.top+1, r.left+1, r.bottom, TColor::Gray); } else { dc.TextRect(r.left, r.top, r.right-1, r.top+1, TColor::White); dc.TextRect(r.left, r.top+1, r.left+1, r.bottom-1, TColor::White); dc.TextRect(r.right-1, r.top+1, r.right, r.bottom, TColor::Gray); dc.TextRect(r.left+1, r.bottom-1, r.right-1, r.bottom, TColor::Gray); } } } void TTinyCaption::PaintCloseBox(TDC& dc, TRect& boxRect, bool pressed) { // // Fill the box with light gray & draw bevel if possible // PaintButton(dc, boxRect, pressed); if (pressed) boxRect.Offset(1,1); // // Do something different to differentiate from standard system menu-- // draw a recessed black box glyph about half the button size, centered // int glyphWidth = boxRect.Width() > 7 ? boxRect.Width()-boxRect.Width()/2-1 : boxRect.Width()-3; int glyphHeight = boxRect.Height() > 7 ? boxRect.Height()-boxRect.Height()/2-1 : boxRect.Height()-3; if (glyphWidth > 1 && glyphHeight > 1) { TRect glyphRect(0, 0, glyphWidth, glyphHeight); glyphRect.Offset(boxRect.left + (boxRect.Width()-glyphWidth-1)/2, boxRect.top + (boxRect.Height()-glyphHeight-1)/2); dc.TextRect(glyphRect, TColor::Gray); glyphRect.Offset(1,1); dc.TextRect(glyphRect, TColor::White); glyphRect.BottomRight().Offset(-1,-1); dc.TextRect(glyphRect, TColor::Black); } } void TTinyCaption::PaintSysBox(TDC& dc, TRect& boxRect, bool /*pressed*/) { // // Dont paint over the left & top borders // boxRect.left++; boxRect.top++; // // Fill the box with light gray // dc.TextRect(boxRect, TColor::LtGray); // // Draw the ventilator (sysmenu) box, with shadow // TPoint begPt = boxRect.TopLeft().OffsetBy(2, (boxRect.Height()-3)/2); TRect ventRect(begPt, TSize(boxRect.Width()-5, 3)); // Draw shadow down and right 1 dc.TextRect(ventRect.left+1, ventRect.top+1, ventRect.right+1, ventRect.bottom+1, TColor::Gray); // Draw ventilator rectangle dc.FrameRect(ventRect, TBrush(TColor::Black)); // Draw white interior of ventilator dc.TextRect(ventRect.left+1, ventRect.top+1, ventRect.right-1, ventRect.top+2, TColor::White); dc.TextRect(boxRect.right, boxRect.top, boxRect.right+1, boxRect.bottom, TColor::Black); } void TTinyCaption::PaintMinBox(TDC& dc, TRect& boxRect, bool pressed) { // // Fill the box with light gray & draw bevel if possible // PaintButton(dc, boxRect, pressed); if (pressed) boxRect.Offset(1,1); int bh = boxRect.Height(); int bw = boxRect.Width(); TPoint begPt = boxRect.TopLeft().OffsetBy((bw+1)/4, (bh+2)/3); TPoint endPt = begPt.OffsetBy((bw+1)/2,0); while (begPt.x < endPt.x) { dc.MoveTo(begPt); dc.LineTo(endPt); begPt.Offset(1,1); endPt.Offset(-1,1); } } void TTinyCaption::PaintMaxBox(TDC& dc, TRect& boxRect, bool pressed) { // // Fill the box with light gray & draw bevel if possible // PaintButton(dc, boxRect, pressed); if (pressed) boxRect.Offset(1,1); // // Down triangle // int bh = boxRect.Height(); int bw = boxRect.Width(); if (IsZoomed()) { TPoint begPt = boxRect.BottomLeft().OffsetBy((bw+1)/4, -bh*3/8); TPoint endPt = begPt.OffsetBy((bw+1)/2, 0); while (begPt.x < endPt.x) { dc.MoveTo(begPt); dc.LineTo(endPt); begPt.Offset(1,1); endPt.Offset(-1,1); } } // // Up triangle // { TPoint begPt = boxRect.TopLeft().OffsetBy((bw+1)/4, IsZoomed() ? bh*3/8 : bh*2/3); TPoint endPt = begPt.OffsetBy((bw+1)/2, 0); while (begPt.x < endPt.x) { dc.MoveTo(begPt); dc.LineTo(endPt); begPt.Offset(1, -1); endPt.Offset(-1, -1); } } } void TTinyCaption::PaintCaption(bool active) { int xOrg; long style = GetWindowLong(GWL_STYLE); if (!(style & WS_CAPTION)) return; // Leave now if there is no caption, it's all we care about // // Paint caption background and caption text if any. // TWindowDC dc(*this); TRect captRect = GetCaptionRect(); HBRUSH captionBrush = CreateSolidBrush(GetSysColor(active ? COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION)); dc.SetTextColor(GetSysColor(active ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT)); dc.FillRect(captRect, captionBrush); DeleteObject(captionBrush); dc.SelectObject(*CaptionFont); dc.SetBkMode(TRANSPARENT); TSize textSize = dc.GetTextExtent(Title, strlen(Title)); // // Calc x coord for text, so that text is centered between caption buttons // xOrg = captRect.right - captRect.left; if (style & WS_MINIMIZEBOX) xOrg -= GetMinBoxRect().Width(); if (style & WS_MAXIMIZEBOX) xOrg -= GetMaxBoxRect().Width(); if ((style & WS_SYSMENU) || CloseBox) xOrg -= GetSysBoxRect().Width(); xOrg -= textSize.cx; if (xOrg<0) xOrg = 0; else xOrg = xOrg/2; xOrg += captRect.left; if ((style & WS_SYSMENU) || CloseBox) xOrg += GetSysBoxRect().Width(); dc.ExtTextOut(xOrg, captRect.top-Border.cy, ETO_CLIPPED, &captRect, Title, strlen(Title) ); dc.RestoreFont(); // // Paint widgets: sysmenu or close button, minimize button, maximize button // They currently all use a black pen // dc.SelectStockObject(BLACK_PEN); // // Paint system menu or close button // if (CloseBox) PaintCloseBox(dc, GetSysBoxRect(), false); else if (style & WS_SYSMENU) PaintSysBox(dc, GetSysBoxRect(), false); // // Paint minimize button // if (style & WS_MINIMIZEBOX) PaintMinBox(dc, GetMinBoxRect(), false); // // Paint maximize button // if (style & WS_MAXIMIZEBOX) PaintMaxBox(dc, GetMaxBoxRect(), false); // // Draw window-frame color line under caption // dc.FrameRect(captRect.left, captRect.bottom-1, captRect.right, captRect.bottom, TBrush(GetSysColor(COLOR_WINDOWFRAME))); } // // NOTE: GetCaptionRect and GetSysBoxRect must be kept in sync! // TRect TTinyCaption::GetCaptionRect() { // // Get caption rect converted to window relative coordinates // TRect captRect = GetWindowRect(); captRect -= captRect.TopLeft(); captRect.left += Frame.cx; captRect.top += Frame.cy; captRect.right -= Frame.cx; captRect.bottom = captRect.top + CaptionHeight; return captRect; } // // Return a rectangle for sysmenu, minimize, or maximize rectangle // TRect TTinyCaption::GetSysBoxRect() { TRect boxRect = GetCaptionRect(); boxRect.right = boxRect.left + CaptionHeight; boxRect.left -= 1; boxRect.top -= 1; return boxRect; } TRect TTinyCaption::GetMinBoxRect() { // // Far right on caption if no max box, else next to max box // TRect boxRect = GetMaxBoxRect(); if (GetWindowLong(GWL_STYLE) & WS_MAXIMIZEBOX) boxRect.Offset(-CaptionHeight, 0); return boxRect; } TRect TTinyCaption::GetMaxBoxRect() { TRect boxRect = GetCaptionRect(); boxRect.left = boxRect.right - CaptionHeight; boxRect.top -= 1; boxRect.right += 1; return boxRect; } // // Get system menu, setup menu items, popup & track it // void TTinyCaption::DoSysMenu() { HMENU hSysMenu = GetSystemMenu(); if (hSysMenu) { uint32 style = GetWindowLong(GWL_STYLE); EnableMenuItem(hSysMenu, SC_RESTORE, (IsIconic() || IsZoomed()) ? MF_ENABLED : MF_GRAYED); EnableMenuItem(hSysMenu, SC_MOVE, (style & WS_CAPTION) ? MF_ENABLED : MF_GRAYED); EnableMenuItem(hSysMenu, SC_SIZE, (style & WS_THICKFRAME) ? MF_ENABLED : MF_GRAYED); EnableMenuItem(hSysMenu, SC_MINIMIZE, ((style&WS_MINIMIZEBOX) && !IsIconic()) ? MF_ENABLED : MF_GRAYED); EnableMenuItem(hSysMenu, SC_MAXIMIZE, ((style&WS_MAXIMIZEBOX) && !IsZoomed()) ? MF_ENABLED : MF_GRAYED); TRect r = GetSysBoxRect(); ClientToScreen(r.TopLeft()); // Cvt pt to screen coord ClientToScreen(r.BottomRight()); TrackPopupMenu(hSysMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON, r.left-Frame.cx, r.top-Frame.cy, 0, HWindow, &r); } } #endif #if !defined(SECTION) || SECTION == 2 IMPLEMENT_STREAMABLE(TTinyCaption); void* TTinyCaption::Streamer::Read(ipstream& is, uint32 /*version*/) const { TTinyCaption* o = GetObject(); o->CaptionFont = 0; is >> o->TCEnabled; if (o->TCEnabled) { int captionHeight; is >> captionHeight >> o->CloseBox;; o->EnableTinyCaption(captionHeight, o->CloseBox); } return o; } void TTinyCaption::Streamer::Write(opstream& os) const { TTinyCaption* o = GetObject(); os << o->TCEnabled; if (o->TCEnabled) { int captionHeight = (100*o->CaptionHeight) / (o->Border.cy+GetSystemMetrics(SM_CYCAPTION)); os << captionHeight << o->CloseBox; } } #endif