1
0
mirror of https://github.com/moparisthebest/FireTray synced 2024-12-22 05:48:49 -05:00

IM icon blinking when private message or cited in channel

This commit is contained in:
foudfou 2012-08-21 22:10:56 +02:00
parent 866bbc1a2e
commit 9f46ef6343
8 changed files with 136 additions and 11 deletions

View File

@ -29,8 +29,9 @@ if ("undefined" == typeof(firetray)) {
firetray.Handler = {
initialized: false,
inMailApp: false,
inBrowserApp: false,
inMailApp: false,
isIMEnabled: false,
appStarted: false,
windows: {},
windowsCount: 0,
@ -200,6 +201,7 @@ firetray.Handler = {
showWindow: function(winId) {},
showHideAllWindows: function() {},
activateLastWindow: function(gtkStatusIcon, gdkEvent, userData) {},
findActiveWindow: function() {},
showAllWindows: function() {
F.LOG("showAllWindows");

View File

@ -14,6 +14,7 @@ Cu.import("resource://firetray/linux/FiretrayIMStatusIcon.jsm");
firetray.InstantMessaging = {
initialized: false,
observedTopics: {},
acknowledgeOnFocus: {},
init: function() {
if (this.initialized) {
@ -25,8 +26,8 @@ firetray.InstantMessaging = {
firetray.Utils.addObservers(firetray.InstantMessaging, [
// "*" // debugging
"account-connected", "account-disconnected", "idle-time-changed",
"new-directed-incoming-message", "new-text", "new-ui-conversation",
"status-changed", "unread-im-count-changed", "visited-status-resolution"
"new-directed-incoming-message", "status-changed",
"unread-im-count-changed"
]);
firetray.IMStatusIcon.init();
@ -50,14 +51,29 @@ firetray.InstantMessaging = {
case "account-disconnected":
case "idle-time-changed":
case "status-changed":
// case "visited-status-resolution":
this.updateIcon();
break;
case "unread-im-count-changed":
case "new-directed-incoming-message": // when PM or cited in channel
let conv = subject.QueryInterface(Ci.prplIMessage).conversation;
F.LOG("conversation name="+conv.name); // normalizedName shouldn't be necessary
let convIsActiveTabInActiveWin = this.isConvActiveTabInActiveWindow(conv);
F.LOG("convIsActiveTabInActiveWin="+convIsActiveTabInActiveWin);
let [unreadTargettedCount, unreadTotalCount] = this.countUnreadMessages();
F.LOG("unreadTotalCount="+unreadTotalCount);
if (!convIsActiveTabInActiveWin) { // don't blink when conv tab already on top
this.acknowledgeOnFocus.must = true;
this.acknowledgeOnFocus.conv = conv;
firetray.IMStatusIcon.setIconBlinking(true);
}
break;
case "new-directed-incoming-message": // when PM or cited in channel: new-directed-incoming-message: [xpconnect wrapped (nsISupports, nsIClassInfo, prplIMessage)] null
case "unread-im-count-changed":
let unreadMsgCount = data;
if (unreadMsgCount == 0)
this.stopIconBlinkingMaybe();
// FIXME: setToolTip
break;
default:
@ -65,6 +81,48 @@ firetray.InstantMessaging = {
}
},
stopIconBlinkingMaybe: function() {
F.WARN("acknowledgeOnFocus.must="+this.acknowledgeOnFocus.must);
let convIsActiveTabInActiveWin = this.isConvActiveTabInActiveWindow(
this.acknowledgeOnFocus.conv);
if (this.acknowledgeOnFocus.must && convIsActiveTabInActiveWin) {
firetray.IMStatusIcon.setIconBlinking(false);
this.acknowledgeOnFocus.must = false;
}
},
isConvActiveTabInActiveWindow: function(conv) {
let activeWin = firetray.Handler.findActiveWindow(),
activeChatTab = null;
if (!activeWin) return false;
activeChatTab = this.findActiveChatTab(activeWin);
let convNameRegex = new RegExp(" - "+conv.name+"$");
return activeChatTab ? convNameRegex.test(activeChatTab.title) : false;
},
findActiveChatTab: function(xid) {
let win = firetray.Handler.windows[xid].chromeWin;
let tabmail = win.document.getElementById("tabmail");
let chatTabs = tabmail.tabModes.chat.tabs;
for each (let tab in chatTabs)
if (tab.tabNode.selected) return tab;
return null;
},
// lifted from chat-messenger-overlay.js
countUnreadMessages: function() {
let convs = Services.conversations.getUIConversations();
let unreadTargettedCount = 0;
let unreadTotalCount = 0;
for each (let conv in convs) {
unreadTargettedCount += conv.unreadTargetedMessageCount;
unreadTotalCount += conv.unreadIncomingMessageCount;
}
return [unreadTargettedCount, unreadTotalCount];
},
updateIcon: function() {
let userStatus = Services.core.globalUserStatus.statusType;
F.LOG("IM status="+userStatus);

View File

@ -43,8 +43,12 @@ firetray.Messaging = {
MailServices.mailSession.AddFolderListener(that.mailSessionListener,
that.mailSessionListener.notificationFlags);
if (Services.prefs.getBoolPref("mail.chat.enabled") && this.existsIMAccount())
// FIXME: add im-icon pref
// FIXME: watch out account-added !!
if (Services.prefs.getBoolPref("mail.chat.enabled") && this.existsIMAccount()) {
firetray.InstantMessaging.init();
firetray.Handler.isIMEnabled = true;
}
this.initialized = true;
},
@ -283,7 +287,7 @@ firetray.Messaging = {
let rootFolder = accountServer.rootFolder; // nsIMsgFolder
if (rootFolder.hasSubFolders) {
let subFolders = rootFolder.subFolders;
while(subFolders.hasMoreElements()) {
while (subFolders.hasMoreElements()) {
let folder = subFolders.getNext().QueryInterface(Ci.nsIMsgFolder);
if (!(folder.flags & excludedFoldersFlags)) {
msgCount = folderCountFunction(folder, msgCount);

View File

@ -237,6 +237,12 @@ function gdk_defines(lib) {
{ "x_root": gobject.gdouble },
{ "y_root": gobject.gdouble }
]);
this.GdkEventFocus = ctypes.StructType("GdkEventFocus", [
{ "type": this.GdkEventType },
{ "window": this.GdkWindow.ptr },
{ "send_event": gobject.gint8 },
{ "in": gobject.gint16 },
]);
this.GdkFilterFunc_t = ctypes.FunctionType(
ctypes.default_abi, this.GdkFilterReturn,

View File

@ -89,6 +89,7 @@ function gobject_defines(lib) {
this.guint16 = ctypes.uint16_t;
this.gint = ctypes.int;
this.gint8 = ctypes.int8_t;
this.gint16 = ctypes.int16_t;
this.gchar = ctypes.char;
this.guchar = ctypes.unsigned_char;
this.gboolean = this.gint;

View File

@ -74,6 +74,10 @@ function gtk_defines(lib) {
this.GCallbackWindowStateEvent_t = ctypes.FunctionType(
ctypes.default_abi, gobject.gboolean,
[this.GtkWidget.ptr, gdk.GdkEventWindowState.ptr, gobject.gpointer]).ptr;
this.GCallbackWidgetFocuEvent_t = ctypes.FunctionType(
ctypes.default_abi, gobject.gboolean,
[this.GtkWidget.ptr, gdk.GdkEventFocus.ptr, gobject.gpointer]).ptr;
lib.lazy_bind("gtk_icon_theme_get_default", this.GtkIconTheme.ptr);
lib.lazy_bind("gtk_icon_theme_get_for_screen", this.GtkIconTheme.ptr, gdk.GdkScreen.ptr);
@ -85,6 +89,7 @@ function gtk_defines(lib) {
lib.lazy_bind("gtk_status_icon_set_from_icon_name", ctypes.void_t, this.GtkStatusIcon.ptr, gobject.gchar.ptr);
lib.lazy_bind("gtk_status_icon_set_from_gicon", ctypes.void_t, this.GtkStatusIcon.ptr, gio.GIcon.ptr);
lib.lazy_bind("gtk_status_icon_set_tooltip_text", ctypes.void_t, this.GtkStatusIcon.ptr, ctypes.char.ptr);
lib.lazy_bind("gtk_status_icon_set_blinking", ctypes.void_t, this.GtkStatusIcon.ptr, gobject.gboolean); // deprecated in gtk3
lib.lazy_bind("gtk_status_icon_set_visible", ctypes.void_t, this.GtkStatusIcon.ptr, gobject.gboolean);
lib.lazy_bind("gtk_menu_new", this.GtkMenu.ptr);
lib.lazy_bind("gtk_menu_item_set_label", ctypes.void_t, this.GtkMenuItem.ptr, gobject.gchar.ptr);
@ -104,7 +109,8 @@ function gtk_defines(lib) {
lib.lazy_bind("gtk_status_icon_set_from_pixbuf", ctypes.void_t, this.GtkStatusIcon.ptr, gdk.GdkPixbuf.ptr);
lib.lazy_bind("gtk_window_list_toplevels", gobject.GList.ptr);
lib.lazy_bind("gtk_window_get_title", gobject.gchar.ptr, this.GtkWindow.ptr);
lib.lazy_bind("gtk_window_is_active", gobject.gboolean, this.GtkWindow.ptr);
lib.lazy_bind("gtk_window_has_toplevel_focus", gobject.gboolean, this.GtkWindow.ptr);
lib.lazy_bind("gtk_widget_get_has_window", gobject.gboolean, this.GtkWidget.ptr);
lib.lazy_bind("gtk_widget_get_window", gdk.GdkWindow.ptr, this.GtkWidget.ptr);
lib.lazy_bind("gtk_widget_get_parent_window", gdk.GdkWindow.ptr, this.GtkWidget.ptr);

View File

@ -11,7 +11,9 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/ctypes.jsm");
Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
Cu.import("resource://firetray/ctypes/linux/gio.jsm");
Cu.import("resource://firetray/ctypes/linux/gdk.jsm");
Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
Cu.import("resource://firetray/linux/FiretrayWindow.jsm");
Cu.import("resource://firetray/commons.js");
firetray.Handler.subscribeLibsForClosing([gobject, gio, gtk]);
@ -32,6 +34,7 @@ firetray.IMStatusIcon = {
o[FIRETRAY_IM_STATUS_OFFLINE] = null;
return o;
})(),
callbacks: {onFocusIn: {}},
init: function() {
if (!firetray.Handler.inMailApp) throw "IMStatusIcon for mail app only";
@ -72,6 +75,31 @@ firetray.IMStatusIcon = {
setIconImage: function(name) {
this.setIconImageFromGIcon(this.themedIcons[name]);
},
setIconBlinking: function(blink) {
gtk.gtk_status_icon_set_blinking(this.trayIcon, blink);
},
/* we could also use x11.FocusIn... just wanted to try a different method */
attachOnFocusInCallback: function(xid) {
F.LOG("attachOnFocusInCallback xid="+xid);
this.callbacks.onFocusIn[xid] = gtk.GCallbackWidgetFocuEvent_t(firetray.IMStatusIcon.onFocusIn);
gobject.g_signal_connect(firetray.Handler.gtkWindows.get(xid),
"focus-in-event", firetray.IMStatusIcon.callbacks.onFocusIn[xid], null);
},
// NOTE: fluxbox issues a FocusIn event when switching workspace by hotkey :(
// (http://sourceforge.net/tracker/index.php?func=detail&aid=3190205&group_id=35398&atid=413960)
onFocusIn: function(widget, event, data) {
F.LOG("onFocusIn");
// let gdkEventFocus = ctypes.cast(event, gdk.GdkEventFocus.ptr);
// let gdkWin = gdkEventFocus.contents.window;
// let xid = firetray.Window.getXIDFromGdkWindow(gdkWin);
// F.LOG("xid="+xid+" in="+gdkEventFocus.contents["in"]);
firetray.InstantMessaging.stopIconBlinkingMaybe();
}
// FIXME: TODO: onclick/activate -> chatHandler.showCurrentConversation()
}; // firetray.IMStatusIcon

View File

@ -516,7 +516,7 @@ firetray.Window = {
if (firetray.Handler.windows[xwin].visible &&
firetray.js.strEquals(xprop.contents.atom, x11.current.Atoms.WM_STATE) &&
firetray.js.strEquals(xprop.contents.state, x11.PropertyNewValue)) {
F.LOG("PropertyNotify: WM_STATE, send_event: "+xprop.contents.send_event+", state: "+xprop.contents.state);
F.LOG("xid="+xwin+" PropertyNotify: WM_STATE, send_event: "+xprop.contents.send_event+", state: "+xprop.contents.state);
winStates = firetray.Window.getXWindowStates(xwin);
isHidden = winStates & FIRETRAY_XWINDOW_HIDDEN;
}
@ -536,7 +536,7 @@ firetray.Window = {
if (hides_single_window) {
firetray.Handler.hideWindow(xwin);
} else
firetray.Handler.hideAllWindows();
firetray.Handler.hideAllWindows();
}
}
@ -595,6 +595,12 @@ firetray.Handler.registerWindow = function(win) {
this.windows[xid].filterWindowCb = gdk.GdkFilterFunc_t(firetray.Window.filterWindow);
gdk.gdk_window_add_filter(gdkWin, this.windows[xid].filterWindowCb, null);
// FIXME: isn't it (c)leaner to do it in x11 window filter ?
if (firetray.Handler.isIMEnabled) {
Cu.import("resource://firetray/linux/FiretrayIMStatusIcon.jsm");
firetray.IMStatusIcon.attachOnFocusInCallback(xid);
}
} catch (x) {
firetray.Window.unregisterWindowByXID(xid);
F.ERROR(x);
@ -656,6 +662,20 @@ firetray.Handler.activateLastWindow = function(gtkStatusIcon, gdkEvent, userData
return stopPropagation;
};
firetray.Handler.findActiveWindow = function() {
let activeWin = null;
for (let xid in firetray.Handler.windows) {
let gtkWin = firetray.Handler.gtkWindows.get(xid);
let isActive = gtk.gtk_window_is_active(gtkWin);
F.LOG(xid+" is active="+isActive);
if (isActive) {
activeWin = xid;
break;
}
}
return activeWin;
};
/**
* init X11 Display and handled XAtoms.