//----------------------------------------------------------------------------
// ObjectWindows
// Copyright (c) 1995, 1996 by Borland International, All Rights Reserved
//
/// \file
/// Implementation of class TNoteTab
/// Added Scroll handling. Contributed by Jogy.
/// Added support for set font and set image. Contributed by Jogy.
//----------------------------------------------------------------------------
#include <owl/pch.h>
#include <owl/contain.h>
#include <owl/control.h>
#include <owl/notetab.h>
#include <owl/uihelper.h>
#include <owl/commctrl.h>
#include <owl/uimetric.h>
#include <owl/updown.h>
#include <owl/uimetric.h>
#include <owl/celarray.h>
#include <owl/theme.h>
#include <owl/gdiplus.h>
#include <algorithm>
using namespace std;
namespace owl {
OWL_DIAGINFO;
#define OWL_CLIP_TAB_BOUNDING_RECT_ 0 // debugging
#define OWL_DRAW_TAB_BOUNDING_RECT_ 0 // debugging
#if OWL_CLIP_TAB_BOUNDING_RECT_
bool ShouldClipTabBoundingRect_ = false;
#endif
#if OWL_DRAW_TAB_BOUNDING_RECT_
bool ShouldDrawTabBoundingRect_ = false;
#endif
//----------------------------------------------------------------------------
DEFINE_RESPONSE_TABLE1(TNoteTab, TControl)
EV_WM_SIZE,
EV_WM_LBUTTONDOWN,
EV_WM_GETDLGCODE,
EV_WM_KEYDOWN,
EV_WM_SETFOCUS,
EV_WM_KILLFOCUS,
EV_WM_HSCROLL,
EV_WM_ERASEBKGND,
END_RESPONSE_TABLE;
//
/// Constructor of NoteTab object. Use this constructor when creating a notetab
/// control from scratch.
//
TNoteTab::TNoteTab(TWindow* parent,
int id,
int x, int y, int w, int h,
TWindow* buddy,
bool dialogBuddy,
TModule* module)
:
TControl(parent, id, 0, x, y, w, h, module),
WindowFace(!dialogBuddy),
Buddy(buddy)
{
InitCtrl();
}
//
/// Constructor of NoteTab object. Use this constructor when aliasing a control
/// defined within a dialog template.
//
TNoteTab::TNoteTab(TWindow* parent,
int resourceId,
TWindow* buddy,
bool dialogBuddy,
TModule* module)
:
TControl(parent, resourceId, module),
WindowFace(!dialogBuddy),
Buddy(buddy)
{
InitCtrl();
}
//
/// Returns "OWL_Notetab" - the Window class name of the notetab control object
//
auto TNoteTab::GetWindowClassName() -> TWindowClassName
{
return TWindowClassName{OWL_NOTETAB};
}
//
/// Overriden virtual of TWindow - Initializes font used by control and resize
/// accordingly.
//
void
TNoteTab::SetupWindow()
{
TControl::SetupWindow();
// Initialize BuddyHandle if we have a Buddy.
//
if (Buddy)
BuddyHandle = Buddy->GetHandle();
}
//
/// Adds a new tabitem to the notetab control
//
int
TNoteTab::Add(
LPCTSTR label,
INT_PTR clientData,
int imageIdx,
TAbsLocation imageLoc,
bool shouldSelect)
{
return Insert(label, GetCount(), clientData, imageIdx, imageLoc, shouldSelect);
}
//
/// Inserts a new TTabItem at the specified index.
//
int
TNoteTab::Insert(
LPCTSTR label,
int index,
INT_PTR clientData,
int imageIdx,
TAbsLocation imageLoc,
bool shouldSelect)
{
PRECONDITION(index >= 0 && index <= GetCount());
TabList.insert(TabList.begin() + index,
TNoteTabItem(label, clientData, imageIdx, imageLoc));
SetTabSize(index);
SetTabRects(FirstVisibleTab);
if (shouldSelect)
SetSel(index);
return index;
}
//
/// Remove the tabitem at the specified 'index'. Returns true on success; false
/// otherwise.
//
bool
TNoteTab::Delete(int index)
{
if (index < 0 || index >= GetCount()) return false; // invalid index
// Mark rectangles that need to be redrawn.
//
if (IsVisible(index))
{
for (int i = index; i < GetCount(); i++)
InvalidateTab(i);
}
// Remove the tab.
//
TabList.erase(TabList.begin() + index);
// Adjust SelectedTab and FirstVisibleTab.
// If a tab before SelectedTab is deleted, then correct the index so that it
// points to the same item. If the SelectedTab was deleted, then do nothing,
// thus moving the selection to the following tab, unless it was the last
// tab, in which case we need to move the selection left. Then do the same
// for FirstVisibleTab.
//
int n = GetCount(); // new count
if (index < SelectedTab || SelectedTab == n)
--SelectedTab;
CHECK(SelectedTab >= -1 && SelectedTab < n);
if (index < FirstVisibleTab || FirstVisibleTab == n)
--FirstVisibleTab;
CHECK(FirstVisibleTab >= -1 && FirstVisibleTab < n);
// Force SelectedTab to have focus.
//
if ((SelectedTab >= 0) && (SelectedTab < n))
InvalidateTab(SelectedTab);
// Calculate new tab rectangles, if any left.
//
if (n > 0)
SetTabRects(FirstVisibleTab);
return true;
}
//
/// Removes all tab items in the notetab control. Always returns true.
//
bool
TNoteTab::DeleteAll()
{
TabList.clear();
FirstVisibleTab = 0;
SelectedTab = -1;
return true;
}
//
/// Return the number of tab items in the notetab control.
//
int
TNoteTab::GetCount() const
{
PRECONDITION(TabList.size() <= INT_MAX);
return static_cast<int>(TabList.size());
}
//
/// Returns the index of the selected tabitem.
///
/// \note Returns a zero-based index or -1 if there are no tab items in the notetab
/// control.
int
TNoteTab::GetSel() const
{
return SelectedTab;
}
//
/// Selects the tabitem at the specified index.
/// \note SetSel does not send any
/// notifications to the parent and/or buddy. Returns the 0 based index of the tab
/// item selected or -1 on failure.
//
int
TNoteTab::SetSel(int index)
{
if (index < 0 || index >= GetCount()) return -1;
if (index == SelectedTab) return index;
// Change the selection.
// First, invalidate area occupied by previously selected item.
//
if (SelectedTab >= 0 && SelectedTab < GetCount())
InvalidateTab(SelectedTab);
// Update selected index and tab rectangles (selected tab may differ from the rest).
//
SelectedTab = index;
SetTabRects(FirstVisibleTab);
// Invalidate area occupied by new selection.
//
if (SelectedTab >= 0 && SelectedTab < GetCount())
InvalidateTab(SelectedTab);
return index;
}
int
TNoteTab::GetMinimalHeight()
{
int labelHeight = 0;
for (int i = 0; i != GetCount(); ++i)
{
SetTabSize(i);
labelHeight = std::max(labelHeight, static_cast<int>(TabList[i].LabelSize.cy));
}
return Margin.cy +
LabelMargin.cy +
labelHeight +
LabelMargin.cy +
(Style3d ? TUIMetric::CyEdge.Get() : 0);
}
//
/// Sets the amount of vertical space above the tabs.
//
void
TNoteTab::SetMargin(const TSize& v)
{
Margin = v;
Update();
}
//
/// Sets the amount of padding around the tab label.
//
void
TNoteTab::SetLabelMargin(const TSize& v)
{
LabelMargin = v;
Update();
}
//
/// Sets the horizontal spacing between image and text in the label.
//
void
TNoteTab::SetLabelImageMargin(int v)
{
LabelImageMargin = v;
Update();
}
//
/// Sets the margin around the focus rectangle for the selected tab.
//
void
TNoteTab::SetFocusMargin(const TSize& v)
{
FocusMargin = v;
Update();
}
//
/// Sets the horizontal distance between two tabs.
//
void
TNoteTab::SetTabSpacing(int v)
{
TabSpacing = v;
Update();
}
//
/// Sets the amount of narrowing on each side of the tab towards the bottom.
//
void
TNoteTab::SetTabTapering(int v)
{
TabTapering = v;
Update();
}
//
/// Sets the amount of extra height of the selected tab.
//
void
TNoteTab::SetSelectedTabProtrusion(int v)
{
SelectedTabProtrusion = v;
Update();
}
//
/// Returns the font used to render the text part of the tab labels.
//
const TFont&
TNoteTab::GetTabFont() const
{
PRECONDITION(TabFont);
return *TabFont;
}
//
/// Sets the font used to render the text part of the tab labels.
//
void
TNoteTab::SetTabFont(const TFont& font)
{
TabFont.reset(new TFont(font.GetObject()));
Update();
}
//
/// Returns the font used to render the text part of the selected tab label.
//
const TFont&
TNoteTab::GetSelectedTabFont() const
{
PRECONDITION(TabFont);
return SelectedTabFont ? *SelectedTabFont : *TabFont;
}
//
/// Sets the font used to render the text part of the selected tab label.
//
void
TNoteTab::SetSelectedTabFont(const TFont& font)
{
SelectedTabFont.reset(new TFont(font.GetObject()));
Update();
}
//
/// Sets the fill color used to paint the tabs.
//
void
TNoteTab::SetTabColor(const TColor& v)
{
TabColor = v;
Update();
}
//
/// Sets the fill color used to paint the selected tab.
/// This color is only used when WindowFace mode is selected.
//
void
TNoteTab::SetSelectedTabColor(const TColor& v)
{
SelectedTabColor = v;
Update();
}
//
/// Sets the pen color used to draw the edges of the tabs.
//
void
TNoteTab::SetEdgeColor(const TColor& v)
{
EdgeColor = v;
Update();
}
//
/// Retrieve information about the tab item at the specified index. Always returns
/// true.
//
bool
TNoteTab::GetItem(int index, TNoteTabItem& tabItem) const
{
PRECONDITION(index >= 0);
PRECONDITION(index < GetCount());
tabItem = TabList[index];
return true;
}
//
/// Functional style overload
//
TNoteTabItem
TNoteTab::GetItem(int index) const
{
TNoteTabItem n;
bool r = GetItem(index, n); CHECK(r); InUse(r);
return n;
}
//
/// Updates information about the tab item at the specified index.
//
bool
TNoteTab::SetItem(int index, const TNoteTabItem& tabItem)
{
if (index < GetCount() && index >= 0) {
TabList[index] = tabItem;
// !BB Need to recalc/invalidate etc...
SetTabSize(index);
SetTabRects(FirstVisibleTab);
InvalidateTab(index);
return true;
}
return false;
}
//
/// Return handle of buddy window associated with the notetab control.
//
HWND
TNoteTab::GetBuddy() const
{
return BuddyHandle;
}
//
/// Sets handle of the buddy window associated with this notetab control.
//
void
TNoteTab::SetBuddy(HWND hwnd)
{
BuddyHandle = hwnd;
Buddy = GetWindowPtr( BuddyHandle );
if( !Buddy )
Buddy = new TWindow( hwnd );
}
//
/// Overrides TWindow virtual member function to handle transfers. There are no
/// transfers for NoteTab controls.
//
uint
TNoteTab::Transfer(void* /*buffer*/, TTransferDirection /*direction*/)
{
return 0;
}
//----------------------------------------------------------------------------
//
/// Initialize internal variables used by NoteTab.
//
void
TNoteTab::InitCtrl()
{
Style3d = true;
ShouldUseThemes = false;
Margin = TSize(5, TUIMetric::CySizeFrame);
LabelMargin = TSize(5, 3);
SelectedTabProtrusion = 0;
LabelImageMargin = 3;
FocusMargin = TSize(2, 2);
TabSpacing = 4;
TabTapering = 4;
TabColor = TColor::Sys3dFace;
SelectedTabColor = TColor::SysWindow;
EdgeColor = TColor::SysWindowFrame;
SetBkgndColor(TColor::Sys3dFace);
TabList.clear();
SetTabFont(TDefaultGuiFont());
SelectedTabFont.reset(); // Causes TabFont to be used for selected tabs.
SelectedTab = -1;
FirstVisibleTab = 0;
CelArray = 0;
OwnedCelArray.reset();
TransparentColor = TColor::Sys3dFace;
ScrollLoc = alRight;
ModifyStyle(0, WS_CLIPCHILDREN); // Clipping eliminates scrollbar flicker.
ScrollButtons = new TUpDown{this, -1, 0, 0, 0, 0};
ScrollButtons->ModifyStyle(WS_TABSTOP, UDS_HORZ); // No focus, please.
}
//
/// Returns the bounding rectangle of a tab given its hit rectangle.
/// The bounding rectangle is the smallest rectangle that encapsulates the
/// whole tab, and outside which the tab will not draw.
/// The bounding rectangle may be larger than the tab's hit rectangle.
//
TRect
TNoteTab::GetBoundingRect(const TRect& tabRect) const
{
// Adjust for the widening of the tab towards the page edge (top).
//
return tabRect.IsEmpty() ? TRect() : tabRect.InflatedBy(TabTapering, 0);
}
//
/// Invalidates the rectangle occupied by the tab at the specified index.
//
void
TNoteTab::InvalidateTab(int index)
{
PRECONDITION(index >= 0 && index < GetCount());
if (!GetHandle()) return;
const TNoteTabItem& tab = TabList[index];
InvalidateRect(GetBoundingRect(tab.Rect));
}
namespace
{
//
// Returns `true` if themes are enabled for this application and
// themed Common Controls (version 6 or later) are in use.
//
// Important: This function must be called after the creation of the main
// window, otherwise it may always return `false`.
//
// Note that IsAppThemed will return `false` if either (a) themes have been
// disabled for the application by selecting "Disable visual themes" in the
// Compatibility tab in the Properties dialog for the executable, or (b)
// themes have been deactivated by selecting the Windows Classic style in
// the Windows XP/7/Vista Control Panel (not available in Windows 8).
// Note that (b) may change at run-time.
//
// Note we do not have to use IsThemeActive here. This function only reports
// the state of the Control Panel setting (Classic vs themed).
//
bool IsThemed_()
{
static const auto v = GetCommCtrlVersion();
return TThemeModule::GetInstance().IsAppThemed() && v >= 0x60000;
}
//
// Defines a base class with common implementation and utilities for derived
// UI part renderers.
//
struct TRenderer_
{
TWindow& Window;
TDC& Dc;
const TFont& TabFont;
const TFont& SelectedTabFont;
TColor TabTextColor;
TRenderer_(
TWindow& w,
TDC& dc,
const TRect& paintRect,
const TFont& tabFont,
const TFont& selectedTabFont,
TColor tabTextColor,
TColor tabColor,
TColor selectedTabColor,
TColor edgeColor
)
: Window(w), Dc(dc), TabFont(tabFont), SelectedTabFont(selectedTabFont), TabTextColor(tabTextColor)
{InUse(paintRect); InUse(tabColor); InUse(selectedTabColor); InUse(edgeColor);}
void PaintPageEdge(const TSize& margin)
{CHECKX(false, _T("Not implemented")); InUse(margin);}
void PaintTabFace(const TRect& tabBoundingRect, TPoint (&pt)[4], bool isSelectedTab)
{CHECKX(false, _T("Not implemented")); InUse(tabBoundingRect); InUse(pt); InUse(isSelectedTab);}
void DrawTabContour(const TPoint (&pt)[4], bool isSelectedTab)
{CHECKX(false, _T("Not implemented")); InUse(pt); InUse(isSelectedTab);}
void PaintTabIcon(const TNoteTabItem& tab, const TCelArray& celArray, TColor transparentColor, const TSize& labelMargin, bool isSelectedTab, int selectedTabProtrusion)
{
CHECK(!tab.Rect.IsEmpty());
int dy = isSelectedTab ? selectedTabProtrusion : 0;
TRect imageRect(tab.Rect.TopLeft() + TSize(0, dy), tab.Rect.BottomRight());
TUIFace face(imageRect, celArray, transparentColor);
TSize size = celArray.CelSize();
TRect srcRect(celArray.CelRect(tab.ImageIdx));
TPoint dstPt(
(tab.ImageLoc == alRight) ? imageRect.Width() - (size.cx + labelMargin.cx) : labelMargin.cx,
(imageRect.Height() - size.cy) / 2);
face.Paint(Dc, srcRect, dstPt, TUIFace::Normal, false, false);
}
void DrawTabText(const TNoteTabItem& tab, const TCelArray* celArray, const TSize& labelMargin, int labelImageMargin, bool isSelectedTab, int selectedTabProtrusion)
{
bool hasImage = celArray && tab.ImageIdx >= 0;
int leftMargin = tab.Rect.left + labelMargin.cx;
int rightMargin = tab.Rect.right - labelMargin.cx;
int topMargin = labelMargin.cy + (isSelectedTab ? selectedTabProtrusion : 0);
TRect labelRect(
(hasImage && tab.ImageLoc == alLeft) ? (leftMargin + celArray->CelSize().cx + labelImageMargin) : leftMargin,
tab.Rect.top + topMargin,
(hasImage && tab.ImageLoc == alRight) ? (rightMargin - celArray->CelSize().cx - labelImageMargin) : rightMargin,
tab.Rect.bottom - labelMargin.cy);
uint16 labelAlign = static_cast<uint16>(!hasImage ? DT_CENTER : (tab.ImageLoc == alRight ? DT_RIGHT : DT_LEFT));
uint16 labelFormat = static_cast<uint16>(DT_SINGLELINE | DT_VCENTER | labelAlign);
int oldBkMode = Dc.SetBkMode(TRANSPARENT);
Dc.SetTextColor(TabTextColor);
Dc.SelectObject(isSelectedTab ? SelectedTabFont : TabFont);
Dc.DrawText(tab.Label, -1, labelRect, labelFormat);
Dc.SetBkMode(oldBkMode);
}
void DrawTabFocusRect(const TNoteTabItem& tab, const TSize& focusMargin, int selectedTabProtrusion)
{
DrawTabFocusRect(Dc, tab, focusMargin, selectedTabProtrusion);
}
static void DrawTabFocusRect(TDC& dc, const TNoteTabItem& tab, const TSize& focusMargin, int selectedTabProtrusion)
{
int oldBkMode = dc.SetBkMode(TRANSPARENT);
TRect selectedRect = TRect(
tab.Rect.TopLeft().OffsetBy(0, selectedTabProtrusion),
tab.Rect.BottomRight())
.InflatedBy(-focusMargin);
dc.DrawFocusRect(selectedRect);
dc.SetBkMode(oldBkMode);
}
protected:
//
// Utilities for concrete renderers.
//
void FillPageEdge(const TSize& margin, const TBrush& brush)
{
TRect r = Window.GetClientRect();
TRect m(r.TopLeft(), TSize(r.Width(), margin.cy));
Dc.FillRect(m, brush);
}
void DrawPageEdge(const TSize& margin, const TPen& pen)
{
TRect r = Window.GetClientRect();
TRect m(r.TopLeft(), TSize(r.Width(), margin.cy));
Dc.SelectObject(pen);
Dc.MoveTo(m.BottomLeft());
Dc.LineTo(m.BottomRight());
}
void FillPolygon(const TPoint (&pt)[4], const TBrush& brush)
{
Dc.SelectStockObject(NULL_PEN);
Dc.SelectObject(brush);
Dc.Polygon(pt, COUNTOF(pt));
}
void DrawContour(const TPoint (&pt)[4], const TPen& pen)
{
#if defined(OWL_GDIPLUS_H)
// Draw anti-aliased lines using GDI+.
//
Gdiplus::Graphics g(Dc);
Gdiplus::Pen gdiPlusPen(gdiplus_cast<Gdiplus::Color>(TColor(pen.GetObject().lopnColor)));
Gdiplus::Point points[COUNTOF(pt)];
std::transform(pt, pt + COUNTOF(pt), points, &gdiplus_cast<Gdiplus::Point, TPoint>);
g.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
g.DrawLines(&gdiPlusPen, points, COUNTOF(points));
#else
Dc.SelectObject(pen);
Dc.Polyline(pt, COUNTOF(pt));
Dc.SetPixel(pt[3], pen.GetObject().lopnColor); // We want that last pixel as well!
#endif
}
};
struct TFlatRenderer_
: TRenderer_
{
TBrush TabBrush;
TPen EdgePen;
TFlatRenderer_(
TWindow& w,
TDC& dc,
const TRect& paintRect,
const TFont& tabFont,
const TFont& selectedTabFont,
TColor tabTextColor,
TColor tabColor,
TColor selectedTabColor,
TColor edgeColor
)
: TRenderer_(w, dc, paintRect, tabFont, selectedTabFont, tabTextColor, tabColor, selectedTabColor, edgeColor),
TabBrush(tabColor),
EdgePen(edgeColor)
{}
void PaintPageEdge(const TSize& margin) // non-virtual override
{
FillPageEdge(margin, TabBrush);
DrawPageEdge(margin, EdgePen);
}
void PaintTabFace(const TRect& tabBoundingRect, TPoint (&pt)[4], bool isSelectedTab)
{
InUse(tabBoundingRect); InUse(isSelectedTab); // non-virtual override
FillPolygon(pt, TabBrush);
}
void DrawTabContour(const TPoint (&pt)[4], bool isSelectedTab) // non-virtual override
{
InUse(isSelectedTab);
DrawContour(pt, EdgePen);
}
};
struct TWindowFaceRenderer_
: TFlatRenderer_
{
TBrush SelectedTabBrush;
TWindowFaceRenderer_(
TWindow& w,
TDC& dc,
const TRect& paintRect,
const TFont& tabFont,
const TFont& selectedTabFont,
TColor tabTextColor,
TColor tabColor,
TColor selectedTabColor,
TColor edgeColor
)
: TFlatRenderer_(w, dc, paintRect, tabFont, selectedTabFont, tabTextColor, tabColor, selectedTabColor, edgeColor),
SelectedTabBrush(selectedTabColor)
{}
void PaintPageEdge(const TSize& margin) // non-virtual override
{
FillPageEdge(margin, SelectedTabBrush);
DrawPageEdge(margin, EdgePen);
}
void PaintTabFace(const TRect& tabBoundingRect, TPoint (&pt)[4], bool isSelectedTab) // non-virtual override
{
InUse(tabBoundingRect);
FillPolygon(pt, isSelectedTab ? SelectedTabBrush : TabBrush);
}
};
struct TStyle3dRenderer_
: TRenderer_
{
TBrush FaceBrush;
TPen LightPen;
TPen HilightPen;
TPen ShadowPen;
TPen DkShadowPen;
TStyle3dRenderer_(
TWindow& w,
TDC& dc,
const TRect& paintRect,
const TFont& tabFont,
const TFont& selectedTabFont,
TColor tabTextColor,
TColor tabColor,
TColor selectedTabColor,
TColor edgeColor
)
: TRenderer_(w, dc, paintRect, tabFont, selectedTabFont, tabTextColor, tabColor, selectedTabColor, edgeColor),
FaceBrush(tabColor),
LightPen(TColor::Sys3dLight),
HilightPen(TColor::Sys3dHilight),
ShadowPen(TColor::Sys3dShadow),
DkShadowPen(TColor::Sys3dDkShadow)
{}
void PaintPageEdge(const TSize& margin) // non-virtual override
{
FillPageEdge(margin, FaceBrush);
// Draw a recessed frame around the tabs.
//
TRect r = Window.GetClientRect();
TRect f(
r.TopLeft().OffsetBy(0, margin.cy),
r.BottomRight());
TUIBorder(f, TUIBorder::WndRecessed).Paint(Dc);
}
void PaintTabFace(const TRect& tabBoundingRect, TPoint (&pt)[4], bool isSelectedTab) // non-virtual override
{
InUse(tabBoundingRect); InUse(isSelectedTab);
FillPolygon(pt, FaceBrush);
}
void DrawTabContour(const TPoint (&pt)[4], bool isSelectedTab) // non-virtual override
{
InUse(isSelectedTab);
Dc.SelectObject(LightPen); // inside left
Dc.MoveTo(pt[0].x + 1, pt[0].y);
Dc.LineTo(pt[1].x + 1, pt[1].y - 1);
Dc.SelectObject(HilightPen); // outside left
Dc.MoveTo(pt[0].x, pt[0].y);
Dc.LineTo(pt[1].x, pt[1].y);
Dc.SelectObject(ShadowPen); // inside bottom & right
Dc.MoveTo(pt[1].x + 1, pt[1].y - 1);
Dc.LineTo(pt[2].x - 1, pt[2].y - 1);
Dc.MoveTo(pt[2].x - 1, pt[2].y);
Dc.LineTo(pt[3].x - 1, pt[3].y);
Dc.SelectObject(DkShadowPen); // outside bottom & right
Dc.MoveTo(pt[1].x + 1, pt[1].y);
Dc.LineTo(pt[2].x, pt[2].y);
Dc.LineTo(pt[3].x, pt[3].y);
}
};
struct TThemeRenderer_
: TRenderer_
{
TPen EdgePen;
TThemeRenderer_(
TWindow& w,
TDC& dc,
const TRect& paintRect,
const TFont& tabFont,
const TFont& selectedTabFont,
TColor tabTextColor,
TColor tabColor,
TColor selectedTabColor,
TColor edgeColor
)
: TRenderer_(w, dc, paintRect, tabFont, selectedTabFont, tabTextColor, tabColor, selectedTabColor, edgeColor),
EdgePen(edgeColor)
{}
void PaintPageEdge(const TSize& margin) // non-virtual override
{
TRect r = Window.GetClientRect();
TRect m(r.TopLeft(), TSize(r.Width(), margin.cy));
TThemePart p(Window, L"TAB", TABP_BODY, 0);
p.DrawBackground(Dc, m);
DrawPageEdge(margin, EdgePen);
}
void PaintTabFace(const TRect& tabBoundingRect, TPoint (&pt)[4], bool isSelectedTab) // non-virtual override
{
int item = isSelectedTab ? TABP_TOPTABITEM : TABP_TABITEM;
int state = isSelectedTab ? TIS_SELECTED : TIS_NORMAL;
TThemePart p(Window, L"TAB", item, state);
const auto savedClipRegion = Dc.GetClipRgn();
TRegion clipRegion(pt, COUNTOF(pt), WINDING);
Dc.SelectClipRgn(clipRegion);
p.DrawBackground(Dc, tabBoundingRect.InflatedBy(0, 1)); // Inflate to not paint the edge.
Dc.SelectClipRgn(savedClipRegion);
}
void DrawTabContour(const TPoint (&pt)[4], bool isSelectedTab) // non-virtual override
{
InUse(isSelectedTab);
DrawContour(pt, EdgePen);
}
};
} // namespace
//
/// Implements the rendering of the window, using the given part renderer.
//
template <class TPartRenderer>
void TNoteTab::PaintTabs(TDC& dc, const TRect& paintRect)
{
if (GetCount() == 0) return;
TPartRenderer renderer(
*this,
dc,
paintRect,
GetTabFont(),
GetSelectedTabFont(),
TColor::SysWindowText,
TabColor,
SelectedTabColor,
EdgeColor);
// Now, go through the tab list in reverse order and paint each tab, except for
// the selected one, which has its painting deferred to the end (since it overlaps
// both its neighbours). We iterate in reverse order so that the tabs overlap
// correctly from left to right.
//
for (int i = GetCount(); --i >= -1;)
{
// Skip the selected tab; it's drawn last. Otherwise get the working index.
//
if (i == GetSel()) continue;
const bool isSelectedTab = (i == -1);
// If this is the final item (selected tab), then draw the page edge.
//
if (isSelectedTab)
renderer.PaintPageEdge(Margin);
// Retrieve tab item information.
//
const TNoteTabItem& tab = TabList[isSelectedTab ? GetSel() : i];
TRect tabBoundingRect = GetBoundingRect(tab.Rect);
if (tabBoundingRect.IsNull()) continue;
CHECK(!tabBoundingRect.IsEmpty());
// If the tab is completely outside the painting area, then skip it.
// Note that we inflate the paint area by 1 here to avoid edge conditions,
// i.e. whether or not a shared edge constitutes touching.
//
if (!(tabBoundingRect.InflatedBy(1, 1).Touches(paintRect)))
continue;
#if OWL_CLIP_TAB_BOUNDING_RECT_
struct TTabBoundingRectClipper
{
TDC& Dc;
TRegion SavedClipRegion;
bool DidSaveClipRegion;
TTabBoundingRectClipper(TDC& d, const TRect& b)
: Dc(d), SavedClipRegion(), DidSaveClipRegion(false)
{
if (!ShouldClipTabBoundingRect_) return;
int rc = Dc.GetClipRgn(SavedClipRegion);
CHECK(rc != -1);
DidSaveClipRegion = rc == 1;
int ri = Dc.IntersectClipRect(b);
CHECK(ri != ERROR);
}
~TTabBoundingRectClipper()
{
if (!ShouldClipTabBoundingRect_) return;
int r = DidSaveClipRegion ?
Dc.SelectClipRgn(SavedClipRegion) :
Dc.RemoveClipRgn();
CHECK(r != ERROR);
}
}
tbrc_(dc, tabBoundingRect);
#endif
// Define the contour of the tab.
//
// Note that we exclude the edge on the right and bottom of our bounding
// rectangle, as per Windows drawing conventions. See the documentation
// for GDI, e.g. for the Rectangle function:
//
// "The rectangle that is drawn excludes the bottom and right edges."
// http://msdn.microsoft.com/en-us/library/windows/desktop/dd162898.aspx
//
// By doing so we draw only within the clipping rectangle that would be
// visible if we had intersected the clipping region with the tab's
// bounding rectangle. This ensures that invalidation of our bounding
// rectangle covers all pixels drawn.
//
int dx = TabTapering; // amount of narrowing on each side
TPoint pt[4] =
{
tabBoundingRect.TopLeft(),
tabBoundingRect.BottomLeft().OffsetBy(dx, -1),
tabBoundingRect.BottomRight().OffsetBy(-dx - 1, -1),
tabBoundingRect.TopRight().OffsetBy(-1, 0)
};
// Fill the face of the tab, draw the contour and paint the label.
// If the note tab has input focus, also draw the focus rectangle.
//
renderer.PaintTabFace(tabBoundingRect, pt, isSelectedTab);
renderer.DrawTabContour(pt, isSelectedTab);
if (isSelectedTab && GetFocus() == GetHandle())
renderer.DrawTabFocusRect(tab, FocusMargin, SelectedTabProtrusion);
bool hasImage = CelArray && tab.ImageIdx >= 0;
if (hasImage)
renderer.PaintTabIcon(tab, *CelArray, TransparentColor, LabelMargin, isSelectedTab, SelectedTabProtrusion);
renderer.DrawTabText(tab, CelArray, LabelMargin, LabelImageMargin, isSelectedTab, SelectedTabProtrusion);
}
#if OWL_DRAW_TAB_BOUNDING_RECT_
if (ShouldDrawTabBoundingRect_)
for (int i = 0; i != GetCount(); ++i)
{
const TNoteTabItem& tab = TabList[i];
if (tab.Rect.IsNull()) continue;
// Use Polyline for accuracy; Rectangle excludes right and bottom edge.
//
TPen tabRectPen(TColor::LtRed, 0, PS_DOT);
dc.SelectObject(tabRectPen);
TRect b = GetBoundingRect(tab.Rect);
TPoint bp[] = {b.TopLeft(), b.BottomLeft(), b.BottomRight(), b.TopRight(), b.TopLeft()};
dc.Polyline(bp, COUNTOF(bp));
}
#endif
dc.RestorePen();
dc.RestoreBrush();
dc.RestoreFont();
}
//
/// TWindow::Paint override
//
void
TNoteTab::Paint(TDC& dc, bool erase, TRect& paintRect)
{
const auto bkgndColor = GetBkgndColor();
if (erase && bkgndColor != TColor::Transparent)
{
const auto color = (bkgndColor != TColor::None) ? bkgndColor : TColor::Sys3dFace;
if (GetCount() == 0)
dc.FillSolidRect(paintRect, color);
else
{
// First create a region consisting of the invalidated parts of the tabs
// area, i.e. excluding the top margin, which will be painted later.
// Then clip this against the tabs, since these will be painted anyway.
// This leaves only the invalidated parts around the tabs. Note that we
// do not exclude the scroller, since we rely on WS_CLIPCHILDREN.
//
TRect c = GetClientRect();
TRect tabsArea(c.TopLeft().OffsetBy(0, Margin.cy), c.BottomRight());
TRegion r(paintRect & tabsArea);
for (TNoteTabItemArray::const_iterator i = TabList.begin(); i != TabList.end(); ++i)
{
const TNoteTabItem& t = *i;
CHECK(t.Rect.IsNull() || !t.Rect.IsEmpty());
r -= t.Rect;
}
dc.FillRgn(r, TBrush{color}); // May be NULLREGION at this point.
}
}
if (GetCount() == 0) return;
if (ShouldUseThemes && IsThemed_())
PaintTabs<TThemeRenderer_>(dc, paintRect);
else if (Style3d)
PaintTabs<TStyle3dRenderer_>(dc, paintRect);
else if (WindowFace)
PaintTabs<TWindowFaceRenderer_>(dc, paintRect);
else
PaintTabs<TFlatRenderer_>(dc, paintRect);
if (ScrollButtons->IsWindowVisible())
{
ScrollButtons->Invalidate(false);
ScrollButtons->UpdateWindow();
}
LastClientRectPainted = GetClientRect(); // See EvSize.
}
//
/// WM_SIZE handler - Relay tab items
//
void
TNoteTab::EvSize(uint sizeType, const TSize& size)
{
TControl::EvSize(sizeType, size);
// Unless the layout of the tabs changes in response to the size change,
// the only stale part of the old painting is possibly the right and bottom
// edges, which are only drawn for 3D style. These need to be invalidated if
// the width or height of the window increases.
//
const TRect& p = LastClientRectPainted;
if (Style3d && !p.IsNull())
{
TRect n = GetClientRect();
if (n.Width() > p.Width()) // Increasing width; invalidate right edge.
{
TRect oldEdge(
p.TopRight().OffsetBy(-TUIMetric::CyEdge, 0),
p.BottomRight());
InvalidateRect(oldEdge);
}
if (n.Height() > p.Height()) // Inreasing height; invalidate bottom edge.
{
TRect oldEdge(
p.BottomLeft().OffsetBy(0, -TUIMetric::CxEdge),
p.BottomRight());
InvalidateRect(oldEdge);
}
}
// Layout tab items.
//
int s = GetSel();
bool hasVisibleSel = GetCount() > 0 && s >= 0 && IsVisible(s);
SetTabRects(FirstVisibleTab);
if (hasVisibleSel)
EnsureVisible(s);
}
//
/// WM_LBUTTONDOWN handler - Checks whether the mouse was clicked on a tab item and
/// selects it.
///
/// \note A notification is sent to the parent before and after selecting the tab.
/// The parent may choose to veto the selection after receiving the first
/// notification.
//
void
TNoteTab::EvLButtonDown(uint /*modKeys*/, const TPoint& point)
{
PRECONDITION(ScrollButtons);
if (ScrollButtons->IsWindowVisible() && GetScrollerArea().Contains(point)) return;
SetFocus();
const auto hitIndex = TabFromPoint(point);
if (hitIndex != -1)
NotifyAndSelect(hitIndex);
}
//
/// Handle WM_SETFOCUS: Draw focus to identify selected tab.
//
void
TNoteTab::EvSetFocus(THandle hWndLostFocus)
{
PRECONDITION(SelectedTab >= 0 && SelectedTab < GetCount());
TControl::EvSetFocus(hWndLostFocus);
InvalidateRect(TabList[SelectedTab].Rect);
UpdateWindow();
}
//
/// Handle WM_KillFOCUS: Remove dotted focus rectangle from selected tab.
//
void
TNoteTab::EvKillFocus(THandle hWndGetFocus)
{
PRECONDITION(SelectedTab >= 0 && SelectedTab < GetCount());
TControl::EvKillFocus(hWndGetFocus);
InvalidateRect(TabList[SelectedTab].Rect);
UpdateWindow();
}
//
/// WM_GETDLGCODE handler - Informs dialog manager that arrow keys are to be used.
//
uint
TNoteTab::EvGetDlgCode(const MSG* msg)
{
return TControl::EvGetDlgCode(msg) | DLGC_WANTARROWS;
}
//
/// WM_KEYDOWN handler - handles arrow keys to allow user to navigate through tab
/// items.
//
void
TNoteTab::EvKeyDown(uint key, uint /*repeatCount*/, uint /*flags*/)
{
int tab = GetSel();
if (tab < 0) return;
CHECK(tab < GetCount());
if (GetKeyState(VK_CONTROL) & 0x8000) switch (key)
{
case VK_RIGHT:
EvHScroll(SB_LINERIGHT, 0, NULL);
break;
case VK_LEFT:
EvHScroll(SB_LINELEFT, 0, NULL);
break;
}
else switch (key)
{
case VK_RIGHT:
{
int last = GetCount() - 1;
if (tab < last)
{
if (NotifyAndSelect(++tab))
{
EnsureVisible(tab);
while (!IsFullyVisible(tab) && GetFirstVisibleTab() < last)
SetFirstVisibleTab(GetFirstVisibleTab() + 1);
}
}
break;
}
case VK_LEFT:
{
if (tab > 0)
{
if (NotifyAndSelect(--tab))
EnsureVisible(tab);
}
break;
}
#if OWL_CLIP_TAB_BOUNDING_RECT_
case VK_F2:
ShouldClipTabBoundingRect_ = !ShouldClipTabBoundingRect_;
Invalidate();
break;
#endif
#if OWL_DRAW_TAB_BOUNDING_RECT_
case VK_F3:
ShouldDrawTabBoundingRect_ = !ShouldDrawTabBoundingRect_;
Invalidate();
break;
#endif
}
}
//
/// Updates the internal information stored about the label of a particular tab
/// item.
//
void
TNoteTab::SetTabSize(int index)
{
PRECONDITION(TabFont);
const TFont& selectedTabFont = GetSelectedTabFont();
// Compute size of label.
// Accommodate the largest font in both dimensions.
// Use screen DC (may be called before 'HWND').
//
TScreenDC dc;
TNoteTabItem& tab = TabList[index];
static const tchar overhangSet[] = _T("dfkmVWY"); // TODO: Add complete set, or query font.
bool hasOverhang = !tab.Label.empty() &&
(TabFont->GetObject().lfItalic || selectedTabFont.GetObject().lfItalic) &&
find(begin(overhangSet), end(overhangSet), tab.Label.back()) != end(overhangSet);
tstring s = tab.Label + (hasOverhang ? _T(" ") : _T(""));
TRect regularExtent = TRect(TPoint(), TabFont->GetTextExtent(dc, s));
TRect selectedExtent = TRect(TPoint(), selectedTabFont.GetTextExtent(dc, s));
tab.LabelSize = (regularExtent | selectedExtent).Size();
// Add image dimensions to LabelSize.
//
if (tab.ImageIdx >= 0 && CelArray)
{
TSize size = CelArray->CelSize();
tab.LabelSize.cx += size.cx + LabelImageMargin;
tab.LabelSize.cy = std::max(tab.LabelSize.cy, size.cy);
}
}
//
/// Calculates the tab rectangle for the given tab and position.
//
TRect
TNoteTab::CalculateTabRect(const TNoteTabItem& tab, const TPoint& p, bool isSelected) const
{
TSize padding(2 * LabelMargin.cx, 2 * LabelMargin.cy + (isSelected ? SelectedTabProtrusion : 0));
return TRect(p.OffsetBy(TabTapering, 0), tab.LabelSize + padding);
};
//
/// Calculates and returns a list of updated tab hit rectangles based on the
/// given start index and the given area for the tabs.
//
TNoteTab::TRects
TNoteTab::CalculateTabRects(int firstTab, const TRect& area) const
{
TRects rects;
rects.reserve(GetCount());
// Reset all the tab rectangles preceeding the first visible tab.
// Value-initialize these by calling `resize` (i.e. insert empty rectangles).
//
rects.resize(std::min(firstTab, GetCount()));
// Assign rectangles to all visible tabs.
//
TPoint p = area.TopLeft();
for (int i = firstTab; i != GetCount(); ++i)
{
const TNoteTabItem& tab = TabList[i];
const TRect r = CalculateTabRect(tab, p, i == SelectedTab);
// If the tab is completely outside the tabs area, then break. In other
// words; include partially visible tabs.
//
const TRect b = GetBoundingRect(r);
bool isVisible = (b.left < area.right);
if (!isVisible)
{
// Reset the rectangles for the remaining tabs, then exit.
//
rects.resize(GetCount());
break;
}
rects.push_back(r);
p.Offset(r.Width() + TabSpacing, 0); // Move to next tab position.
}
return rects;
}
//
/// Assigns the given new rectangle to the given tab item.
/// Both old and new rectangle is invalidated in the client area.
//
void
TNoteTab::AssignTabRect(TNoteTabItem& tab, const TRect& newRect)
{
TRect& r = tab.Rect;
if (r == newRect) return;
if (GetHandle() && !r.IsNull())
InvalidateRect(GetBoundingRect(r));
r = newRect;
if (GetHandle() && !r.IsNull())
InvalidateRect(GetBoundingRect(r));
}
namespace
{
//
// Returns true if the horizontal projection of the given rect `r`
// lies within the horizontal projection of the given rect `area`.
//
bool ContainsHorizontalExtents_(const TRect& area, const TRect& r)
{
if (area.IsEmpty() || r.IsEmpty()) return false;
return r.left >= area.left && r.right <= area.right;
}
} // namespace
//
/// Lays out tab items (and scroll buttons) with the specified index at the leftmost.
/// Scroll buttons are enabled when needed (unless the scroll location is set to alNone).
//
void TNoteTab::SetTabRects(int firstTab)
{
const auto tabCount = GetCount();
PRECONDITION((tabCount == 0 && firstTab == 0) || (firstTab >= 0 && firstTab < tabCount));
if (!GetHandle()) return;
// First, calculate tab layout with no scroll buttons present. If we have multiple tabs, and all
// tabs did not fit, then we need to reserve space for the scroll buttons (unless ScrollLoc ==
// alNone), so calculate the layout anew within the dedicated tabs area. Finally, store the
// effective tabs area, and assign tab rectangles for the new layout.
//
const auto scrollingTabsArea = GetScrollingTabsArea();
const auto scrollerArea = GetScrollerArea();
const auto allArea = scrollingTabsArea | scrollerArea;
auto newRects = CalculateTabRects(firstTab, allArea);
const auto needScrolling = ScrollLoc != alNone && tabCount > 1 &&
(
firstTab != 0 || // Invisible tab to the left.
newRects.back().IsNull() || // Invisible tab to the right.
!ContainsHorizontalExtents_(allArea, GetBoundingRect(newRects.back())) // Tab does not fit.
);
if (needScrolling)
newRects = CalculateTabRects(firstTab, scrollingTabsArea);
EffectiveTabsArea = needScrolling ? scrollingTabsArea : allArea;
CHECK(static_cast<int>(newRects.size()) == tabCount);
for (auto i = 0; i != tabCount; ++i)
AssignTabRect(TabList[i], newRects[i]);
// If we need scrolling, then calculate the upper index of the scroll range. Then update and show
// the scroll buttons. Otherwise, hide the scroll buttons.
//
CHECK(ScrollButtons && ScrollButtons->GetHandle());
if (needScrolling)
{
const auto countNotFullyVisibleTabs = [&]
{
PRECONDITION(tabCount > 0);
PRECONDITION(firstTab >= 0 && firstTab < tabCount);
auto n = firstTab; // Invisible tabs to the left.
for (auto i = tabCount - 1; i >= firstTab && !IsFullyVisible(i); --i)
++n; // Invisible (or partially visible) tab to the right.
CHECK(n >= 0 && n <= tabCount);
return n;
};
const auto upper = min(countNotFullyVisibleTabs(), tabCount - 1);
CHECK(upper > 0 && upper < tabCount);
ScrollButtons->SetRange(0, upper);
ScrollButtons->SetPos(firstTab);
ScrollButtons->MoveWindow(scrollerArea, true);
ScrollButtons->ShowWindow(SW_SHOW);
ScrollButtons->UpdateWindow();
}
else
ScrollButtons->ShowWindow(SW_HIDE);
}
//
/// Returns the index of the tab item at the specified window coordinate. Returns -1
/// on failure.
//
int
TNoteTab::TabFromPoint(const TPoint& pt) const
{
for (int i = 0; i < GetCount(); i++)
{
const TNoteTabItem& tab = TabList[i];
if (tab.Rect.Contains(pt))
return i;
}
return -1;
}
//
/// Selects the tab at the given index and sends the appropriate notifications.
/// Returns false if the change was vetoed by the buddy/parent, true otherwise.
/// Does nothing and returns false if the tab at the given index is already selected.
//
bool
TNoteTab::NotifyAndSelect(int index)
{
PRECONDITION(index >= 0);
PRECONDITION(index < GetCount());
if (index == SelectedTab) return false;
HWND receiver = Buddy ? BuddyHandle : ::GetParent(GetHandle());
// First notify that we're about to change selection.
//
TNotify nSelChanging(*this, Attr.Id, TCN_SELCHANGING);
bool wasVetoed = SendNotification(receiver, Attr.Id, nSelChanging);
if (wasVetoed) return false;
// Set new selection and notify that it has changed.
//
SetSel(index);
TNotify nSelChange(*this, Attr.Id, TCN_SELCHANGE);
SendNotification(receiver, Attr.Id, nSelChange);
return true;
}
//
// Refreshes the drawing of the control.
// If the control has not been created yet, does nothing.
//
void
TNoteTab::Update()
{
if (!GetHandle()) return;
for (int i = 0; i != GetCount(); ++i)
SetTabSize(i);
SetTabRects(GetFirstVisibleTab());
Invalidate();
}
//
/// Returns the desired location of the scrollers within the tab.
//
TRect
TNoteTab::GetScrollerArea() const
{
TRect rect = GetClientRect();
rect.bottom -= TUIMetric::CyFixedFrame;
rect.top = std::max(
rect.top + Margin.cy + TUIMetric::CyFixedFrame,
rect.bottom - TUIMetric::CyHScroll);
switch (ScrollLoc)
{
default:
case alNone:
rect.left = rect.right = 0;
break;
case alLeft:
rect.left += TUIMetric::CxFixedFrame;
rect.right = rect.left + 2 * TUIMetric::CxHScroll;
break;
case alRight:
rect.left = rect.right - 2 * TUIMetric::CxHScroll;
rect.right -= TUIMetric::CxFixedFrame;
break;
}
return rect;
}
//
/// Returns the rectangle of the area reserved for tabs when scrolling is active.
//
TRect
TNoteTab::GetScrollingTabsArea() const
{
// First retrieve left, top and right borders
//
TRect rect = GetClientRect();
rect.Inflate(-Margin.cx, 0);
rect.top += Margin.cy;
TRect scrollArea = GetScrollerArea();
switch (ScrollLoc)
{
default:
case alRight:
rect.right -= scrollArea.Width();
break;
case alLeft:
rect.left += scrollArea.Width();
break;
}
return rect;
}
//
/// Returns true if the tab item at the specified index is visible.
/// Note that true is returned even if the tab is only partially visible.
/// Use IsFullyVisible to check for full visibility.
//
bool
TNoteTab::IsVisible(int index) const
{
PRECONDITION(index < GetCount() && index >= 0);
return TabList[index].Rect.IsNull() ? false : true;
}
//
/// Returns true if the horizontal projection of the tab item at the specified
/// index is contained within the horizontal projection of the current
/// effective tabs area. Vertical visibility is ignored.
//
bool
TNoteTab::IsFullyVisible(int index) const
{
PRECONDITION(index < GetCount() && index >= 0);
if (EffectiveTabsArea.IsEmpty()) return false;
if (!IsVisible(index)) return false;
TRect b = GetBoundingRect(TabList[index].Rect);
CHECK(!b.IsEmpty());
return ContainsHorizontalExtents_(EffectiveTabsArea, b);
}
//
/// Sets FirstVisibleTab to index if index is valid.
//
void
TNoteTab::SetFirstVisibleTab(int index)
{
if (index == GetFirstVisibleTab() || index < 0 || index >= GetCount()) return;
FirstVisibleTab = index;
SetTabRects(FirstVisibleTab);
}
//
/// If the tab specified by index is not visible, it is scrolled into view.
/// If the given index is -1, the index of the selected tab is used instead.
/// Returns true if successful, false on failure.
//
bool
TNoteTab::EnsureVisible(int index)
{
PRECONDITION(GetCount() > 0);
PRECONDITION(index >= -1 && index < GetCount());
int i = (index == -1) ? GetSel() : index; CHECK(i >= 0);
while (i != GetFirstVisibleTab() && !IsFullyVisible(i))
{
int v = GetFirstVisibleTab(); CHECK(v != i);
SetFirstVisibleTab(v + (i < v ? -1 : +1));
}
return true;
}
//
/// Sets the first visible tab to the given thumb position.
//
void TNoteTab::EvHScroll(uint scrollCode, uint thumbPos, [[maybe_unused]] HWND hWndCtl)
{
if (scrollCode == SB_THUMBPOSITION)
SetFirstVisibleTab(thumbPos);
}
//
/// Disables automatic background erasure by returning `false`.
//
bool
TNoteTab::EvEraseBkgnd(HDC)
{
return false;
}
//
/// Set the scroller location.
//
void
TNoteTab::SetScrollLocation(TAbsLocation loc)
{
if (ScrollLoc == loc)
return;
ScrollLoc = loc;
SetTabRects(FirstVisibleTab);
}
//
/// Sets the bitmap array to be used for the tabs.
/// Use SetCelArrayTransparentColor to set the bitmap pixel color that should
/// not be drawn. The default color is TColor::Sys3dFace.
//
void
TNoteTab::SetCelArray(TCelArray* array, TAutoDelete del)
{
CelArray = array;
OwnedCelArray.reset(del == AutoDelete ? array : 0);
Update();
}
//
/// Sets the color assigned to denote transparency in the bitmaps used for the
/// tabs (see SetCelArray). All the bitmap pixels of this color will be fully
/// transparent (i.e. not drawn).
//
void
TNoteTab::SetCelArrayTransparentColor(const TColor& c)
{
TransparentColor = c;
Update();
}
} // OWL namespace
/* ========================================================================== */
↑ V730 It is possible that not all members of a class are initialized inside the constructor. Consider inspecting: BuddyHandle.
↑ V730 It is possible that not all members of a class are initialized inside the constructor. Consider inspecting: BuddyHandle.
↑ V823 Decreased performance. Object may be created in-place in the 'TabList' container. Consider replacing methods: 'insert' -> 'emplace'.