抛弃GdCpp*.dll/pdb历史重新建库。libhv和Sqlite的dll保留

This commit is contained in:
Zhang Jianjun
2026-02-02 16:09:02 +08:00
parent f148ca49e3
commit 4a2a284ac0
292 changed files with 350450 additions and 0 deletions

View File

@@ -0,0 +1,38 @@
#pragma once
// 只读的Edit代替Static显示文本方便设置背景色
class CBCGPEditReadOnly
: public CBCGPEdit
{
public:
COLORREF defBkColor; // Edit的默认背景色
// 根据告警基本设置背景色.
// 0:正常1:警告2:错误
void setWarinLevel(uint32_t level) {
// 获取当前颜色,相同则不设置
auto color = GetColorTheme();
switch (level)
{
case 0:
if (color.m_clrBackground != defBkColor) {
color.m_clrBackground = defBkColor;
SetColorTheme(color);
}
break;
case 1:
if (color.m_clrBackground != RGB(255, 255, 0)) {
color.m_clrBackground = RGB(255, 255, 0);
SetColorTheme(color);
}
break;
case 2:
if (color.m_clrBackground != RGB(255, 0, 0)) {
color.m_clrBackground = RGB(255, 0, 0);
SetColorTheme(color);
}
default:
break;
}
}
};

View File

@@ -0,0 +1,112 @@
// CDlgDownCnt.cpp: 实现文件
//
#include "pch.h"
#include "CDlgDownCnt.h"
#define IDC_STATIC_DOWNCNT 1001
#define IDC_STATIC_INFO 1002
// CDlgDownCnt 对话框
IMPLEMENT_DYNAMIC(CDlgDownCnt, CBCGPDialog)
CDlgDownCnt::CDlgDownCnt(const std::wstring& str, int downcnt)
: CBCGPDialog(nullptr, nullptr)
, infoStr(str)
, downCnt(downcnt)
{
EnableVisualManagerStyle();
}
CDlgDownCnt::~CDlgDownCnt()
{
}
void CDlgDownCnt::DoDataExchange(CDataExchange* pDX)
{
CBCGPDialog::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CDlgDownCnt, CBCGPDialog)
ON_WM_TIMER()
END_MESSAGE_MAP()
// CDlgDownCnt 消息处理程序
void CDlgDownCnt::OnTimer(UINT_PTR nIDEvent)
{
downCnt--;
if (downCnt <= 0) {
KillTimer(1);
EndDialog(0);
}
SetDlgItemInt(IDC_STATIC_DOWNCNT, downCnt);
CBCGPDialog::OnTimer(nIDEvent);
}
BOOL CDlgDownCnt::OnInitDialog()
{
CBCGPDialog::OnInitDialog();
// 获取窗口大小
CRect rc;
GetWindowRect(rc);
// 窗口四周保留32像素边界然后labelCount、labelInfo纵向排列各占一半高度。
int margin = globalUtils.ScaleByDPI(32) ;
int halfHeight = (rc.Height() - 2 * margin) / 2;
// 设置labelCount的位置
CRect rcCount = CRect(margin, margin, rc.Width() - margin, margin + halfHeight);
// 设置labelInfo的位置
CRect rcInfo = CRect(margin, margin + halfHeight, rc.Width() - margin, rc.Height() - margin);
if (!labelCount.Create(_T(""), WS_CHILD | WS_VISIBLE | SS_CENTER, rcCount, this, IDC_STATIC_DOWNCNT))
{
return FALSE;
}
if (!labelInfo.Create(_T(""), WS_CHILD | WS_VISIBLE | SS_CENTER, rcInfo, this, IDC_STATIC_INFO))
{
return FALSE;
}
m_InfoFont.CreatePointFont(180, L"宋体");
m_CountFont.CreatePointFont(240, L"宋体");
labelInfo.SetFont(&m_InfoFont);
labelCount.SetFont(&m_CountFont);
labelInfo.SetWindowText(infoStr.c_str());
SetDlgItemInt(IDC_STATIC_DOWNCNT, downCnt);
SetTimer(1, 1000, NULL);
return TRUE; // return TRUE unless you set the focus to a control
// 异常: OCX 属性页应返回 FALSE
}
BOOL CDlgDownCnt::CreateDialogTemplate()
{
CRect rect(0, 0, 400, 200);
CString strCaption = _T("倒计时");
DLGTEMPLATE* pTemplate = dlgTemplate.CreateTemplate(WS_POPUP | WS_CAPTION | WS_SYSMENU | DS_SETFONT, rect, strCaption);
if (!InitModalIndirect(pTemplate, NULL))
{
return FALSE;
}
return TRUE;
}
void runDownCntDlg(const std::wstring& str, int downcnt)
{
CDlgDownCnt dlg(str, downcnt);
dlg.CreateDialogTemplate();
dlg.DoModal();
}

View File

@@ -0,0 +1,47 @@
#pragma once
#ifndef _CDLG_DOWNCNT_H_
#define _CDLG_DOWNCNT_H_
#include "CDlgTemplate.h"
// CDlgDownCnt 对话框
class CDlgDownCnt : public CBCGPDialog
{
DECLARE_DYNAMIC(CDlgDownCnt)
public:
CDlgDownCnt(const std::wstring & str, int downcnt=5); // 标准构造函数
virtual ~CDlgDownCnt();
// 对话框数据
#ifdef AFX_DESIGN_TIME
// enum { IDD = IDD_DIALOG_DOWNCNT };
#endif
protected:
std::wstring infoStr;
int downCnt = 5;
// 提示信息字体
CFont m_InfoFont;
// 倒计时字体
CFont m_CountFont;
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
DECLARE_MESSAGE_MAP()
public:
afx_msg void OnTimer(UINT_PTR nIDEvent);
virtual BOOL OnInitDialog();
CBCGPStatic labelCount;
CBCGPStatic labelInfo;
CDlgTemplate dlgTemplate;
BOOL CreateDialogTemplate();
};
void runDownCntDlg(const std::wstring& str, int downcnt);
#endif // _CDLG_DOWNCNT_H_

