From 10fd1487a993ba1090e0bc0a770655a539681e7d Mon Sep 17 00:00:00 2001 From: LI Daobing Date: Thu, 29 Jan 2026 21:27:40 -0800 Subject: [PATCH 1/7] 1 --- .../hicolor/16x16/apps/iptux-icon-reverse.png | Bin 0 -> 1000 bytes .../hicolor/22x22/apps/iptux-icon-reverse.png | Bin 0 -> 1644 bytes .../hicolor/24x24/apps/iptux-icon-reverse.png | Bin 0 -> 1875 bytes .../hicolor/32x32/apps/iptux-icon-reverse.png | Bin 0 -> 2912 bytes .../hicolor/48x48/apps/iptux-icon-reverse.png | Bin 0 -> 5491 bytes .../hicolor/64x64/apps/iptux-icon-reverse.png | Bin 0 -> 7902 bytes .../iptux/pixmaps/icon/iptux-icon-reverse.png | Bin 0 -> 7902 bytes src/iptux/AppIndicator.cpp | 57 +++++++++++++++ src/iptux/AppIndicator.h | 1 + src/iptux/AppIndicatorDummy.cpp | 4 ++ src/iptux/AppIndicatorMac.mm | 65 ++++++++++++++++++ src/iptux/Application.cpp | 18 +++++ 12 files changed, 145 insertions(+) create mode 100644 share/icons/hicolor/16x16/apps/iptux-icon-reverse.png create mode 100644 share/icons/hicolor/22x22/apps/iptux-icon-reverse.png create mode 100644 share/icons/hicolor/24x24/apps/iptux-icon-reverse.png create mode 100644 share/icons/hicolor/32x32/apps/iptux-icon-reverse.png create mode 100644 share/icons/hicolor/48x48/apps/iptux-icon-reverse.png create mode 100644 share/icons/hicolor/64x64/apps/iptux-icon-reverse.png create mode 100644 share/iptux/pixmaps/icon/iptux-icon-reverse.png diff --git a/share/icons/hicolor/16x16/apps/iptux-icon-reverse.png b/share/icons/hicolor/16x16/apps/iptux-icon-reverse.png new file mode 100644 index 0000000000000000000000000000000000000000..00d1ea7d05b2985a88af89999d179e31514d72a4 GIT binary patch literal 1000 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 N002ovPDHLkV1l59xWNDb literal 0 HcmV?d00001 diff --git a/share/icons/hicolor/32x32/apps/iptux-icon-reverse.png b/share/icons/hicolor/32x32/apps/iptux-icon-reverse.png new file mode 100644 index 0000000000000000000000000000000000000000..ac52f41ce48417321635ef0c94bb192f37b252a0 GIT binary patch literal 2912 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�F(5`^&?r&Gf!|{_W(*a^A)+B5;1ds>H^gfU z7RGh8_|&kkbJH=cfb`?jzA`JX1QShf^^&6_u00$}2Nz|opbfB#R+ zXqpnQHe=t6o%_}2k~`n>W%_%uENdd5ZSe+wC18od!eA-EQefl#Mk&D(!4k0WJZFhl z5S4gaCwB4gN{X8-N32Y257XU7j8d{OeboLRnn`6b3r1TjEE zT^;-0_*d4hS(AGAVB2?&5dF8r>RZMuT4qwJ%y??SOd9Nq!R~GH6zArmp~d&oug%1- zL5gF&7>(`oV>h-8=%p)v^g=H7w?BAy_jmnj_&Dw<7A;)(8;zgjfkTH5v3T)fZ&5^U zT7Ub(4Y%EL-{P|9E%`mK&rBUZV!4?y&kIU!`Y>HkPe==b*px=R2!s9QpFxrh(f&b04of^@B)ty^?M^LvXSYk)tx|G)8>uRqEsTf)Ed$eu8K$#>f3%#D56DuU`en#b;Y| zLvZ_^50-WxKCo_PBxqIorNW0PQZvE6 p>@voTR@OH&)Z6(xvwgYi{{fMkrC!PGN_YSO002ovPDHLkV1lVn2l4;_ literal 0 HcmV?d00001 diff --git a/share/icons/hicolor/64x64/apps/iptux-icon-reverse.png b/share/icons/hicolor/64x64/apps/iptux-icon-reverse.png new file mode 100644 index 0000000000000000000000000000000000000000..985df507c485c8786aab7e97c83993c934d72cca GIT binary patch literal 7902 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.emit(); } }; +static gboolean blinkTimerCallback(gpointer data) { + auto priv = static_cast(data); + priv->blinkState = !priv->blinkState; + if (priv->blinkState) { + app_indicator_set_icon_full(priv->indicator, "iptux-icon-reverse", + "iptux-icon-reverse"); + } else { + app_indicator_set_icon_full(priv->indicator, "iptux-icon", "iptux-icon"); + } + return G_SOURCE_CONTINUE; +} + +static void startBlinkTimer(IptuxAppIndicatorPrivate* priv) { + if (priv->blinkTimerId) return; + priv->blinkState = false; + priv->blinkTimerId = g_timeout_add(500, blinkTimerCallback, priv); +} + +static void stopBlinkTimer(IptuxAppIndicatorPrivate* priv) { + if (priv->blinkTimerId) { + g_source_remove(priv->blinkTimerId); + priv->blinkTimerId = 0; + } + priv->blinkState = false; +} + IptuxAppIndicator::IptuxAppIndicator(GActionGroup* action_group) { this->priv = std::make_shared(this); @@ -61,6 +92,18 @@ IptuxAppIndicator::IptuxAppIndicator(GActionGroup* action_group) { void IptuxAppIndicator::SetUnreadCount(int i) { priv->unreadCount = i; if (priv->mode == STATUS_ICON_MODE_NONE) return; + + if (priv->mode == STATUS_ICON_MODE_BLINKING) { + if (i > 0) { + startBlinkTimer(priv.get()); + } else { + stopBlinkTimer(priv.get()); + app_indicator_set_icon_full(priv->indicator, "iptux-icon", "iptux-icon"); + app_indicator_set_status(priv->indicator, APP_INDICATOR_STATUS_ACTIVE); + } + return; + } + if (i > 0) { app_indicator_set_status(priv->indicator, APP_INDICATOR_STATUS_ATTENTION); } else { @@ -69,7 +112,14 @@ void IptuxAppIndicator::SetUnreadCount(int i) { } void IptuxAppIndicator::SetMode(StatusIconMode mode) { + StatusIconMode oldMode = priv->mode; priv->mode = mode; + + if (oldMode == STATUS_ICON_MODE_BLINKING) { + stopBlinkTimer(priv.get()); + app_indicator_set_icon_full(priv->indicator, "iptux-icon", "iptux-icon"); + } + if (mode == STATUS_ICON_MODE_NONE) { app_indicator_set_status(priv->indicator, APP_INDICATOR_STATUS_PASSIVE); } else { @@ -77,4 +127,11 @@ void IptuxAppIndicator::SetMode(StatusIconMode mode) { } } +void IptuxAppIndicator::StopBlinking() { + stopBlinkTimer(priv.get()); + app_indicator_set_icon_full(priv->indicator, "iptux-icon", "iptux-icon"); + if (priv->mode == STATUS_ICON_MODE_NONE) return; + app_indicator_set_status(priv->indicator, APP_INDICATOR_STATUS_ACTIVE); +} + } // namespace iptux diff --git a/src/iptux/AppIndicator.h b/src/iptux/AppIndicator.h index 56c5a4c96..f136a11b6 100644 --- a/src/iptux/AppIndicator.h +++ b/src/iptux/AppIndicator.h @@ -14,6 +14,7 @@ class IptuxAppIndicator { IptuxAppIndicator(GActionGroup* action_group); void SetUnreadCount(int count); void SetMode(StatusIconMode mode); + void StopBlinking(); sigc::signal sigActivateMainWindow; diff --git a/src/iptux/AppIndicatorDummy.cpp b/src/iptux/AppIndicatorDummy.cpp index 0d421f95a..fac4d442f 100644 --- a/src/iptux/AppIndicatorDummy.cpp +++ b/src/iptux/AppIndicatorDummy.cpp @@ -12,4 +12,8 @@ void IptuxAppIndicator::SetUnreadCount(int) { void IptuxAppIndicator::SetMode(StatusIconMode) { // Dummy implementation } + +void IptuxAppIndicator::StopBlinking() { + // Dummy implementation +} } // namespace iptux diff --git a/src/iptux/AppIndicatorMac.mm b/src/iptux/AppIndicatorMac.mm index cfa286df2..d4d4d9fd3 100644 --- a/src/iptux/AppIndicatorMac.mm +++ b/src/iptux/AppIndicatorMac.mm @@ -111,6 +111,9 @@ - (void)statusItemClicked:(id)sender { public: IptuxAppIndicatorPrivate() {} ~IptuxAppIndicatorPrivate() { + if (blinkTimerId) { + g_source_remove(blinkTimerId); + } if (statusItem) { [[NSStatusBar systemStatusBar] removeStatusItem:statusItem]; [statusItem release]; @@ -124,14 +127,20 @@ - (void)statusItemClicked:(id)sender { if (attentionIcon) { [attentionIcon release]; } + if (reverseIcon) { + [reverseIcon release]; + } } NSStatusItem* statusItem = nil; IptuxStatusItemHelper* helper = nil; NSImage* normalIcon = nil; NSImage* attentionIcon = nil; + NSImage* reverseIcon = nil; StatusIconMode mode = STATUS_ICON_MODE_NORMAL; int unreadCount = 0; + guint blinkTimerId = 0; + bool blinkState = false; }; IptuxAppIndicator::IptuxAppIndicator(GActionGroup* action_group) { @@ -143,6 +152,7 @@ - (void)statusItemClicked:(id)sender { // Load icons priv->normalIcon = loadIcon("iptux-icon", 64); priv->attentionIcon = loadIcon("iptux-attention", 64); + priv->reverseIcon = loadIcon("iptux-icon-reverse", 64); // Create status item priv->statusItem = @@ -186,10 +196,48 @@ - (void)statusItemClicked:(id)sender { [menu release]; } +static gboolean blinkTimerCallback(gpointer data) { + auto priv = static_cast(data); + if (!priv->statusItem) return G_SOURCE_REMOVE; + priv->blinkState = !priv->blinkState; + if (priv->blinkState && priv->reverseIcon) { + priv->statusItem.button.image = priv->reverseIcon; + } else if (priv->normalIcon) { + priv->statusItem.button.image = priv->normalIcon; + } + return G_SOURCE_CONTINUE; +} + +static void startBlinkTimer(IptuxAppIndicatorPrivate* priv) { + if (priv->blinkTimerId) return; + priv->blinkState = false; + priv->blinkTimerId = g_timeout_add(500, blinkTimerCallback, priv); +} + +static void stopBlinkTimer(IptuxAppIndicatorPrivate* priv) { + if (priv->blinkTimerId) { + g_source_remove(priv->blinkTimerId); + priv->blinkTimerId = 0; + } + priv->blinkState = false; +} + void IptuxAppIndicator::SetUnreadCount(int count) { priv->unreadCount = count; if (!priv->statusItem || priv->mode == STATUS_ICON_MODE_NONE) return; + if (priv->mode == STATUS_ICON_MODE_BLINKING) { + if (count > 0) { + startBlinkTimer(priv.get()); + } else { + stopBlinkTimer(priv.get()); + if (priv->normalIcon) { + priv->statusItem.button.image = priv->normalIcon; + } + } + return; + } + if (count > 0 && priv->attentionIcon) { priv->statusItem.button.image = priv->attentionIcon; } else if (priv->normalIcon) { @@ -198,12 +246,29 @@ - (void)statusItemClicked:(id)sender { } void IptuxAppIndicator::SetMode(StatusIconMode mode) { + StatusIconMode oldMode = priv->mode; priv->mode = mode; if (!priv->statusItem) return; priv->statusItem.visible = (mode != STATUS_ICON_MODE_NONE); + + if (oldMode == STATUS_ICON_MODE_BLINKING) { + stopBlinkTimer(priv.get()); + if (priv->normalIcon) { + priv->statusItem.button.image = priv->normalIcon; + } + } + if (mode != STATUS_ICON_MODE_NONE) { SetUnreadCount(priv->unreadCount); } } +void IptuxAppIndicator::StopBlinking() { + stopBlinkTimer(priv.get()); + if (!priv->statusItem || priv->mode == STATUS_ICON_MODE_NONE) return; + if (priv->normalIcon) { + priv->statusItem.button.image = priv->normalIcon; + } +} + } // namespace iptux diff --git a/src/iptux/Application.cpp b/src/iptux/Application.cpp index cc9b2062a..a0ce82b0f 100644 --- a/src/iptux/Application.cpp +++ b/src/iptux/Application.cpp @@ -135,6 +135,24 @@ void Application::onStartup(Application& self) { }); self.cthrd->sigUnreadMsgCountUpdated.connect( sigc::mem_fun(*self.app_indicator, &IptuxAppIndicator::SetUnreadCount)); + + g_signal_connect(self.app, "window-added", + G_CALLBACK(+[](GtkApplication*, GtkWindow* window, + gpointer user_data) { + g_signal_connect( + window, "notify::is-active", + G_CALLBACK(+[](GtkWindow* win, GParamSpec*, + gpointer ud) { + if (gtk_window_is_active(win)) { + auto a = static_cast(ud); + if (a->app_indicator) { + a->app_indicator->StopBlinking(); + } + } + }), + user_data); + }), + &self); } bool use_app_menu = true; From dccfd5b1a3b66a88d8bd00a242e4816563a9f6da Mon Sep 17 00:00:00 2001 From: LI Daobing Date: Thu, 29 Jan 2026 21:53:00 -0800 Subject: [PATCH 2/7] 1 --- src/iptux/AppIndicatorMac.mm | 31 ++++++++++++++++++++++++++++--- src/iptux/Application.cpp | 20 +++----------------- src/iptux/DialogBase.cpp | 6 ++++-- src/iptux/DialogBase.h | 2 +- src/iptux/UiCoreThread.cpp | 4 +++- src/iptux/UiModels.cpp | 3 +++ 6 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/iptux/AppIndicatorMac.mm b/src/iptux/AppIndicatorMac.mm index d4d4d9fd3..109657989 100644 --- a/src/iptux/AppIndicatorMac.mm +++ b/src/iptux/AppIndicatorMac.mm @@ -6,6 +6,8 @@ #include #include +#include "iptux-utils/output.h" + #import // Objective-C helper class for NSStatusItem callbacks. @@ -198,33 +200,54 @@ - (void)statusItemClicked:(id)sender { static gboolean blinkTimerCallback(gpointer data) { auto priv = static_cast(data); - if (!priv->statusItem) return G_SOURCE_REMOVE; + if (!priv->statusItem) { + LOG_DEBUG("blinkTimerCallback: statusItem is nil, removing timer"); + return G_SOURCE_REMOVE; + } priv->blinkState = !priv->blinkState; if (priv->blinkState && priv->reverseIcon) { + LOG_DEBUG("blinkTimerCallback: switching to reverse icon"); priv->statusItem.button.image = priv->reverseIcon; } else if (priv->normalIcon) { + LOG_DEBUG("blinkTimerCallback: switching to normal icon"); priv->statusItem.button.image = priv->normalIcon; + } else { + LOG_DEBUG("blinkTimerCallback: no icon available (blinkState=%d, reverseIcon=%p, normalIcon=%p)", + priv->blinkState, priv->reverseIcon, priv->normalIcon); } return G_SOURCE_CONTINUE; } static void startBlinkTimer(IptuxAppIndicatorPrivate* priv) { - if (priv->blinkTimerId) return; + if (priv->blinkTimerId) { + LOG_DEBUG("startBlinkTimer: timer already running (id=%u)", priv->blinkTimerId); + return; + } priv->blinkState = false; priv->blinkTimerId = g_timeout_add(500, blinkTimerCallback, priv); + LOG_DEBUG("startBlinkTimer: blinking started (timerId=%u)", priv->blinkTimerId); } static void stopBlinkTimer(IptuxAppIndicatorPrivate* priv) { if (priv->blinkTimerId) { + LOG_DEBUG("stopBlinkTimer: blinking stopped (timerId=%u)", priv->blinkTimerId); g_source_remove(priv->blinkTimerId); priv->blinkTimerId = 0; + } else { + LOG_DEBUG("stopBlinkTimer: no timer was running"); } priv->blinkState = false; } void IptuxAppIndicator::SetUnreadCount(int count) { + LOG_DEBUG("SetUnreadCount: count=%d, mode=%d, statusItem=%p", count, priv->mode, + priv->statusItem); priv->unreadCount = count; - if (!priv->statusItem || priv->mode == STATUS_ICON_MODE_NONE) return; + if (!priv->statusItem || priv->mode == STATUS_ICON_MODE_NONE) { + LOG_DEBUG("SetUnreadCount: early return (statusItem=%p, mode=%d)", + priv->statusItem, priv->mode); + return; + } if (priv->mode == STATUS_ICON_MODE_BLINKING) { if (count > 0) { @@ -246,6 +269,7 @@ static void stopBlinkTimer(IptuxAppIndicatorPrivate* priv) { } void IptuxAppIndicator::SetMode(StatusIconMode mode) { + LOG_DEBUG("SetMode: mode=%d (old=%d)", mode, priv->mode); StatusIconMode oldMode = priv->mode; priv->mode = mode; if (!priv->statusItem) return; @@ -264,6 +288,7 @@ static void stopBlinkTimer(IptuxAppIndicatorPrivate* priv) { } void IptuxAppIndicator::StopBlinking() { + LOG_DEBUG("StopBlinking called"); stopBlinkTimer(priv.get()); if (!priv->statusItem || priv->mode == STATUS_ICON_MODE_NONE) return; if (priv->normalIcon) { diff --git a/src/iptux/Application.cpp b/src/iptux/Application.cpp index a0ce82b0f..ff21a4594 100644 --- a/src/iptux/Application.cpp +++ b/src/iptux/Application.cpp @@ -136,23 +136,9 @@ void Application::onStartup(Application& self) { self.cthrd->sigUnreadMsgCountUpdated.connect( sigc::mem_fun(*self.app_indicator, &IptuxAppIndicator::SetUnreadCount)); - g_signal_connect(self.app, "window-added", - G_CALLBACK(+[](GtkApplication*, GtkWindow* window, - gpointer user_data) { - g_signal_connect( - window, "notify::is-active", - G_CALLBACK(+[](GtkWindow* win, GParamSpec*, - gpointer ud) { - if (gtk_window_is_active(win)) { - auto a = static_cast(ud); - if (a->app_indicator) { - a->app_indicator->StopBlinking(); - } - } - }), - user_data); - }), - &self); + // Removed notify::is-active StopBlinking handler. + // Blinking now stops only when the user interacts with the dialog + // (button-press-event or key-press-event via ClearNotify). } bool use_app_menu = true; diff --git a/src/iptux/DialogBase.cpp b/src/iptux/DialogBase.cpp index b885f4ad2..2729ff43c 100644 --- a/src/iptux/DialogBase.cpp +++ b/src/iptux/DialogBase.cpp @@ -225,7 +225,8 @@ void DialogBase::MainWindowSignalSetup(GtkWindow* window) { G_CALLBACK(DragDataReceived), this); g_signal_connect(window, "configure-event", G_CALLBACK(WindowConfigureEvent), &dtset); - g_signal_connect(window, "focus-in-event", G_CALLBACK(ClearNotify), NULL); + g_signal_connect(window, "button-press-event", G_CALLBACK(ClearNotify), NULL); + g_signal_connect(window, "key-press-event", G_CALLBACK(ClearNotify), NULL); } /** @@ -470,7 +471,8 @@ void DialogBase::DialogDestory(DialogBase* dialog) { /** * 清除提示,这个提示只是窗口闪动的提示 */ -gboolean DialogBase::ClearNotify(GtkWidget* window, GdkEventConfigure*) { +gboolean DialogBase::ClearNotify(GtkWidget* window, GdkEvent*) { + LOG_DEBUG("ClearNotify: user interaction on dialog window"); if (gtk_window_get_urgency_hint(GTK_WINDOW(window))) gtk_window_set_urgency_hint(GTK_WINDOW(window), FALSE); DialogBase* self = diff --git a/src/iptux/DialogBase.h b/src/iptux/DialogBase.h index 356c1d9fc..87eca92d9 100644 --- a/src/iptux/DialogBase.h +++ b/src/iptux/DialogBase.h @@ -64,7 +64,7 @@ class DialogBase : public SessionAbstract, public sigc::trackable { // 回调部分 static void DialogDestory(DialogBase*); - static gboolean ClearNotify(GtkWidget* window, GdkEventConfigure* event); + static gboolean ClearNotify(GtkWidget* window, GdkEvent* event); static void DragDataReceived(DialogBase* dlgpr, GdkDragContext* context, gint x, diff --git a/src/iptux/UiCoreThread.cpp b/src/iptux/UiCoreThread.cpp index 757059f04..d2eabb7c7 100644 --- a/src/iptux/UiCoreThread.cpp +++ b/src/iptux/UiCoreThread.cpp @@ -494,7 +494,9 @@ void UiCoreThread::PopItemFromEnclosureList(FileInfo* file) { delete file; } -void UiCoreThread::onGroupInfoMsgCountUpdate(GroupInfo* grpinf, int, int) { +void UiCoreThread::onGroupInfoMsgCountUpdate(GroupInfo* grpinf, int oldCount, int newCount) { + LOG_DEBUG("onGroupInfoMsgCountUpdate: oldCount=%d, newCount=%d, totalUnread=%d", + oldCount, newCount, unread_msg_count()); sigGroupInfoUpdated.emit(grpinf); sigUnreadMsgCountUpdated.emit(unread_msg_count()); } diff --git a/src/iptux/UiModels.cpp b/src/iptux/UiModels.cpp index ec243be59..fc1f4041c 100644 --- a/src/iptux/UiModels.cpp +++ b/src/iptux/UiModels.cpp @@ -532,11 +532,14 @@ void GroupInfo::clearInputBuffer() { void GroupInfo::addMsgCount(int i) { int oldCount = getUnreadMsgCount(); allMsgCount += i; + LOG_DEBUG("addMsgCount: i=%d, oldCount=%d, newCount=%d", i, oldCount, + getUnreadMsgCount()); signalUnreadMsgCountUpdated.emit(this, oldCount, getUnreadMsgCount()); } void GroupInfo::readAllMsg() { int oldCount = getUnreadMsgCount(); + LOG_DEBUG("readAllMsg: oldCount=%d", oldCount); if (oldCount != 0) { readMsgCount = allMsgCount; signalUnreadMsgCountUpdated.emit(this, oldCount, getUnreadMsgCount()); From 4524741f0ceb0b248f56055d14bddb33fbc69658 Mon Sep 17 00:00:00 2001 From: LI Daobing Date: Thu, 29 Jan 2026 22:01:47 -0800 Subject: [PATCH 3/7] 1 --- src/iptux/AppIndicator.cpp | 21 +++++++++++++++++++-- src/iptux/DialogBase.cpp | 11 ++++++----- src/iptux/DialogBase.h | 2 +- 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/iptux/AppIndicator.cpp b/src/iptux/AppIndicator.cpp index 88cb69d78..249a2bc51 100644 --- a/src/iptux/AppIndicator.cpp +++ b/src/iptux/AppIndicator.cpp @@ -4,6 +4,8 @@ #include #include +#include "iptux-utils/output.h" + namespace iptux { class IptuxAppIndicatorPrivate { @@ -37,24 +39,33 @@ static gboolean blinkTimerCallback(gpointer data) { auto priv = static_cast(data); priv->blinkState = !priv->blinkState; if (priv->blinkState) { + LOG_DEBUG("blinkTimerCallback: switching to reverse icon"); app_indicator_set_icon_full(priv->indicator, "iptux-icon-reverse", "iptux-icon-reverse"); } else { + LOG_DEBUG("blinkTimerCallback: switching to normal icon"); app_indicator_set_icon_full(priv->indicator, "iptux-icon", "iptux-icon"); } return G_SOURCE_CONTINUE; } static void startBlinkTimer(IptuxAppIndicatorPrivate* priv) { - if (priv->blinkTimerId) return; + if (priv->blinkTimerId) { + LOG_DEBUG("startBlinkTimer: timer already running (id=%u)", priv->blinkTimerId); + return; + } priv->blinkState = false; priv->blinkTimerId = g_timeout_add(500, blinkTimerCallback, priv); + LOG_DEBUG("startBlinkTimer: blinking started (timerId=%u)", priv->blinkTimerId); } static void stopBlinkTimer(IptuxAppIndicatorPrivate* priv) { if (priv->blinkTimerId) { + LOG_DEBUG("stopBlinkTimer: blinking stopped (timerId=%u)", priv->blinkTimerId); g_source_remove(priv->blinkTimerId); priv->blinkTimerId = 0; + } else { + LOG_DEBUG("stopBlinkTimer: no timer was running"); } priv->blinkState = false; } @@ -90,8 +101,12 @@ IptuxAppIndicator::IptuxAppIndicator(GActionGroup* action_group) { } void IptuxAppIndicator::SetUnreadCount(int i) { + LOG_DEBUG("SetUnreadCount: count=%d, mode=%d", i, priv->mode); priv->unreadCount = i; - if (priv->mode == STATUS_ICON_MODE_NONE) return; + if (priv->mode == STATUS_ICON_MODE_NONE) { + LOG_DEBUG("SetUnreadCount: early return (mode=NONE)"); + return; + } if (priv->mode == STATUS_ICON_MODE_BLINKING) { if (i > 0) { @@ -112,6 +127,7 @@ void IptuxAppIndicator::SetUnreadCount(int i) { } void IptuxAppIndicator::SetMode(StatusIconMode mode) { + LOG_DEBUG("SetMode: mode=%d (old=%d)", mode, priv->mode); StatusIconMode oldMode = priv->mode; priv->mode = mode; @@ -128,6 +144,7 @@ void IptuxAppIndicator::SetMode(StatusIconMode mode) { } void IptuxAppIndicator::StopBlinking() { + LOG_DEBUG("StopBlinking called"); stopBlinkTimer(priv.get()); app_indicator_set_icon_full(priv->indicator, "iptux-icon", "iptux-icon"); if (priv->mode == STATUS_ICON_MODE_NONE) return; diff --git a/src/iptux/DialogBase.cpp b/src/iptux/DialogBase.cpp index 2729ff43c..06e0530d1 100644 --- a/src/iptux/DialogBase.cpp +++ b/src/iptux/DialogBase.cpp @@ -225,8 +225,7 @@ void DialogBase::MainWindowSignalSetup(GtkWindow* window) { G_CALLBACK(DragDataReceived), this); g_signal_connect(window, "configure-event", G_CALLBACK(WindowConfigureEvent), &dtset); - g_signal_connect(window, "button-press-event", G_CALLBACK(ClearNotify), NULL); - g_signal_connect(window, "key-press-event", G_CALLBACK(ClearNotify), NULL); + g_signal_connect(window, "event-after", G_CALLBACK(ClearNotify), NULL); } /** @@ -471,14 +470,16 @@ void DialogBase::DialogDestory(DialogBase* dialog) { /** * 清除提示,这个提示只是窗口闪动的提示 */ -gboolean DialogBase::ClearNotify(GtkWidget* window, GdkEvent*) { - LOG_DEBUG("ClearNotify: user interaction on dialog window"); +void DialogBase::ClearNotify(GtkWidget* window, GdkEvent* event) { + GdkEventType type = gdk_event_get_event_type(event); + if (type != GDK_BUTTON_PRESS && type != GDK_KEY_PRESS) + return; + LOG_DEBUG("ClearNotify: user interaction on dialog window (type=%d)", type); if (gtk_window_get_urgency_hint(GTK_WINDOW(window))) gtk_window_set_urgency_hint(GTK_WINDOW(window), FALSE); DialogBase* self = (DialogBase*)g_object_get_data(G_OBJECT(window), "session-class"); self->grpinf->readAllMsg(); - return FALSE; } /** diff --git a/src/iptux/DialogBase.h b/src/iptux/DialogBase.h index 87eca92d9..e6a45142b 100644 --- a/src/iptux/DialogBase.h +++ b/src/iptux/DialogBase.h @@ -64,7 +64,7 @@ class DialogBase : public SessionAbstract, public sigc::trackable { // 回调部分 static void DialogDestory(DialogBase*); - static gboolean ClearNotify(GtkWidget* window, GdkEvent* event); + static void ClearNotify(GtkWidget* window, GdkEvent* event); static void DragDataReceived(DialogBase* dlgpr, GdkDragContext* context, gint x, From 25e500e524a5ecb764823b4e751f516403d1e93d Mon Sep 17 00:00:00 2001 From: LI Daobing Date: Thu, 29 Jan 2026 22:25:00 -0800 Subject: [PATCH 4/7] 1 --- src/iptux/AppIndicator.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/iptux/AppIndicator.cpp b/src/iptux/AppIndicator.cpp index 249a2bc51..9e0ac0c75 100644 --- a/src/iptux/AppIndicator.cpp +++ b/src/iptux/AppIndicator.cpp @@ -84,7 +84,8 @@ IptuxAppIndicator::IptuxAppIndicator(GActionGroup* action_group) { app_indicator_set_status(priv->indicator, APP_INDICATOR_STATUS_ACTIVE); app_indicator_set_attention_icon_full(priv->indicator, "iptux-attention", "iptux-attention"); - + app_indicator_set_icon_theme_path(priv->indicator, + "/usr/local/share/icons/hicolor"); app_indicator_set_title(priv->indicator, _("Iptux")); priv->menuBuilder = From 10499b390c67df3e4d79c07b518be2e5aaf8d439 Mon Sep 17 00:00:00 2001 From: LI Daobing Date: Thu, 29 Jan 2026 22:30:57 -0800 Subject: [PATCH 5/7] 1 --- src/config.h.in | 1 + src/iptux/AppIndicator.cpp | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config.h.in b/src/config.h.in index 10bba306f..f2882649a 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -11,6 +11,7 @@ #define __LOCALE_PATH "@SHARE_DIR@/locale" #define __SOUND_PATH "@SHARE_IPTUX_DIR@/sound" #define __UI_PATH "@SHARE_IPTUX_DIR@/ui" +#define __ICON_PATH "@SHARE_DIR@/icons/hicolor" #define IPTUX_PATH "/iptux" #define LOG_PATH "/iptux/log" diff --git a/src/iptux/AppIndicator.cpp b/src/iptux/AppIndicator.cpp index 9e0ac0c75..c46ca8f09 100644 --- a/src/iptux/AppIndicator.cpp +++ b/src/iptux/AppIndicator.cpp @@ -84,8 +84,7 @@ IptuxAppIndicator::IptuxAppIndicator(GActionGroup* action_group) { app_indicator_set_status(priv->indicator, APP_INDICATOR_STATUS_ACTIVE); app_indicator_set_attention_icon_full(priv->indicator, "iptux-attention", "iptux-attention"); - app_indicator_set_icon_theme_path(priv->indicator, - "/usr/local/share/icons/hicolor"); + app_indicator_set_icon_theme_path(priv->indicator, __ICON_PATH); app_indicator_set_title(priv->indicator, _("Iptux")); priv->menuBuilder = From 190ee9192bab1bf0326c52eb566f96443e6c07e8 Mon Sep 17 00:00:00 2001 From: LI Daobing Date: Fri, 30 Jan 2026 22:34:49 -0800 Subject: [PATCH 6/7] Increment message count only for PAL message source Updated GroupInfo::_addMsgPara to call addMsgCount(1) only when the message source type is PAL, preventing unnecessary count increments for other message types. --- src/iptux/UiModels.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/iptux/UiModels.cpp b/src/iptux/UiModels.cpp index fc1f4041c..a75d1de84 100644 --- a/src/iptux/UiModels.cpp +++ b/src/iptux/UiModels.cpp @@ -809,7 +809,9 @@ void GroupInfo::_addMsgPara(const MsgPara& para, time_t now) { break; } } - addMsgCount(1); + if (para.stype == MessageSourceType::PAL) { + addMsgCount(1); + } } bool transModelIsFinished(TransModel* model) { From 0636e40b810770eda5c1bc2ff834ebaa4048d920 Mon Sep 17 00:00:00 2001 From: LI Daobing Date: Fri, 30 Jan 2026 23:21:16 -0800 Subject: [PATCH 7/7] 1 --- src/iptux/DialogBase.cpp | 12 ++++++++---- src/iptux/DialogPeer.cpp | 6 +++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/iptux/DialogBase.cpp b/src/iptux/DialogBase.cpp index 06e0530d1..dc15c8444 100644 --- a/src/iptux/DialogBase.cpp +++ b/src/iptux/DialogBase.cpp @@ -471,10 +471,14 @@ void DialogBase::DialogDestory(DialogBase* dialog) { * 清除提示,这个提示只是窗口闪动的提示 */ void DialogBase::ClearNotify(GtkWidget* window, GdkEvent* event) { - GdkEventType type = gdk_event_get_event_type(event); - if (type != GDK_BUTTON_PRESS && type != GDK_KEY_PRESS) - return; - LOG_DEBUG("ClearNotify: user interaction on dialog window (type=%d)", type); + if (event) { + GdkEventType type = gdk_event_get_event_type(event); + if (type != GDK_BUTTON_PRESS && type != GDK_KEY_PRESS) + return; + LOG_DEBUG("ClearNotify: user interaction on dialog window (type=%d)", type); + } else { + LOG_DEBUG("ClearNotify: called directly (window is active)"); + } if (gtk_window_get_urgency_hint(GTK_WINDOW(window))) gtk_window_set_urgency_hint(GTK_WINDOW(window), FALSE); DialogBase* self = diff --git a/src/iptux/DialogPeer.cpp b/src/iptux/DialogPeer.cpp index c19a450d8..95ff247af 100644 --- a/src/iptux/DialogPeer.cpp +++ b/src/iptux/DialogPeer.cpp @@ -1068,9 +1068,9 @@ void DialogPeer::onNewFileReceived(GroupInfo*) { void DialogPeer::onGroupInfoUpdated(GroupInfo* groupInfo) { if (groupInfo != this->grpinf) return; - if (gtk_window_is_active(GTK_WINDOW(this->window))) { - ClearNotify(GTK_WIDGET(this->window), nullptr); - } + // Don't auto-read messages just because the window is active. + // Messages are marked as read only on user interaction + // (button-press or key-press via ClearNotify). } void DialogPeer::refreshSendAction() {