TDC::SelectObject does not return anything. This is unlike the Windows API function SelectObject it encapsulates, which returns the object replaced. It is also unlike other GDI state functions, such as TDC::SetBkColor, TDC::SetBkMode, TDC::SetTextAlign, TDC::SetTextColor and the Windows API functions they encapsulate, which all return the previous setting.
This makes it difficult to restore the GDI state after changing it using SelectObject. Note that the functions TDC::RestorePen, TDC::RestoreBrush, TDC::RestoreFont, TDC::RestorePalette, TDC::RestoreBitmap and TDC::RestoreObjects are all pretty useless, since they restore the original object, not the previous one. The original object is the object that was in effect before the first call to TDC::SelectObject, or since the last call to the corresponding restore function (or RestoreObjects, which restores all). In short, these functions do not support any nesting.
For example:
dc.SelectObject(font1);
dc.TextOut(...);
if (blah)
{
dc.SelectObject(font2);
dc.TextOut(...);
dc.RestoreFont(); // Goes back to default font, not font1.
}
dc.TextOut(...); // Appears in default font, not font1.
dc.RestoreFont(); // Useless. Already back to default font.
The problem is made worse when dividing the drawing operations into separate functions. For a function to be able to restore the setting to the previous value, it would have to query TDC about the current object before changing it.
I propose changing SelectObject to return the replaced object, to allow proper restoration of the previous setting, in a consistent manner:
const auto oldFont1 = dc.SelectObject(font1);
dc.TextOut(...);
if (blah)
{
const auto oldFont2 = dc.SelectObject(font2);
dc.TextOut(...);
dc.SelectFont(oldFont2); // Goes back to font1.
}
dc.TextOut(...); // Appears in font1, as expected.
dc.SelectFont(oldFont1); // Restores the default font.
PS. Of course, this is not good C++. The fonts are not restored in the case of an exception. For exception safety, I use RAII wrappers in my application to make sure exceptions do not mess up the GDI state:
const auto autoFont1 = TAutoFont{dc, font1}; // Selects font1.
dc.TextOut(...);
if (blah)
{
const auto autoFont2 = TAutoFont{dc, font2};
dc.TextOut(...);
} // Destruction of autoFont2 restores font1.
dc.TextOut(...); // Appears in font1, as expected.
// Destructor of autoFont1 restores default font.
I do not propose RAII wrappers in this ticket, since it is a pure extension that can be proposed separately. However, let me know what you think.
Discussion: Selecting and restoring objects in TDC
Discussion: Selecting and restoring objects in TDC
Feature Requests: #178
Wiki: Selecting_and_restoring_objects_in_TDC
Anonymous
This feature was implemented in Owlet in [r5432].
This revision also changes TDC::SelectStockObject to return the replaced object, and adds overloads for the various object types.
For example:
Related
Commit: [r5432]