View File

@@ -0,0 +1,59 @@
#pragma once
#ifndef _CDLG_TEMPLATE_H
#define _CDLG_TEMPLATE_H
#define DLG_TEMPLATE_BUFFER_MAX_LENGTH 1024
class CDlgTemplate
{
public:
CDlgTemplate(int nBufferLength = DLG_TEMPLATE_BUFFER_MAX_LENGTH) {
m_pBuffer = new WORD[nBufferLength];
}
~CDlgTemplate() {
if (m_pBuffer)
{
delete[] m_pBuffer;
m_pBuffer = nullptr;
}
}
private:
WORD* m_pBuffer;
public:
DLGTEMPLATE* CreateTemplate(DWORD dwStyle, CRect& rect, CString strCaption, DWORD dwStyleEx = 0)
{
WORD* pTemp = m_pBuffer;
DLGTEMPLATE* pDlgTemp = (DLGTEMPLATE*)pTemp;
// 对话框模版
pDlgTemp->style = dwStyle;
pDlgTemp->dwExtendedStyle = dwStyleEx;
pDlgTemp->cdit = 0;
pDlgTemp->cx = rect.Width();
pDlgTemp->cy = rect.Height();
pDlgTemp->x = (short)rect.left;
pDlgTemp->y = (short)rect.top;
pTemp = (WORD*)(pDlgTemp + 1);
// 菜单
*pTemp++ = 0;
// class
*pTemp++ = 0;
// caption
CStringW wstrCaption = strCaption;
wcscpy_s((WCHAR*)pTemp, strCaption.GetLength()+1, wstrCaption);
pTemp += strCaption.GetLength() + 1;
// 设置字体
*pTemp++ = 8; // 字体大小
wcscpy_s((WCHAR*)pTemp, wcslen(L"MS Shell Dlg") + 1, L"MS Shell Dlg"); // 字体名称
pTemp += wcslen(L"MS Shell Dlg") + 1;
return pDlgTemp;
}
};
#endif // _CDLG_TEMPLATE_H

View File

@@ -0,0 +1,104 @@
#pragma once
#ifndef _EDIT_WITHBTN_H_
#define _EDIT_WITHBTN_H_
#include <BCGPEdit.h>
// 带按钮的编辑框,增加设置按钮功能的接口和按钮文本
// 需要调用EnableBrowseButton()使能然后对_OnBrowseFunc赋值
class CEditWithBtn :
public CBCGPEdit
{
public:
CEditWithBtn(LPCTSTR szLabel=_T("...")) {
EnableBrowseButton(TRUE, szLabel);
}
// 执行点击按钮后的回调函数
void OnBrowse() override
{
if (_OnBrowseFunc) _OnBrowseFunc();
}
// 执行更新后的回调函数
void OnAfterUpdate() override
{
if (_OnAfterUpdateFunc) _OnAfterUpdateFunc();
}
// 设置点击按钮后的回调函数
inline void setOnBrowseFunc(std::function<void(void)> func) {
_OnBrowseFunc = func;
}
// 设置编辑框内容更新后的回调函数
inline void setOnAfterUpdateFunc(std::function<void(void)> func) {
_OnAfterUpdateFunc = func;
}
protected:
// 点击按钮后的回调函数
std::function<void(void)> _OnBrowseFunc;
// 更新后的回调函数
std::function<void(void)> _OnAfterUpdateFunc;
};
// 带打开文件功能的编辑框,增加更新后的回调
// 需要调用EnableFileBrowseButton()设置参数然后对_OnAfterUpdateFunc赋值
class CEditWithOpenFile :
public CBCGPEdit
{
public:
// 执行更新后的回调函数
void OnAfterUpdate() override
{
if (_OnAfterUpdateFunc) _OnAfterUpdateFunc();
}
// 设置编辑框内容更新后的回调函数
inline void setOnAfterUpdateFunc(std::function<void(void)> func) {
_OnAfterUpdateFunc = func;
}
protected:
// 更新后的回调函数
std::function<void(void)> _OnAfterUpdateFunc;
};
// 带计算周长的编辑框,增加更新后的回调
class CEditWithPiCalc :
public CBCGPEdit
{
public:
// 构造函数,设置小数位数。-1表示不限制小数位数
CEditWithPiCalc(int decimal=8) {
CList<UINT, UINT>lstCommands;
lstCommands.AddTail(CBCGPCalculator::idCommandAdvPi);
CString str;
if (decimal < 0) {
CBCGPEdit::EnableCalculatorButton(nullptr, &lstCommands, nullptr);
}
else {
str.Format(_T("%%.%df"), decimal);
CBCGPEdit::EnableCalculatorButton(nullptr, &lstCommands, str);
}
}
// 设置编辑框内容更新后的回调函数
inline void setOnAfterUpdateFunc(std::function<void(void)> func) {
_OnAfterUpdateFunc = func;
}
// 执行更新后的回调函数
void OnAfterUpdate() override
{
if (_OnAfterUpdateFunc) _OnAfterUpdateFunc();
}
protected:
// 更新后的回调函数
std::function<void(void)> _OnAfterUpdateFunc;
};
#endif // !_EDIT_WITHBTN_H_

View File

@@ -0,0 +1,6 @@
#include "pch.h"
#include "CKsComboBox.h"
// 特例化
template class _CKsComboBox<CComboBox>;
template class _CKsComboBox<CBCGPComboBox>;

