1
0
mirror of https://github.com/moparisthebest/FireTray synced 2024-08-13 15:53:47 -04:00

Merge branch 'im'

Conflicts:
	src/defaults/preferences/prefs.js
	src/modules/FiretrayHandler.jsm
	src/modules/FiretrayMessaging.jsm
	src/modules/VersionChange.jsm
	src/modules/commons.js
	src/modules/linux/FiretrayStatusIcon.jsm
	src/modules/linux/FiretrayWindow.jsm
This commit is contained in:
foudfou 2012-09-04 00:25:29 +02:00
commit ae0f41e1e4
29 changed files with 749 additions and 212 deletions

View File

@ -89,4 +89,4 @@ Acknowledgment
[Nils Maier](https://addons.mozilla.org/fr/firefox/addon/minimizetotray-revived/
"MinToTrayR addon page").
* kind support from Neil Deaking, Bobby Holley
* default icons borrowed from Mozilla and Pidgin

View File

@ -5,6 +5,7 @@ const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://firetray/FiretrayHandler.jsm");
Cu.import("resource://firetray/FiretrayChat.jsm");
Cu.import("resource://firetray/commons.js");
const TREEROW_ACCOUNT_OR_SERVER_TYPE_NAME = 0;
@ -640,6 +641,14 @@ var firetrayUIOptions = {
if (!/\d/.test(charStr))
event.preventDefault();
}
},
toggleChat: function(enabled) {
log.debug("Chat icon enable="+enabled);
if (enabled)
firetray.Chat.init();
else
firetray.Chat.shutdown();
}
};

View File

@ -30,6 +30,7 @@
<preference id="pref_app_icon_filename" name="extensions.firetray.app_icon_filename" type="string"/>
<preference id="pref_scroll_hides" name="extensions.firetray.scroll_hides" type="bool" />
<preference id="pref_scroll_mode" name="extensions.firetray.scroll_mode" type="string" />
<preference id="pref_chat_icon_enable" name="extensions.firetray.chat_icon_enable" type="bool" />
<preference id="pref_mail_notification_enabled" name="extensions.firetray.mail_notification_enabled" type="bool" />
<preference id="pref_folder_count_recursive" name="extensions.firetray.folder_count_recursive" type="bool" />
<preference id="pref_only_favorite_folders" name="extensions.firetray.only_favorite_folders" type="bool" />
@ -45,6 +46,7 @@
<tab label="&windows_options;"/>
<tab label="&icon_options;"/>
<tab label="&mail_options;" id="mail_tab" />
<tab label="&chat_options;" />
</tabs>
<tabpanels flex="1">
@ -52,27 +54,27 @@
<vbox pack="start" align="left" flex="1">
<checkbox id="ui_hides_on_close" preference="pref_hides_on_close"
label="&bool_hides_on_close.label;"
accesskey="&bool_hides_on_close.accesskey;"
label="&hides_on_close.label;"
accesskey="&hides_on_close.accesskey;"
oncommand="firetrayUIOptions.updateWindowAndIconOptions();"/>
<checkbox id="ui_hides_on_minimize" preference="pref_hides_on_minimize"
label="&bool_hides_on_minimize.label;"
accesskey="&bool_hides_on_minimize.accesskey;"
label="&hides_on_minimize.label;"
accesskey="&hides_on_minimize.accesskey;"
oncommand="firetrayUIOptions.updateWindowAndIconOptions();"/>
<checkbox id="ui_hides_single_window" preference="pref_hides_single_window"
label="&bool_hides_single_window.label;"
accesskey="&bool_hides_single_window.accesskey;"
tooltiptext="&bool_hides_single_window.tooltip;"/>
label="&hides_single_window.label;"
accesskey="&hides_single_window.accesskey;"
tooltiptext="&hides_single_window.tooltip;"/>
<checkbox id="ui_start_hidden" preference="pref_start_hidden"
label="&bool_start_hidden.label;"
accesskey="&bool_start_hidden.accesskey;"/>
label="&start_hidden.label;"
accesskey="&start_hidden.accesskey;"/>
<checkbox id="ui_show_activates" preference="pref_show_activates"
label="&bool_show_activates.label;"
accesskey="&bool_show_activates.accesskey;"
tooltiptext="&bool_show_activates.tooltip;"/>
label="&show_activates.label;"
accesskey="&show_activates.accesskey;"
tooltiptext="&show_activates.tooltip;"/>
<checkbox id="ui_remember_desktop" preference="pref_remember_desktop"
label="&bool_remember_desktop.label;"
accesskey="&bool_remember_desktop.accesskey;"/>
label="&remember_desktop.label;"
accesskey="&remember_desktop.accesskey;"/>
</vbox>
</tabpanel>
@ -114,8 +116,8 @@
</radiogroup>
<checkbox id="ui_show_icon_on_hide" preference="pref_show_icon_on_hide"
label="&bool_show_icon_on_hide.label;"
accesskey="&bool_show_icon_on_hide.accesskey;"/>
label="&show_icon_on_hide.label;"
accesskey="&show_icon_on_hide.accesskey;"/>
<checkbox id="ui_scroll_hides" preference="pref_scroll_hides"
label="&enable_mouse_scroll.label;"
@ -286,6 +288,17 @@
</vbox>
</tabpanel>
<tabpanel id="chat_tabpanel" flex="1">
<vbox align="left" flex="1">
<checkbox id="ui_chat_icon_enable" preference="pref_chat_icon_enable"
label="&chat_icon_enable.label;"
accesskey="&chat_icon_enable.accesskey;"
oncommand="firetrayUIOptions.toggleChat(this.checked);"/>
</vbox>
</tabpanel>
</tabpanels>
</tabbox>

View File

