diff --git a/Gtk4/gtk158_draw_gtk4/CMakeLists.txt b/Gtk4/gtk158_draw_gtk4/CMakeLists.txt
new file mode 100644
index 0000000..2345f60
--- /dev/null
+++ b/Gtk4/gtk158_draw_gtk4/CMakeLists.txt
@@ -0,0 +1,88 @@
+set(CMAKE_CXX_STANDARD 17)
+cmake_minimum_required(VERSION 3.0.0)
+project(gtk158_draw_gtk4 VERSION 1.0.0)
+
+list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../../GCR_CMake/macros)
+include(GlibCompileResourcesSupport)
+
+include(CTest)
+enable_testing()
+
+set(CPACK_PROJECT_NAME ${PROJECT_NAME})
+set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
+
+include(CPack)
+include_directories(.)
+include_directories(..)
+
+#Find PkgConfig to use gtkmm3
+find_package (PkgConfig REQUIRED)
+pkg_check_modules (GTKMM4 REQUIRED gtkmm-4.0)
+include_directories (${GTKMM4_INCLUDE_DIRS})
+link_directories (${GTKMM4_LIBRARY_DIRS})
+
+#Find Gettext
+# find_package (Gettext REQUIRED)
+# set(PO_DIR ${CMAKE_BINARY_DIR}/po/zh_CN/LC_MESSAGES)
+
+#Source files
+set(SOURCE_FILE src/main.cc src/MineSweeper.cc src/InputBox.cc src/ScoresWin.cc)
+
+#Compile Resource
+
+set(RESOURCE_LIST
+ icons/scalable/status/1mines.svg
+ icons/scalable/status/2mines.svg
+ icons/scalable/status/3mines.svg
+ icons/scalable/status/4mines.svg
+ icons/scalable/status/5mines.svg
+ icons/scalable/status/6mines.svg
+ icons/scalable/status/7mines.svg
+ icons/scalable/status/8mines.svg
+ icons/scalable/status/exploded.svg
+ icons/scalable/status/flag.svg
+ icons/scalable/status/incorrect.svg
+ icons/scalable/status/maybe.svg
+ icons/scalable/status/mine.svg
+ STRIPBLANKS minesweeper.ui
+ STRIPBLANKS win_input.ui
+ STRIPBLANKS scoreswin.ui
+ STRIPBLANKS mine_menu.xml)
+
+compile_gresources(RESOURCE_FILE
+ XML_OUT
+ TYPE EMBED_C
+ RESOURCES ${RESOURCE_LIST}
+ PREFIX "/org/gtk/daleclack"
+ SOURCE_DIR ${PROJECT_SOURCE_DIR}/res)
+
+# Add a custom target to the makefile. Now make builds our resource file.
+# It depends on the output RESOURCE_FILE.
+
+add_custom_target(resource ALL DEPENDS ${RESOURCE_FILE})
+
+#For win32 platform,use rc resource and .ico icon
+if(WIN32)
+ SET(CMAKE_RC_COMPILER windres)
+ set(app_WINRC ../icon.rc)
+ set_property(SOURCE ../icon.rc APPEND PROPERTY
+ OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/../icon.ico
+ )
+ add_executable(${PROJECT_NAME} WIN32 ${app_WINRC} ${SOURCE_FILE} ${RESOURCE_FILE})
+ add_custom_command( TARGET ${PROJECT_NAME}
+ COMMAND echo * > ${CMAKE_BINARY_DIR}/.gitignore
+ COMMAND echo **/* > ${CMAKE_BINARY_DIR}/.hgignore)
+else()
+ add_executable(${PROJECT_NAME} ${SOURCE_FILE} ${RESOURCE_FILE})
+ add_custom_command( TARGET ${PROJECT_NAME}
+ COMMAND echo \"*\" > ${CMAKE_BINARY_DIR}/.gitignore
+ COMMAND echo \"**/*\" > ${CMAKE_BINARY_DIR}/.hgignore)
+endif(WIN32)
+
+#Add command to generate .gitignore and .mo files
+# add_custom_command( TARGET ${PROJECT_NAME}
+# COMMAND mkdir -p ${PO_DIR}
+# COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} ${CMAKE_SOURCE_DIR}/po/zh_CN.po -o ${PO_DIR}/${PROJECT_NAME}.mo)
+
+SET (CMAKE_EXTRA_CXX_FLAGS ${GTKMM4_CFLAGS_OTHER})
+target_link_libraries (${PROJECT_NAME} ${GTKMM4_LIBRARIES} -lpthread)
diff --git a/Gtk4/gtk159_minesweeper4/.vscode/settings.json b/Gtk4/gtk159_minesweeper4/.vscode/settings.json
new file mode 100644
index 0000000..65a114e
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/.vscode/settings.json
@@ -0,0 +1,89 @@
+{
+ "files.associations": {
+ "cctype": "cpp",
+ "clocale": "cpp",
+ "cmath": "cpp",
+ "cstdarg": "cpp",
+ "cstddef": "cpp",
+ "cstdio": "cpp",
+ "cstdlib": "cpp",
+ "cstring": "cpp",
+ "ctime": "cpp",
+ "cwchar": "cpp",
+ "cwctype": "cpp",
+ "array": "cpp",
+ "atomic": "cpp",
+ "bit": "cpp",
+ "*.tcc": "cpp",
+ "bitset": "cpp",
+ "chrono": "cpp",
+ "codecvt": "cpp",
+ "compare": "cpp",
+ "concepts": "cpp",
+ "condition_variable": "cpp",
+ "cstdint": "cpp",
+ "deque": "cpp",
+ "list": "cpp",
+ "map": "cpp",
+ "set": "cpp",
+ "string": "cpp",
+ "unordered_map": "cpp",
+ "unordered_set": "cpp",
+ "vector": "cpp",
+ "exception": "cpp",
+ "algorithm": "cpp",
+ "functional": "cpp",
+ "iterator": "cpp",
+ "memory": "cpp",
+ "memory_resource": "cpp",
+ "numeric": "cpp",
+ "optional": "cpp",
+ "random": "cpp",
+ "ratio": "cpp",
+ "string_view": "cpp",
+ "system_error": "cpp",
+ "tuple": "cpp",
+ "type_traits": "cpp",
+ "utility": "cpp",
+ "fstream": "cpp",
+ "initializer_list": "cpp",
+ "iomanip": "cpp",
+ "iosfwd": "cpp",
+ "iostream": "cpp",
+ "istream": "cpp",
+ "limits": "cpp",
+ "mutex": "cpp",
+ "new": "cpp",
+ "numbers": "cpp",
+ "ostream": "cpp",
+ "semaphore": "cpp",
+ "sstream": "cpp",
+ "stdexcept": "cpp",
+ "stop_token": "cpp",
+ "streambuf": "cpp",
+ "thread": "cpp",
+ "cinttypes": "cpp",
+ "typeinfo": "cpp",
+ "valarray": "cpp",
+ "variant": "cpp",
+ "__bit_reference": "cpp",
+ "__bits": "cpp",
+ "__config": "cpp",
+ "__debug": "cpp",
+ "__errc": "cpp",
+ "__hash_table": "cpp",
+ "__locale": "cpp",
+ "__mutex_base": "cpp",
+ "__node_handle": "cpp",
+ "__split_buffer": "cpp",
+ "__threading_support": "cpp",
+ "__tree": "cpp",
+ "__tuple": "cpp",
+ "__verbose_abort": "cpp",
+ "ios": "cpp",
+ "locale": "cpp",
+ "queue": "cpp",
+ "stack": "cpp"
+ },
+ "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools"
+}
\ No newline at end of file
diff --git a/Gtk4/gtk159_minesweeper4/CMakeLists.txt b/Gtk4/gtk159_minesweeper4/CMakeLists.txt
new file mode 100644
index 0000000..bb583f4
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/CMakeLists.txt
@@ -0,0 +1,88 @@
+set(CMAKE_CXX_STANDARD 17)
+cmake_minimum_required(VERSION 3.0.0)
+project(gtk142_minesweeper3 VERSION 1.0.0)
+
+list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/../../GCR_CMake/macros)
+include(GlibCompileResourcesSupport)
+
+include(CTest)
+enable_testing()
+
+set(CPACK_PROJECT_NAME ${PROJECT_NAME})
+set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
+
+include(CPack)
+include_directories(.)
+include_directories(..)
+
+#Find PkgConfig to use gtkmm3
+find_package (PkgConfig REQUIRED)
+pkg_check_modules (GTKMM4 REQUIRED gtkmm-4.0)
+include_directories (${GTKMM4_INCLUDE_DIRS})
+link_directories (${GTKMM4_LIBRARY_DIRS})
+
+#Find Gettext
+# find_package (Gettext REQUIRED)
+# set(PO_DIR ${CMAKE_BINARY_DIR}/po/zh_CN/LC_MESSAGES)
+
+#Source files
+set(SOURCE_FILE src/main.cc src/MineSweeper.cc src/InputBox.cc src/ScoresWin.cc)
+
+#Compile Resource
+
+set(RESOURCE_LIST
+ icons/scalable/status/1mines.svg
+ icons/scalable/status/2mines.svg
+ icons/scalable/status/3mines.svg
+ icons/scalable/status/4mines.svg
+ icons/scalable/status/5mines.svg
+ icons/scalable/status/6mines.svg
+ icons/scalable/status/7mines.svg
+ icons/scalable/status/8mines.svg
+ icons/scalable/status/exploded.svg
+ icons/scalable/status/flag.svg
+ icons/scalable/status/incorrect.svg
+ icons/scalable/status/maybe.svg
+ icons/scalable/status/mine.svg
+ STRIPBLANKS minesweeper.ui
+ STRIPBLANKS win_input.ui
+ STRIPBLANKS scoreswin.ui
+ STRIPBLANKS mine_menu.xml)
+
+compile_gresources(RESOURCE_FILE
+ XML_OUT
+ TYPE EMBED_C
+ RESOURCES ${RESOURCE_LIST}
+ PREFIX "/org/gtk/daleclack"
+ SOURCE_DIR ${PROJECT_SOURCE_DIR}/res)
+
+# Add a custom target to the makefile. Now make builds our resource file.
+# It depends on the output RESOURCE_FILE.
+
+add_custom_target(resource ALL DEPENDS ${RESOURCE_FILE})
+
+#For win32 platform,use rc resource and .ico icon
+if(WIN32)
+ SET(CMAKE_RC_COMPILER windres)
+ set(app_WINRC ../icon.rc)
+ set_property(SOURCE ../icon.rc APPEND PROPERTY
+ OBJECT_DEPENDS ${PROJECT_SOURCE_DIR}/../icon.ico
+ )
+ add_executable(${PROJECT_NAME} WIN32 ${app_WINRC} ${SOURCE_FILE} ${RESOURCE_FILE})
+ add_custom_command( TARGET ${PROJECT_NAME}
+ COMMAND echo * > ${CMAKE_BINARY_DIR}/.gitignore
+ COMMAND echo **/* > ${CMAKE_BINARY_DIR}/.hgignore)
+else()
+ add_executable(${PROJECT_NAME} ${SOURCE_FILE} ${RESOURCE_FILE})
+ add_custom_command( TARGET ${PROJECT_NAME}
+ COMMAND echo \"*\" > ${CMAKE_BINARY_DIR}/.gitignore
+ COMMAND echo \"**/*\" > ${CMAKE_BINARY_DIR}/.hgignore)
+endif(WIN32)
+
+#Add command to generate .gitignore and .mo files
+# add_custom_command( TARGET ${PROJECT_NAME}
+# COMMAND mkdir -p ${PO_DIR}
+# COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} ${CMAKE_SOURCE_DIR}/po/zh_CN.po -o ${PO_DIR}/${PROJECT_NAME}.mo)
+
+SET (CMAKE_EXTRA_CXX_FLAGS ${GTKMM4_CFLAGS_OTHER})
+target_link_libraries (${PROJECT_NAME} ${GTKMM4_LIBRARIES} -lpthread)
diff --git a/Gtk4/gtk159_minesweeper4/res/cambalache/.gitignore b/Gtk4/gtk159_minesweeper4/res/cambalache/.gitignore
new file mode 100644
index 0000000..25284c2
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/res/cambalache/.gitignore
@@ -0,0 +1 @@
+*.ui
\ No newline at end of file
diff --git a/Gtk4/gtk159_minesweeper4/res/cambalache/minesweeper.cmb b/Gtk4/gtk159_minesweeper4/res/cambalache/minesweeper.cmb
new file mode 100644
index 0000000..6e75fcc
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/res/cambalache/minesweeper.cmb
@@ -0,0 +1,58 @@
+
+
+
+
+ (1,None,"minesweeper.ui","minesweeper.ui",None,None,None,None,None,None,None),
+ (2,None,None,"win_input.ui",None,None,None,None,None,None,None),
+ (3,None,None,"scoreswin.ui",None,None,None,None,None,None,None)
+
+
+
+ (1,1,"GtkContainer","resize-mode","queue",None,None,None,None,None),
+ (1,1,"GtkWindow","default-height","400",None,None,None,None,None),
+ (1,1,"GtkWindow","default-width","700",None,None,None,None,None),
+ (1,3,"GtkStack","transition-type","slide-right",None,None,None,None,None),
+ (1,3,"GtkWidget","halign","baseline",None,None,None,None,None),
+ (1,4,"GtkGrid","column-homogeneous","True",None,None,None,None,None),
+ (1,4,"GtkGrid","column-spacing","2",None,None,None,None,None),
+ (1,4,"GtkGrid","row-homogeneous","True",None,None,None,None,None),
+ (1,4,"GtkWidget","halign","center",None,None,None,None,None),
+ (1,4,"GtkWidget","valign","center",None,None,None,None,None),
+ (1,5,"GtkOrientable","orientation","vertical",None,None,None,None,None),
+ (2,1,"GtkWidget","expand","True",None,None,None,None,None),
+ (2,1,"GtkWidget","height-request","200",None,None,None,None,None),
+ (2,1,"GtkWidget","margin-start","3",None,None,None,None,None),
+ (2,1,"GtkWidget","width-request","400",None,None,None,None,None),
+ (2,1,"GtkWindow","gravity","center",None,None,None,None,None),
+ (3,2,"GtkOrientable","orientation","vertical",None,None,None,None,None),
+ (3,3,"GtkWidget","height-request","3",None,None,None,None,None),
+ (3,4,"GtkWidget","height-request","300",None,None,None,None,None),
+ (3,4,"GtkWidget","valign","start",None,None,None,None,None),
+ (3,4,"GtkWidget","width-request","400",None,None,None,None,None),
+ (3,5,"GtkButton","label","Close",None,None,None,None,None),
+ (3,6,"GtkLabel","label"," ",None,None,None,None,None),
+ (3,6,"GtkWidget","hexpand","True",None,None,None,None,None)
+
+
+ (3,2,3,"GtkBoxLayoutChild","fill","False",None,None,None,None),
+ (3,2,3,"GtkBoxLayoutChild","position","2",None,None,None,None),
+ (3,2,4,"GtkBoxLayoutChild","position","1",None,None,None,None),
+ (3,3,5,"GtkBoxLayoutChild","position","2",None,None,None,None),
+ (3,3,6,"GtkBoxLayoutChild","fill","False",None,None,None,None),
+ (3,3,6,"GtkBoxLayoutChild","position","1",None,None,None,None)
+
+
diff --git a/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/1mines.svg b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/1mines.svg
new file mode 100644
index 0000000..6bba912
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/1mines.svg
@@ -0,0 +1,80 @@
+
+
+
+
diff --git a/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/2mines.svg b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/2mines.svg
new file mode 100644
index 0000000..f9ac25f
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/2mines.svg
@@ -0,0 +1,80 @@
+
+
+
+
diff --git a/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/3mines.svg b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/3mines.svg
new file mode 100644
index 0000000..57d249b
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/3mines.svg
@@ -0,0 +1,80 @@
+
+
+
+
diff --git a/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/4mines.svg b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/4mines.svg
new file mode 100644
index 0000000..12906ee
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/4mines.svg
@@ -0,0 +1,80 @@
+
+
+
+
diff --git a/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/5mines.svg b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/5mines.svg
new file mode 100644
index 0000000..7cb4c23
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/5mines.svg
@@ -0,0 +1,80 @@
+
+
+
+
diff --git a/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/6mines.svg b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/6mines.svg
new file mode 100644
index 0000000..960c218
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/6mines.svg
@@ -0,0 +1,80 @@
+
+
+
+
diff --git a/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/7mines.svg b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/7mines.svg
new file mode 100644
index 0000000..7e117a5
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/7mines.svg
@@ -0,0 +1,80 @@
+
+
+
+
diff --git a/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/8mines.svg b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/8mines.svg
new file mode 100644
index 0000000..a3894cc
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/8mines.svg
@@ -0,0 +1,80 @@
+
+
+
+
diff --git a/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/exploded.svg b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/exploded.svg
new file mode 100644
index 0000000..3211acf
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/exploded.svg
@@ -0,0 +1,125 @@
+
+
diff --git a/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/flag.svg b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/flag.svg
new file mode 100644
index 0000000..ceb16a3
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/flag.svg
@@ -0,0 +1,90 @@
+
+
diff --git a/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/incorrect.svg b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/incorrect.svg
new file mode 100644
index 0000000..ceb16a3
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/incorrect.svg
@@ -0,0 +1,90 @@
+
+
diff --git a/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/maybe.svg b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/maybe.svg
new file mode 100644
index 0000000..1edbd8b
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/maybe.svg
@@ -0,0 +1,101 @@
+
+
diff --git a/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/mine.svg b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/mine.svg
new file mode 100644
index 0000000..c3faf76
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/res/icons/scalable/status/mine.svg
@@ -0,0 +1,125 @@
+
+
diff --git a/Gtk4/gtk159_minesweeper4/res/mine_menu.xml b/Gtk4/gtk159_minesweeper4/res/mine_menu.xml
new file mode 100644
index 0000000..96984dc
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/res/mine_menu.xml
@@ -0,0 +1,27 @@
+
+
+
+
\ No newline at end of file
diff --git a/Gtk4/gtk159_minesweeper4/res/minesweeper.ui b/Gtk4/gtk159_minesweeper4/res/minesweeper.ui
new file mode 100644
index 0000000..42f72bd
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/res/minesweeper.ui
@@ -0,0 +1,41 @@
+
+
+
+
+
diff --git a/Gtk4/gtk159_minesweeper4/res/scoreswin.ui b/Gtk4/gtk159_minesweeper4/res/scoreswin.ui
new file mode 100644
index 0000000..d1a262c
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/res/scoreswin.ui
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+ 1
+ vertical
+
+
+ 1
+ 400
+ 300
+ start
+
+
+
+
+ center
+ 1
+ 3
+
+
+ center
+ 1
+ 1
+
+
+
+
+
+ 1
+ Close
+
+
+
+
+
+
+
+
diff --git a/Gtk4/gtk159_minesweeper4/res/win_input.ui b/Gtk4/gtk159_minesweeper4/res/win_input.ui
new file mode 100644
index 0000000..d1ac8ed
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/res/win_input.ui
@@ -0,0 +1,82 @@
+
+
+
+
+ 300
+ 150
+
+
+ 1
+ 10
+ 10
+ 10
+ 10
+ vertical
+ 2
+
+
+ center
+ center
+ 1
+ 1
+ vertical
+
+
+ center
+ center
+ 1
+ 1
+
+
+ center
+ center
+ Input Name:
+
+
+
+
+ 1
+ center
+ center
+
+
+
+
+
+
+ Show Scores Window
+ 1
+
+
+
+
+
+
+
+ btn_ok
+ btn_cancel
+
+
+
+ center
+ 1
+
+
+ 1
+ OK
+ 1
+ 1
+
+
+
+
+ 1
+ Cancel
+ 1
+ 1
+
+
+
+
+
+
diff --git a/Gtk4/gtk159_minesweeper4/src/InputBox.cc b/Gtk4/gtk159_minesweeper4/src/InputBox.cc
new file mode 100644
index 0000000..c46deeb
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/src/InputBox.cc
@@ -0,0 +1,99 @@
+#include "InputBox.hh"
+
+InputBox::InputBox(BaseObjectType *cobject, const Glib::RefPtr &ref_Glade)
+ : Gtk::Dialog(cobject),
+ ref_builder(ref_Glade)
+{
+ // Get Widgets
+ entry_name = ref_builder->get_widget("entry_name");
+ check_scores = ref_builder->get_widget("check_scores");
+ entry_name->signal_activate().connect(sigc::mem_fun(*this, &InputBox::entry_activated));
+}
+
+void InputBox::on_response(int response_id)
+{
+ // Open a file to save json data
+ std::fstream outfile;
+ outfile.open("scores.json", std::ios_base::out);
+ if (outfile.is_open())
+ {
+ // Insert data to json
+ std::string name = std::string((entry_name->get_text()).c_str());
+ names.push_back(name);
+ times.push_back(game_time);
+ data["name"] = names;
+ data["time"] = times;
+
+ // Output data
+ outfile << data;
+ }
+ outfile.close();
+ if (response_id == Gtk::ResponseType::OK)
+ {
+ read_scores(check_scores->get_active());
+ }
+ hide();
+}
+
+void InputBox::read_scores(bool show_scores_win)
+{
+ // If show scores checkbutton is checked, show scores window
+ if (show_scores_win)
+ {
+ scores_win1->update_and_show();
+ }
+}
+
+void InputBox::set_game_time(int time)
+{
+ // Try to open json file
+ std::fstream jsonfile;
+ jsonfile.open("scores.json", std::ios_base::in);
+
+ // If json file opened, read the data
+ if (jsonfile.is_open())
+ {
+ data = json::parse(jsonfile);
+ std::vector names1 = data["name"];
+ std::vector times1 = data["time"];
+ names = names1;
+ times = times1;
+ }
+ else
+ {
+ // Otherwist, create json data
+ data = json::parse(R"(
+ {
+ "name":[" "],
+ "time":[0]
+ }
+ )");
+ }
+ jsonfile.close();
+
+ // Initalize time
+ game_time = time;
+}
+
+void InputBox::entry_activated()
+{
+ // Default response
+ response(Gtk::ResponseType::OK);
+}
+
+void InputBox::set_scores_window(ScoresWin *win1)
+{
+ // Bind Scores Window
+ scores_win1 = win1;
+}
+
+InputBox *InputBox::create()
+{
+ // Create a inputbox object
+ auto builder = Gtk::Builder::create_from_resource("/org/gtk/daleclack/win_input.ui");
+
+ InputBox *dialog;
+ dialog = Gtk::Builder::get_widget_derived(builder, "dialog");
+
+ return dialog;
+}
diff --git a/Gtk4/gtk159_minesweeper4/src/InputBox.hh b/Gtk4/gtk159_minesweeper4/src/InputBox.hh
new file mode 100644
index 0000000..228ddc7
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/src/InputBox.hh
@@ -0,0 +1,38 @@
+#pragma once
+
+#include
+#include "jsonfile.hh"
+#include "ScoresWin.hh"
+
+class InputBox : public Gtk::Dialog{
+ public:
+ static InputBox *create();
+ InputBox(BaseObjectType *cobject, const Glib::RefPtr &ref_Glade);
+ void read_scores(bool show_scores_win = true);
+ void set_game_time(int time);
+ void set_scores_window(ScoresWin *win1);
+
+ protected:
+ void on_response(int response_id) override;
+
+ private:
+ int game_time;
+
+ // Data to write to json file
+ json data;
+ std::vector names;
+ std::vector times;
+
+ // Builder Object
+ Glib::RefPtr ref_builder;
+
+ // Child widget
+ Gtk::Entry *entry_name;
+ Gtk::CheckButton *check_scores;
+
+ // Scores Window
+ ScoresWin *scores_win1;
+
+ // Signal Handlers
+ void entry_activated();
+};
\ No newline at end of file
diff --git a/Gtk4/gtk159_minesweeper4/src/MineSweeper.cc b/Gtk4/gtk159_minesweeper4/src/MineSweeper.cc
new file mode 100644
index 0000000..321d121
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/src/MineSweeper.cc
@@ -0,0 +1,302 @@
+#include "MineSweeper.hh"
+#include
+#include
+
+MineSweeper::MineSweeper()
+ : main_box(Gtk::Orientation::VERTICAL, 5),
+ btn_box(Gtk::Orientation::HORIZONTAL, 5),
+ cell(nullptr)
+{
+ // Initalize Window
+ set_title("MineSweeper");
+ set_titlebar(header);
+ header.set_show_title_buttons();
+ header.set_decoration_layout("close,minimize,maximize:menu");
+ header.pack_end(menu_btn);
+ set_icon_name("org.gtk.daleclack");
+
+ // Initalize Menu
+ menu_builder = Gtk::Builder::create_from_resource("/org/gtk/daleclack/mine_menu.xml");
+ auto object = menu_builder->get_object("mine_menu");
+ popover1.set_menu_model(object);
+ // popover1.show();
+ menu_btn.set_popover(popover1);
+
+ // Add Actions
+ add_action("new_game", sigc::mem_fun(*this, &MineSweeper::new_game));
+ add_action("scores", sigc::mem_fun(*this, &MineSweeper::show_scores));
+ add_action("show_mines", sigc::mem_fun(*this, &MineSweeper::show_mines));
+ add_action("quit", sigc::mem_fun(*this, &MineSweeper::hide));
+
+ // Default setting
+ reset_game();
+
+ // Buttons
+ btnstart.set_label("Start/Reset");
+ btnpause.set_label("Pause");
+ btnshow.set_label("Show All");
+ btnexit.set_label("Exit");
+ btn_box.append(btnstart);
+ btn_box.append(btnpause);
+ btn_box.append(btnshow);
+ btn_box.append(btnexit);
+ btnstart.signal_clicked().connect(sigc::mem_fun(*this, &MineSweeper::new_game));
+ btnshow.signal_clicked().connect(sigc::mem_fun(*this, &MineSweeper::show_mines));
+ btnexit.signal_clicked().connect(sigc::mem_fun(*this, &MineSweeper::hide));
+
+ // Pack widgets
+ status_label.set_halign(Gtk::Align::CENTER);
+ btn_box.set_halign(Gtk::Align::CENTER);
+ mine_grid.set_halign(Gtk::Align::CENTER);
+ main_box.append(status_label);
+ main_box.append(mine_grid);
+ main_box.append(btn_box);
+
+ // Create a dialog
+ input_dialog = InputBox::create();
+
+ // Create Scores Window
+ scores_win = ScoresWin::create();
+
+ // Bind windows
+ input_dialog->set_transient_for(*this);
+ scores_win->set_transient_for(*this);
+ input_dialog->set_scores_window(scores_win);
+
+ // Show everything
+ set_child(main_box);
+ //show_all_children();
+}
+
+MineSweeper::~MineSweeper(){
+ // Delete all resources
+ delete input_dialog;
+ delete scores_win;
+ if(cell != nullptr){
+ delete[] cell;
+ }
+}
+
+void MineSweeper::new_game(){
+ // New game = reset game
+ reset_game();
+}
+
+void MineSweeper::reset_game(int width, int height, int mines)
+{
+ // Clear the cells
+ if(cell != nullptr){
+ delete[] cell;
+ }
+
+ cell = new MineCell[width * height];
+ // Reset timer
+ mytimer.disconnect();
+ timer_count = 0;
+ mytimer = Glib::signal_timeout().connect(sigc::mem_fun(*this, &MineSweeper::timer_func), 1000);
+
+ mine_count = 0;
+ mines_clear = 0;
+ mine_grid.set_sensitive();
+ // Reset all data
+ for (int i = 0; i < width; i++)
+ {
+ for (int j = 0; j < height; j++)
+ {
+ // Initalize cell
+ cell[i * 7 + j].set_has_frame();
+ cell[i * 7 + j].set_image_from_icon_name("", Gtk::IconSize::LARGE);
+ // cell[i * 7 + j].set_always_show_image();
+ cell[i * 7 + j].set_size_request(40, 40);
+ cell[i * 7 + j].mines_around = 0;
+ cell[i * 7 + j].has_mine = false;
+ cell[i * 7 + j].cleared = false;
+ }
+ }
+
+ // Reset mines
+ while (mine_count < mines)
+ {
+ int index = g_random_int_range(0, width * height);
+ if (!(cell[index].has_mine))
+ {
+ cell[index].has_mine = true;
+ // cell[index].set_label("x");
+ mine_count++;
+ }
+ }
+ // std::cout << mine_count << std::endl;
+ // game_ended = false;
+ // winned = true;
+ game_status = GameStatus::Running;
+ status_label.set_label(" ");
+ calc_mines();
+
+ // Append buttons to grid
+ for (int i = 0; i < height; i++)
+ {
+ for (int j = 0; j < width; j++)
+ {
+ // cell[i * 7 + j].set_label("?");
+ cell[i * width + j].signal_clicked().connect(sigc::bind(
+ sigc::mem_fun(*this, &MineSweeper::cell_clicked), &cell[i * width + j]));
+ mine_grid.attach(cell[i * width + j], j, i);
+ cell[i * width + j].set_has_frame();
+ cell[i * width + j].x = j;
+ cell[i * width + j].y = i;
+ cell[i * width + j].cleared = false;
+ }
+ }
+
+ mine_grid.show();
+}
+
+void MineSweeper::pause_game(){
+
+}
+
+void MineSweeper::calc_mines()
+{
+ // Calculate the mines around a cell
+ for (int i = 0; i < 7; i++)
+ {
+ for (int j = 0; j < 7; j++)
+ {
+ int index1, index2;
+ // The Search cell should not over the grids
+ for (index1 = MAX(0, i - 1); index1 < MIN(i + 1, 6) + 1; index1++)
+ {
+ for (index2 = MAX(0, j - 1); index2 < MIN(j + 1, 6) + 1; index2++)
+ {
+ if ((cell[index1 * 7 + index2].has_mine))
+ {
+ cell[i * 7 + j].mines_around++;
+ }
+ }
+ }
+ }
+ }
+}
+
+void MineSweeper::show_mines()
+{
+ // Show all cell with a mine
+ for (int i = 0; i < 49; i++)
+ {
+ if (cell[i].has_mine)
+ {
+ cell[i].set_image_from_icon_name("mine", Gtk::IconSize::LARGE);
+ }
+ }
+}
+
+void MineSweeper::show_scores(){
+ // Show Scores Window
+ input_dialog->read_scores();
+}
+
+void MineSweeper::game_lost(int explode_index){
+ // When a cell with mine is clicked, show other mines
+ for (int i = 0; i < 49; i++)
+ {
+ if (cell[i].has_mine && i != explode_index)
+ {
+ cell[i].set_image_from_icon_name("mine", Gtk::IconSize::LARGE);
+ }
+ }
+}
+
+bool MineSweeper::timer_func()
+{
+ // Set timer
+ char tmp[50];
+ timer_count++;
+ sprintf(tmp, "Time:%d", timer_count);
+ status_label.set_label(tmp);
+ return true;
+}
+
+void MineSweeper::cell_clicked(MineCell *cell1)
+{
+ cell1->set_has_frame(false);
+ if (game_status == GameStatus::Running && !cell1->cleared)
+ {
+ //
+ // If get mine, the game will end now
+ if (cell1->has_mine)
+ {
+ // Set game to stop
+ // winned = false;
+ game_status = GameStatus::Ended;
+ cell1->cleared = true;
+ cell1->set_image_from_icon_name("exploded", Gtk::IconSize::LARGE);
+
+ // End the game
+ game_lost(cell1->y * 7 + cell1->x);
+ status_label.set_label("You lost!");
+ // game_ended = true;
+ mytimer.disconnect();
+ mine_grid.set_sensitive(false);
+ }
+ else
+ {
+ // If no mines, check the cell around
+ check_mines(cell1->x, cell1->y);
+ }
+ }
+}
+
+void MineSweeper::check_mines(int pos_x, int pos_y)
+{
+ if (pos_x >= 0 && pos_x <= 6 &&
+ pos_y >= 0 && pos_y <= 6)
+ {
+ if (!cell[pos_y * 7 + pos_x].has_mine &&
+ !cell[pos_y * 7 + pos_x].cleared)
+ {
+ mines_clear++;
+ // Show the cell has no mines around
+ if (cell[pos_y * 7 + pos_x].mines_around == 0)
+ {
+ cell[pos_y * 7 + pos_x].set_image_from_icon_name("", Gtk::IconSize::LARGE);
+ }
+ else
+ {
+ // Show the numbers of mines around a cell
+ char *label = g_strdup_printf("%dmines", cell[pos_y * 7 + pos_x].mines_around);
+ cell[pos_y * 7 + pos_x].set_image_from_icon_name(label, Gtk::IconSize::LARGE);
+ g_free(label);
+ }
+
+ // make the cell without mines cleared
+ cell[pos_y * 7 + pos_x].set_has_frame(false);
+ cell[pos_y * 7 + pos_x].cleared = true;
+
+ // Check the cells around a cell that has no mines
+ if (cell[pos_y * 7 + pos_x].mines_around == 0)
+ {
+ check_mines((pos_x - 1), (pos_y - 1));
+ check_mines((pos_x + 1), (pos_y + 1));
+ check_mines((pos_x - 1), (pos_y + 1));
+ check_mines((pos_x + 1), (pos_y - 1));
+ check_mines(pos_x, (pos_y - 1));
+ check_mines(pos_x, (pos_y + 1));
+ check_mines((pos_x + 1), pos_y);
+ check_mines((pos_x - 1), pos_y);
+ }
+ }
+ }
+
+ // If all the mines has cleared, you has winned
+ if (mines_clear == 40)
+ {
+ // Stop the game
+ status_label.set_label("You winned!");
+ game_status = GameStatus::Winned;
+ mytimer.disconnect();
+
+ // Save the time of game
+ input_dialog->set_game_time(timer_count);
+ input_dialog->show();
+ }
+}
diff --git a/Gtk4/gtk159_minesweeper4/src/MineSweeper.hh b/Gtk4/gtk159_minesweeper4/src/MineSweeper.hh
new file mode 100644
index 0000000..5a25fa9
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/src/MineSweeper.hh
@@ -0,0 +1,81 @@
+#pragma once
+
+#include
+#include "InputBox.hh"
+#include "ScoresWin.hh"
+
+// The status of the minesweeper game
+enum class GameStatus
+{
+ Running,
+ Winned,
+ Ended,
+ Paused
+};
+
+// The cell of the minesweeper game,
+// and the mines is under the cell
+class MineCell : public Gtk::Button
+{
+public:
+ bool has_mine = false; // Whether the grid has mine
+ bool cleared = false; // Whether the mine is cleared
+ int mines_around; // The number near the grid
+ int x, y; // The Position of the grid
+ MineCell()
+ {
+ // Set button style
+ mines_around = 0;
+ }
+};
+
+// Main class
+class MineSweeper : public Gtk::ApplicationWindow
+{
+public:
+ MineSweeper();
+ ~MineSweeper();
+
+private:
+ // HeaderBar
+ Gtk::HeaderBar header;
+ Gtk::MenuButton menu_btn;
+ Gtk::PopoverMenu popover1;
+
+ // Child widgets
+ Gtk::Grid mine_grid;
+ Gtk::Label status_label;
+ Gtk::Box main_box, btn_box;
+ Gtk::Button btnstart, btnpause, btnshow, btnexit;
+
+ // The cell to place mines
+ MineCell *cell;
+ GameStatus game_status; // Use enum class for the status of game
+ // bool winned, game_ended; // The status of game(win/end)
+ int mines_clear, mine_count; // Whether the mine is cleared
+
+ // Menu
+ Glib::RefPtr menu_builder;
+
+ // Timer
+ int timer_count;
+ sigc::connection mytimer;
+
+ // Input dialog
+ InputBox *input_dialog;
+
+ // Scores Window
+ ScoresWin *scores_win;
+
+ // Signal Handlers
+ void new_game(); // "New Game" handler
+ void reset_game(int width = 7, int height = 7, int mines = 9); // Reset all mines
+ void pause_game(); // Pause or continue the game
+ void calc_mines(); // Get the mines around
+ void show_mines(); // Show all mines
+ void show_scores(); // Show all scores
+ void game_lost(int explode_index); // You lost the game
+ void cell_clicked(MineCell *cell1); // Open a cell
+ bool timer_func(); // Timer
+ void check_mines(int pos_x, int pos_y); // Check if there is a mine
+};
diff --git a/Gtk4/gtk159_minesweeper4/src/ScoresWin.cc b/Gtk4/gtk159_minesweeper4/src/ScoresWin.cc
new file mode 100644
index 0000000..242d520
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/src/ScoresWin.cc
@@ -0,0 +1,80 @@
+#include "ScoresWin.hh"
+
+ScoresWin::ScoresWin(BaseObjectType *cobject, const Glib::RefPtr &ref_Glade)
+ : Gtk::Window(cobject),
+ ref_builder(ref_Glade)
+{
+ // Get Widgets
+ btnclose = ref_builder->get_widget("btnclose");
+ tree_view = ref_builder->get_widget("scores_view");
+ btnclose->signal_clicked().connect(sigc::mem_fun(*this, &ScoresWin::hide));
+
+ // Create the list store
+ store = Gtk::ListStore::create(column1);
+ store->set_sort_column(column1.win_time, Gtk::SortType::ASCENDING);
+ store->set_sort_func(column1.win_time, sigc::mem_fun(*this, &ScoresWin::sort_func));
+ // store->set_default_sort_func(sigc::mem_fun(*this, &ScoresWin::sort_func));
+ tree_view->set_model(store);
+ tree_view->append_column("name", column1.player_name);
+ tree_view->append_column("time", column1.win_time);
+}
+
+void ScoresWin::update_and_show()
+{
+ std::fstream infile;
+ infile.open("scores.json", std::ios_base::in);
+
+ if (infile.is_open())
+ {
+ // Read data from json file
+ json data = json::parse(infile);
+ std::vector name_vec = data["name"];
+ std::vector time_vec = data["time"];
+
+ // Clear the store
+ store->clear();
+
+ // Append data to the store
+ for (int i = 0; i < name_vec.size(); i++)
+ {
+ auto row = *(store->append());
+ row[column1.player_name] = name_vec[i];
+ row[column1.win_time] = time_vec[i];
+ }
+ }
+
+ show();
+}
+
+int ScoresWin::sort_func(const Gtk::TreeModel::const_iterator &iter1, const Gtk::TreeModel::const_iterator &iter2)
+{
+ // Sort by the game time
+ auto row1 = *iter1;
+ auto row2 = *iter2;
+ if (row1[column1.win_time] < row2[column1.win_time])
+ {
+ // g_print("test1\n");
+ return -1;
+ }
+ if (row1[column1.win_time] == row2[column1.win_time])
+ {
+ // g_print("test2\n");
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+}
+
+ScoresWin *ScoresWin::create()
+{
+ // Create a window
+ auto builder = Gtk::Builder::create_from_resource("/org/gtk/daleclack/scoreswin.ui");
+ ScoresWin *main_win;
+
+ // builder->get_widget_derived("scores_win", main_win);
+ main_win = Gtk::Builder::get_widget_derived(builder, "scores_win");
+
+ return main_win;
+}
diff --git a/Gtk4/gtk159_minesweeper4/src/ScoresWin.hh b/Gtk4/gtk159_minesweeper4/src/ScoresWin.hh
new file mode 100644
index 0000000..21e1d0f
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/src/ScoresWin.hh
@@ -0,0 +1,35 @@
+#pragma once
+
+#include
+#include "jsonfile.hh"
+
+class ScoresWin : public Gtk::Window{
+ public:
+ static ScoresWin *create();
+ ScoresWin(BaseObjectType *cobject, const Glib::RefPtr &ref_Glade);
+ void update_and_show();
+
+ private:
+ Glib::RefPtr ref_builder;
+
+ // Child widgets
+ Gtk::Button *btnclose;
+ Gtk::TreeView *tree_view;
+
+ // TreeView data
+ class ModelColumns : public Gtk::TreeModelColumnRecord{
+ public:
+ ModelColumns(){
+ add(player_name);
+ add(win_time);
+ }
+ Gtk::TreeModelColumn player_name;
+ Gtk::TreeModelColumn win_time;
+ };
+ ModelColumns column1;
+
+ Glib::RefPtr store;
+
+ // Sort function
+ int sort_func(const Gtk::TreeModel::const_iterator &iter1, const Gtk::TreeModel::const_iterator &iter2);
+};
diff --git a/Gtk4/gtk159_minesweeper4/src/jsonfile.hh b/Gtk4/gtk159_minesweeper4/src/jsonfile.hh
new file mode 100644
index 0000000..6fbdabb
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/src/jsonfile.hh
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "../json_nlohmann/json.hpp"
+#include
+#include
+
+using json = nlohmann::json;
diff --git a/Gtk4/gtk159_minesweeper4/src/main.cc b/Gtk4/gtk159_minesweeper4/src/main.cc
new file mode 100644
index 0000000..6110cf2
--- /dev/null
+++ b/Gtk4/gtk159_minesweeper4/src/main.cc
@@ -0,0 +1,8 @@
+#include "MineSweeper.hh"
+
+int main(int argc, char **argv){
+ // Create a application and run
+ auto app = Gtk::Application::create("org.gtk.daleclack");
+
+ return app->make_window_and_run(argc,argv);
+}