View File

@@ -0,0 +1,229 @@
#pragma once
//#include "KsCPP_Exports.h"
#include <afxwin.h>
#include <cstdint>
#include <string>
#include "StringHelper.h"
template <typename T = CComboBox>
class _CKsComboBox : public T
{
public:
using CComboBox::AddString;
int AddString(const std::wstring& str)
{
return CComboBox::AddString(str.c_str());
}
int AddString(const std::string& str)
{
return CComboBox::AddString(utf8_16(str).c_str());
}
void AddStrings(const wchar_t *const* str, size_t num) {
for (int i = 0; i < num; i++) {
AddString(str[i]);
}
}
void AddStrings(const char *const* str, size_t num) {
for (int i = 0; i < num; i++) {
AddString(utf8_16(str[i]).c_str());
}
}
template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
void AddStrings(const wchar_t* const * str, const T *val, size_t num) {
for (int i = 0; i < num; i++) {
AddString(str[i]);
SetItemData(i, val[i]);
}
}
template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
void AddStrings(const char* const* str, const T* val, size_t num) {
for (int i = 0; i < num; i++) {
AddString(utf8_16(str[i]).c_str());
SetItemData(i, val[i]);
}
}
template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
void AddStrings(const T *val, size_t num, int w = 0)
{
CString tmp;
if (w == 0) {
for (int i = 0; i < num; i++) {
tmp.Format(_T("%d"), val[i]);
AddString(tmp);
}
}
else {
CString formatStr;
formatStr.Format(_T("%%dd"), w);
for (int i = 0; i < num; i++) {
tmp.Format(formatStr, val[i]);
AddString(tmp);
}
}
}
template<typename STRT, typename T> //T为整形或枚举
int AddWithValue(STRT str, T val)
{
auto index = AddString(str);
if (index >= 0) {
SetItemData(index, (DWORD_PTR)val);
}
return index;
}
template<typename STRT, typename T>
int AddWithPtr(STRT str, T* val)
{
auto index = AddString(str);
if (index >= 0) {
SetItemDataPtr(index, val);
}
return index;
}
uint16_t ToUShort(uint16_t def=0, bool *ok=nullptr) {
CString tmp;
int index = GetCurSel();
if (index < 0) {
if (ok) *ok = false;
return def;
}
else {
GetLBText(index, tmp);
if (ok) *ok = true; //注意没有判断是否是数值串
return uint16_t(_tcstol(tmp, NULL, 10));
}
};
uint8_t ToU8(uint8_t def = 0, bool* ok = nullptr) {
CString tmp;
int index = GetCurSel();
if (index < 0) {
if (ok) *ok = false;
return def;
}
else {
GetLBText(index, tmp);
if (ok) *ok = true; //注意没有判断是否是数值串
return uint8_t(_tcstol(tmp, NULL, 10));
}
};
// 获取当前选项的字符串
bool getCurrentText(CString& strSelectedText) {
int nSel = GetCurSel(); // 假设m_ComboBox是你的CComboBox对象
if (nSel != CB_ERR) // 检查是否有选中项
{
GetLBText(nSel, strSelectedText); // 获取选中的文本
return true;
}
return false;
}
// Combo选择指定字符串的选项
void setCurrentText (const CString& str, bool must_exist=false) {
int index = FindStringExact(0, str);
if (index >= 0) { // 注意选项必须存在!
SetCurSel(index);
}
else {
SetCurSel(-1);
}
if (!must_exist) {
SetWindowText(str);
}
};
template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
void setCurrentNumberText(const T &val, bool must_exist = false) {
CString str;
str.Format(_T("%d"), val);
int index = FindStringExact(0, str);
if (index >= 0) { // 注意选项必须存在!
SetCurSel(index);
}
else {
SetCurSel(-1);
}
if (!must_exist) {
SetWindowText(str);
}
};
template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
int FindItemData(T val) {
for (int index = 0; index < GetCount(); index++) {
auto v = T(CComboBox::GetItemData(index));
if (val == v) {
return index;
}
}
return -1;
}
int FindItemDataPtr(void* ptr) {
for (int index = 0; index < GetCount(); index++) {
if (ptr == CComboBox::GetItemDataPtr(index)) {
return index;
}
}
return -1;
}
template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
int SetCurSelByData(T val, bool clearifnotfound=true) {
auto index = FindItemData(val);
if (index >= 0) {
SetCurSel(index);
return index;
} else {
if (clearifnotfound) SetCurSel(-1);
return CB_ERR;
}
}
int SetCurSelByData(void* ptr, bool clearifnotfound=true) {
auto index = FindItemDataPtr(ptr);
if (index >= 0) {
SetCurSel(index);
return index;
} else {
if (clearifnotfound) SetCurSel(-1);
return CB_ERR;
}
}
uint32_t GetCurData()
{
auto index = GetCurSel();
ASSERT(index >= 0);
return uint32_t(GetItemData(index));
}
void * GetCurDataPtr()
{
auto index = GetCurSel();
ASSERT(index >= 0);
return GetItemDataPtr(index);
}
};
// 不同基类特例化
template class _CKsComboBox<CComboBox>;
template class _CKsComboBox<CBCGPComboBox>;
typedef _CKsComboBox<CComboBox> CKsComboBox;
typedef _CKsComboBox<CBCGPComboBox> CKsBCGPComboBox;

View File

