基于GTK的简易MFC对话框兼容层 -- MfcToGTK

为了便于移植一些简单的测试工具,这种测试工具在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();
    // 解析 注意 字符串不要有英文逗号 用"&comma;" 代替
	/*
	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&comma;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, "&comma;", ",");
		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

看看演示效果

https://github.com/lif1234567892/MfcToGTK

相关推荐
fengyehongWorld2 小时前
Linux 安装frp实现内网穿透
linux·运维·服务器
一_个前端2 小时前
[TailScale]-打通你的局域网服务器到公网电脑
linux
DO_Community2 小时前
Omarchy 将研发基础设施迁移至 DigitalOcean 云平台
linux·人工智能·llm·agent·omarchy
米糕闯编程2 小时前
鱼香ros2(三)Linux操作总结
linux·机器人·ros2
一技安身3 小时前
【信创】银河麒麟V10服务器使用安装光盘Packages自建本地yum源
linux·运维·服务器
小小、码农3 小时前
C++11右值引用与移动语义 —— 从拷贝资源到转移资源
开发语言·网络·c++·网络协议
码农客栈3 小时前
Linux I2C驱动实验
linux·运维·驱动开发
charlie1145141914 小时前
C++ 的新标准容器:flat_map、inplace_vector 与 mdspan
开发语言·c++·开源项目
唠玖馆5 小时前
c++AVL树
开发语言·c++