@ -7,24 +7,25 @@
<!ENTITY windows_options "Windows">
<!ENTITY icon_options "Icon">
<!ENTITY mail_options "Mail">
<!ENTITY chat_options "Chat">
<!ENTITY bool_hides_on_close.label "Closing window hides to tray">
<!ENTITY bool_hides_on_close.accesskey "C">
<!ENTITY bool_hides_on_minimize.label "Minimizing window hides to tray">
<!ENTITY bool_hides_on_minimize.accesskey "M">
<!ENTITY bool_hides_single_window.label "Hide windows individually">
<!ENTITY bool_hides_single_window.tooltip "not all windows at once">
<!ENTITY bool_hides_single_window.accesskey "H">
<!ENTITY bool_start_hidden.label "Start application hidden to tray">
<!ENTITY bool_start_hidden.accesskey "S">
<!ENTITY bool_show_activates.label "Activate restored windows">
<!ENTITY bool_show_activates.accesskey "e">
<!ENTITY bool_show_activates.tooltip "restored windows raise on top and get focus">
<!ENTITY bool_remember_desktop.label "Remember desktop (per-window)">
<!ENTITY bool_remember_desktop.accesskey "R">
<!ENTITY hides_on_close.label "Closing window hides to tray">
<!ENTITY hides_on_close.accesskey "C">
<!ENTITY hides_on_minimize.label "Minimizing window hides to tray">
<!ENTITY hides_on_minimize.accesskey "M">
<!ENTITY hides_single_window.label "Hide windows individually">
<!ENTITY hides_single_window.tooltip "not all windows at once">
<!ENTITY hides_single_window.accesskey "H">
<!ENTITY start_hidden.label "Start application hidden to tray">
<!ENTITY start_hidden.accesskey "S">
<!ENTITY show_activates.label "Activate restored windows">
<!ENTITY show_activates.accesskey "e">
<!ENTITY show_activates.tooltip "restored windows raise on top and get focus">
<!ENTITY remember_desktop.label "Remember desktop (per-window)">
<!ENTITY remember_desktop.accesskey "R">
<!ENTITY bool_show_icon_on_hide.label "Show tray icon only if a window is hidden">
<!ENTITY bool_show_icon_on_hide.accesskey "w">
<!ENTITY show_icon_on_hide.label "Show tray icon only if a window is hidden">
<!ENTITY show_icon_on_hide.accesskey "w">
<!ENTITY app_icon_type "Application icon type">
<!ENTITY app_icon_themed.label "Default themed">
<!ENTITY app_icon_themed.accesskey "D">
@ -81,3 +82,6 @@
<!ENTITY account_or_server_type_excluded.tooltip "Includes accounts or types into unread messages count">
<!ENTITY account_or_server_type_order "Order">
<!ENTITY account_or_server_type_order.tooltip "Order in which mail server types are displayed. Double-clic to edit.">
<!ENTITY chat_icon_enable.label "Enable chat icon">
<!ENTITY chat_icon_enable.accesskey "E">

View File

@ -0,0 +1 @@
../../../../../pidgin-tray-available.png

View File

@ -0,0 +1 @@
../../../../../pidgin-tray-away.png

View File

@ -0,0 +1 @@
../../../../../pidgin-tray-busy.png

View File

