667 lines
17 KiB
C++
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());
|
|
}
|
|
|