diff --git a/src/api/iptux-core/Models.h b/src/api/iptux-core/Models.h index 3110c280..8da802ba 100644 --- a/src/api/iptux-core/Models.h +++ b/src/api/iptux-core/Models.h @@ -12,6 +12,7 @@ #ifndef IPTUX_MODELS_H #define IPTUX_MODELS_H +#include #include #include @@ -28,8 +29,8 @@ namespace iptux { * 消息来源类型. */ enum class MessageSourceType { - PAL, ///< 好友 - SELF, ///< 自身 + PAL, ///< 好友 + SELF, ///< 自身 MST_ERROR ///< 错误 }; @@ -56,6 +57,8 @@ typedef enum { GROUP_BELONG_TYPE_BROADCAST ///< 广播 } GroupBelongType; +enum class PalProtocol : uint8_t { AUTO, IPMSG, IPTUX }; + class PalKey { public: PalKey(in_addr ipv4, int port); @@ -137,11 +140,13 @@ class PalInfo { bool isOnline() const; bool isChanged() const; bool isInBlacklist() const; + PalProtocol protocol() const; PalInfo& setCompatible(bool value); PalInfo& setOnline(bool value); PalInfo& setChanged(bool value); PalInfo& setInBlacklist(bool value); + PalInfo& setProtocol(PalProtocol value); private: in_addr ipv4_; ///< 好友IP @@ -157,6 +162,7 @@ class PalInfo { uint8_t online : 1; uint8_t changed : 1; uint8_t in_blacklist : 1; + uint8_t protocol_; }; /// pointer to PalInfo diff --git a/src/iptux-core/Models.cpp b/src/iptux-core/Models.cpp index b04e5716..aa90eae4 100644 --- a/src/iptux-core/Models.cpp +++ b/src/iptux-core/Models.cpp @@ -33,6 +33,7 @@ PalInfo::PalInfo(in_addr ipv4, uint16_t port) online = 0; changed = 0; in_blacklist = 0; + protocol_ = static_cast(PalProtocol::AUTO); } PalInfo::PalInfo(const string& ipv4, uint16_t port) @@ -43,6 +44,7 @@ PalInfo::PalInfo(const string& ipv4, uint16_t port) online = 0; changed = 0; in_blacklist = 0; + protocol_ = static_cast(PalProtocol::AUTO); } PalInfo::~PalInfo() { @@ -52,7 +54,16 @@ PalInfo::~PalInfo() { } bool PalInfo::isCompatible() const { - return compatible; + switch (protocol()) { + case PalProtocol::AUTO: + return compatible; + case PalProtocol::IPMSG: + return false; + case PalProtocol::IPTUX: + return true; + } + g_assert_not_reached(); + return false; } bool PalInfo::isOnline() const { @@ -63,6 +74,10 @@ bool PalInfo::isChanged() const { return changed; } +PalProtocol PalInfo::protocol() const { + return static_cast(protocol_); +} + PalInfo& PalInfo::setCompatible(bool value) { this->compatible = value; return *this; @@ -78,6 +93,11 @@ PalInfo& PalInfo::setChanged(bool value) { return *this; } +PalInfo& PalInfo::setProtocol(PalProtocol value) { + protocol_ = static_cast(value); + return *this; +} + PalInfo& PalInfo::setName(const std::string& name) { this->name = utf8MakeValid(name); return *this; diff --git a/src/iptux-core/ModelsTest.cpp b/src/iptux-core/ModelsTest.cpp index 31950c14..88791566 100644 --- a/src/iptux-core/ModelsTest.cpp +++ b/src/iptux-core/ModelsTest.cpp @@ -12,6 +12,24 @@ TEST(PalInfo, GetKey) { ASSERT_EQ(info.GetKey().ToString(), "127.0.0.1:2425"); } +TEST(PalInfo, ProtocolOverride) { + PalInfo info("127.0.0.1", 2425); + + info.setCompatible(true); + EXPECT_EQ(info.protocol(), PalProtocol::AUTO); + EXPECT_TRUE(info.isCompatible()); + + info.setProtocol(PalProtocol::IPMSG); + EXPECT_FALSE(info.isCompatible()); + + info.setProtocol(PalProtocol::IPTUX); + info.setCompatible(false); + EXPECT_TRUE(info.isCompatible()); + + info.setProtocol(PalProtocol::AUTO); + EXPECT_FALSE(info.isCompatible()); +} + TEST(PalKey, CopyConstructor) { PalKey key1(inAddrFromString("1.2.3.4"), 1234); PalKey key2 = key1; diff --git a/src/iptux/RevisePal.cpp b/src/iptux/RevisePal.cpp index 97df7974..a523f3fa 100644 --- a/src/iptux/RevisePal.cpp +++ b/src/iptux/RevisePal.cpp @@ -23,6 +23,44 @@ namespace iptux { +namespace { + +void AttachLabel(GtkGrid* grid, GtkWidget* label, gint row) { + gtk_label_set_xalign(GTK_LABEL(label), 1.0); + gtk_widget_set_halign(label, GTK_ALIGN_END); + gtk_grid_attach(grid, label, 0, row, 1, 1); +} + +void AppendProtocolOption(GtkComboBoxText* combo, const char* option) { + gtk_combo_box_text_append_text(combo, option); +} + +gint ProtocolToActive(PalProtocol protocol) { + switch (protocol) { + case PalProtocol::AUTO: + return 0; + case PalProtocol::IPMSG: + return 1; + case PalProtocol::IPTUX: + return 2; + } + g_assert_not_reached(); + return 0; +} + +PalProtocol ActiveToProtocol(gint active) { + switch (active) { + case 1: + return PalProtocol::IPMSG; + case 2: + return PalProtocol::IPTUX; + default: + return PalProtocol::AUTO; + } +} + +} // namespace + /** * 类构造函数. * @param pl @@ -117,70 +155,71 @@ GtkWidget* RevisePal::CreateMainDialog() { * @return 主窗体 */ GtkWidget* RevisePal::CreateAllArea() { - GtkWidget *box, *hbox; + GtkWidget* grid; GtkWidget *label, *button, *widget; GtkTreeModel* model; - box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + grid = gtk_grid_new(); + gtk_grid_set_row_spacing(GTK_GRID(grid), 6); + gtk_grid_set_column_spacing(GTK_GRID(grid), 6); /* 好友昵称 */ - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); label = gtk_label_new(_("Pal's nickname:")); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + AttachLabel(GTK_GRID(grid), label, 0); widget = gtk_entry_new(); g_object_set(widget, "has-tooltip", TRUE, NULL); - gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); + gtk_widget_set_hexpand(widget, TRUE); + gtk_grid_attach(GTK_GRID(grid), widget, 1, 0, 2, 1); g_signal_connect(widget, "query-tooltip", G_CALLBACK(entry_query_tooltip), _("Please input pal's new nickname!")); g_datalist_set_data(&widset, "nickname-entry-widget", widget); /* 好友群组 */ - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); label = gtk_label_new(_("Pal's group name:")); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + AttachLabel(GTK_GRID(grid), label, 1); widget = gtk_entry_new(); g_object_set(widget, "has-tooltip", TRUE, NULL); - gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); + gtk_widget_set_hexpand(widget, TRUE); + gtk_grid_attach(GTK_GRID(grid), widget, 1, 1, 2, 1); g_signal_connect(widget, "query-tooltip", G_CALLBACK(entry_query_tooltip), _("Please input pal's new group name!")); g_datalist_set_data(&widset, "group-entry-widget", widget); /* 好友系统编码 */ - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); label = gtk_label_new(_("System coding:")); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + AttachLabel(GTK_GRID(grid), label, 2); widget = gtk_entry_new(); g_object_set(widget, "has-tooltip", TRUE, NULL); - gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); + gtk_widget_set_hexpand(widget, TRUE); + gtk_grid_attach(GTK_GRID(grid), widget, 1, 2, 2, 1); g_signal_connect(widget, "query-tooltip", G_CALLBACK(entry_query_tooltip), _("Be SURE to know what you are doing!")); g_datalist_set_data(&widset, "encode-entry-widget", widget); /* 好友头像 */ - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); label = gtk_label_new(_("Pal's face picture:")); - gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + AttachLabel(GTK_GRID(grid), label, 3); model = GTK_TREE_MODEL(g_datalist_get_data(&mdlset, "icon-model")); widget = CreateIconTree(model); - gtk_box_pack_start(GTK_BOX(hbox), widget, TRUE, TRUE, 0); + gtk_widget_set_hexpand(widget, TRUE); + gtk_grid_attach(GTK_GRID(grid), widget, 1, 3, 1, 1); g_datalist_set_data(&widset, "icon-combo-widget", widget); button = gtk_button_new_with_label("..."); - gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0); + g_object_set_data(G_OBJECT(button), "icon-combo-widget", widget); + gtk_grid_attach(GTK_GRID(grid), button, 2, 3, 1, 1); g_signal_connect(button, "clicked", G_CALLBACK(AddNewIcon), &widset); - /* 协议兼容性 */ - hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 0); - widget = gtk_check_button_new_with_label( - _("Be compatible with iptux's protocol (DANGEROUS)")); - gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, FALSE, 0); - g_datalist_set_data(&widset, "compatible-check-widget", widget); - - return box; + /* 协议 */ + label = gtk_label_new(_("Protocol:")); + AttachLabel(GTK_GRID(grid), label, 4); + widget = gtk_combo_box_text_new(); + AppendProtocolOption(GTK_COMBO_BOX_TEXT(widget), "AUTO"); + AppendProtocolOption(GTK_COMBO_BOX_TEXT(widget), "IPMSG"); + AppendProtocolOption(GTK_COMBO_BOX_TEXT(widget), "IPTUX"); + gtk_grid_attach(GTK_GRID(grid), widget, 1, 4, 2, 1); + g_datalist_set_data(&widset, "protocol-combo-widget", widget); + + return grid; } /** @@ -206,10 +245,13 @@ void RevisePal::SetAllValue() { if (!pal->icon_file().empty()) { active = IconfileGetItemPos(model, pal->icon_file().c_str()); gtk_combo_box_set_active(GTK_COMBO_BOX(widget), active); + } else if (gtk_tree_model_iter_n_children(model, NULL) > 0) { + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), 0); } - /* 预置兼容性 */ - widget = GTK_WIDGET(g_datalist_get_data(&widset, "compatible-check-widget")); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), pal->isCompatible()); + /* 预置协议 */ + widget = GTK_WIDGET(g_datalist_get_data(&widset, "protocol-combo-widget")); + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), + ProtocolToActive(pal->protocol())); } /** @@ -251,29 +293,33 @@ void RevisePal::ApplyReviseData() { widget = GTK_WIDGET(g_datalist_get_data(&widset, "icon-combo-widget")); model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget)); active = gtk_combo_box_get_active(GTK_COMBO_BOX(widget)); - snprintf(path, MAX_PATHLEN, "%d", active); - gtk_tree_model_get_iter_from_string(model, &iter, path); - gtk_tree_model_get(model, &iter, 1, &file, -1); - if (pal->icon_file() != file) { - snprintf(path, MAX_PATHLEN, __PIXMAPS_PATH "/icon/%s", file); - if (access(path, F_OK) != 0) { - g_free(file); - snprintf(path, MAX_PATHLEN, "%s" ICON_PATH "/%" PRIx32 ".png", - g_get_user_cache_dir(), ntohl(pal->ipv4().s_addr)); - pal->set_icon_file( - stringFormat("%" PRIx32, (uint32_t)ntohl(pal->ipv4().s_addr))); - gtk_tree_model_get(model, &iter, 0, &pixbuf, -1); - gdk_pixbuf_save(pixbuf, path, "png", NULL, NULL); - g_object_unref(pixbuf); + if (active >= 0) { + snprintf(path, MAX_PATHLEN, "%d", active); + gtk_tree_model_get_iter_from_string(model, &iter, path); + gtk_tree_model_get(model, &iter, 1, &file, -1); + if (pal->icon_file() != file) { + snprintf(path, MAX_PATHLEN, __PIXMAPS_PATH "/icon/%s", file); + if (access(path, F_OK) != 0) { + g_free(file); + snprintf(path, MAX_PATHLEN, "%s" ICON_PATH "/%" PRIx32 ".png", + g_get_user_cache_dir(), ntohl(pal->ipv4().s_addr)); + pal->set_icon_file( + stringFormat("%" PRIx32, (uint32_t)ntohl(pal->ipv4().s_addr))); + gtk_tree_model_get(model, &iter, 0, &pixbuf, -1); + gdk_pixbuf_save(pixbuf, path, "png", NULL, NULL); + g_object_unref(pixbuf); + } else { + pal->set_icon_file(file); + } } else { - pal->set_icon_file(file); + g_free(file); } - } else - g_free(file); + } - /* 获取兼容性 */ - widget = GTK_WIDGET(g_datalist_get_data(&widset, "compatible-check-widget")); - pal->setCompatible(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget))); + /* 获取协议 */ + widget = GTK_WIDGET(g_datalist_get_data(&widset, "protocol-combo-widget")); + pal->setProtocol( + ActiveToProtocol(gtk_combo_box_get_active(GTK_COMBO_BOX(widget)))); /* 设置好友信息已被手工修改 */ pal->setChanged(true); @@ -415,6 +461,8 @@ void RevisePal::AddNewIcon(GtkWidget* button, GData** widset) { return; combo = GTK_WIDGET(g_object_get_data(G_OBJECT(button), "icon-combo-widget")); + if (!GTK_IS_COMBO_BOX(combo)) + return; model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo)); active = IconfileGetItemPos(model, filename); gtk_combo_box_set_active(GTK_COMBO_BOX(combo), active); diff --git a/src/iptux/RevisePal.h b/src/iptux/RevisePal.h index 2433ae7d..e119b604 100644 --- a/src/iptux/RevisePal.h +++ b/src/iptux/RevisePal.h @@ -19,6 +19,8 @@ namespace iptux { +class RevisePalTestPeer; + class RevisePal { public: RevisePal(Application* app, GtkWindow* parent, PalInfo* pl); @@ -53,8 +55,9 @@ class RevisePal { private: static gint IconfileGetItemPos(GtkTreeModel* model, const char* pathname); - //回调处理部分 + // 回调处理部分 private: + friend class RevisePalTestPeer; static void AddNewIcon(GtkWidget* button, GData** widset); }; diff --git a/src/iptux/RevisePalTest.cpp b/src/iptux/RevisePalTest.cpp index 87ed00e4..ee2b4e69 100644 --- a/src/iptux/RevisePalTest.cpp +++ b/src/iptux/RevisePalTest.cpp @@ -8,6 +8,34 @@ using namespace std; using namespace iptux; +namespace iptux { + +class RevisePalTestPeer { + public: + static GtkWidget* CreateAllArea(RevisePal& pal) { + return pal.CreateAllArea(); + } + static void SetAllValue(RevisePal& pal) { pal.SetAllValue(); } + static GtkWidget* GetWidget(RevisePal& pal, const char* key) { + return GTK_WIDGET(g_datalist_get_data(&pal.widset, key)); + } + static GtkWidget* FindButtonByLabel(GtkWidget* container, const char* label) { + GList* children = gtk_container_get_children(GTK_CONTAINER(container)); + for (GList* child = children; child; child = child->next) { + GtkWidget* widget = GTK_WIDGET(child->data); + if (GTK_IS_BUTTON(widget) && + g_strcmp0(gtk_button_get_label(GTK_BUTTON(widget)), label) == 0) { + g_list_free(children); + return widget; + } + } + g_list_free(children); + return nullptr; + } +}; + +} // namespace iptux + TEST(RevisePal, Constructor) { Application* app = CreateApplication(); @@ -25,3 +53,60 @@ TEST(RevisePal, ReviseEntryDo) { RevisePal::ReviseEntryDo(app, nullptr, &palInfo, false); DestroyApplication(app); } + +TEST(RevisePal, CreateAllAreaUsesGridAndProtocolCombo) { + Application* app = CreateApplication(); + + PalInfo palInfo("127.0.0.1", 2425); + RevisePal pal(app, nullptr, &palInfo); + + auto area = RevisePalTestPeer::CreateAllArea(pal); + EXPECT_TRUE(GTK_IS_GRID(area)); + + auto widget = RevisePalTestPeer::GetWidget(pal, "protocol-combo-widget"); + ASSERT_TRUE(GTK_IS_COMBO_BOX(widget)); + auto model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget)); + EXPECT_EQ(gtk_tree_model_iter_n_children(model, nullptr), 3); + + gtk_widget_destroy(area); + DestroyApplication(app); +} + +TEST(RevisePal, IconButtonKeepsIconComboBinding) { + Application* app = CreateApplication(); + + PalInfo palInfo("127.0.0.1", 2425); + RevisePal pal(app, nullptr, &palInfo); + + auto area = RevisePalTestPeer::CreateAllArea(pal); + auto iconCombo = RevisePalTestPeer::GetWidget(pal, "icon-combo-widget"); + auto button = RevisePalTestPeer::FindButtonByLabel(area, "..."); + + ASSERT_TRUE(GTK_IS_BUTTON(button)); + EXPECT_EQ(g_object_get_data(G_OBJECT(button), "icon-combo-widget"), + iconCombo); + + gtk_widget_destroy(area); + DestroyApplication(app); +} + +TEST(RevisePal, ProtocolSelectionReflectsPalProtocol) { + Application* app = CreateApplication(); + + PalInfo palInfo("127.0.0.1", 2425); + palInfo.setProtocol(PalProtocol::IPTUX); + RevisePal pal(app, nullptr, &palInfo); + + auto area = RevisePalTestPeer::CreateAllArea(pal); + RevisePalTestPeer::SetAllValue(pal); + + auto widget = RevisePalTestPeer::GetWidget(pal, "protocol-combo-widget"); + ASSERT_EQ(gtk_combo_box_get_active(GTK_COMBO_BOX(widget)), 2); + + palInfo.setProtocol(PalProtocol::AUTO); + RevisePalTestPeer::SetAllValue(pal); + EXPECT_EQ(gtk_combo_box_get_active(GTK_COMBO_BOX(widget)), 0); + + gtk_widget_destroy(area); + DestroyApplication(app); +}