@@ -0,0 +1,110 @@
#pragma once
//#include "FiberCamera_Exports.h"
#include <afxwin.h>
#ifndef WM_QUIT_THREAD
#define WM_QUIT_THREAD (WM_USER +0)
#endif
/// \brief 工作线程类。
/// 多线程有多种实现方式,本类适合:
/// 1. 指定一个函数作为线程的主循环。
/// 2. 需要用到windows的消息系统。
///
/// 简单用法:
/// 1. 工作类相关的某个类里定义一个成员:
/// CMFCThread * m_Thread;
///
/// 2. 构造实例,但未启动线程:
/// m_Thread = new CMFCThread(<静态函数指针>);
/// 或:
/// m_Thread = new CMFCThread(std::bind(<类成员函数指针>, this));
///
///
/// 3. 启动线程:
/// if(m_Thread){
/// m_Thread->Start();
/// }
///
/// 4. 终止线程:
/// if(m_Thread){
/// m_Thread->Quit();
/// }
class CMFCThread :
public CWinThread // 继承CWinThread的目的是需要用到windows的消息系统
{
public:
// DECLARE_DYNAMIC(CMFCThread) // 需要MFC的动态类型信息时打开。本类只创建实例指针不需要动态类型信息。
using run_func_t = int(void);
/// @brief 用静态函数指针构造工作线程实例
/// @param func
CMFCThread(run_func_t* func)
: _Run(func)
{
}
/// @brief 用bind()封装类成员函数、lambda函数等
/// @param func
CMFCThread(std::function<int()> func)
: _Run(func)
{
}
protected:
/// 主循环函数可以指定静态函数或、类成员函数、lambda函数
std::function<int()> _Run;
/// 直接返回TRUE缺省不会进run()而是进入CWinThread的消息循环
virtual BOOL InitInstance()
{
return TRUE;
//return CWinThread::InitInstance();
}
public:
/// @brief 线程主函数,内部改成了函数调用,方便简单应用场景不需要再重载。
virtual int Run() override
{
ASSERT(_Run != nullptr); // 使用前必须设置函数指针
return _Run();
}
/// @brief 设置函数指针和优先级
/// @param prio 线程优先级默认THREAD_PRIORITY_NORMAL。
bool Start(int prio = THREAD_PRIORITY_NORMAL) {
if (CreateThread()) {
if (prio != THREAD_PRIORITY_NORMAL) {
SetThreadPriority(prio);
}
return true;
}
else {
return false;
}
}
/// @brief 外部调用结束线程
void Quit(int ret=0) {
if(m_nThreadID) ::PostThreadMessage(m_nThreadID, WM_QUIT_THREAD, WPARAM(ret), 0);
}
/// @brief 等待线程结束。与Quit()分开方便先所有子线程调用一遍Quit(),再一个个等。
/// @param ms 等待时间,单位毫秒
bool Wait(int ms) {
DWORD uResult = WaitForSingleObject(this->m_hThread, ms);
if(WAIT_OBJECT_0 == uResult) {
//alog->info(u8"线程正常退出");
return true;
}
else {
alog->warn(u8"线程退出超时 {}", uResult);
return false;
}
}
};

View File

@@ -0,0 +1,23 @@
#include "pch.h"
#include "MFC\CPushButton.h"
BEGIN_MESSAGE_MAP(CPushButton, CButton)
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONUP()
END_MESSAGE_MAP()
void CPushButton::OnLButtonDown(UINT nFlags, CPoint point)
{
if (userOnLButtonDown) userOnLButtonDown(nFlags, point);
__super::OnLButtonDown(nFlags, point);
}
void CPushButton::OnLButtonUp(UINT nFlags, CPoint point)
{
if (userOnLButtonUp) userOnLButtonUp(nFlags, point);
__super::OnLButtonUp(nFlags, point);
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include <functional>
class CPushButton : public CBCGPButton
{
public:
CPushButton()
{
}
virtual ~CPushButton() {
}
std::function<void(UINT, CPoint)> userOnLButtonDown;
std::function<void(UINT, CPoint)> userOnLButtonUp;
protected:
afx_msg void OnLButtonDown(UINT nFlags, CPoint point);//按下和弹起事件
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
DECLARE_MESSAGE_MAP()
};

View File

@@ -0,0 +1,666 @@
#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());
}

188
staticOnly/KsMFC/CWizard.h Normal file
View File

