为了便于移植一些简单的测试工具,这种测试工具在Win上是简单的MFC对话框程序,
包含 Static Edit CheckBox Button,到linux上我们并不想再实现一个基于Console的程序,于是我们用GTK库实现一个兼容层,可以让MFC对话框小程序在少量修改的情况下在linux上编译并成功运行
(1)基于解析资源文件的GTK窗口底层核心
cpp
#pragma once
#include <string>
#include <vector>
#include <map>
#include <gtk/gtk.h>
using namespace std;
#define IDOK 1
#define IDCANCEL 2
#define IDABORT 3
#define IDRETRY 4
#define IDIGNORE 5
#define IDYES 6
#define IDNO 7
// 控件定义结构
struct ControlDef {
int id;
std::string type;
std::string text;
int x, y, width, height;
};
enum WidgetType
{
WIDGET_T_None,
WIDGET_T_Static,
WIDGET_T_Text,
WIDGET_T_PushBtn,
WIDGET_T_CheckBox,
};
struct WidgetAndType
{
WidgetType _ty=WIDGET_T_None;
GtkWidget * pWidget=0;
};
// 资源解析器类
class GStyleBaseWinFromRes
{
public:
GStyleBaseWinFromRes();
~GStyleBaseWinFromRes();
// 解析 注意 字符串不要有英文逗号 用"," 代替
/*
DIALOG w,h,title
EDITTEXT id,l,t,w,h
LTEXT text,id, l,t,w,h
PUSHBUTTON text,id, l,t,w,h
CHECKBOX text,id, "unuseType",unuseStyle,l,t,w,h
EDITTEXT IDC_EDIT1,53,16,352,14,ES_AUTOHSCROLL
LTEXT "选择文件",IDC_STATIC,7,19,33,8
EDITTEXT IDC_EDIT2,53,35,352,14,ES_AUTOHSCROLL
LTEXT "选择目录",IDC_STATIC,7,39,33,8
EDITTEXT IDC_EDIT3,53,53,352,14,ES_AUTOHSCROLL
LTEXT "选择库目录",IDC_STATIC,7,57,41,8
PUSHBUTTON "...",IDC_B_SelLibDir,408,53,20,14
PUSHBUTTON "...",IDC_B_SelFile,408,16,20,14
PUSHBUTTON "...",IDC_B_SelDir,408,35,20,14
PUSHBUTTON "ClientID",IDC_B_CID,7,91,50,14
EDITTEXT IDC_EDIT4,53,70,352,14,ES_AUTOHSCROLL
CHECKBOX "UsePrivateCloud,hostport=",IDC_Chk_UsePrivCloud,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,127,112,116,10
EDITTEXT IDC_E_PRIVHOSTPORT,245,110,156,14,ES_AUTOHSCROLL
PUSHBUTTON "私有云设置",IDC_BSetPrivateCloud,202,129,50,14
* */
bool ParseResourceString(const std::string &strResDesc);
// 创建GTK界面
void CreateGTKInterface();
virtual void OnControlClick(GtkWidget *widget);
virtual void OnDestroy();
// 销毁整个界面
void DestroyGTKInterface();
// 获取控件列表
const std::vector<ControlDef>& GetControls() const;
int GetWidgetID(GtkWidget *widget);
GtkWidget * GetWidgetByID(int id);
WidgetType GetWidgetType(const string& strT);
WidgetType GetWidgetTypeByID(int id);
int m_modalRet = 0; // 存储模态返回值:IDOK/IDCANCEL
GMainLoop* m_modalLoop = nullptr; // 模态嵌套事件循环
float m_Scale=1.5f;
protected:
std::vector<ControlDef> m_controls;
int m_dialogWidth = 0;
int m_dialogHeight = 0;
std::string m_dialogTitle;
map<int, WidgetAndType> m_controlMap;
GtkWidget * m_hWnd_Widget=0;
GtkWindow* m_hWnd=0;
};
// 全局函数声明
GtkWidget* CreateControlFromDefinition(const ControlDef& control);
GtkWidget* CreateEditBox(const ControlDef& control);
GtkWidget* CreateButton(const ControlDef& control);
GtkWidget* CreateCheckBox(const ControlDef& control);
GtkWidget* CreateLabel(const ControlDef& control);
cpp
#include "GStyleBaseWinFromRes.h"
#include <string>
#include <vector>
#include "MFCGTKUtils.h"
using namespace std;
extern int ConvertControlID(const char *pIDSTR);
static int MyConvertControlID(const char *pIDSTR)
{
if (0 == strcmp(pIDSTR, "IDC_STATIC"))
return 99000001;
return ConvertControlID(pIDSTR);
}
static void StringReplace(std::string &str, const std::string &old_str, const std::string &new_str)
{
size_t pos = 0;
while ((pos = str.find(old_str, pos)) != std::string::npos)
{
str.replace(pos, old_str.length(), new_str);
pos += new_str.length();
}
}
GStyleBaseWinFromRes::GStyleBaseWinFromRes()
{
}
GStyleBaseWinFromRes::~GStyleBaseWinFromRes()
{
DestroyGTKInterface();
}
bool GStyleBaseWinFromRes::ParseResourceString(const std::string &strResDesc)
{
vector<string> vetLines;
MFCUtils::StringSpiltEx(strResDesc, vetLines, "\n", false, true);
for (size_t iL = 0; iL < vetLines.size(); iL++)
{
std::string line = vetLines[iL];
string tail;
string strType = MFCUtils::StringFirstSplit(line, " ", tail);
vector<string> atrs;
MFCUtils::StringSpiltEx(tail, atrs, ",", false, true);
int id = 0;
int ctrl_left = 0, ctrl_top = 0;
int ctrl_width = 0, ctrl_height = 0;
string strCtrlText;
if (0 == strType.compare("DIALOG")) //DIALOG w,h
{
if (atrs.size() >= 2)
{
m_dialogWidth = m_Scale * atoi(atrs[0].c_str());
m_dialogHeight = m_Scale * atoi(atrs[1].c_str());
}
if (atrs.size() >= 3)
{
m_dialogTitle = atrs[2];
}
}
else if (0 == strType.compare("EDITTEXT")) // IDC_EDIT1,53,16,352,14,ES_AUTOHSCROLL
{
if (atrs.size() >= 5)
{
id = MyConvertControlID(atrs[0].c_str());
ctrl_left = m_Scale * atoi(atrs[1].c_str());
ctrl_top = m_Scale * atoi(atrs[2].c_str());
ctrl_width = m_Scale * atoi(atrs[3].c_str());
ctrl_height = m_Scale * atoi(atrs[4].c_str());
}
}
else if (0 == strType.compare("LTEXT")) //text,id, l,t,w,h
{
if (atrs.size() >= 6)
{
strCtrlText = atrs[0];
id = MyConvertControlID(atrs[1].c_str());
ctrl_left = m_Scale * atoi(atrs[2].c_str());
ctrl_top = m_Scale * atoi(atrs[3].c_str());
ctrl_width = m_Scale * atoi(atrs[4].c_str());
ctrl_height = m_Scale * atoi(atrs[5].c_str());
}
}
else if (0 == strType.compare("PUSHBUTTON")) //text,id, l,t,w,h
{
if (atrs.size() >= 6)
{
strCtrlText = atrs[0];
id = MyConvertControlID(atrs[1].c_str());
ctrl_left = m_Scale * atoi(atrs[2].c_str());
ctrl_top = m_Scale * atoi(atrs[3].c_str());
ctrl_width = m_Scale * atoi(atrs[4].c_str());
ctrl_height = m_Scale * atoi(atrs[5].c_str());
}
}
else if (0 == strType.compare("CHECKBOX")) //text,id, "unuseType",unuseStyle,l,t,w,h
{
if (atrs.size() >= 8)
{
strCtrlText = atrs[0];
id = MyConvertControlID(atrs[1].c_str());
ctrl_left = m_Scale * atoi(atrs[4].c_str());
ctrl_top = m_Scale * atoi(atrs[5].c_str());
ctrl_width = m_Scale * atoi(atrs[6].c_str());
ctrl_height = m_Scale * atoi(atrs[7].c_str());
}
}
StringReplace(strCtrlText, ",", ",");
MFCUtils::TrimStr(strCtrlText, '\"');
MFCUtils::TrimStr(m_dialogTitle, '\"');
// 添加到控件列表
if (id > 0)
{
ControlDef control;
control.id = id;
control.type = strType;
control.x = ctrl_left;
control.y = ctrl_top;
control.width = ctrl_width;
control.height = ctrl_height;
control.text = strCtrlText;
m_controls.push_back(control);
}
}
return true;
}
void GStyleBaseWinFromRes::OnControlClick(GtkWidget *widget)
{
///g_object_set_data(G_OBJECT(pWid), "ctrl_id", GINT_TO_POINTER(control.id));
gpointer gpID = g_object_get_data(G_OBJECT(widget), "ctrl_id");
int ctrlId = GPOINTER_TO_INT(gpID);
switch (ctrlId)
{
case 1: // 对应你定义的控件ID
g_print("选择文件按钮被点击\n");
break;
case 2:
g_print("选择目录按钮被点击\n");
break;
// 其他控件事件处理
default:
g_print("控件ID:%d 触发事件\n", ctrlId);
}
}
void G_OnControlClick(GtkWidget *widget, gpointer user_data)
{
GStyleBaseWinFromRes *pThis = (GStyleBaseWinFromRes*) (user_data);
pThis->OnControlClick(widget);
}
void GStyleBaseWinFromRes::OnDestroy()
{
DestroyGTKInterface();
}
// 销毁整个界面
void GStyleBaseWinFromRes::DestroyGTKInterface()
{
if (GTK_IS_WINDOW(m_hWnd_Widget))
{
// 销毁主窗口会自动递归销毁所有子控件、容器
gtk_widget_destroy(m_hWnd_Widget);
}
// 所有指针置空、map清空,避免野指针
m_hWnd_Widget = nullptr;
m_controlMap.clear();
}
GtkWidget* GStyleBaseWinFromRes::GetWidgetByID(int id)
{
map<int, WidgetAndType>::iterator ite = m_controlMap.find(id);
if (ite != m_controlMap.end())
return ite->second.pWidget;
return 0;
}
WidgetType GStyleBaseWinFromRes::GetWidgetTypeByID(int id)
{
map<int, WidgetAndType>::iterator ite = m_controlMap.find(id);
if (ite != m_controlMap.end())
return ite->second._ty;
return WIDGET_T_None;
}
WidgetType GStyleBaseWinFromRes::GetWidgetType(const string &strT)
{
if (strT == "EDITTEXT")
{
return WIDGET_T_Text;
}
else if (strT == "PUSHBUTTON")
{
return WIDGET_T_PushBtn;
}
else if (strT == "LTEXT")
{
return WIDGET_T_Static;
}
else if (strT == "CHECKBOX")
{
return WIDGET_T_CheckBox;
}
return WIDGET_T_None;
}
int GStyleBaseWinFromRes::GetWidgetID(GtkWidget *widget)
{
gpointer gpID = g_object_get_data(G_OBJECT(widget), "ctrl_id");
int ctrlId = GPOINTER_TO_INT(gpID);
return ctrlId;
}
// 主窗口关闭回调:自动清理类内指针
void OnMainWindowDestroy(GtkWidget *widget, gpointer user_data)
{
GStyleBaseWinFromRes *pParser = static_cast<GStyleBaseWinFromRes*>(user_data);
pParser->OnDestroy();
}
// 窗口关闭回调:退出模态循环
gboolean OnModalWindowClose(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
GStyleBaseWinFromRes *pDlg = static_cast<GStyleBaseWinFromRes*>(user_data);
pDlg->m_modalRet = IDCANCEL; // 默认关闭返回取消
if (pDlg->m_modalLoop && g_main_loop_is_running(pDlg->m_modalLoop))
{
g_main_loop_quit(pDlg->m_modalLoop); // 退出嵌套循环
}
return FALSE;
}
// 创建GTK界面
void GStyleBaseWinFromRes::CreateGTKInterface()
{
// 创建主窗口
m_hWnd_Widget = gtk_window_new(GTK_WINDOW_TOPLEVEL); //GTK_WINDOW_POPUP
if (m_hWnd_Widget)
m_hWnd = GTK_WINDOW(m_hWnd_Widget);
gtk_window_set_title(GTK_WINDOW(m_hWnd_Widget), m_dialogTitle.c_str());
gtk_window_set_default_size(GTK_WINDOW(m_hWnd_Widget), m_dialogWidth, m_dialogHeight);
// 固定窗口大小,避免用户拖动窗口导致布局错乱
gtk_window_set_resizable(GTK_WINDOW(m_hWnd_Widget), FALSE);
// ========== 新增模态配置 ==========
// 设为模态:阻塞父窗口输入
gtk_window_set_modal(GTK_WINDOW(m_hWnd_Widget), TRUE);
// 可选:绑定父窗口,模态时父窗口自动灰掉,符合系统交互逻辑
// gtk_window_set_transient_for(GTK_WINDOW(m_hWnd_Widget), 父窗口GTK_WINDOW指针);
// 绑定窗口关闭信号:关闭时退出模态循环
g_signal_connect(m_hWnd_Widget, "delete-event", G_CALLBACK(OnModalWindowClose), this);
// 绑定窗口关闭信号,传当前类实例为参数
g_signal_connect(m_hWnd_Widget, "destroy", G_CALLBACK(OnMainWindowDestroy), this);
// 替换为GtkFixed绝对布局容器
GtkWidget *fixed = gtk_fixed_new();
gtk_container_add(GTK_CONTAINER(m_hWnd_Widget), fixed);
// 清空之前的控件映射
m_controlMap.clear();
// 为每个控件创建GTK组件
for (const auto &control : m_controls)
{
GtkWidget *pWid = CreateControlFromDefinition(control);
if (pWid)
{
// 核心:按资源定义的x/y坐标把控件放到固定容器里
gtk_fixed_put(GTK_FIXED(fixed), pWid, control.x, control.y);
// 把控件ID和控件关联,方便后续查找
g_object_set_data(G_OBJECT(pWid), "ctrl_id", GINT_TO_POINTER(control.id));
WidgetAndType wtt;
wtt.pWidget = pWid;
wtt._ty = GetWidgetType(control.type);
m_controlMap[control.id] = wtt;
// 可选:绑定控件事件,比如按钮点击事件
if (control.type == "PUSHBUTTON")
{
g_signal_connect(pWid, "clicked", G_CALLBACK(G_OnControlClick), GINT_TO_POINTER(this));
}
else if (control.type == "CHECKBOX")
{
g_signal_connect(pWid, "toggled", G_CALLBACK(G_OnControlClick), GINT_TO_POINTER(this));
}
}
}
// 5. CSS清除默认内边距,这一步是实现12x12的关键
const char *css = "button, entry, checkbutton, radiobutton, combobox, label, scale {padding: 0px; margin: 0px; border-width: 1px; min-height: 0px; min-width: 0px;} \n"
"checkbutton check, radiobutton radio { padding: 0px; margin: 0px; min-height: 0px; min-width: 0px;}\n"
"combobox box { padding: 0px; margin: 0px;} ";
GtkCssProvider *provider = gtk_css_provider_new();
gtk_css_provider_load_from_data(provider, css, -1, NULL);
gtk_style_context_add_provider_for_screen(gdk_screen_get_default(), GTK_STYLE_PROVIDER(provider),
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
// 获取控件列表
const std::vector<ControlDef>& GStyleBaseWinFromRes::GetControls() const
{
return m_controls;
}
// 全局函数实现
GtkWidget* CreateControlFromDefinition(const ControlDef &control)
{
if (control.type == "EDITTEXT")
{
return CreateEditBox(control);
}
else if (control.type == "PUSHBUTTON")
{
return CreateButton(control);
}
else if (control.type == "LTEXT")
{
return CreateLabel(control);
}
else if (control.type == "CHECKBOX")
{
return CreateCheckBox(control);
}
return 0;
}
GtkWidget* CreateEditBox(const ControlDef &control)
{
GtkWidget *entry = gtk_entry_new();
gtk_widget_set_size_request(entry, control.width, control.height);
// 添加到容器中
return entry;
}
GtkWidget* CreateButton(const ControlDef &control)
{
GtkWidget *button = gtk_button_new_with_label(control.text.c_str());
gtk_widget_set_size_request(button, control.width, control.height);
// 添加到容器中
return button;
}
GtkWidget* CreateCheckBox(const ControlDef &control)
{
GtkWidget *checkbox = gtk_check_button_new_with_label(control.text.c_str());
gtk_widget_set_size_request(checkbox, control.width, control.height);
// 添加到容器中
return checkbox;
}
GtkWidget* CreateLabel(const ControlDef &control)
{
GtkWidget *label = gtk_label_new(control.text.c_str());
gtk_widget_set_size_request(label, control.width, control.height);
// 添加到容器中
return label;
}
(2)CDialog基类轻巧实现:原则就是需要再实现不需要的不要写
cpp
#pragma once
#include "CDataExchange.h"
#include "wintypes.h"
#include "GStyleBaseWinFromRes.h"
#include "MFC.h"
class CDialog :public GStyleBaseWinFromRes
{
public:
CDialog();
virtual ~CDialog();
public:
virtual void DoDataExchange(CDataExchange* pDX);
virtual BOOL OnInitDialog();
void UpdateData(BOOL bToVar);
void DDX_Text(CDataExchange* pDX, int ctrlID, CStringW& strVar);
void DDX_Check(CDataExchange* pDX, int ctrlID, BOOL& boolVar);
int DoModal();
void SetRsourceString(const char* pRes);
string m_strResString;
};
cpp
#include "CDialog.h"
CDialog::CDialog()
{
}
CDialog::~CDialog()
{
}
void CDialog::DoDataExchange(CDataExchange *pDX)
{
}
BOOL CDialog::OnInitDialog()
{
return TRUE;
}
void CDialog::UpdateData(BOOL bToVar)
{
CDataExchange exg;
exg.bToVar = bToVar;
DoDataExchange(&exg);
}
int CDialog::DoModal()
{
if (ParseResourceString(m_strResString))
{
CreateGTKInterface();
OnInitDialog();
gtk_widget_show_all(m_hWnd_Widget);
// 创建嵌套事件循环,阻塞直到窗口关闭
m_modalLoop = g_main_loop_new(nullptr, FALSE);
g_main_loop_run (m_modalLoop);
g_main_loop_unref(m_modalLoop);
m_modalLoop = nullptr;
// 窗口关闭后销毁,返回结果
DestroyGTKInterface();
return m_modalRet;
}
}
void CDialog::SetRsourceString(const char *pRes)
{
m_strResString = pRes;
}
void CDialog::DDX_Text(CDataExchange *pDX, int ctrlID, CStringW &strVar)
{
GtkWidget *widget = this->GetWidgetByID(ctrlID);
WidgetType wt = this->GetWidgetTypeByID(ctrlID);
if (pDX->bToVar)
{
if (wt == WIDGET_T_Static)
{
const char *text = gtk_label_get_text(GTK_LABEL(widget));
strVar = (const wchar_t*) CA2W(text, CP_UTF8);
}
else if (wt == WIDGET_T_Text)
{
const char *text = gtk_entry_get_text(GTK_ENTRY(widget));
strVar = (const wchar_t*) CA2W(text, CP_UTF8);
}
else if (wt == WIDGET_T_PushBtn)
{
const char *text = gtk_button_get_label(GTK_BUTTON(widget));
strVar = (const wchar_t*) CA2W(text, CP_UTF8);
}
else if (wt == WIDGET_T_CheckBox)
{
const char *text = gtk_button_get_label(GTK_BUTTON(widget));
strVar = (const wchar_t*) CA2W(text, CP_UTF8);
}
}
else
{
if (wt == WIDGET_T_Static)
{
gtk_label_set_text(GTK_LABEL(widget), (const char*) CW2A(strVar, CP_UTF8));
}
else if (wt == WIDGET_T_Text)
{
gtk_entry_set_text(GTK_ENTRY(widget), (const char*) CW2A(strVar, CP_UTF8));
}
else if (wt == WIDGET_T_PushBtn)
{
gtk_button_set_label(GTK_BUTTON(widget), (const char*) CW2A(strVar, CP_UTF8));
}
else if (wt == WIDGET_T_CheckBox)
{
gtk_button_set_label(GTK_BUTTON(widget), (const char*) CW2A(strVar, CP_UTF8));
}
}
}
void CDialog::DDX_Check(CDataExchange *pDX, int ctrlID, BOOL &boolVar)
{
GtkWidget *widget = this->GetWidgetByID(ctrlID);
WidgetType wt = this->GetWidgetTypeByID(ctrlID);
if (pDX->bToVar)
{
if (wt == WIDGET_T_CheckBox)
{
gboolean is_check = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
boolVar = !!is_check;
}
}
else
{
if (wt == WIDGET_T_CheckBox)
{
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), boolVar);
}
}
}
(3)MFC常规兼容层(CString等)非完整实现
cpp
#pragma once
#include "wintypes.h"
#include <string>
#include <locale>
#include <codecvt>
using namespace std;
#ifndef CP_UTF8
#define CP_UTF8 65001
#endif
#ifndef CP_ACP
#define CP_ACP 0
#endif
#define afx_msg
#define OUT
#define IN
class CWnd
{
};
class CW2A
{
public:
explicit CW2A(const wchar_t *wstr, unsigned int codePage = CP_UTF8);
operator const char*() const
{
return m_utf8Str.c_str();
}
private:
std::string m_utf8Str;
};
class CA2W
{
public:
explicit CA2W(const char *str, unsigned int codePage = CP_UTF8);
operator const wchar_t*() const
{
return m_wstr.c_str();
}
private:
std::wstring m_wstr;
};
////////////////////////////////////////////////////////////////////
//CStringW
///////////////////////////////////////////////////////////////////
class CStringW
{
public:
CStringW();
CStringW(const CStringW &other);
CStringW(const wchar_t *pstr);
~CStringW();
public:
CStringW& operator=(const CStringW &other);
operator const wchar_t*() const;
void Format(const wchar_t *lpFmt, ...);
int GetLength() const;
wchar_t *pstr = 0;
};
////////////////////////////////////////////////////////////////////
//CStringA
///////////////////////////////////////////////////////////////////
class CStringA
{
public:
CStringA();
CStringA(const CStringA &other);
CStringA(const char *pstr);
~CStringA();
public:
CStringA& operator=(const CStringA &other);
operator const char*() const;
void Format(const char *lpFmt, ...);
int GetLength() const;
char *pstr = 0;
};
////////////////////////////////////////////////////////////////////
//CString Utils
///////////////////////////////////////////////////////////////////
CStringW operator+(const CStringW &l, const char *r);
CStringW operator+(const CStringW &l, const CStringW &r);
typedef CStringW CString;
struct _GtkWindow;
typedef _GtkWindow GtkWindow;
CString SelDir(GtkWindow *parent);
CString SelFile(GtkWindow *parent = nullptr, const char *title = "选择文件", const char *filterStr = nullptr);
typedef const wchar_t *LPCWSTR;
typedef LPCWSTR LPCTSTR;
#define _countof(ARRAY) (sizeof(ARRAY)/sizeof(ARRAY[0]))
#define DECLARE_MESSAGE_MAP() virtual void OnControlClick(GtkWidget *widget);
#define BEGIN_MESSAGE_MAP(DLG,BASEDLG)\
void DLG::OnControlClick(GtkWidget *widget){\
int widtetid=GetWidgetID(widget);\
#define END_MESSAGE_MAP() }
#define ON_WM_PAINT()
#define ON_WM_QUERYDRAGICON()
#define ON_BN_CLICKED(IDC_BTN, fnptr) {\
auto fn=fnptr;\
if(widtetid==IDC_BTN){ (this->*fn)();}\
}
cpp
#include "MFC.h"
#include <gtk/gtk.h>
// 等价于你Windows下的SelDir函数
// 参数parent: 父窗口指针(对应原函数的HWND,传nullptr则为全局模态)
// 返回值:选中的目录路径(UTF8编码,用户点取消则返回空串)
CString SelDir(GtkWindow *parent)
{
std::string strDir;
const char *dialogTitle = "select dir";
// 1. 创建目录选择对话框(优先用Native原生对话框,系统风格更统一)
GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
GtkFileChooserNative *dialog = gtk_file_chooser_native_new(dialogTitle, parent, action, "选择", // 确定按钮文本
"取消"); // 取消按钮文本
// 2. 对应你原BROWSEINFO的flag配置
// BIF_NONEWFOLDERBUTTON: 禁止新建文件夹
gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(dialog), FALSE);
// BIF_RETURNONLYFSDIRS: 只允许选择真实文件系统路径(默认开启)
// BIF_NEWDIALOGSTYLE: GTK默认就是新样式对话框
// 3. 阻塞运行对话框,等用户操作
gint res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(dialog));
if (res == GTK_RESPONSE_ACCEPT) // 用户点击了确定按钮
{
// 获取选中的路径,返回的字符串需要手动释放
gchar *szPath = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
if (szPath)
{
strDir = szPath;
g_free(szPath);
}
}
// 4. 销毁对话框释放内存
g_object_unref(dialog);
return (LPCWSTR) CA2W(strDir.c_str(), CP_UTF8);
}
// 直接返回选中的文件路径,用户点取消返回空串
// 参数1: 父窗口指针,传nullptr则是全局模态
// 参数2: 对话框标题,可选
// 参数3: 文件过滤规则,可选,格式: "描述|*.ext1;*.ext2" 比如 "可执行文件|*.exe;*.dll"
CStringW SelFile(GtkWindow *parent, const char *title, const char *filterStr)
{
std::string retPath;
GtkFileChooserNative *dialog = gtk_file_chooser_native_new(title, parent, GTK_FILE_CHOOSER_ACTION_OPEN, "确定", "取消");
if (!dialog)
return L"";
// 可选:设置文件过滤
if (filterStr && *filterStr)
{
std::string filterStrCopy = filterStr;
size_t splitPos = filterStrCopy.find('|');
if (splitPos != std::string::npos)
{
std::string filterName = filterStrCopy.substr(0, splitPos);
std::string filterPatterns = filterStrCopy.substr(splitPos + 1);
GtkFileFilter *filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, filterName.c_str());
// 拆分多个后缀
size_t pos = 0;
while (pos != std::string::npos)
{
size_t nextPos = filterPatterns.find(';', pos);
std::string pattern = filterPatterns.substr(pos, nextPos - pos);
gtk_file_filter_add_pattern(filter, pattern.c_str());
pos = nextPos == std::string::npos ? nextPos : nextPos + 1;
}
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog), filter);
}
}
// 运行对话框
gint res = gtk_native_dialog_run(GTK_NATIVE_DIALOG(dialog));
if (res == GTK_RESPONSE_ACCEPT)
{
gchar *path = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
if (path)
{
retPath = path;
g_free(path);
}
}
g_object_unref(dialog);
return (LPCWSTR) CA2W(retPath.c_str(), CP_UTF8);
}
CW2A::CW2A(const wchar_t *wstr, unsigned int codePage)
{
if (!wstr || codePage != CP_UTF8)
return;
try
{
#if __SIZEOF_WCHAR_T__ == 2
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> conv;
#else
std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
#endif
m_utf8Str = conv.to_bytes(wstr);
}
catch (...)
{
m_utf8Str.clear();
}
}
CA2W::CA2W(const char *str, unsigned int codePage)
{
if (!str || (codePage != CP_UTF8 && codePage != CP_ACP))
return;
try
{
#if __SIZEOF_WCHAR_T__ == 2
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> conv;
#else
std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
#endif
m_wstr = conv.from_bytes(str);
}
catch (...)
{
m_wstr.clear();
}
}
////////////////////////////////////////////////////////////////////
//CStringW
///////////////////////////////////////////////////////////////////
CStringW::CStringW()
{
pstr = wcsdup(L"");
}
// 拷贝构造:深拷贝
CStringW::CStringW(const CStringW &other)
{
pstr = wcsdup(other.pstr);
}
CStringW::CStringW(const wchar_t *pstr)
{
if (pstr)
this->pstr = wcsdup(pstr);
}
CStringW::~CStringW()
{
if (pstr)
{
free (pstr);
pstr = 0;
}
}
// 赋值运算符:深拷贝,避免自赋值
CStringW& CStringW::operator=(const CStringW &other)
{
if (this == &other)
return *this;
free (pstr);
pstr = wcsdup(other.pstr);
return *this;
}
CStringW::operator const wchar_t*() const
{
return pstr;
}
// 可变参数格式化,自动计算缓冲区大小无溢出
void CStringW::Format(const wchar_t *lpFmt, ...)
{
if (!lpFmt)
{
free (pstr);
pstr = wcsdup(L"");
return;
}
va_list args;
va_start(args, lpFmt);
// 先计算需要的缓冲区长度
int reqLen = vswprintf(nullptr, 0, lpFmt, args);
va_end(args);
if (reqLen <= 0)
{
free (pstr);
pstr = wcsdup(L"");
return;
}
// 分配内存后格式化
free (pstr);
pstr = (wchar_t*) malloc((reqLen + 1) * sizeof(wchar_t));
va_start(args, lpFmt);
vswprintf(pstr, reqLen + 1, lpFmt, args);
va_end(args);
}
int CStringW::GetLength() const
{
return wcslen(pstr);
}
////////////////////////////////////////////////////////////////////
//CStringA
///////////////////////////////////////////////////////////////////
CStringA::CStringA()
{
pstr = strdup("");
}
// 拷贝构造
CStringA::CStringA(const CStringA &other)
{
pstr = strdup(other.pstr);
}
CStringA::CStringA(const char *pstr)
{
if (pstr)
this->pstr = strdup(pstr);
}
CStringA::~CStringA()
{
if (pstr)
{
free (pstr);
pstr = 0;
}
}
// 赋值运算符
CStringA& CStringA::operator=(const CStringA &other)
{
if (this == &other)
return *this;
free (pstr);
pstr = strdup(other.pstr);
return *this;
}
CStringA::operator const char*() const
{
return pstr;
}
// 格式化
void CStringA::Format(const char *lpFmt, ...)
{
if (!lpFmt)
{
free (pstr);
pstr = strdup("");
return;
}
va_list args;
va_start(args, lpFmt);
int reqLen = vsnprintf(nullptr, 0, lpFmt, args);
va_end(args);
if (reqLen <= 0)
{
free (pstr);
pstr = strdup("");
return;
}
free (pstr);
pstr = (char*) malloc(reqLen + 1);
va_start(args, lpFmt);
vsnprintf(pstr, reqLen + 1, lpFmt, args);
va_end(args);
}
int CStringA::GetLength() const
{
return strlen(pstr);
}
////////////////////////////////////////////////////////////////////
//CString Utils
///////////////////////////////////////////////////////////////////
// 重载+:CStringW + UTF8 char*,自动转码拼接
CStringW operator+(const CStringW &l, const char *r)
{
if (!r || !*r)
return l;
CA2W wR(r, CP_UTF8); // 复用之前的UTF8转宽字符工具
size_t lenL = l.GetLength();
size_t lenR = wcslen((const wchar_t*) wR);
CStringW res;
free(res.pstr);
res.pstr = (wchar_t*) malloc(sizeof(wchar_t) * (lenL + lenR + 1));
wcscpy(res.pstr, l.pstr);
wcscat(res.pstr, (const wchar_t*) wR);
return res;
}
// 重载+:两个CStringW拼接
CStringW operator+(const CStringW &l, const CStringW &r)
{
size_t lenL = l.GetLength();
size_t lenR = r.GetLength();
CStringW res;
free(res.pstr);
res.pstr = (wchar_t*) malloc(sizeof(wchar_t) * (lenL + lenR + 1));
wcscpy(res.pstr, l.pstr);
wcscat(res.pstr, r.pstr);
return res;
}
(杂项代码)
cpp
#pragma once
#include <vector>
#include <string>
using namespace std;
namespace MFCUtils
{
void StringSpilt(const string &str, vector<string> &arr, const string &cut);
void TrimLeftStr(string &str, char chtr = 0);
void TrimRightStr(string &str, char chtr = 0);
void TrimStr(string &str, char chtr = 0);
void StringSpiltEx(const string &str, vector<string> &arr, const string &cut, bool bAllowEmpty, bool bTrim);
string StringFirstSplit(string in, const char *fp, string &tail);
}
;
cpp
#include "MFCGTKUtils.h"
#include <string.h>
void MFCUtils::StringSpilt(const string &str, vector<string> &arr, const string &cut)
{
StringSpiltEx(str, arr, cut, true, false);
}
//判断非空白字符
#define IsNonSpaceChar(ch) ((ch) != ' ' && (ch) != '\t' && (ch) != '\r' && (ch) != '\n')
void MFCUtils::TrimLeftStr(string &str, char chtr)
{
size_t len = str.length();
for (size_t i = 0; i < len; i++) //find first non-space
{
char ch = str[i];
if ((0 == chtr && IsNonSpaceChar(ch)) || (chtr != 0 && ch != chtr)) //left i shift
{
size_t nShiftLeft = i;
if (nShiftLeft > 0)
{
size_t nRemain = len - i;
for (size_t j = 0; j < nRemain; j++)
{ //shift left all char
str[j] = str[nShiftLeft + j];
}
str.erase(len - nShiftLeft, nShiftLeft);
}
return;
}
}
//not find non-space means all is space
str = "";
}
void MFCUtils::TrimRightStr(string &str, char chtr)
{
int len = (int) str.length();
for (int i = (len - 1); i >= 0; i--) //find last non-space
{
char ch = str[i];
if ((0 == chtr && IsNonSpaceChar(ch)) || (chtr != 0 && ch != chtr)) //left i shift
{
int nChopTail = (len - 1) - i;
if (nChopTail > 0)
{
str.erase(len - nChopTail, nChopTail);
}
return;
}
}
//not find non-space means all is space
str = "";
}
void MFCUtils::TrimStr(string &str, char chtr)
{
TrimLeftStr(str,chtr);
TrimRightStr(str,chtr);
}
void MFCUtils::StringSpiltEx(const string &str, vector<string> &arr, const string &cut, bool bAllowEmpty, bool bTrim)
{
// str: 需要切割的字符串
// arr: 切割后的字符串数组存放位置
// cut:需要切割的符号
string::size_type pos1, pos2;
pos2 = str.find(cut);
pos1 = 0;
while (string::npos != pos2)
{
string sToPush = str.substr(pos1, pos2 - pos1);
if (bTrim)
TrimStr(sToPush);
if (bAllowEmpty || sToPush.length())
{
arr.push_back(sToPush);
}
pos1 = pos2 + cut.size();
pos2 = str.find(cut, pos1);
}
if (pos1 != str.length())
{
string sToPush = str.substr(pos1);
if (bTrim)
TrimStr(sToPush);
if (bAllowEmpty || sToPush.length())
{
arr.push_back(sToPush);
}
}
}
string MFCUtils::StringFirstSplit(string in, const char *fp, string &tail)
{
size_t f = in.find(fp);
if (f != string::npos)
{
tail = in.substr(f + strlen(fp));
TrimStr(tail);
string h = in.substr(0, f);
TrimStr(h);
return h;
}
return "";
}
(*)关于使用
资源这部分从rc考出来改成字符串硬编码到代码中
cpp
int main(int argc, char* argv[]) {
printf("hello\n");
gtk_init(&argc, &argv);
CMFCtestvdcoreDlg dlg;
dlg.SetRsourceString(
"DIALOG 435, 295, \"TestVDDemo\" \r\n"
"EDITTEXT IDC_EDIT1,53,16,352,14,ES_AUTOHSCROLL\r\n"
"LTEXT \"选择文件\",IDC_STATIC,7,19,33,8\r\n"
"EDITTEXT IDC_EDIT2,53,35,352,14,ES_AUTOHSCROLL\r\n"
"LTEXT \"选择目录\",IDC_STATIC,7,39,33,8\r\n"
"EDITTEXT IDC_EDIT3,53,53,352,14,ES_AUTOHSCROLL\r\n"
"LTEXT \"选择库目录\",IDC_STATIC,7,57,41,8\r\n"
"PUSHBUTTON \"...\",IDC_B_SelLibDir,408,53,20,14\r\n"
"PUSHBUTTON \"...\",IDC_B_SelFile,408,16,20,14\r\n"
"PUSHBUTTON \"...\",IDC_B_SelDir,408,35,20,14\r\n"
);//省略一些。。
dlg.DoModal();
return 0;
}
另外就是需要自己实现一个资源字符串和整数的映射
cpp
#define MAKE_ID(XXX) if(strcmp(pIDSTR,#XXX)==0)return XXX;
int ConvertControlID(const char *pIDSTR)
{
MAKE_ID(IDC_EDIT1);
MAKE_ID(IDC_EDIT2);
MAKE_ID(IDC_EDIT3);
MAKE_ID(IDC_B_SelLibDir);
MAKE_ID(IDC_B_SelFile);
MAKE_ID(IDC_B_SelDir);
MAKE_ID(IDC_B_CID);
MAKE_ID(IDC_EDIT4);
return 0;
}
#undef MAKE_ID
看看演示效果