@ -0,0 +1 @@
../../../../../pidgin-tray-offline.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -20,6 +20,7 @@ pref("extensions.firetray.new_mail_icon_names", '["indicator-messages-new", "mai
pref("extensions.firetray.show_icon_on_hide", false);
pref("extensions.firetray.scroll_hides", true);
pref("extensions.firetray.scroll_mode", "down_hides");
pref("extensions.firetray.chat_icon_enable", true);
pref("extensions.firetray.message_count_type", 0);
pref("extensions.firetray.mail_notification_enabled", true);
@ -31,5 +32,5 @@ pref("extensions.firetray.folder_count_recursive", true);
// Ci.nsMsgFolderFlags.Archive|Drafts|Junk|Queue|SentMail|Trash|Virtual
pref("extensions.firetray.excluded_folders_flags", 1077956384);
// exposed in 1 tree, hence 2 branches: serverTypes, excludedAccounts
pref("extensions.firetray.mail_accounts", '{ "serverTypes": {"pop3":{"order":1,"excluded":false}, "imap":{"order":1,"excluded":false}, "movemail":{"order":2,"excluded":true}, "none":{"order":3,"excluded":false}, "rss":{"order":4,"excluded":true}, "nntp":{"order":5,"excluded":true}}, "excludedAccounts": [] }'); // JSON
pref("extensions.firetray.mail_accounts", '{ "serverTypes": {"pop3":{"order":1,"excluded":false}, "imap":{"order":1,"excluded":false}, "movemail":{"order":2,"excluded":true}, "none":{"order":3,"excluded":false}, "rss":{"order":4,"excluded":true}, "nntp":{"order":5,"excluded":true}, "exquilla":{"order":6,"excluded":true}}, "excludedAccounts": [] }'); // JSON
pref("extensions.firetray.only_favorite_folders", false);

View File

@ -5,7 +5,7 @@
<em:unpack>true</em:unpack> <!-- needed for embedded icons -->
<em:type>2</em:type>
<em:name>FireTray</em:name>
<em:version>0.4.2</em:version>
<em:version>0.4.2</em:version> <!-- change FIRETRAY_VERSION accordingly ! -->
<em:creator>Hua Luo, Francesco Solero, Foudil BRÉTEL</em:creator>
<em:contributor>Hua Luo, Francesco Solero (Firetray original authors)</em:contributor>
<em:homepageURL>https://github.com/foudfou/firetray</em:homepageURL>
@ -18,7 +18,7 @@
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!-- Firefox -->
<em:minVersion>7.0</em:minVersion>
<em:minVersion>13.0</em:minVersion>
<em:maxVersion>15.0a1</em:maxVersion>
</Description>
</em:targetApplication>
@ -26,8 +26,8 @@
<em:targetApplication> <!-- Thunderbird -->
<Description>
<em:id>{3550f703-e582-4d05-9a08-453d09bdfdc6}</em:id>
<em:minVersion>7.0</em:minVersion>
<em:maxVersion>15.0a1</em:maxVersion>
<em:minVersion>15.0</em:minVersion>
<em:maxVersion>17.0a1</em:maxVersion>
</Description>
</em:targetApplication>

View File

@ -0,0 +1,169 @@
/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
var EXPORTED_SYMBOLS = [ "firetray" ];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource:///modules/imServices.jsm");
Cu.import("resource://firetray/commons.js");
Cu.import("resource://firetray/linux/FiretrayChatStatusIcon.jsm");
let log = firetray.Logger.getLogger("firetray.Chat");
firetray.Chat = {
initialized: false,
observedTopics: {},
acknowledgeOnFocus: {},
init: function() {
if (this.initialized) {
log.warn("Chat already initialized");
return;
}
log.debug("Enabling Chat");
firetray.Utils.addObservers(firetray.Chat, [
// "*" // debugging
"account-connected", "account-disconnected", "idle-time-changed",
"new-directed-incoming-message", "status-changed",
"unread-im-count-changed"
]);
firetray.ChatStatusIcon.init();
this.initialized = true;
},
shutdown: function() {
if (!this.initialized) return;
log.debug("Disabling Chat");
firetray.ChatStatusIcon.shutdown();
firetray.Utils.removeAllObservers(firetray.Chat);
this.initialized = false;
},
observe: function(subject, topic, data) {
log.debug("RECEIVED Chat: "+topic+" subject="+subject+" data="+data);
switch (topic) {
case "account-connected":
case "account-disconnected":
case "idle-time-changed":
case "status-changed":
this.updateIcon();
break;
case "new-directed-incoming-message": // when PM or cited in channel
let conv = subject.QueryInterface(Ci.prplIMessage).conversation;
log.debug("conversation name="+conv.name); // normalizedName shouldn't be necessary
let convIsActiveTabInActiveWin = this.isConvActiveTabInActiveWindow(conv);
log.debug("convIsActiveTabInActiveWin="+convIsActiveTabInActiveWin);
if (!convIsActiveTabInActiveWin) { // don't blink when conv tab already on top
this.acknowledgeOnFocus.must = true;
this.acknowledgeOnFocus.conv = conv;
firetray.ChatStatusIcon.setIconBlinking(true);
}
break;
case "unread-im-count-changed":
let unreadMsgCount = data;
if (unreadMsgCount == 0)
this.stopIconBlinkingMaybe();
let localizedTooltip = PluralForm.get(
unreadMsgCount,
firetray.Utils.strings.GetStringFromName("tooltip.unread_messages"))
.replace("#1", unreadMsgCount);
firetray.ChatStatusIcon.setIconTooltip(localizedTooltip);
break;
default:
log.warn("unhandled topic: "+topic);
}
},
stopIconBlinkingMaybe: function() {
log.debug("acknowledgeOnFocus.must="+this.acknowledgeOnFocus.must);
if (!this.acknowledgeOnFocus.must) return;
let convIsActiveTabInActiveWin = this.isConvActiveTabInActiveWindow(
this.acknowledgeOnFocus.conv);
log.debug("convIsActiveTabInActiveWin="+convIsActiveTabInActiveWin);
if (this.acknowledgeOnFocus.must && convIsActiveTabInActiveWin) {
firetray.ChatStatusIcon.setIconBlinking(false);
this.acknowledgeOnFocus.must = false;
}
},
isConvActiveTabInActiveWindow: function(conv) {
let activeWin = firetray.Handler.findActiveWindow(),
activeChatTab = null;
if (!firetray.Handler.windows[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;
},
updateIcon: function() {
let globalConnectedStatus = this.globalConnectedStatus();
let userStatus;
if (globalConnectedStatus)
userStatus = Services.core.globalUserStatus.statusType;
else
userStatus = Ci.imIStatusInfo.STATUS_OFFLINE;
log.debug("IM status="+userStatus);
let iconName;
switch (userStatus) {
case Ci.imIStatusInfo.STATUS_OFFLINE: // 1
iconName = FIRETRAY_IM_STATUS_OFFLINE;
break;
case Ci.imIStatusInfo.STATUS_IDLE: // 4
case Ci.imIStatusInfo.STATUS_AWAY: // 5
iconName = FIRETRAY_IM_STATUS_AWAY;
break;
case Ci.imIStatusInfo.STATUS_AVAILABLE: // 7
iconName = FIRETRAY_IM_STATUS_AVAILABLE;
break;
case Ci.imIStatusInfo.STATUS_UNAVAILABLE: // 6
iconName = FIRETRAY_IM_STATUS_BUSY;
break;
case Ci.imIStatusInfo.STATUS_UNKNOWN: // 0
case Ci.imIStatusInfo.STATUS_INVISIBLE: // 2
case Ci.imIStatusInfo.STATUS_MOBILE: // 3
default:
// ignore
}
log.debug("IM status changed="+iconName);
if (iconName)
firetray.ChatStatusIcon.setIconImage(iconName);
},
globalConnectedStatus: function() {
let accounts = Services.accounts.getAccounts();
let globalConnected = false;
while (accounts.hasMoreElements()) {
let account = accounts.getNext().QueryInterface(Ci.imIAccount);
log.debug("account="+account+" STATUS="+account.statusInfo.statusType+" connected="+account.connected);
globalConnected = globalConnected || account.connected;
}
log.debug("globalConnected="+globalConnected);
return globalConnected;
}
};

View File

@ -29,21 +29,20 @@ let log = firetray.Logger.getLogger("firetray.Handler");
// other global functions
// (https://developer.mozilla.org/en/XUL_School/JavaScript_Object_Management)
firetray.Handler = {
FILENAME_DEFAULT: null,
FILENAME_SUFFIX: "32.png",
FILENAME_BLANK: null,
FILENAME_NEWMAIL: null,
initialized: false,
inMailApp: false,
inBrowserApp: false,
inMailApp: false,
appStarted: false,
windows: {},
windowsCount: 0,
visibleWindowsCount: 0,
observedTopics: {},
ctypesLibs: {}, // {"lib1": lib1, "lib2": lib2}
appId: (function(){return Services.appinfo.ID;})(),
appName: (function(){return Services.appinfo.name;})(),
appStartupTopic: null,
runtimeABI: (function(){return Services.appinfo.XPCOMABI;})(),
runtimeOS: (function(){return Services.appinfo.OS;})(), // "WINNT", "Linux", "Darwin"
addonRootDir: (function(){
@ -79,12 +78,14 @@ firetray.Handler = {
this.inBrowserApp = true;
log.debug('inMailApp: '+this.inMailApp+', inBrowserApp: '+this.inBrowserApp);
this.FILENAME_DEFAULT = firetray.Utils.chromeToPath(
"chrome://firetray/skin/" + this.appName.toLowerCase() + this.FILENAME_SUFFIX);
this.FILENAME_BLANK = firetray.Utils.chromeToPath(
"chrome://firetray/skin/blank-icon.png");
this.FILENAME_NEWMAIL = firetray.Utils.chromeToPath(
"chrome://firetray/skin/message-mail-new.png");
this.appStartupTopic = this.getAppStartupTopic(this.appId);
VersionChange.init(FIRETRAY_ID, FIRETRAY_VERSION, FIRETRAY_PREF_BRANCH);
VersionChange.addHook(["install", "upgrade", "reinstall"], firetray.VersionChangeHandler.showReleaseNotes);
VersionChange.addHook(["upgrade", "reinstall"], firetray.VersionChangeHandler.tryEraseOldOptions);
VersionChange.addHook(["upgrade", "reinstall"], firetray.VersionChangeHandler.correctMailNotificationType);
VersionChange.addHook(["upgrade", "reinstall"], firetray.VersionChangeHandler.correctMailServerTypes);
VersionChange.applyHooksAndWatchUninstall();
firetray.StatusIcon.init();
firetray.Handler.showHideIcon();
@ -103,19 +104,8 @@ firetray.Handler = {
}
}
Services.obs.addObserver(this, this.getAppStartupTopic(this.appId), false);
Services.obs.addObserver(this, "xpcom-will-shutdown", false);
Services.obs.addObserver(this, "profile-change-teardown", false);
let welcome = function(ver) {
firetray.Handler.openTab(FIRETRAY_SPLASH_PAGE+"#v"+ver);
firetray.Handler.tryEraseOldOptions();
firetray.Handler.correctMailNotificationType();
};
VersionChange.setInstallHook(welcome);
VersionChange.setUpgradeHook(welcome);
VersionChange.setReinstallHook(welcome);
VersionChange.watch();
firetray.Utils.addObservers(firetray.Handler, [ this.appStartupTopic,
"xpcom-will-shutdown", "profile-change-teardown" ]);
this.preventWarnOnClose();
@ -124,23 +114,40 @@ firetray.Handler = {
},
shutdown: function() {
log.debug("Disabling Handler");
firetray.PrefListener.unregister();
if (this.inMailApp)
firetray.Messaging.shutdown();
firetray.StatusIcon.shutdown();
firetray.Window.shutdown();
// watchout order and sufficiency of lib closings (tryCloseLibs())
this.tryCloseLibs();
Services.obs.removeObserver(this, this.getAppStartupTopic(this.appId), false);
Services.obs.removeObserver(this, "xpcom-will-shutdown", false);
Services.obs.removeObserver(this, "profile-change-teardown", false);
firetray.Utils.removeAllObservers(this);
this.appStarted = false;
this.initialized = false;
return true;
},
tryCloseLibs: function() {
try {
for (libName in this.ctypesLibs) {
let lib = this.ctypesLibs[libName];
if (lib.available())
lib.close();
};
} catch(x) { log.error(x); }
},
subscribeLibsForClosing: function(libs) {
for (let i=0, len=libs.length; i<len; ++i) {
let lib = libs[i];
if (!this.ctypesLibs.hasOwnProperty(lib.name))
this.ctypesLibs[lib.name] = lib;
}
},
observe: function(subject, topic, data) {
switch (topic) {
case "sessionstore-windows-restored":
@ -194,6 +201,7 @@ firetray.Handler = {
showWindow: function(winId) {},
showHideAllWindows: function() {},
activateLastWindow: function(gtkStatusIcon, gdkEvent, userData) {},
findActiveWindow: function() {},
showAllWindows: function() {
log.debug("showAllWindows");
@ -294,75 +302,6 @@ firetray.Handler = {
} catch (x) { log.error(x); }
},
openTab: function(url) {
if (this.appId === FIRETRAY_THUNDERBIRD_ID)
this.openMailTab(url);
else if (this.appId === FIRETRAY_FIREFOX_ID || this.appId === FIRETRAY_SEAMONKEY_ID)
this.openBrowserTab(url);
else
log.error("unsupported application");
},
openMailTab: function(url) {
let mail3PaneWindow = Services.wm.getMostRecentWindow("mail:3pane");
if (mail3PaneWindow) {
var tabmail = mail3PaneWindow.document.getElementById("tabmail");
mail3PaneWindow.focus();
}
if (tabmail) {
firetray.Utils.timer(function() {
log.debug("openMailTab");
tabmail.openTab("contentTab", {contentPage: url});
}, FIRETRAY_DELAY_BROWSER_STARTUP_MILLISECONDS, Ci.nsITimer.TYPE_ONE_SHOT);
}
},
openBrowserTab: function(url) {
let win = Services.wm.getMostRecentWindow("navigator:browser");
log.debug("WIN="+win);
if (win) {
var mainWindow = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindow);
mainWindow.setTimeout(function(win){
log.debug("openBrowser");
mainWindow.gBrowser.selectedTab = mainWindow.gBrowser.addTab(url);
}, 1000);
}
},
tryEraseOldOptions: function() {
let v03Options = [
"close_to_tray", "minimize_to_tray", "start_minimized", "confirm_exit",
"restore_to_next_unread", "mail_count_type", "show_mail_count",
"dont_count_spam", "dont_count_archive", "dont_count_drafts",
"dont_count_sent", "dont_count_templates", "show_mail_notification",
"show_icon_only_minimized", "use_custom_normal_icon",
"use_custom_special_icon", "custom_normal_icon", "custom_special_icon",
"text_color", "scroll_to_hide", "scroll_action", "grab_multimedia_keys",
"hide_show_mm_key", "accounts_to_exclude" ];
let v040b2Options = [ 'mail_notification' ];
let oldOptions = v03Options.concat(v040b2Options);
for (let i = 0, length = oldOptions.length; i<length; ++i) {
try {
firetray.Utils.prefService.clearUserPref(oldOptions[i]);
} catch (x) {}
}
},
correctMailNotificationType: function() {
if (firetray.Utils.prefService.getIntPref('message_count_type') ===
FIRETRAY_MESSAGE_COUNT_TYPE_NEW)
firetray.Utils.prefService.setIntPref('mail_notification_type',
FIRETRAY_NOTIFICATION_NEWMAIL_ICON);
},
quitApplication: function() {
try {
firetray.Utils.timer(function() {
@ -421,3 +360,94 @@ firetray.PrefListener = new PrefListener(
default:
}
});
firetray.VersionChangeHandler = {
showReleaseNotes: function() {
firetray.VersionChangeHandler.openTab(FIRETRAY_SPLASH_PAGE+"#v"+FIRETRAY_VERSION);
},
openTab: function(url) {
if (this.appId === FIRETRAY_THUNDERBIRD_ID)
this.openMailTab(url);
else if (this.appId === FIRETRAY_FIREFOX_ID || this.appId === FIRETRAY_SEAMONKEY_ID)
this.openBrowserTab(url);
else
log.error("unsupported application");
},
openMailTab: function(url) {
let mail3PaneWindow = Services.wm.getMostRecentWindow("mail:3pane");
if (mail3PaneWindow) {
var tabmail = mail3PaneWindow.document.getElementById("tabmail");
mail3PaneWindow.focus();
}
if (tabmail) {
firetray.Utils.timer(function() {
log.debug("openMailTab");
tabmail.openTab("contentTab", {contentPage: url});
}, FIRETRAY_DELAY_BROWSER_STARTUP_MILLISECONDS, Ci.nsITimer.TYPE_ONE_SHOT);
}
},
openBrowserTab: function(url) {
let win = Services.wm.getMostRecentWindow("navigator:browser");
log.debug("WIN="+win);
if (win) {
var mainWindow = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindow);
mainWindow.setTimeout(function(win){
log.debug("openBrowser");
mainWindow.gBrowser.selectedTab = mainWindow.gBrowser.addTab(url);
}, 1000);
}
},
tryEraseOldOptions: function() {
let v03Options = [
"close_to_tray", "minimize_to_tray", "start_minimized", "confirm_exit",
"restore_to_next_unread", "mail_count_type", "show_mail_count",
"dont_count_spam", "dont_count_archive", "dont_count_drafts",
"dont_count_sent", "dont_count_templates", "show_mail_notification",
"show_icon_only_minimized", "use_custom_normal_icon",
"use_custom_special_icon", "custom_normal_icon", "custom_special_icon",
"text_color", "scroll_to_hide", "scroll_action", "grab_multimedia_keys",
"hide_show_mm_key", "accounts_to_exclude" ];
let v040b2Options = [ 'mail_notification' ];
let oldOptions = v03Options.concat(v040b2Options);
for (let i = 0, length = oldOptions.length; i<length; ++i) {
try {
let option = oldOptions[i];
firetray.Utils.prefService.clearUserPref(option);
} catch (x) {}
}
},
correctMailNotificationType: function() {
if (firetray.Utils.prefService.getIntPref('message_count_type') ===
FIRETRAY_MESSAGE_COUNT_TYPE_NEW) {
firetray.Utils.prefService.setIntPref('mail_notification_type',
FIRETRAY_NOTIFICATION_NEWMAIL_ICON);
log.warn("mail notification type set to newmail icon.");
}
},
correctMailServerTypes: function() {
let mailAccounts = firetray.Utils.getObjPref('mail_accounts');
let serverTypes = mailAccounts["serverTypes"];
if (!serverTypes["exquilla"]) {
serverTypes["exquilla"] = {"order":6,"excluded":true};
let prefObj = {"serverTypes":serverTypes, "excludedAccounts":mailAccounts["excludedAccounts"]};
firetray.Utils.setObjPref('mail_accounts', prefObj);
log.warn("mail server types corrected");
}
}
};

View File

@ -7,8 +7,10 @@ const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource:///modules/mailServices.js");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/PluralForm.jsm");
Cu.import("resource://firetray/commons.js");
Cu.import("resource://firetray/FiretrayChat.jsm");
const FLDRS_UNINTERESTING = {
Archive: Ci.nsMsgFolderFlags.Archive,
@ -29,6 +31,7 @@ firetray.Messaging = {
cleaningTimer: null,
currentMsgCount: null,
newMsgCount: null,
observedTopics: {},
init: function() {
if (this.initialized) {
@ -37,15 +40,18 @@ firetray.Messaging = {
}
log.debug("Enabling Messaging");
// there is no means to detect account-removed event
this.cleaningTimer = firetray.Utils.timer(firetray.Messaging.cleanExcludedAccounts,
FIRETRAY_DELAY_PREF_CLEANING_MILLISECONDS, Ci.nsITimer.TYPE_REPEATING_SLACK);
log.debug(this.cleaningTimer+"="+FIRETRAY_DELAY_PREF_CLEANING_MILLISECONDS);
firetray.Utils.addObservers(firetray.Messaging, [ "account-added",
"account-removed"]);
let that = this;
MailServices.mailSession.AddFolderListener(that.mailSessionListener,
that.mailSessionListener.notificationFlags);
if (Services.prefs.getBoolPref("mail.chat.enabled") &&
firetray.Utils.prefService.getBoolPref("chat_icon_enable") &&
this.existsChatAccount())
firetray.Chat.init();
this.initialized = true;
},
@ -53,14 +59,48 @@ firetray.Messaging = {
if (!this.initialized) return;
log.debug("Disabling Messaging");
this.cleaningTimer.cancel();
if (firetray.hasOwnProperty('Chat')) firetray.Chat.shutdown();
MailServices.mailSession.RemoveFolderListener(this.mailSessionListener);
firetray.Handler.setIconImageDefault();
firetray.Utils.removeAllObservers(firetray.Messaging);
this.initialized = false;
},
// FIXME: this should definetely be done in Chat, but IM accounts
// seem not be initialized at this stage (Exception... "'TypeError:
// this._items is undefined' when calling method:
// [nsISimpleEnumerator::hasMoreElements]"), and we're unsure if we should
// initAccounts() ourselves...
existsChatAccount: function() {
let accounts = new this.Accounts();
for (let accountServer in accounts)
if (accountServer.type === 'im') {
log.debug("found im server: "+accountServer.prettyName);
return true;
}
return false;
},
observe: function(subject, topic, data) {
log.debug("RECEIVED Messaging: "+topic+" subject="+subject+" data="+data);
switch (topic) {
case "account-removed":
this.cleanExcludedAccounts();
if (subject.QueryInterface(Ci.imIAccount) && !this.existsChatAccount())
firetray.Chat.shutdown();
break;
case "account-added":
if (subject.QueryInterface(Ci.imIAccount) && !firetray.Chat.initialized)
firetray.Chat.init();
break;
default:
log.warn("unhandled topic: "+topic);
}
},
/* removes removed accounts from excludedAccounts pref. NOTE: Can't be called
at shutdown because MailServices.accounts no longer available */
cleanExcludedAccounts: function() {
@ -92,6 +132,8 @@ firetray.Messaging = {
}
},
// https://bugzilla.mozilla.org/show_bug.cgi?id=715799 for TB15+
// mozINewMailNotificationService (alternative message counting)
/* http://mxr.mozilla.org/comm-central/source/mailnews/base/public/nsIFolderListener.idl */
mailSessionListener: {
notificationFlags:
@ -236,10 +278,14 @@ firetray.Messaging = {
this.newMsgCount = 0;
let accounts = new this.Accounts();
for (let accountServer in accounts) { // nsIMsgAccount
if (!serverTypes[accountServer.type]) {
if (accountServer.type === 'im') {
continue; // IM messages are counted elsewhere
} else if (!serverTypes[accountServer.type]) {
log.warn("'"+accountServer.type+"' server type is not handled");
continue;
}
log.debug("is servertype excluded: "+serverTypes[accountServer.type].excluded+", account exclusion index: "+excludedAccounts.indexOf(accountServer.key));
if ((serverTypes[accountServer.type].excluded) ||
(excludedAccounts.indexOf(accountServer.key) >= 0))

View File

@ -5,25 +5,38 @@ const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/AddonManager.jsm");
Cu.import("resource://firetray/commons.js");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://firetray/logging.jsm");
let log = firetray.Logger.getLogger("VersionChange");
/**
* handles version changes.
* use setInstallHook(), setUpgradeHook(), setReinstallHook()
* http://mike.kaply.com/2011/02/02/running-add-on-code-at-first-run-and-upgrade/
*/
var VersionChange = {
curVersion: null,
initialized: false,
addonId: null,
addonVersion: null,
addOnPrefs: null,
init: function(id, version, prefBranch) {
log.debug("VersionChange got: id="+id+" ver="+version+" prefBranch="+prefBranch);
this.addOnId = id;
this.addonVersion = version;
this.addOnPrefs = Services.prefs.getBranch(prefBranch);
this.initialized = true;
},
versionComparator: Cc["@mozilla.org/xpcom/version-comparator;1"]
.getService(Ci.nsIVersionComparator),
watch: function() {
applyHooksAndWatchUninstall: function() {
if (!this.initialized) throw "VersionChange not initialized";
this.onVersionChange(this.addonVersion); // AddonManager.getAddonByID() async, whereas we need sync call
AddonManager.addAddonListener(this.uninstallListener);
AddonManager.getAddonByID(FIRETRAY_ID, this.onVersionChange.bind(this));
log.debug("version change watching enabled");
},
@ -31,56 +44,74 @@ var VersionChange = {
// detect reinstall later
uninstallListener: {
onUninstalling: function(addon) {
if (addon.id !== FIRETRAY_ID) return;
firetray.Utils.prefService.clearUserPref("installedVersion");
if (addon.id !== this.addonId) return;
this.addOnPrefs.clearUserPref("installedVersion");
},
onOperationCancelled: function(addon) {
if (addon.id !== FIRETRAY_ID) return;
if (addon.id !== this.addonId) return;
let beingUninstalled = (addon.pendingOperations & AddonManager.PENDING_UNINSTALL) != 0;
if (beingUninstalled)
firetray.Utils.prefService.clearUserPref("installedVersion");
this.addOnPrefs.clearUserPref("installedVersion");
}
},
onVersionChange: function(addon) {
log.debug("VERSION: "+addon.version);
onVersionChange: function() {
log.debug("VERSION: "+this.addonVersion);
this.curVersion = addon.version;
var firstrun = firetray.Utils.prefService.getBoolPref("firstrun");
var firstrun = this.addOnPrefs.getBoolPref("firstrun");
if (firstrun) {
log.debug("FIRST RUN");
this.initPrefs();
this.installHook(this.curVersion);
this._applyHooks("install");
} else {
try {
var installedVersion = firetray.Utils.prefService.getCharPref("installedVersion");
var versionDelta = this.versionComparator.compare(this.curVersion, installedVersion);
var installedVersion = this.addOnPrefs.getCharPref("installedVersion");
var versionDelta = this.versionComparator.compare(this.addonVersion, installedVersion);
if (versionDelta > 0) {
firetray.Utils.prefService.setCharPref("installedVersion", this.curVersion);
this.addOnPrefs.setCharPref("installedVersion", this.addonVersion);
log.debug("UPGRADE");
this.upgradeHook(this.curVersion);
this._applyHooks("upgrade");
}
} catch (ex) {
log.debug("REINSTALL");
this.initPrefs();
this.reinstallHook(this.curVersion);
this._applyHooks("reinstall");
}
}
},
initPrefs: function() {
firetray.Utils.prefService.setBoolPref("firstrun", false);
firetray.Utils.prefService.setCharPref("installedVersion", VersionChange.curVersion);
this.addOnPrefs.setBoolPref("firstrun", false);
this.addOnPrefs.setCharPref("installedVersion", VersionChange.addonVersion);
},
installHook: function(ver){},
upgradeHook: function(ver){},
reinstallHook: function(ver){},
setInstallHook: function(fun) {this.installHook = fun;},
setUpgradeHook: function(fun) {this.upgradeHook = fun;},
setReinstallHook: function(fun) {this.reinstallHook = fun;}
_hooks: [], // collection of callbacks {id: 1, categories: [], fun: function}
addHook: function(categories, fun) {
if (Object.prototype.toString.call(categories) !== "[object Array]") throw new TypeError();
let id = this._hooks.push({})-1;
this._hooks[id] = {id: id, categories: categories, fun: fun};
return id;
},
removeHook: function(id) {return this._hooks[id].splice(id-1, 1);},
removeCategoryFromHook: function(category, id) {
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
},
_applyHooks: function(category) {
try {
log.debug("_hooks.len="+this._hooks.length+" category="+category);
for (let i=0,len=this._hooks.length; i<len; ++i) {
let cb = this._hooks[i];
if (cb.categories.indexOf(category) > -1) cb.fun();
else log.debug("cb id="+cb.id+" not in category: "+cb.categories+"\n"+cb.fun);
}
} catch(x){log.error(x);}
}
};

View File

@ -1,13 +1,15 @@
/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* for now, logging facilities (imported from logging.jsm) are automatically
provided by this module */
/* for now, logging facilities (imported from logging.jsm) and Services are
automatically provided by this module */
var EXPORTED_SYMBOLS =
[ "firetray", "FIRETRAY_ID", "FIRETRAY_SPLASH_PAGE",
"FIRETRAY_APPLICATION_ICON_TYPE_THEMED",
[ "firetray", "FIRETRAY_ID", "FIRETRAY_VERSION", "FIRETRAY_PREF_BRANCH",
"FIRETRAY_SPLASH_PAGE", "FIRETRAY_APPLICATION_ICON_TYPE_THEMED",
"FIRETRAY_APPLICATION_ICON_TYPE_CUSTOM",
"FIRETRAY_NOTIFICATION_UNREAD_MESSAGE_COUNT",
"FIRETRAY_NOTIFICATION_NEWMAIL_ICON", "FIRETRAY_NOTIFICATION_CUSTOM_ICON",
"FIRETRAY_IM_STATUS_AVAILABLE", "FIRETRAY_IM_STATUS_AWAY",
"FIRETRAY_IM_STATUS_BUSY", "FIRETRAY_IM_STATUS_OFFLINE",
"FIRETRAY_DELAY_BROWSER_STARTUP_MILLISECONDS",
"FIRETRAY_DELAY_NOWAIT_MILLISECONDS",
"FIRETRAY_DELAY_PREF_CLEANING_MILLISECONDS",
@ -22,23 +24,30 @@ const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://firetray/logging.jsm");
const FIRETRAY_VERSION = "0.4.2"; // needed for sync call of onVersionChange() :(
const FIRETRAY_PREF_BRANCH = "extensions.firetray.";
const FIRETRAY_ID = "{9533f794-00b4-4354-aa15-c2bbda6989f8}";
const FIRETRAY_SPLASH_PAGE = "http://foudfou.github.com/FireTray/";
const FIRETRAY_APPLICATION_ICON_TYPE_THEMED = 0;
const FIRETRAY_APPLICATION_ICON_TYPE_CUSTOM = 1;
const FIRETRAY_APPLICATION_ICON_TYPE_THEMED = 0;
const FIRETRAY_APPLICATION_ICON_TYPE_CUSTOM = 1;
const FIRETRAY_MESSAGE_COUNT_TYPE_UNREAD = 0;
const FIRETRAY_MESSAGE_COUNT_TYPE_NEW = 1;
const FIRETRAY_NOTIFICATION_UNREAD_MESSAGE_COUNT = 0;
const FIRETRAY_NOTIFICATION_NEWMAIL_ICON = 1;
const FIRETRAY_NOTIFICATION_CUSTOM_ICON = 2;
const FIRETRAY_IM_STATUS_AVAILABLE = "user-available";
const FIRETRAY_IM_STATUS_AWAY = "user-away";
const FIRETRAY_IM_STATUS_BUSY = "user-busy";
const FIRETRAY_IM_STATUS_OFFLINE = "user-offline";
const FIRETRAY_DELAY_BROWSER_STARTUP_MILLISECONDS = 500;
const FIRETRAY_DELAY_NOWAIT_MILLISECONDS = 0;
const FIRETRAY_DELAY_PREF_CLEANING_MILLISECONDS = 15*60*1000;
const FIRETRAY_MESSAGE_COUNT_TYPE_UNREAD = 0;
const FIRETRAY_MESSAGE_COUNT_TYPE_NEW = 1;
const FIRETRAY_FIREFOX_ID = "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}";
const FIRETRAY_THUNDERBIRD_ID = "{3550f703-e582-4d05-9a08-453d09bdfdc6}";
const FIRETRAY_SONGBIRD_ID = "songbird@songbirdnest.com";
@ -56,9 +65,30 @@ if ("undefined" == typeof(firetray)) {
let log = firetray.Logger.getLogger("firetray.commons");
firetray.Utils = {
prefService: Services.prefs.getBranch("extensions.firetray."),
prefService: Services.prefs.getBranch(FIRETRAY_PREF_BRANCH),
strings: Services.strings.createBundle("chrome://firetray/locale/overlay.properties"),
addObservers: function(handler, topics){
topics.forEach(function(topic){
Services.obs.addObserver(this, topic, false);
this.observedTopics[topic] = true;
log.debug("registred "+topic+" for "+handler);
}, handler);
},
removeObservers: function(handler, topics) {
topics.forEach(function(topic){
Services.obs.removeObserver(this, topic);
delete this.observedTopics[topic];
}, handler);
},
removeAllObservers: function(handler) {
for (let topic in handler.observedTopics)
Services.obs.removeObserver(handler, topic);
handler.observedTopics = {};
},
getObjPref: function(prefStr) {
try {
var objPref = JSON.parse(
@ -191,15 +221,6 @@ firetray.Utils = {
timer.initWithCallback({ notify: callback },
delay, timerType);
return timer;
},
tryCloseLibs: function(libs) {
try {
libs.forEach(function(lib) {
if (lib.available())
lib.close();
});
} catch(x) { log.error(x); }
}
};

View File

@ -123,6 +123,8 @@ function ctypes_library(aName, aABIs, aDefines, aGlobal) {
}
}
this.name = aName;
this.close = function() {
log.debug("Closing library " + aName);
library.close();

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

@ -16,6 +16,7 @@ function gio_defines(lib) {
this.GIcon = ctypes.StructType("GIcon");
this.GThemedIcon = ctypes.StructType("GThemedIcon");
lib.lazy_bind("g_themed_icon_new", this.GIcon.ptr, ctypes.char.ptr);
lib.lazy_bind("g_themed_icon_new_from_names", this.GIcon.ptr, ctypes.char.ptr.ptr, ctypes.int);
}

View File

@ -1,4 +1,4 @@
/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -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;
@ -151,6 +152,8 @@ function gobject_defines(lib) {
lib.lazy_bind("g_signal_handler_block", ctypes.void_t, this.gpointer, this.gulong);
lib.lazy_bind("g_signal_handler_unblock", ctypes.void_t, this.gpointer, this.gulong);
/* NOTE: we can't easily work with g_object_get_property() because it uses
GValue, which is an opaque struct, and thus can't be initialized by ctypes */
}
new ctypes_library(GOBJECT_LIBNAME, GOBJECT_ABIS, gobject_defines, this);

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,12 +109,15 @@ 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);
lib.lazy_bind("gtk_window_set_decorated", ctypes.void_t, this.GtkWindow.ptr, gobject.gboolean);
lib.lazy_bind("gtk_widget_is_focus", gobject.gboolean, this.GtkWidget.ptr);
lib.lazy_bind("gtk_widget_has_focus", gobject.gboolean, this.GtkWidget.ptr);
lib.lazy_bind("gtk_widget_hide_on_delete", gobject.gboolean, this.GtkWidget.ptr);
lib.lazy_bind("gtk_widget_hide", ctypes.void_t, this.GtkWidget.ptr);
lib.lazy_bind("gtk_widget_show", ctypes.void_t, this.GtkWidget.ptr);

View File

@ -0,0 +1,117 @@
/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
var EXPORTED_SYMBOLS = [ "firetray" ];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
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]);
if ("undefined" == typeof(firetray.Handler))
log.error("This module MUST be imported from/after FiretrayHandler !");
let log = firetray.Logger.getLogger("firetray.ChatStatusIcon");
firetray.ChatStatusIcon = {
GTK_THEME_ICON_PATH: null,
initialized: false,
trayIcon: null,
appId: (function(){return Services.appinfo.ID;})(),
themedIcons: (function(){let o = {};
o[FIRETRAY_IM_STATUS_AVAILABLE] = null;
o[FIRETRAY_IM_STATUS_AWAY] = null;
o[FIRETRAY_IM_STATUS_BUSY] = null;
o[FIRETRAY_IM_STATUS_OFFLINE] = null;
return o;
})(),
callbacks: {onFocusIn: {}},
init: function() {
if (!firetray.Handler.inMailApp) throw "ChatStatusIcon for mail app only";
if (!firetray.GtkIcons.initialized) throw "GtkIcons should have been initialized by StatusIcon";
this.trayIcon = gtk.gtk_status_icon_new();
this.loadThemedIcons();
this.setIconImage(FIRETRAY_IM_STATUS_OFFLINE);
this.setIconTooltipDefault();
this.initialized = true;
return true;
},
shutdown: function() {
this.destroyIcons();
this.initialized = false;
},
loadThemedIcons: function() {
for (let name in this.themedIcons)
this.themedIcons[name] = gio.g_themed_icon_new(name);
},
destroyIcons: function() {
for (let name in this.themedIcons) {
let gicon = this.themedIcons[name];
gicon = gobject.g_object_unref(gicon);
}
gobject.g_object_unref(this.trayIcon);
},
setIconImageFromGIcon: function(gicon) {
if (!firetray.ChatStatusIcon.trayIcon || !gicon)
log.error("Icon missing");
log.debug(gicon);
gtk.gtk_status_icon_set_from_gicon(firetray.ChatStatusIcon.trayIcon, gicon);
},
setIconImage: function(name) {
this.setIconImageFromGIcon(this.themedIcons[name]);
},
setIconBlinking: function(blink) {
gtk.gtk_status_icon_set_blinking(this.trayIcon, blink);
},
setIconTooltip: function(txt) {
if (!this.trayIcon) return false;
gtk.gtk_status_icon_set_tooltip_text(this.trayIcon, txt);
return true;
},
setIconTooltipDefault: function() {
this.setIconTooltip(firetray.Handler.appName+" Chat");
},
attachOnFocusInCallback: function(xid) {
log.debug("attachOnFocusInCallback xid="+xid);
this.callbacks.onFocusIn[xid] = gtk.GCallbackWidgetFocuEvent_t(firetray.ChatStatusIcon.onFocusIn);
gobject.g_signal_connect(firetray.Handler.gtkWindows.get(xid),
"focus-in-event", firetray.ChatStatusIcon.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) {
log.debug("onFocusIn");
// let gdkEventFocus = ctypes.cast(event, gdk.GdkEventFocus.ptr);
// let gdkWin = gdkEventFocus.contents.window;
// let xid = firetray.Window.getXIDFromGdkWindow(gdkWin);
// log.debug("xid="+xid+" in="+gdkEventFocus.contents["in"]);
firetray.Chat.stopIconBlinkingMaybe();
}
// FIXME: TODO: onclick/activate -> chatHandler.showCurrentConversation()
}; // firetray.ChatStatusIcon

View File

@ -0,0 +1,49 @@
/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
var EXPORTED_SYMBOLS = [ "firetray" ];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
Cu.import("resource://firetray/commons.js");
firetray.Handler.subscribeLibsForClosing([gtk]);
if ("undefined" == typeof(firetray.StatusIcon))
log.error("This module MUST be imported from/after StatusIcon !");
let log = firetray.Logger.getLogger("firetray.GtkIcons");
firetray.GtkIcons = {
initialized: false,
GTK_THEME_ICON_PATH: null,
init: function() {
try {
if (this.initialized) return true;
this.loadDefaultTheme();
this.initialized = true;
return true;
} catch (x) {
log.error(x);
return false;
}
},
shutdown: function() {
this.initialized = false;
},
loadDefaultTheme: function() {
this.GTK_THEME_ICON_PATH = firetray.Utils.chromeToPath("chrome://firetray/skin/linux/icons");
log.debug(this.GTK_THEME_ICON_PATH);
let gtkIconTheme = gtk.gtk_icon_theme_get_default();
log.debug("gtkIconTheme="+gtkIconTheme);
gtk.gtk_icon_theme_append_search_path(gtkIconTheme, this.GTK_THEME_ICON_PATH);
}
};

View File

@ -12,6 +12,7 @@ Cu.import("resource://gre/modules/ctypes.jsm");
Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
Cu.import("resource://firetray/commons.js");
firetray.Handler.subscribeLibsForClosing([gobject, gtk]);
let log = firetray.Logger.getLogger("firetray.PopupMenu");
@ -62,7 +63,7 @@ firetray.PopupMenu = {
},
shutdown: function() {
firetray.Utils.tryCloseLibs([gobject, gtk]);
log.debug("Disabling PopupMenu");
this.initialized = false;
},

View File

@ -17,6 +17,7 @@ Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
Cu.import("resource://firetray/ctypes/linux/pango.jsm");
Cu.import("resource://firetray/ctypes/linux/pangocairo.jsm");
Cu.import("resource://firetray/commons.js");
firetray.Handler.subscribeLibsForClosing([cairo, gobject, gdk, gio, gtk, pango, pangocairo]);
let log = firetray.Logger.getLogger("firetray.StatusIcon");
@ -25,7 +26,7 @@ if ("undefined" == typeof(firetray.Handler))
firetray.StatusIcon = {
GTK_THEME_ICON_PATH: null,
FILENAME_BLANK: null,
initialized: false,
callbacks: {}, // pointers to JS functions. MUST LIVE DURING ALL THE EXECUTION
@ -38,26 +39,16 @@ firetray.StatusIcon = {
defaultNewMailIconName: null,
init: function() {
this.FILENAME_BLANK = firetray.Utils.chromeToPath(
"chrome://firetray/skin/blank-icon.png");
Cu.import("resource://firetray/linux/FiretrayGtkIcons.jsm");
firetray.GtkIcons.init();
this.defineIconNames();
this.loadThemedIcons();
try {
this.GTK_THEME_ICON_PATH = firetray.Utils.chromeToPath("chrome://firetray/skin/linux/icons");
log.debug(this.GTK_THEME_ICON_PATH);
let gtkIconTheme = gtk.gtk_icon_theme_get_default();
log.debug("gtkIconTheme="+gtkIconTheme);
gtk.gtk_icon_theme_append_search_path(gtkIconTheme, this.GTK_THEME_ICON_PATH);
this.loadThemedIcons();
this.trayIcon = gtk.gtk_status_icon_new();
} catch (x) {
log.error(x);
return false;
}
this.trayIcon = gtk.gtk_status_icon_new();
firetray.Handler.setIconImageDefault();
firetray.Handler.setIconTooltipDefault();
Cu.import("resource://firetray/linux/FiretrayPopupMenu.jsm");
@ -71,8 +62,10 @@ firetray.StatusIcon = {
},
shutdown: function() {
log.debug("Disabling StatusIcon");
firetray.PopupMenu.shutdown();
firetray.Utils.tryCloseLibs([cairo, gobject, gdk, gio, gtk, pango, pangocairo]);
// FIXME: should destroy/hide icon here
firetray.GtkIcons.shutdown();
this.initialized = false;
},
@ -245,7 +238,8 @@ firetray.Handler.setIconText = function(text, color) { // FIXME: function too lo
try {
// build background from image
let specialIcon = gdk.gdk_pixbuf_new_from_file(this.FILENAME_BLANK, null); // GError **error);
let specialIcon = gdk.gdk_pixbuf_new_from_file(
firetray.StatusIcon.FILENAME_BLANK, null); // GError **error);
let dest = gdk.gdk_pixbuf_copy(specialIcon);
let w = gdk.gdk_pixbuf_get_width(specialIcon);
let h = gdk.gdk_pixbuf_get_height(specialIcon);

View File

@ -23,6 +23,7 @@ Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
Cu.import("resource://firetray/ctypes/linux/libc.jsm");
Cu.import("resource://firetray/ctypes/linux/x11.jsm");
Cu.import("resource://firetray/commons.js");
firetray.Handler.subscribeLibsForClosing([gobject, gdk, gtk, libc, x11, glib]);
let log = firetray.Logger.getLogger("firetray.Window");
@ -63,7 +64,6 @@ firetray.Window = {
},
shutdown: function() {
firetray.Utils.tryCloseLibs([gobject, gdk, gtk, libc, x11, glib]);
this.initialized = false;
},
@ -588,6 +588,11 @@ 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);
if (firetray.Handler.inMailApp && firetray.Chat.initialized) { // missing import ok
Cu.import("resource://firetray/linux/FiretrayChatStatusIcon.jsm");
firetray.ChatStatusIcon.attachOnFocusInCallback(xid);
}
} catch (x) {
firetray.Window.unregisterWindowByXID(xid);
log.error(x);
@ -649,6 +654,29 @@ firetray.Handler.activateLastWindow = function(gtkStatusIcon, gdkEvent, userData
return stopPropagation;
};
/* gtk_window_is_active() not reliable */
firetray.Handler.findActiveWindow = function() {
let rootWin = x11.XDefaultRootWindow(x11.current.Display);
let [propsFound, nitems] =
firetray.Window.getXWindowProperties(rootWin, x11.current.Atoms._NET_ACTIVE_WINDOW);
log.debug("ACTIVE_WINDOW propsFound, nitems="+propsFound+", "+nitems);
if (!propsFound) return null;
let activeWin = null;
if (firetray.js.strEquals(nitems.value, 0))
log.warn("active window not found");
else if (firetray.js.strEquals(nitems.value, 1))
activeWin = propsFound.contents[0];
else
throw new RangeError("more than one active window found");
x11.XFree(propsFound);
log.debug("ACTIVE_WINDOW="+activeWin);
return activeWin;
};
/**
* init X11 Display and handled XAtoms.