抛弃GdCpp*.dll/pdb历史重新建库。libhv和Sqlite的dll保留
This commit is contained in:
38
staticOnly/KsMFC/CBCGPEditReadOnly.h
Normal file
38
staticOnly/KsMFC/CBCGPEditReadOnly.h
Normal 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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
112
staticOnly/KsMFC/CDlgDownCnt.cpp
Normal file
112
staticOnly/KsMFC/CDlgDownCnt.cpp
Normal 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();
|
||||
}
|
||||
47
staticOnly/KsMFC/CDlgDownCnt.h
Normal file
47
staticOnly/KsMFC/CDlgDownCnt.h
Normal 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_
|
||||
59
staticOnly/KsMFC/CDlgTemplate.h
Normal file
59
staticOnly/KsMFC/CDlgTemplate.h
Normal 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
|
||||
104
staticOnly/KsMFC/CEditWithBtn.h
Normal file
104
staticOnly/KsMFC/CEditWithBtn.h
Normal 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_
|
||||
6
staticOnly/KsMFC/CKsComboBox.cpp
Normal file
6
staticOnly/KsMFC/CKsComboBox.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "pch.h"
|
||||
#include "CKsComboBox.h"
|
||||
|
||||
// 特例化
|
||||
template class _CKsComboBox<CComboBox>;
|
||||
template class _CKsComboBox<CBCGPComboBox>;
|
||||
229
staticOnly/KsMFC/CKsComboBox.h
Normal file
229
staticOnly/KsMFC/CKsComboBox.h
Normal 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;
|
||||
110
staticOnly/KsMFC/CMFCThread.h
Normal file
110
staticOnly/KsMFC/CMFCThread.h
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
23
staticOnly/KsMFC/CPushButton.cpp
Normal file
23
staticOnly/KsMFC/CPushButton.cpp
Normal 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);
|
||||
}
|
||||
23
staticOnly/KsMFC/CPushButton.h
Normal file
23
staticOnly/KsMFC/CPushButton.h
Normal 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()
|
||||
};
|
||||
|
||||
666
staticOnly/KsMFC/CWizard.cpp
Normal file
666
staticOnly/KsMFC/CWizard.cpp
Normal 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
188
staticOnly/KsMFC/CWizard.h
Normal 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);
|
||||
};
|
||||
|
||||
240
staticOnly/KsMFC/CWizardPage.cpp
Normal file
240
staticOnly/KsMFC/CWizardPage.cpp
Normal 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;
|
||||
}
|
||||
95
staticOnly/KsMFC/CWizardPage.h
Normal file
95
staticOnly/KsMFC/CWizardPage.h
Normal 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;
|
||||
|
||||
|
||||
};
|
||||
|
||||
156
staticOnly/KsMFC/ComPortTools.cpp
Normal file
156
staticOnly/KsMFC/ComPortTools.cpp
Normal 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
|
||||
40
staticOnly/KsMFC/ComPortTools.h
Normal file
40
staticOnly/KsMFC/ComPortTools.h
Normal 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
|
||||
116
staticOnly/KsMFC/customcells.cpp
Normal file
116
staticOnly/KsMFC/customcells.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
60
staticOnly/KsMFC/customcells.h
Normal file
60
staticOnly/KsMFC/customcells.h
Normal 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_)
|
||||
0
staticOnly/KsMFC/与MFC相关,不宜编译到dll.txt
Normal file
0
staticOnly/KsMFC/与MFC相关,不宜编译到dll.txt
Normal file
Reference in New Issue
Block a user