@@ -0,0 +1,188 @@
#pragma once
//#include "KsCPP_Exports.h"
#include "..\inc\properties.h"
#include <map>
#include <list>
#include <vector>
class CWizardPage;
class CWizard : public CBCGPDialog
{
READONLY_PROPERTY(int, start, -1, startId); //setStartId
READONLY_PROPERTY(int, current, -1, currentId);
public:
CWizard(UINT nIDTemplate, CWnd* pParentWnd = NULL);
typedef std::map<int, CWizardPage*> PageMap;
enum Direction {
Backward,
Forward
};
enum WizardButton {
BackButton,
NextButton,
CommitButton,
FinishButton,
CancelButton,
HelpButton,
//CustomButton1,
//CustomButton2,
//CustomButton3,
//Stretch,
NStandardButtons ,
NButtons = NStandardButtons,
NoButton = -1,
};
enum WizardOption {
NoOptions,
IndependentPages = 0x00000001,
//IgnoreSubTitles = 0x00000002,
//ExtendedWatermarkPixmap = 0x00000004,
NoDefaultButton = 0x00000008,
NoBackButtonOnStartPage = 0x00000010,
NoBackButtonOnLastPage = 0x00000020,
DisabledBackButtonOnLastPage = 0x00000040,
HaveNextButtonOnLastPage = 0x00000080,
HaveFinishButtonOnEarlyPages = 0x00000100,
NoCancelButton = 0x00000200,
//CancelButtonOnLeft = 0x00000400,
HaveHelpButton = 0x00000800,
HelpButtonOnRight = 0x00001000,
//HaveCustomButton1 = 0x00002000,
//HaveCustomButton2 = 0x00004000,
//HaveCustomButton3 = 0x00008000,
NoCancelButtonOnLastPage = 0x00010000
};
WizardOption opts= CWizard::NoBackButtonOnStartPage;
int addPage(CWizardPage* page);
void setPage(int id, CWizardPage* page);
void removePage(int id);
CWizardPage* page(int id) const {
auto it = pageMap.find(id);
if (it == pageMap.end()) return nullptr;
else return it->second;
}
bool hasVisitedPage(int id) const {
auto it = std::find(history.begin(), history.end(), id);
return it != history.end();
}
std::vector<int> visitedIds() const {
return history;
}
std::list<int> pageIds() const;
CWizardPage* currentPage() const {
return page(current);
}
void setStartId(int theid);
virtual bool validateCurrentPage();
virtual int nextId() const;
void setButtonText(WizardButton which, const CString& text);
//CString buttonText(WizardButton which) const;
void _q_updateButtonStates();
void updateButtonTexts();
void updateButtonLayout();
void back();
void next();
void restart();
virtual void initializePage(int id);
virtual void cleanupPage(int id);
/*!
This virtual function is called by QWizard::validateCurrentPage()
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 returns \c true.
When possible, it is usually better style to disable the \uicontrol
Next or \uicontrol Finish button (by specifying \l{mandatory fields} or
reimplementing isComplete()) than to reimplement validatePage().
\sa QWizard::validateCurrentPage(), isComplete()
*/
virtual bool validatePage()
{
return true;
}
protected:
PageMap pageMap;
std::vector<int> history;
bool startSetByUser = false;
bool canContinue = false;
bool canFinish = false;
std::map<int, CString> buttonCustomTexts;
CFont fontTitle;
CFont fontDescription;
CBCGPStatic labelTitle;
CBCGPStatic labelDesc;
// 窗口、控件大小/位置,本类默认一套数值
// 子类构造时可以修改子类OnInitDialog或OnSize里应用不支持动态修改
int wWidht = 1280;
int wHeight = 960;
// 字体大小设置
int fontSize_title = 240;
int fontSize_desc = 180;
// 控件高度设置
int titleHeight = 32;
int descHeight = 64;
int titleLeft = 32;
int descLeft = 48;
int titleTop = 16; // title顶部到窗口的距离
int descTop = 8; // 描述顶部到title底部的距离
int descBottom = 16; //描述底部空白
int btnWidth = 100;
int btnHeight = 48;
int btnTop = 16; // 按钮顶部空白
int btnBottom = 16; // 按钮底部空白
int btnRight = 64; // 按钮右侧空白
int btnSpace = 16; // 按钮间隔
// 标题和描述占用的高度在OnSize()用上面的参数计算
int headH;
// 按钮占用的高度在OnSize()用上面的参数计算
int footH;
CBCGPButton btns[CWizard::NButtons];
void init() {
}
void reset();
void cleanupPagesNotInHistory();
void switchToPage(int newId, Direction direction);
void updateCurrentPage();
void onChangeTitle();
void onChangeDesc();
friend class CWizardPage;
virtual BOOL OnInitDialog();
afx_msg void OnSize(UINT nType, int cx, int cy);
};

View File

