Files
GdCpp12/staticOnly/KsMFC/CWizard.cpp

667 lines
17 KiB
C++

#include "pch.h"
#include "MFC/CWizard.h"
#include "MFC/CWizardPage.h"
CWizard::CWizard(UINT nIDTemplate, CWnd* pParentWnd)
: CBCGPDialog(nIDTemplate, pParentWnd)
{
EnableVisualManagerStyle();
}
BOOL CWizard::OnInitDialog()
{
CBCGPDialog::OnInitDialog();
fontTitle.CreatePointFont(fontSize_title, L"楷体");
fontDescription.CreatePointFont(fontSize_desc, L"楷体");
labelTitle.SetFont(&fontTitle);
labelDesc.SetFont(&fontDescription);
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
void CWizard::OnSize(UINT nType, int cx, int cy)
{
CBCGPDialog::OnSize(nType, cx, cy);
if (!labelTitle.m_hWnd) return;
// TODO: 在此处添加消息处理程序代码
CRect r;
GetClientRect(&r);
int x = globalUtils.ScaleByDPI(titleLeft);
int y = globalUtils.ScaleByDPI(titleTop);
int h = globalUtils.ScaleByDPI(titleHeight);
labelTitle.MoveWindow(x, y, r.Width() - x * 2, h);
x = globalUtils.ScaleByDPI(descLeft);
y += globalUtils.ScaleByDPI(descTop)+h;
h = globalUtils.ScaleByDPI(descHeight);
labelDesc.MoveWindow(x, y, r.Width() - x * 2, h);
headH = y + h + globalUtils.ScaleByDPI(descBottom);
int btnW = globalUtils.ScaleByDPI(btnWidth), btnH = globalUtils.ScaleByDPI(btnHeight);
int btnS = globalUtils.ScaleByDPI(btnSpace), btnR = globalUtils.ScaleByDPI(btnRight), btnB = globalUtils.ScaleByDPI(btnBottom);
footH = btnB + btnH + globalUtils.ScaleByDPI(btnTop);
x = r.right - btnW - btnR;
y = r.bottom - btnH - btnB;
if (opts & HaveHelpButton) {
btns[HelpButton].MoveWindow(x, y, btnW, btnH); x -= btnW + btnS;
}
else {
btns[HelpButton].ShowWindow(SW_HIDE);
}
btns[CancelButton].MoveWindow(x, y, btnW, btnH); x -= btnW + btnS;
// 下面3个键位置重合
btns[FinishButton].MoveWindow(x, y, btnW, btnH); //x -= btnW + btnS;
btns[CommitButton].MoveWindow(x, y, btnW, btnH); //x -= btnW + btnS;
btns[NextButton].MoveWindow(x, y, btnW, btnH); x -= btnW + btnS;
btns[BackButton].MoveWindow(x, y, btnW, btnH);
}
void CWizard::reset()
{
if (current != -1) {
currentPage()->ShowWindow(SW_HIDE);
cleanupPagesNotInHistory();
for (auto i = history.size() - 1; i >= 0; --i)
cleanupPage(history.at(i));
history.clear();
for (auto &[id, p] : pageMap)
p->initialized = false;
current = -1;
// emit q->currentIdChanged(-1);
}
if (current != -1) {
current = -1;
}
}
void CWizard::cleanupPagesNotInHistory()
{
for (auto &[idx, p] : pageMap) {
auto it = std::find(history.begin(), history.end(), idx);
if (p->initialized && it == history.end()) {
cleanupPage(idx);
p->initialized = false;
}
}
}
void CWizard::switchToPage(int newId, Direction direction)
{
// disableUpdates();
int oldId = current;
if (CWizardPage* oldPage = currentPage()) {
oldPage->ShowWindow(SW_HIDE);
if (direction == Backward) {
if (!(opts & CWizard::IndependentPages)) {
cleanupPage(oldId);
oldPage->initialized = false;
}
ASSERT(history.back() == oldId);
history.pop_back();
ASSERT(history.back() == newId);
}
}
current = newId;
CWizardPage* newPage = currentPage();
if (newPage) {
if (direction == Forward) {
if (!newPage->initialized) {
newPage->initialized = true;
initializePage(current);
}
history.push_back(current);
}
newPage->ShowWindow(SW_SHOW);
labelTitle.SetWindowText(newPage->title());
labelDesc.SetWindowText(newPage->desc());
}
canContinue = (nextId() != -1);
canFinish = (newPage && newPage->isFinalPage());
_q_updateButtonStates();
updateButtonTexts();
const CWizard::WizardButton nextOrCommit =
newPage && newPage->isCommitPage() ? CWizard::CommitButton : CWizard::NextButton;
CBCGPButton* nextOrFinishButton =
&btns[canContinue ? nextOrCommit : CWizard::FinishButton];
//CWnd* candidate = nullptr;
///*
// If there is no default button and the Next or Finish button
// is enabled, give focus directly to it as a convenience to the
// user. This is the normal case on OS X.
// Otherwise, give the focus to the new page's first child that
// can handle it. If there is no such child, give the focus to
// Next or Finish.
//*/
//if ((opts & QWizard::NoDefaultButton) && nextOrFinishButton->isEnabled()) {
// candidate = nextOrFinishButton;
//}
//else if (newPage) {
// candidate = iWantTheFocus(newPage);
//}
//if (!candidate)
// candidate = nextOrFinishButton;
//candidate->setFocus();
//if (wizStyle == QWizard::MacStyle)
// q->updateGeometry();
//enableUpdates();
//updateLayout();
//updatePalette();
//emit q->currentIdChanged(current);
}
void CWizard::updateCurrentPage()
{
if (currentPage()) {
canContinue = (nextId() != -1);
canFinish = currentPage()->isFinalPage();
}
else {
canContinue = false;
canFinish = false;
}
_q_updateButtonStates();
updateButtonTexts();
}
/*!
\property QWizard::startId
\brief the ID of the first page
If this property isn't explicitly set, this property defaults to
the lowest page ID in this wizard, or -1 if no page has been
inserted yet.
\sa restart(), nextId()
*/
void CWizard::setStartId(int theid)
{
int newStart = theid;
if (theid == -1)
newStart = pageMap.size() ? pageMap.begin()->first : -1;
if (start == newStart) {
startSetByUser = theid != -1;
return;
}
if (pageMap.find(newStart) != pageMap.end()) {
//alog->warn("CWizard::setStartId: Invalid page ID {}", newStart);
return;
}
start = newStart;
startSetByUser = theid != -1;
}
static void setVisible(CWnd* w, bool en)
{
ASSERT(w != nullptr);
w->ShowWindow(en ? SW_SHOW : SW_HIDE);
}
void CWizard::_q_updateButtonStates()
{
//disableUpdates();
const auto* p = currentPage();
bool complete = p && p->isComplete();
btns[BackButton].EnableWindow(history.size() > 1
&& !page(history.at(history.size() - 2))->isCommitPage()
&& (!canFinish || !(opts & CWizard::DisabledBackButtonOnLastPage)));
btns[NextButton].EnableWindow(canContinue && complete);
btns[CommitButton].EnableWindow(canContinue && complete);
btns[FinishButton].EnableWindow(canFinish && complete);
const bool backButtonVisible =
(history.size() > 1 || !(opts & CWizard::NoBackButtonOnStartPage))
&& (canContinue || !(opts & CWizard::NoBackButtonOnLastPage));
bool commitPage = p && p->isCommitPage();
setVisible(&btns[BackButton], backButtonVisible);
setVisible(&btns[NextButton], !commitPage && (canContinue || (opts & CWizard::HaveNextButtonOnLastPage)));
setVisible(&btns[CommitButton], commitPage && canContinue);
setVisible(&btns[FinishButton], (canFinish || (opts & CWizard::HaveFinishButtonOnEarlyPages)));
if (!(opts & CWizard::NoCancelButton))
setVisible(&btns[CancelButton], (canContinue || !(opts & CWizard::NoCancelButtonOnLastPage)));
//bool useDefault = !(opts & CWizard::NoDefaultButton);
//if (QPushButton* nextPush = qobject_cast<QPushButton*>(btn.next))
// nextPush->setDefault(canContinue && useDefault && !commitPage);
//if (QPushButton* commitPush = qobject_cast<QPushButton*>(btn.commit))
// commitPush->setDefault(canContinue && useDefault && commitPage);
//if (QPushButton* finishPush = qobject_cast<QPushButton*>(btn.finish))
// finishPush->setDefault(!canContinue && useDefault);
}
#include "CLang.h"
static const wchar_t* buttonDefaultText(int which)
{
if (gLang == CLang::zh) {
switch (which) {
case CWizard::BackButton:
return L"< 上一步";
case CWizard::NextButton:
return L"下一步 >";
case CWizard::CommitButton:
return L"提交";
case CWizard::FinishButton:
return L"完成";
case CWizard::CancelButton:
return L"取消";
case CWizard::HelpButton:
return L"帮助";
default:
return L"";
}
}
else {
switch (which) {
case CWizard::BackButton:
return L"< Back";
case CWizard::NextButton:
return L"Next >";
case CWizard::CommitButton:
return L"Commit";
case CWizard::FinishButton:
return L"Finish";
case CWizard::CancelButton:
return L"Cancel";
case CWizard::HelpButton:
return L"Help";
default:
return L"";
}
}
}
void CWizard::updateButtonTexts()
{
for (int i = 0; i < CWizard::NButtons; ++i) {
if (currentPage() && currentPage()->buttonCustomTexts.find(i) != currentPage()->buttonCustomTexts.end()) {
btns[i].SetWindowText(currentPage()->buttonCustomTexts.find(i)->second);
}
else {
auto it = buttonCustomTexts.find(i);
if (it != buttonCustomTexts.end())
btns[i].SetWindowText(it->second);
else if (i < NStandardButtons)
btns[i].SetWindowText(buttonDefaultText(i));
}
}
}
void CWizard::updateButtonLayout()
{
// Back Next Commit Finish Cancel Help
}
/*!
Adds the given \a page to the wizard, and returns the page's ID.
The ID is guaranteed to be larger than any other ID in the
QWizard so far.
\sa setPage(), page(), pageAdded()
*/
int CWizard::addPage(CWizardPage* page)
{
int theid = 0;
if (!pageMap.empty())
theid = pageMap.rbegin()->first + 1;
setPage(theid, page);
return theid;
}
/*!
\fn void QWizard::setPage(int id, QWizardPage *page)
Adds the given \a page to the wizard with the given \a id.
\note Adding a page may influence the value of the startId property
in case it was not set explicitly.
\sa addPage(), page(), pageAdded()
*/
void CWizard::setPage(int theid, CWizardPage* page)
{
if (!page) {
alog->error("CWizard::setPage: Cannot insert null page");
return;
}
if (theid == -1) {
alog->error("CWizard::setPage: Cannot insert page with ID -1");
return;
}
auto it = pageMap.find(theid);
if (it != pageMap.end()) {
alog->error("CWizard::setPage: Page with duplicate ID {} ignored", theid);
return;
}
//page->setParent(d->pageFrame);
//QVector<QWizardField>& pendingFields = page->d_func()->pendingFields;
// for (int i = 0; i < pendingFields.count(); ++i)
// d->addField(pendingFields.at(i));
// pendingFields.clear();
// connect(page, SIGNAL(completeChanged()), this, SLOT(_q_updateButtonStates()));
pageMap.insert({ theid, page });
page->_wizard = this;
page->ShowWindow(SW_HIDE);
if (!startSetByUser && pageMap.cbegin()->first == theid)
start = theid;
//emit pageAdded(theid);
}
/*!
Removes the page with the given \a id. cleanupPage() will be called if necessary.
\note Removing a page may influence the value of the startId property.
\since 4.5
\sa addPage(), setPage(), pageRemoved(), startId()
*/
void CWizard::removePage(int id)
{
CWizardPage* removedPage = nullptr;
// update startItem accordingly
if (pageMap.size() > 0) { // only if we have any pages
auto it = pageMap.cbegin();
if (start == id) {
const int firstId = it->first;
if (firstId == id) {
if (pageMap.size() > 1)
start = (++it)->first; // secondId
else
start = -1; // removing the last page
}
else { // startSetByUser has to be "true" here
start = firstId;
}
startSetByUser = false;
}
}
auto it = pageMap.find(id);
if (it != pageMap.end()) {
//emit pageRemoved(id);
auto fi = std::find(history.begin(), history.end(), id);
if (fi == history.end()) {
// Case 1: removing a page not in the history
removedPage = it->second;
pageMap.erase(it);
updateCurrentPage();
}
else if (id != current) {
// Case 2: removing a page in the history before the current page
removedPage = it->second;
pageMap.erase(it);
history.erase(fi);
_q_updateButtonStates();
}
else if (history.size() == 1) {
// Case 3: removing the current page which is the first (and only) one in the history
reset();
removedPage = it->second;
pageMap.erase(it);
if (pageMap.empty())
updateCurrentPage();
else
restart();
}
else {
// Case 4: removing the current page which is not the first one in the history
back();
removedPage = it->second;
pageMap.erase(it);
updateCurrentPage();
}
if (removedPage) {
if (removedPage->initialized) {
cleanupPage(id);
removedPage->initialized = false;
}
//d->pageVBoxLayout->removeWidget(removedPage);
}
}
}
/*!
Sets the text on button \a which to be \a text.
By default, the text on buttons depends on the wizardStyle. For
example, on \macos, the \uicontrol Next button is called \uicontrol
Continue.
To add extra buttons to the wizard (e.g., a \uicontrol Print button),
one way is to call setButtonText() with CustomButton1,
CustomButton2, or CustomButton3 to set their text, and make the
buttons visible using the HaveCustomButton1, HaveCustomButton2,
and/or HaveCustomButton3 options.
Button texts may also be set on a per-page basis using QWizardPage::setButtonText().
\sa setButton(), button(), setButtonLayout(), setOptions(), QWizardPage::setButtonText()
*/
void CWizard::setButtonText(WizardButton which, const CString& text)
{
// if (!d->ensureButton(which))
// return;
ASSERT(which >= 0);
ASSERT(which < NButtons);
buttonCustomTexts.insert({ which, text });
auto* curPage = currentPage();
if (!curPage || curPage->buttonCustomTexts.find(which) == curPage->buttonCustomTexts.end()) {
btns[which].SetWindowText(text);
}
}
/*!
Goes back to the previous page.
This is equivalent to pressing the \uicontrol Back button.
\sa next(), accept(), reject(), restart()
*/
void CWizard::back()
{
auto n = history.size() - 2;
if (n < 0)
return;
switchToPage(history[n], CWizard::Backward);
}
/*!
Advances to the next page.
This is equivalent to pressing the \uicontrol Next or \uicontrol Commit button.
\sa nextId(), back(), accept(), reject(), restart()
*/
void CWizard::next()
{
if (current == -1)
return;
if (validateCurrentPage()) {
int next = nextId();
if (next != -1) {
if (std::find(history.begin(), history.end(), next) != history.end()) {
alog->warn("CWizard::next: Page {} already met", next);
return;
}
if (pageMap.find(next) == pageMap.end()) {
alog->warn("CWizard::next: No such page {}", next);
return;
}
switchToPage(next, CWizard::Forward);
}
}
}
/*!
Restarts the wizard at the start page. This function is called automatically when the
wizard is shown.
\sa startId()
*/
void CWizard::restart()
{
//disableUpdates();
reset();
switchToPage(startId(), CWizard::Forward);
//enableUpdates();
}
/*!
\fn void QWizard::initializePage(int id)
This virtual function is called by QWizard to prepare page \a id
just before it is shown either as a result of QWizard::restart()
being called, or as a result of the user clicking \uicontrol Next. (However, if the \l
QWizard::IndependentPages option is set, this function is only
called the first time the page is shown.)
By reimplementing this function, you can ensure that the page's
fields are properly initialized based on fields from previous
pages.
The default implementation calls QWizardPage::initializePage() on
page(\a id).
\sa QWizardPage::initializePage(), cleanupPage()
*/
void CWizard::initializePage(int theid)
{
CWizardPage* p = this->page(theid);
if (p) p->initializePage();
}
/*!
\fn void QWizard::cleanupPage(int id)
This virtual function is called by QWizard to clean up page \a id just before the
user leaves it by clicking \uicontrol Back (unless the \l QWizard::IndependentPages option is set).
The default implementation calls QWizardPage::cleanupPage() on
page(\a id).
\sa QWizardPage::cleanupPage(), initializePage()
*/
void CWizard::cleanupPage(int theid)
{
CWizardPage* p = this->page(theid);
if (p)
p->cleanupPage();
}
/*!
This virtual function is called by QWizard when the user clicks
\uicontrol Next or \uicontrol Finish to perform some last-minute validation.
If it returns \c true, the next page is shown (or the wizard
finishes); otherwise, the current page stays up.
The default implementation calls QWizardPage::validatePage() on
the currentPage().
When possible, it is usually better style to disable the \uicontrol
Next or \uicontrol Finish button (by specifying \l{mandatory fields} or
by reimplementing QWizardPage::isComplete()) than to reimplement
validateCurrentPage().
\sa QWizardPage::validatePage(), currentPage()
*/
bool CWizard::validateCurrentPage()
{
CWizardPage* page = currentPage();
if (!page)
return true;
return page->validatePage();
}
/*!
This virtual function is called by QWizard to find out which page
to show when the user clicks the \uicontrol Next button.
The return value is the ID of the next page, or -1 if no page follows.
The default implementation calls QWizardPage::nextId() on the
currentPage().
By reimplementing this function, you can specify a dynamic page
order.
\sa QWizardPage::nextId(), currentPage()
*/
int CWizard::nextId() const
{
const CWizardPage* p = currentPage();
if (!p)
return -1;
return p->nextId();
}
void CWizard::onChangeTitle()
{
ASSERT(currentPage() != nullptr);
labelTitle.SetWindowText(currentPage()->title());
}
void CWizard::onChangeDesc()
{
ASSERT(currentPage() != nullptr);
labelDesc.SetWindowText(currentPage()->desc());
}