@@ -0,0 +1,240 @@
#include "pch.h"
#include "MFC/CWizardPage.h"
#include "MFC/CWizard.h"
CWizardPage::CWizardPage(UINT nIDTemplate, CWizard* pParentWnd)
: CBCGPDialog(nIDTemplate, pParentWnd)
, _wizard(pParentWnd)
{
EnableVisualManagerStyle();
}
void CWizardPage::setTitle(const wchar_t* t)
{
_title = t;
if (_wizard && _wizard->currentPage() == this)
_wizard->onChangeTitle();
}
void CWizardPage::setTitle(const CString& t)
{
_title = t;
if (_wizard && _wizard->currentPage() == this)
_wizard->onChangeTitle();
}
void CWizardPage::setDesc(const wchar_t* t)
{
_desc = t;
if (_wizard && _wizard->currentPage() == this)
_wizard->onChangeDesc();
}
void CWizardPage::setDesc(const CString& t)
{
_desc = t;
if (_wizard && _wizard->currentPage() == this)
_wizard->onChangeDesc();
}
/*!
This virtual function is called by QWizard::cleanupPage() when
the user leaves the page by clicking \uicontrol Back (unless the \l QWizard::IndependentPages
option is set).
The default implementation resets the page's fields to their
original values (the values they had before initializePage() was
called).
\sa QWizard::cleanupPage(), initializePage(), QWizard::IndependentPages
*/
void CWizardPage::cleanupPage()
{
//if (d->wizard) {
// const QVector<QWizardField>& fields = d->wizard->d_func()->fields;
// for (const auto& field : fields) {
// if (field.page == this)
// field.object->setProperty(field.property, field.initialValue);
// }
//}
}
/*!
This virtual function is called by QWizard to determine whether
the \uicontrol Next or \uicontrol Finish button should be enabled or
disabled.
The default implementation returns \c true if all \l{mandatory
fields} are filled; otherwise, it returns \c false.
If you reimplement this function, make sure to emit completeChanged(),
from the rest of your implementation, whenever the value of isComplete()
changes. This ensures that QWizard updates the enabled or disabled state of
its buttons. An example of the reimplementation is
available \l{http://doc.qt.io/archives/qq/qq22-qwizard.html#validatebeforeitstoolate}
{here}.
\sa completeChanged(), isFinalPage()
*/
bool CWizardPage::isComplete() const
{
//if (!_wizard)
// return true;
// const QVector<QWizardField>& wizardFields = d->wizard->d_func()->fields;
// for (int i = wizardFields.count() - 1; i >= 0; --i) {
// const QWizardField& field = wizardFields.at(i);
// if (field.page == this && field.mandatory) {
// QVariant value = field.object->property(field.property);
// if (value == field.initialValue)
// return false;
//
//#if QT_CONFIG(lineedit)
// if (QLineEdit* lineEdit = qobject_cast<QLineEdit*>(field.object)) {
// if (!lineEdit->hasAcceptableInput())
// return false;
// }
//#endif
//#if QT_CONFIG(spinbox)
// if (QAbstractSpinBox* spinBox = qobject_cast<QAbstractSpinBox*>(field.object)) {
// if (!spinBox->hasAcceptableInput())
// return false;
// }
//#endif
// }
// }
return true;
}
/*!
Explicitly sets this page to be final if \a finalPage is true.
After calling setFinalPage(true), isFinalPage() returns \c true and the \uicontrol
Finish button is visible (and enabled if isComplete() returns
true).
After calling setFinalPage(false), isFinalPage() returns \c true if
nextId() returns -1; otherwise, it returns \c false.
\sa isComplete(), QWizard::HaveFinishButtonOnEarlyPages
*/
void CWizardPage::setFinalPage(bool finalPage)
{
explicitlyFinal = finalPage;
auto w = this->wizard();
if (w && w->currentPage() == this)
w->updateCurrentPage();
}
/*!
This function is called by QWizard to determine whether the \uicontrol
Finish button should be shown for this page or not.
By default, it returns \c true if there is no next page
(i.e., nextId() returns -1); otherwise, it returns \c false.
By explicitly calling setFinalPage(true), you can let the user perform an
"early finish".
\sa isComplete(), QWizard::HaveFinishButtonOnEarlyPages
*/
bool CWizardPage::isFinalPage() const
{
if (explicitlyFinal)
return true;
auto w = static_cast<const CWizard *> (_wizard);
if (w && w->currentPage() == this) {
// try to use the QWizard implementation if possible
return w->nextId() == -1;
}
else {
return nextId() == -1;
}
}
/*!
Sets this page to be a commit page if \a commitPage is true; otherwise,
sets it to be a normal page.
A commit page is a page that represents an action which cannot be undone
by clicking \uicontrol Back or \uicontrol Cancel.
A \uicontrol Commit button replaces the \uicontrol Next button on a commit page. Clicking this
button simply calls QWizard::next() just like clicking \uicontrol Next does.
A page entered directly from a commit page has its \uicontrol Back button disabled.
\sa isCommitPage()
*/
void CWizardPage::setCommitPage(bool commitPage)
{
commit = commitPage;
auto w = this->wizard();
if (w && w->currentPage() == this)
w->updateCurrentPage();
}
/*!
Returns \c true if this page is a commit page; otherwise returns \c false.
\sa setCommitPage()
*/
bool CWizardPage::isCommitPage() const
{
return commit;
}
/*!
Sets the text on button \a which to be \a text on this page.
By default, the text on buttons depends on the QWizard::wizardStyle,
but may be redefined for the wizard as a whole using QWizard::setButtonText().
\sa buttonText(), QWizard::setButtonText(), QWizard::buttonText()
*/
void CWizardPage::setButtonText(int which, const CString& text)
{
ASSERT(which >= 0);
ASSERT(which < CWizard::NButtons);
buttonCustomTexts.insert({which, text });
if (wizard() && wizard()->currentPage() == this)
wizard()->btns[which].SetWindowText(text);
}
/*!
This virtual function is called by QWizard::nextId() 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.
By default, this function returns the lowest ID greater than the ID
of the current page, or -1 if there is no such ID.
By reimplementing this function, you can specify a dynamic page
order. For example:
\snippet dialogs/licensewizard/licensewizard.cpp 18
\sa QWizard::nextId()
*/
int CWizardPage::nextId() const
{
if (!_wizard)
return -1;
bool foundCurrentPage = false;
const auto& pageMap = _wizard->pageMap;
CWizard::PageMap::const_iterator i = pageMap.cbegin();
CWizard::PageMap::const_iterator end = pageMap.cend();
for (; i != end; ++i) {
if (i->second == this) {
foundCurrentPage = true;
}
else if (foundCurrentPage) {
return i->first;
}
}
return -1;
}

View File

@@ -0,0 +1,95 @@
#pragma once
//#include "KsCPP_Exports.h"
#include <map>
#include "properties.h"
class CWizard;
class CWizardPage : public CBCGPDialog
{
READONLY_PROPERTY(CString, _title, L"", title) // setTitle另定
READONLY_PROPERTY(CString, _desc, L"", desc) // setDesc另定
READONLY_POINTER_PROPERTY(CWizard, _wizard, nullptr, wizard)
void setTitle(const wchar_t* t);
void setTitle(const CString& t);
void setDesc(const wchar_t* t);
void setDesc(const CString& t);
public:
CWizardPage(UINT nIDTemplate, CWizard* pParentWnd = NULL);
/// @brief 构造函数已经指定IDD和父窗口时可以用这个简化的Create()
void Create() {
ASSERT(m_pParentWnd != nullptr);
ASSERT(m_lpszTemplateName !=nullptr);
Create(m_lpszTemplateName, m_pParentWnd);
}
using CBCGPDialog::Create;
enum TriState { Tri_Unknown = -1, Tri_False, Tri_True };
void setFinalPage(bool finalPage);
bool isFinalPage() const;
void setCommitPage(bool commitPage);
bool isCommitPage() const;
/*!
This virtual function is called by QWizard::initializePage() to
prepare the page 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. For example:
\snippet dialogs/classwizard/classwizard.cpp 17
The default implementation does nothing.
\sa QWizard::initializePage(), cleanupPage(), QWizard::IndependentPages
*/
virtual void initializePage(){}
virtual void cleanupPage();
/*!
This virtual function is called by QWizard::validateCurrentPage()
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 returns \c true.
When possible, it is usually better style to disable the \uicontrol
Next or \uicontrol Finish button (by specifying \l{mandatory fields} or
reimplementing isComplete()) than to reimplement validatePage().
\sa QWizard::validateCurrentPage(), isComplete()
*/
virtual bool validatePage() {return true;}
virtual bool isComplete() const;
virtual int nextId() const;
void setButtonText(int which, const CString& text);
// 很多Radio、CheckBox的点击事件只需要更新数据不需要其它所以这里提供一个简化的UpdateData
afx_msg void CallUpdateData() {
UpdateData();
}
protected:
mutable TriState completeState = Tri_Unknown;
bool explicitlyFinal = false;
bool commit = false;
bool initialized = false;
std::map<int, CString> buttonCustomTexts;
friend class CWizard;
};

View File

@@ -0,0 +1,156 @@
#include "pch.h"
#include <stdio.h>
#include "ComPortTools.h"
#include <setupapi.h>
#include <devguid.h>
#pragma comment(lib,"SetupAPI.lib")
void COM_InitPortList(std::vector<sComPort>& ports, const wchar_t* name = L"")
{
ports.clear();
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
#ifdef UNICODE
wchar_t buf[256];
#else
char buf[256];
#endif
int i;
int defaultComId = -1;
// Create a HDEVINFO of all Ports device class
hDevInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_PORTS,
0, // Enumerator
0,
DIGCF_PRESENT | DIGCF_PROFILE);
// DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
// Insert error handling here.
return;
}
// Enumerate through all devices in Set.
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
CString str, subStr;
int index1, index2;
sComPort port;
for (i = 0; SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInfoData); i++)
{
SetupDiGetDeviceRegistryProperty(hDevInfo, &DeviceInfoData, SPDRP_FRIENDLYNAME, NULL, (PBYTE)buf, sizeof(buf), NULL);
str = buf;
if (name && wcslen(name)) {
if (str.Find(name) < 0) continue;
}
index1 = str.Find(_T("(COM"), 0);
index2 = str.Find(_T(")"), index1);
subStr = str.Mid(index1 + 1, index2 - index1 - 1);
#ifdef UNICODE
port.port = _wtoi(subStr.GetBuffer(subStr.GetLength()) + 3);
#else
port.port = atoi(subStr.GetBuffer(subStr.GetLength()) + 3);
#endif
port.portName = str;
ports.push_back(port);
}
SetupDiDestroyDeviceInfoList(hDevInfo);
}
#ifdef _AFXDLL
void COM_PortListToCombo(const std::vector<sComPort>& ports, CComboBox* Box, int defaultCom)
{
int i;
int defaultComId = -1;
int index;
for (i = 0; i<ports.size(); i++)
{
index = Box->AddString(ports[i].portName.c_str());
Box->SetItemData(index, ports[i].port);
if (defaultCom == ports[i].port) defaultComId = index;
}
if (ports.size() > 0) {
if (defaultComId >= 0)
{
Box->SetCurSel(defaultComId);
}
else
{
Box->SetCurSel(0);
}
}
return;
}
void COM_RefreshPortList(const wchar_t* name, CComboBox* Box)
{
int lastport = int(Box->GetItemData(Box->GetCurSel()));
Box->ResetContent();
std::vector<sComPort> ports;
COM_InitPortList(ports, name);
if (ports.size() > 0) {
COM_PortListToCombo(ports, Box, 0);
int i;
for (i = 0; i < Box->GetCount(); i++) {
if (lastport == Box->GetItemData(i)) {
Box->SetCurSel(i);
break;
}
}
if (i == Box->GetCount()) {
Box->SetCurSel(0);
}
}
}
int COM_GetSelectedPort(CComboBox* Box)
{
int index = Box->GetCurSel();
if (index >= 0) {
return int(Box->GetItemData(index));
}
else {
return 0;
}
}
//CComboBox必须sort属性=false
void COM_InitBaudList(const int* List, int Num, int default_val, CComboBox* Box)
{
int i, index;
int default_id = 0;
TCHAR buf[10];
for (i = 0; i < Num; i++) {
_itot_s(List[i], buf, 10, 10);
index = Box->AddString(buf);
Box->SetItemData(index, List[i]);
if (default_val == List[i])
default_id = i;
}
Box->SetCurSel(default_id);
}
void COM_InitBaudList(const std::vector<uint32_t>& List, int default_val, CComboBox* Box)
{
int i, index;
int default_id = 0;
TCHAR buf[10];
for (i = 0; i < List.size(); i++) {
_itot_s(List[i], buf, 10, 10);
index = Box->AddString(buf);
Box->SetItemData(index, List[i]);
if (default_val == List[i])
default_id = i;
}
Box->SetCurSel(default_id);
}
#endif

View File

@@ -0,0 +1,40 @@
#pragma once
#include <cstdint>
#include <vector>
#include <string>
// ----- 串口常用函数 -----------
struct sComPort{
int port=0; // 串口编号
std::wstring portName; // 串口名称
// 默认构造函数
sComPort() = default;
// 复制构造函数
sComPort(const sComPort& other)
: port(other.port), portName(other.portName)
{
}
};
/// 查找串口列表,返回串口号列表
/// @param name 串口名字必须包含的字符串例如CH340。如果为nullptr或""则返回所有串口
void COM_InitPortList(std::vector<sComPort> &ports, const wchar_t * name);
#ifdef _AFXDLL
// 搜索所有串口名称添加到ComboBox整形串口号绑定到每一个选项的data里。
// 如果列表不为空且defaultCom在列表中则自动选中否则选择第一项
void COM_PortListToCombo(const std::vector<sComPort>& ports, CComboBox* Box, int defaultCom);
// 刷新串口列表。
// 如果之前选中的串口还在,自动选择
// 如果之前选中的串口不在了,自动选择第一项
void COM_RefreshPortList (const wchar_t* name, CComboBox* Box);
int COM_GetSelectedPort (CComboBox* Box);
void COM_InitBaudList (const int* List, int Num, int default_val, CComboBox* Box);
void COM_InitBaudList(const std::vector<uint32_t>& List, int default_val, CComboBox* Box);
#endif

View File

@@ -0,0 +1,116 @@
//*******************************************************************************
// COPYRIGHT NOTES
// ---------------
// This is a sample for BCGControlBar Library Professional Edition
// Copyright (C) 1998-2024 BCGSoft Ltd.
// All rights reserved.
//
// This source code can be used, distributed or modified
// only under terms and conditions
// of the accompanying license agreement.
//*******************************************************************************
//
// CustomCells.cpp: implementation of the CColorGridItem class.
//
//////////////////////////////////////////////////////////////////////
#include "pch.h"
//#include "BCGPGridExample.h"
#include "CustomCells.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
////////////////////////////////////////////////////////////////////////////////
// CButtonItem Class
CButtonItem::CButtonItem (LPCTSTR lpszText, UINT id) :
CBCGPGridItem (lpszText)
{
m_bAllowEdit = FALSE;
m_id = id;
}
//*****************************************************************************************
void CButtonItem::OnDrawValue (CDC* pDC, CRect rect)
{
ASSERT_VALID (pDC);
CBCGPGridCtrl* pGridCtrl = GetOwnerList ();
OnFillBackground (pDC, rect);
const int nMargin = globalUtils.ScaleByDPI(1, pGridCtrl);
rect.DeflateRect(nMargin, nMargin);
COLORREF clrText = globalData.clrBarText;
if (pGridCtrl != NULL && pGridCtrl->IsVisualManagerStyle())
{
clrText = CBCGPVisualManager::GetInstance()->OnDrawPropListPushButton(pDC, rect, NULL, TRUE, FALSE, IsEnabled(), FALSE, IsHighlighted());
}
else
{
clrText = CBCGPVisualManager::GetInstance()->CBCGPVisualManager::OnDrawPropListPushButton(pDC, rect, NULL, TRUE, FALSE, IsEnabled(), FALSE, IsHighlighted());
}
COLORREF clrTextOld = pDC->SetTextColor(clrText);
CString strText = (LPCTSTR)(_bstr_t) m_varValue;
pDC->DrawText (strText, rect, DT_CENTER | DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX);
pDC->SetTextColor (clrTextOld);
}
//*****************************************************************************************
BOOL CButtonItem::OnClickValue (UINT uiMsg, CPoint point)
{
if (uiMsg != WM_LBUTTONDOWN)
{
return CBCGPGridItem::OnClickValue (uiMsg, point);
}
CBCGPGridCtrl* pGridCtrl = GetOwnerList();
ASSERT_VALID (pGridCtrl);
pGridCtrl->SendMessage (WM_COMMAND,
MAKEWPARAM (m_id, BN_CLICKED));
return TRUE;
}
//*****************************************************************************************
BOOL CButtonItem::PushChar (UINT nChar)
{
if (nChar == VK_SPACE || nChar == VK_RETURN)
{
CBCGPGridCtrl* pGridCtrl = GetOwnerList();
ASSERT_VALID (pGridCtrl);
pGridCtrl->SendMessage (WM_COMMAND,
MAKEWPARAM (m_id, BN_CLICKED));
return TRUE;
}
return CBCGPGridItem::PushChar(nChar);
}
//*****************************************************************************************
void CButtonItem::OnMouseMove(const CPoint& /*point*/)
{
if (!IsHighlighted())
{
Highlight();
Redraw();
}
}
//*****************************************************************************************
void CButtonItem::OnMouseLeave()
{
if (IsHighlighted())
{
Highlight(FALSE);
Redraw();
}
}

View File

@@ -0,0 +1,60 @@
//*******************************************************************************
// COPYRIGHT NOTES
// ---------------
// This is a sample for BCGControlBar Library Professional Edition
// Copyright (C) 1998-2024 BCGSoft Ltd.
// All rights reserved.
//
// This source code can be used, distributed or modified
// only under terms and conditions
// of the accompanying license agreement.
//*******************************************************************************
//
// CustomCells.h: interface for the CColorGridItem class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_CUSTOMCELLS_H__2E159741_6592_4EA9_BBB1_B18CEE7D0BD5__INCLUDED_)
#define AFX_CUSTOMCELLS_H__2E159741_6592_4EA9_BBB1_B18CEE7D0BD5__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
////////////////////////////////////////////////////////////////////////////////
// CButtonItem Class
class CButtonItem : public CBCGPGridItem
{
public:
CButtonItem (LPCTSTR lpszText, UINT id);
protected:
virtual BOOL IsPushButton() const
{
return TRUE;
}
virtual BOOL OnSetCursor () const
{
return FALSE; /* Use default */
}
virtual BOOL IsTextOverflowingAllowed() const
{
return FALSE;
}
virtual BOOL IsHotTrackEnabled() const { return TRUE; }
virtual void OnMouseMove(const CPoint& point);
virtual void OnMouseLeave();
virtual void OnDrawValue (CDC* pDC, CRect rect);
virtual BOOL OnClickValue (UINT uiMsg, CPoint point);
virtual BOOL PushChar (UINT nChar);
UINT m_id;
};
/////////////////////////////////////////////////////////////////////////////
#endif // !defined(AFX_CUSTOMCELLS_H__2E159741_6592_4EA9_BBB1_B18CEE7D0BD5__INCLUDED_)