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

Merge branch 'winnt'

This commit is contained in:
foudfou 2014-04-12 15:35:19 +02:00
commit 9d08b8203f
44 changed files with 3026 additions and 901 deletions

View File

@ -111,7 +111,9 @@ modules_sources := $(wildcard $(modules_dir)/*.js) \
$(wildcard $(modules_dir)/*.jsm) \
$(wildcard $(modules_dir)/ctypes/*.jsm) \
$(wildcard $(modules_dir)/ctypes/linux/*.jsm) \
$(wildcard $(modules_dir)/linux/*.jsm)
$(wildcard $(modules_dir)/ctypes/winnt/*.jsm) \
$(wildcard $(modules_dir)/linux/*.jsm) \
$(wildcard $(modules_dir)/winnt/*.jsm)
# The components (JSM) dir.
components_dir := components
@ -142,7 +144,7 @@ $(xpi_built): check_version check_loglevel $(build_dir) $(build_includes)
.PHONY: build
build: $(xpi_built)
@echo
@echo "Build finished successfully."
@echo "Build finished successfully in $(build_dir)."
@echo
# This cleans all temporary files and directories created by 'make'.

View File

@ -15,14 +15,15 @@ const TREELEVEL_EXCLUDED_ACCOUNTS = 1;
const PREF_DEFAULT_PANE = "pref-pane-windows";
let log = firetray.Logging.getLogger("firetray.UIOptions");
var firetrayUIOptions = {
strings: null,
prefwindow: null,
listeners: {},
onLoad: function(e) {
log.debug("FULL FEATURED="+firetray.Handler.support['full_feat']);
this.strings = document.getElementById("firetray-options-strings");
this.prefwindow = document.getElementById("firetray-preferences");
if (!this.prefwindow)
@ -36,40 +37,54 @@ var firetrayUIOptions = {
this.hidePrefPane("pref-pane-mail");
}
if (firetray.Handler.isChatProvided()) {
Cu.import("resource://firetray/FiretrayChat.jsm");
if (firetray.Handler.isChatProvided() &&
FIRETRAY_CHAT_SUPPORTED_OS.indexOf(firetray.Handler.runtimeOS) > -1) {
Cu.import("resource://firetray/"+firetray.Handler.runtimeOS+"/FiretrayChat.jsm");
this.initChatControls();
} else
} else {
this.hidePrefPane("pref-pane-chat");
};
this.updateWindowAndIconOptions();
this.updateScrollOptions();
this.initAppIconType();
this.initAppIconNames();
if (firetray.Handler.inMailApp)
this.initNewMailIconNames();
if (firetray.Handler.support['full_feat']) {
this.initAppIconNames();
if (firetray.Handler.inMailApp)
this.initNewMailIconNames();
} else {
this.hideUnsupportedOptions();
};
window.sizeToContent();
},
onQuit: function(e) {
// cleaning: removeEventListener on cells
// NOTE: not sure this is necessary on window close
let tree = document.getElementById("ui_tree_mail_accounts");
let that = this;
for (let i=0, len=tree.view.rowCount; i<len ; ++i) {
let cells = tree.view.getItemAtIndex(i).getElementsByTagName("treecell");
if (tree.view.getLevel(i) === TREELEVEL_SERVER_TYPES) {
// account_or_server_type_excluded, account_or_server_type_order
[cells[1], cells[2]].map(
function(c) {
c.removeEventListener(
'DOMAttrModified', that._userChangeValueTreeServerTypes, true);
});
} else if (tree.view.getLevel(i) === TREELEVEL_EXCLUDED_ACCOUNTS) {
cells[1].removeEventListener(
'DOMAttrModified', that._userChangeValueTree, true);
}
if (firetray.Handler.inMailApp) {
this.removeListeners();
this.removeMailAccountsObserver();
}
},
hideUnsupportedOptions: function() { // full_feat
// windows prefs
['ui_hides_last_only', 'ui_show_activates', 'ui_remember_desktop']
.forEach(function(id){
document.getElementById(id).hidden = true;
});
// icon prefs
['app_icon_default', 'ui_show_icon_on_hide', 'ui_scroll_hides',
'ui_radiogroup_scroll'].forEach(function(id){
document.getElementById(id).hidden = true;
});
document.getElementById("ui_scroll_hides").setAttribute("oncommand", void(0));
// mail prefs
document.getElementById("newmail_icon_names").hidden = true;
for (let i=1; i<4; ++i) {
document.getElementById("radio_mail_notification_newmail_icon_name"+i).
setAttribute("observes", void(0));
}
},
@ -80,8 +95,10 @@ var firetrayUIOptions = {
radio.hidden = true;
},
hideElement: function(targetNode, hiddenval) {
targetNode.hidden = hiddenval;
hideChildren: function(group, hiddenval) {
let children = group.childNodes;
for (let i=0, len=children.length; i<len ; ++i)
children[i].hidden = hiddenval;
},
disableChildren: function(group, disableval) {
@ -119,8 +136,9 @@ var firetrayUIOptions = {
},
updateScrollOptions: function() {
let scroll_hides = document.getElementById("ui_scroll_hides").checked;
this.disableChildren(document.getElementById("ui_radiogroup_scroll"), !scroll_hides);
let ui_scroll_hides = document.getElementById("ui_scroll_hides");
this.disableChildren(document.getElementById("ui_radiogroup_scroll"),
!ui_scroll_hides.checked);
},
initAppIconType: function() {
@ -174,26 +192,28 @@ var firetrayUIOptions = {
if (val) iconNames.push(val);
}
log.debug("iconNames="+iconNames);
firetray.Utils.setArrayPref(prefIconNames, iconNames);
firetray.Utils.setArrayPref(prefIconNames, iconNames); // FIXME: should be a <preference>
},
disableIconTypeMaybe: function(appIconType) {
if (firetray.Handler.support['full_feat']) {
let appIconDefaultGroup = document.getElementById("app_icon_default");
this.disableNChildren(appIconDefaultGroup, 2,
(appIconType !== FIRETRAY_APPLICATION_ICON_TYPE_THEMED));
}
let appIconCustomGroup = document.getElementById("app_icon_custom");
this.disableChildren(appIconCustomGroup,
(appIconType !== FIRETRAY_APPLICATION_ICON_TYPE_CUSTOM));
let appIconDefaultGroup = document.getElementById("app_icon_default");
this.disableNChildren(appIconDefaultGroup, 2,
(appIconType !== FIRETRAY_APPLICATION_ICON_TYPE_THEMED));
},
initMailControls: function() {
this.populateExcludedFoldersList();
this.populateTreeAccountsOrServerTypes();
this.addMailAccountsObserver();
this.initMessageCountSettings();
this.initNotificationSettings();
this.initMailTrigger();
this.toggleNotifications(firetray.Utils.prefService.getBoolPref("mail_notification_enabled"));
},
@ -208,16 +228,20 @@ var firetrayUIOptions = {
FIRETRAY_NOTIFICATION_MESSAGE_COUNT;
document.getElementById("ui_radio_mail_notification_newmail_icon").value =
FIRETRAY_NOTIFICATION_NEWMAIL_ICON;
document.getElementById("ui_radio_mail_notification_custom_mail_icon").value =
document.getElementById("ui_radio_mail_notification_mail_icon_custom").value =
FIRETRAY_NOTIFICATION_CUSTOM_ICON;
document.getElementById("ui_mail_notification_enabled").checked =
(firetray.Utils.prefService.getBoolPref("mail_notification_enabled"));
let radioMailNotify = document.getElementById("ui_radiogroup_mail_notification");
let mailNotifyRadio = document.getElementById("ui_radiogroup_mail_notification");
let prefMailNotificationType = firetray.Utils.prefService.getIntPref("mail_notification_type");
radioMailNotify.selectedIndex = this.radioGetIndexByValue(radioMailNotify, prefMailNotificationType);
mailNotifyRadio.selectedIndex = this.radioGetIndexByValue(mailNotifyRadio, prefMailNotificationType);
// this.disableNotificationMaybe(prefMailNotificationType); // done in toggleNotifications()
/* We need to ensure assigning selectedIndex in disableMessageCountMaybe()
does change the corresponding preference. */
let listener = {evt:'select', fn:firetrayUIOptions.userChangedValue, cap:true};
this.addListener(mailNotifyRadio, listener);
},
initMessageCountSettings: function() {
@ -243,30 +267,25 @@ var firetrayUIOptions = {
blinkStyle.selectedIndex = this.radioGetIndexByValue(blinkStyle, prefBlinkStyle);
},
userChangedValue: function(e) {
document.getElementById('pref-pane-mail').userChangedValue(e.originalTarget);
},
radioGetIndexByValue: function(radio, value) {
for (let i=0, len=radio.itemCount; i<len; ++i)
if (+radio.getItemAtIndex(i).value == value) return i;
return -1;
},
initMailTrigger: function() {
document.getElementById("ui_mail_change_trigger").value =
firetray.Utils.prefService.getCharPref("mail_change_trigger");
},
updateMailTrigger: function() {
let mailTrigger = document.getElementById("ui_mail_change_trigger").value.trim();
firetray.Utils.prefService.setCharPref("mail_change_trigger", mailTrigger);
saveMailChangeTrigger: function(uiElt) {
return uiElt.value.trim();
},
updateNotificationSettings: function() {
log.debug("updateNotificationSettings");
let radioMailNotify = document.getElementById("ui_radiogroup_mail_notification");
let mailNotificationType = +radioMailNotify.getItemAtIndex(radioMailNotify.selectedIndex).value;
firetray.Utils.prefService.setIntPref("mail_notification_type", mailNotificationType);
let mailNotifyRadio = document.getElementById("ui_radiogroup_mail_notification");
let mailNotificationType = +mailNotifyRadio.getItemAtIndex(mailNotifyRadio.selectedIndex).value;
this.disableNotificationMaybe(mailNotificationType);
firetray.Messaging.updateIcon();
},
updateMessageCountSettings: function() {
@ -275,12 +294,6 @@ var firetrayUIOptions = {
this.disableMessageCountMaybe(messageCountType);
},
updateChatBlinkSettings: function() {
let radioBlinkStyle = document.getElementById("ui_chat_icon_blink_style");
let blinkStyle = +radioBlinkStyle.getItemAtIndex(radioBlinkStyle.selectedIndex).value;
firetray.Utils.prefService.setIntPref("chat_icon_blink_style", blinkStyle);
},
disableNotificationMaybe: function(notificationSetting) {
log.debug("disableNotificationMaybe: "+notificationSetting);
@ -288,11 +301,13 @@ var firetrayUIOptions = {
this.disableChildren(iconTextColor,
(notificationSetting !== FIRETRAY_NOTIFICATION_MESSAGE_COUNT));
let newMailIconNames = document.getElementById("newmail_icon_names");
this.disableNChildren(newMailIconNames, 2,
(notificationSetting !== FIRETRAY_NOTIFICATION_NEWMAIL_ICON));
if (firetray.Handler.support['full_feat']) {
let newMailIconNames = document.getElementById("newmail_icon_names");
this.disableNChildren(newMailIconNames, 2,
(notificationSetting !== FIRETRAY_NOTIFICATION_NEWMAIL_ICON));
}
let customIconGroup = document.getElementById("custom_mail_icon");
let customIconGroup = document.getElementById("mail_icon_custom");
this.disableChildren(customIconGroup,
(notificationSetting !== FIRETRAY_NOTIFICATION_CUSTOM_ICON));
},
@ -304,13 +319,14 @@ var firetrayUIOptions = {
let notificationUnreadCount = document.getElementById("ui_mail_notification_unread_count");
this.disableElementsRecursive(notificationUnreadCount, msgCountTypeIsNewMessages);
let radioMailNotify = document.getElementById("ui_radiogroup_mail_notification");
let mailNotificationType = +radioMailNotify.getItemAtIndex(radioMailNotify.selectedIndex).value;
let mailNotifyRadio = document.getElementById("ui_radiogroup_mail_notification");
let mailNotificationType = +mailNotifyRadio.getItemAtIndex(mailNotifyRadio.selectedIndex).value;
if (msgCountTypeIsNewMessages && (mailNotificationType === FIRETRAY_NOTIFICATION_MESSAGE_COUNT)) {
radioMailNotify.selectedIndex = this.radioGetIndexByValue(radioMailNotify, FIRETRAY_NOTIFICATION_NEWMAIL_ICON);
let newMailIconNames = document.getElementById("newmail_icon_names");
this.disableNChildren(newMailIconNames, 2, false);
firetray.Utils.prefService.setIntPref("mail_notification_type", FIRETRAY_NOTIFICATION_NEWMAIL_ICON);
mailNotifyRadio.selectedIndex = this.radioGetIndexByValue(mailNotifyRadio, FIRETRAY_NOTIFICATION_NEWMAIL_ICON);
if (firetray.Handler.support['full_feat']) {
let newMailIconNames = document.getElementById("newmail_icon_names");
this.disableNChildren(newMailIconNames, 2, false);
}
}
},
@ -357,7 +373,7 @@ var firetrayUIOptions = {
chooseMailIconFile: function() {
let updateIcon = firetray.Messaging.updateIcon.bind(firetray.Messaging);
this._chooseIconFile("custom_mail_icon_filename", updateIcon);
this._chooseIconFile("mail_icon_custom_filename", updateIcon);
},
_chooseIconFile: function(elementId, callback) {
@ -377,7 +393,10 @@ var firetrayUIOptions = {
}};
filePicker.init(window, "Select Icon", nsIFilePicker.modeOpen); // FIXME: i18n
filePicker.appendFilters(nsIFilePicker.filterImages);
if (firetray.Handler.runtimeOS === "winnt")
filePicker.appendFilter("Icon", "*.bmp; *.ico"); // TODO: support more formats ?
else
filePicker.appendFilters(nsIFilePicker.filterImages);
filePicker.open(fpCallback);
},
@ -389,44 +408,52 @@ var firetrayUIOptions = {
},
/**
* NOTE: folder exceptions for unread messages count are *stored* in
* preferences as excluded, but *shown* as "not included"
* NOTE: we store folder type *exceptions* for unread messages count. This is
* easier than storing all possible included folder types. The drawback is
* that we must inverse the selection in the UI: we show exceptions as "not
* included".
*/
populateExcludedFoldersList: function() {
let excludedFoldersList = document.getElementById('excluded_folders_list');
let prefExcludedFoldersFlags = firetray.Utils.prefService
.getIntPref("excluded_folders_flags");
log.debug("prefExcludedFoldersFlags="+prefExcludedFoldersFlags.toString(16));
for (let folderType in FLDRS_UNINTERESTING) {
let localizedFolderType = this.strings.getString(folderType);
let item = excludedFoldersList.appendItem(localizedFolderType, folderType);
let folderTypeVal = FLDRS_UNINTERESTING[folderType];
let item = excludedFoldersList.appendItem(localizedFolderType, folderTypeVal);
item.setAttribute("observes", "broadcaster-notification-disabled");
log.debug("folder: "+folderType);
if (!(FLDRS_UNINTERESTING[folderType] & prefExcludedFoldersFlags)) {
let folderTypeSet = (folderTypeVal & prefExcludedFoldersFlags);
log.debug("folder: "+folderType+" folderTypeVal="+folderTypeVal+" folderTypeSet="+folderTypeSet);
if (!folderTypeSet) {
excludedFoldersList.ensureElementIsVisible(item); // bug 326445
excludedFoldersList.addItemToSelection(item); // doesn't trigger onselect
excludedFoldersList.addItemToSelection(item); // does trigger onselect...
}
}
// ...so we add onselect handler after the listbox is populated. 'select'
// also fired on unselect.
let listener = {evt:'select', fn:firetrayUIOptions.userChangedValue, cap:true};
this.addListener(excludedFoldersList, listener);
},
updateExcludedFoldersPref: function() {
let excludedFoldersList = document.getElementById('excluded_folders_list');
loadExcludedFoldersFlags: function(uiElt) {
// we can't do much here since onLoad() not yet applied at onsyncfrompreference...
},
log.debug("LAST SELECTED: "+excludedFoldersList.currentItem.label);
let excludedFoldersFlags = null;
for (let i = 0, len=excludedFoldersList.itemCount; i<len; ++i) {
let folder = excludedFoldersList.getItemAtIndex(i);
saveExcludedFoldersFlags: function(uiElt) {
log.debug("LAST SELECTED: "+uiElt.currentItem.label);
let excludedFoldersFlags = 0;
for (let i = 0, len=uiElt.itemCount; i<len; ++i) {
let folder = uiElt.getItemAtIndex(i);
if (folder.selected)
excludedFoldersFlags &= ~FLDRS_UNINTERESTING[folder.value];
excludedFoldersFlags &= ~folder.value; // clear
else
excludedFoldersFlags |= FLDRS_UNINTERESTING[folder.value];
excludedFoldersFlags |= folder.value; // set
}
log.debug("excluded folders flags: "+excludedFoldersFlags);
firetray.Utils.prefService.setIntPref("excluded_folders_flags",
excludedFoldersFlags);
firetray.Messaging.updateMsgCountWithCb();
log.debug("excluded folders flags: "+excludedFoldersFlags.toString(16));
return excludedFoldersFlags;
},
/**
@ -442,15 +469,11 @@ var firetrayUIOptions = {
if (disable === true) {
cells[i].setAttribute('properties', "disabled");
if (i === TREEROW_ACCOUNT_OR_SERVER_TYPE_EXCLUDED) {
cells[i].removeEventListener(
'DOMAttrModified', that._userChangeValueTree, true);
cells[i].setAttribute('editable', "false");
}
} else {
cells[i].removeAttribute('properties');
if (i === TREEROW_ACCOUNT_OR_SERVER_TYPE_EXCLUDED) {
cells[i].addEventListener(
'DOMAttrModified', that._userChangeValueTree, true);
cells[i].setAttribute('editable', "true");
}
}
@ -460,39 +483,6 @@ var firetrayUIOptions = {
}
},
/**
* needed for triggering actual preference change and saving
*/
_userChangeValueTree: function(event) {
if (event.attrName == "label") log.debug("label changed!");
if (event.attrName == "value") log.debug("value changed!");
document.getElementById("pref-pane-mail")
.userChangedValue(document.getElementById("ui_tree_mail_accounts"));
firetray.Messaging.updateMsgCountWithCb();
},
_userChangeValueTreeServerTypes: function(event) {
if (event.attrName === "value") { // checkbox
let checkboxCell = event.originalTarget;
let tree = document.getElementById("ui_tree_mail_accounts");
let subRows = firetray.Utils.XPath(
checkboxCell,
'ancestor::xul:treeitem[1]/child::xul:treechildren/xul:treeitem/xul:treerow');
log.debug("subRows="+subRows);
for (let i=0, len=subRows.length; i<len; ++i) {
firetrayUIOptions._disableTreeRow(
subRows[i], (checkboxCell.getAttribute("value") === "false"));
}
} else if (event.attrName == "label") { // text
log.warn("NOT IMPLEMENTED YET: move row to new rank"); // TODO
}
this._userChangeValueTree(event);
},
/**
* NOTE: account exceptions for unread messages count are *stored* in
* preferences as excluded, but *shown* as "not included"
@ -541,15 +531,11 @@ var firetrayUIOptions = {
// account_or_server_type_excluded => checkbox
let cellExcluded = document.createElement('treecell');
cellExcluded.setAttribute('value',!serverTypes[serverTypeName].excluded);
cellExcluded.addEventListener( // CAUTION: removeEventListener in onQuit()
'DOMAttrModified', that._userChangeValueTreeServerTypes, true);
typeRow.appendChild(cellExcluded);
// account_or_server_type_order
let cellOrder = document.createElement('treecell');
cellOrder.setAttribute('label',serverTypes[serverTypeName].order);
cellOrder.addEventListener( // CAUTION: removeEventListener in onQuit()
'DOMAttrModified', that._userChangeValueTreeServerTypes, true);
typeRow.appendChild(cellOrder);
target.appendChild(typeItem);
@ -585,9 +571,6 @@ var firetrayUIOptions = {
if (rowDisabled === true) {
accountCell.setAttribute('properties', "disabled");
accountCell.setAttribute('editable', "false");
} else {
accountCell.addEventListener( // CAUTION: removeEventListener in onQuit()
'DOMAttrModified', that._userChangeValueTree, true);
}
accountRow.appendChild(accountCell);
@ -649,14 +632,72 @@ var firetrayUIOptions = {
}
let tree = document.getElementById("ui_tree_mail_accounts");
tree.addEventListener("keypress", that.onKeyPressTreeAccountsOrServerTypes, true);
let listener = {evt:'keypress', fn:firetrayUIOptions.onKeyPressTreeAccountsOrServerTypes, cap:true};
this.addListener(tree, listener);
},
addListener: function(elt, listenerData) {
elt.addEventListener(listenerData['evt'], listenerData['fn'], listenerData['cap']);
this.listeners[elt.id] = listenerData;
},
removeListeners: function() {
for (id in this.listeners) {
let listener = listeners[id];
document.getElementById(id)
.removeEventListener(listener['evt'], listener['fn'], listener['cap']);
}
},
onMutation: function(mutation) {
log.debug("mutation: type="+mutation.type+" node="+mutation.target.nodeName+" attr="+mutation.attributeName);
if (mutation.type !== "attributes") return;
if (mutation.attributeName === "value") { // checkbox
log.debug("value changed!");
let checkboxCell = mutation.target;
let tree = document.getElementById("ui_tree_mail_accounts");
let subRows = firetray.Utils.XPath(
checkboxCell,
'ancestor::xul:treeitem[1]/child::xul:treechildren/xul:treeitem/xul:treerow');
log.debug("subRows="+subRows);
for (let i=0, len=subRows.length; i<len; ++i) {
firetrayUIOptions._disableTreeRow(
subRows[i], (checkboxCell.getAttribute("value") === "false"));
}
} else if (mutation.attributeName == "label") { // text
log.debug("label changed!");
log.warn("NOT IMPLEMENTED YET: move row to new rank"); // TODO
} else {
return;
}
document.getElementById("pref-pane-mail")
.userChangedValue(document.getElementById("ui_tree_mail_accounts"));
},
addMailAccountsObserver: function() {
this.mutationObserver = new MutationObserver(function(mutations) {
mutations.forEach(firetrayUIOptions.onMutation);
});
let config = { attributes: true, childList: true, characterData: false, subtree: true };
let target = document.querySelector('#ui_mail_accounts');
this.mutationObserver.observe(target, config);
},
removeMailAccountsObserver: function() {
this.mutationObserver.disconnect();
this.mutationobserver = null;
},
/*
* Save the "mail_accounts" preference. This is called by the pref's system
* when the GUI element is altered.
*/
saveTreeAccountsOrServerTypes: function() {
saveTreeAccountsOrServerTypes: function() { // FIXME: broken ?
let tree = document.getElementById("ui_tree_mail_accounts");
log.debug("VIEW="+ tree.view + ", rowCount="+tree.view.rowCount);

View File

@ -67,7 +67,7 @@
<preferences>
<preference id="pref_app_icon_type" name="extensions.firetray.app_icon_type" type="int"/>
<preference id="pref_app_icon_filename" name="extensions.firetray.app_icon_filename" type="string"/>
<preference id="pref_app_icon_custom" name="extensions.firetray.app_icon_custom" type="string"/>
<preference id="pref_show_icon_on_hide" name="extensions.firetray.show_icon_on_hide" type="bool"/>
<preference id="pref_scroll_hides" name="extensions.firetray.scroll_hides" type="bool" />
<preference id="pref_scroll_mode" name="extensions.firetray.scroll_mode" type="string" />
@ -97,7 +97,7 @@
<radio id="ui_app_icon_type_custom" label="&app_icon_custom.label;"
accesskey="&app_icon_custom.accesskey;" />
<hbox id="app_icon_custom" align="center" flex="1" >
<textbox id="app_icon_custom_filename" preference="pref_app_icon_filename" flex="1" />
<textbox id="app_icon_custom_filename" preference="pref_app_icon_custom" flex="1" />
<button id="app_icon_custom_select" label="&choose;"
accesskey="&choose.accesskey;"
oncommand="firetrayUIOptions.chooseAppIconFile()" />
@ -129,14 +129,17 @@
<preferences>
<preference id="pref_mail_notification_enabled" name="extensions.firetray.mail_notification_enabled" type="bool" />
<preference id="pref_mail_notification_type" name="extensions.firetray.mail_notification_type" type="int" />
<preference id="pref_message_count_type" name="extensions.firetray.message_count_type" type="int" />
<preference id="pref_icon_text_color" name="extensions.firetray.icon_text_color" type="string" />
<preference id="pref_custom_mail_icon" name="extensions.firetray.custom_mail_icon" type="string" />
<preference id="pref_mail_icon_custom" name="extensions.firetray.mail_icon_custom" type="string" />
<preference id="pref_excluded_folders_flags" name="extensions.firetray.excluded_folders_flags" type="int" />
<preference id="pref_mail_change_trigger" name="extensions.firetray.mail_change_trigger" type="string"/>
<preference id="pref_mail_accounts" name="extensions.firetray.mail_accounts" type="string"/>
<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" />
<preference id="pref_mail_urgency_hint" name="extensions.firetray.mail_urgency_hint" type="bool" />
<preference id="pref_mail_change_trigger" name="extensions.firetray.mail_change_trigger" type="string" />
<preference id="pref_mail_get_attention" name="extensions.firetray.mail_get_attention" type="bool" />
</preferences>
<vbox align="left" flex="1">
@ -169,7 +172,7 @@
observes="broadcaster-notification-disabled" />
</caption>
<radiogroup id="ui_radiogroup_mail_notification">
<radiogroup id="ui_radiogroup_mail_notification" preference="pref_mail_notification_type">
<hbox id="ui_mail_notification_unread_count">
<radio id="ui_radio_mail_notification_unread_count" label="&mail_notification_unread_count.label;"
accesskey="&mail_notification_unread_count.accesskey;"
@ -207,15 +210,14 @@
</hbox>
</hbox>
<hbox>
<radio id="ui_radio_mail_notification_custom_mail_icon" label="&mail_notification_custom_mail_icon.label;"
accesskey="&mail_notification_custom_mail_icon.accesskey;"
<radio id="ui_radio_mail_notification_mail_icon_custom" label="&mail_notification_mail_icon_custom.label;"
accesskey="&mail_notification_mail_icon_custom.accesskey;"
oncommand="firetrayUIOptions.updateNotificationSettings()"
observes="broadcaster-notification-disabled" />
<hbox id="custom_mail_icon" align="center" flex="1" >
<textbox id="custom_mail_icon_filename" preference="pref_custom_mail_icon"
observes="broadcaster-notification-disabled"
onchange="firetray.Messaging.updateIcon();" flex="1" />
<button id="custom_mail_icon_select" label="&choose;"
<hbox id="mail_icon_custom" align="center" flex="1" >
<textbox id="mail_icon_custom_filename" preference="pref_mail_icon_custom"
observes="broadcaster-notification-disabled" flex="1" />
<button id="mail_icon_custom_select" label="&choose;"
accesskey="&choose.accesskey;"
observes="broadcaster-notification-disabled"
oncommand="firetrayUIOptions.chooseMailIconFile()" />
@ -238,7 +240,8 @@
<listbox id="excluded_folders_list" rows="7" flex="1" seltype="multiple"
tooltiptext="&excluded_folders_list.tooltip;"
onselect="firetrayUIOptions.updateExcludedFoldersPref()"
preference="pref_excluded_folders_flags"
onsynctopreference="return firetrayUIOptions.saveExcludedFoldersFlags(this);"
observes="broadcaster-notification-disabled" />
</groupbox>
@ -295,15 +298,16 @@
<label control="ui_mail_change_trigger" value="&mail_change_trigger.label;"
accesskey="&mail_change_trigger.accesskey;" />
<textbox id="ui_mail_change_trigger" size="18" placeholder="&mail_change_trigger.placeholder;"
onchange="firetrayUIOptions.updateMailTrigger();" flex="1"
tooltiptext="&mail_change_trigger.tooltip;"
tooltiptext="&mail_change_trigger.tooltip;" flex="1"
preference="pref_mail_change_trigger"
onsynctopreference="return firetrayUIOptions.saveMailChangeTrigger(this);"
observes="broadcaster-notification-disabled" />
</hbox>
<checkbox id="ui_mail_urgency_hint"
label="&mail_urgency_hint.label;"
accesskey="&mail_urgency_hint.accesskey;"
preference="pref_mail_urgency_hint"
<checkbox id="ui_mail_get_attention"
label="&mail_get_attention.label;"
accesskey="&mail_get_attention.accesskey;"
preference="pref_mail_get_attention"
observes="broadcaster-notification-disabled"/>
</vbox>
@ -338,10 +342,8 @@
<label control="ui_chat_icon_blink_style" observes="broadcaster-chat-icon-disabled"
value="&chat_icon_blink_style.label;" accesskey="&chat_icon_blink_style.accesskey;" />
<radio id="ui_chat_icon_blink_style_normal" label="&chat_icon_blink_style_normal;"
oncommand="firetrayUIOptions.updateChatBlinkSettings()"
observes="broadcaster-chat-icon-disabled"/>
<radio id="ui_chat_icon_blink_style_fade" label="&chat_icon_blink_style_fade;"
oncommand="firetrayUIOptions.updateChatBlinkSettings()"
observes="broadcaster-chat-icon-disabled"/>
</hbox>
</radiogroup>

View File

@ -27,16 +27,17 @@ var firetrayChrome = { // each new window gets a new firetrayChrome !
this.winId = firetray.Handler.registerWindow(win);
win.addEventListener('close', firetrayChrome.onClose, true);
this.hijackTitlebarButtons();
firetray_log.debug('Firetray LOADED: ' + init);
return true;
},
/* NOTE: don't do firetray.Handler.initialized=false here, otherwise after a
window close, a new window will create a new handler (and hence, a new tray
icon) */
onQuit: function(win) {
win.removeEventListener('close', firetrayChrome.onClose, true);
firetray.Handler.unregisterWindow(win);
firetray_log.info("windowsCount="+firetray.Handler.windowsCount+", visibleWindowsCount="+firetray.Handler.visibleWindowsCount);
firetray_log.debug('Firetray UNLOADED !');
@ -49,24 +50,96 @@ var firetrayChrome = { // each new window gets a new firetrayChrome !
called *after* the popup */
onClose: function(event) {
firetray_log.debug('Firetray CLOSE');
let win = event.originalTarget;
if (!win instanceof ChromeWindow)
throw new TypeError('originalTarget not a ChromeWindow');
let hides_on_close = firetray.Utils.prefService.getBoolPref('hides_on_close');
firetray_log.debug('hides_on_close: '+hides_on_close);
if (hides_on_close) {
let hides_single_window = firetray.Utils.prefService.getBoolPref('hides_single_window');
let hides_last_only = firetray.Utils.prefService.getBoolPref('hides_last_only');
firetray_log.debug('hides_single_window='+hides_single_window+', windowsCount='+firetray.Handler.windowsCount);
if (hides_last_only && (firetray.Handler.windowsCount > 1)) return;
if (!hides_on_close) return false;
if (hides_single_window)
firetray.Handler.hideWindow(firetrayChrome.winId);
else
firetray.Handler.hideAllWindows();
let hides_single_window = firetray.Utils.prefService.getBoolPref('hides_single_window');
let hides_last_only = firetray.Utils.prefService.getBoolPref('hides_last_only');
firetray_log.debug('hides_single_window='+hides_single_window+', windowsCount='+firetray.Handler.windowsCount);
if (hides_last_only && (firetray.Handler.windowsCount > 1)) return true;
event && event.preventDefault();
if (hides_single_window)
firetray.Handler.hideWindow(firetrayChrome.winId);
else
firetray.Handler.hideAllWindows();
if (event) event.preventDefault();
return true;
},
/*
* Minimize/Restore/Close buttons can be overlayed by titlebar (fake) buttons
* which do not fire the events that we rely on (see Bug 827880). This is why
* we override the fake buttons' default actions.
*/
hijackTitlebarButtons: function() {
Object.keys(this.titlebarDispatch).map(function(val, idx) {
let fInfo = firetrayChrome.replaceCommand(val, firetrayChrome.titlebarDispatch[val]['new']);
if (fInfo) {
firetrayChrome.titlebarDispatch[val]['old'] = fInfo[0];
firetray_log.debug('replaced command='+val+' type='+fInfo[1]+' func='+fInfo[0]);
firetrayChrome.titlebarDispatch[val]['type'] = fInfo[1];
}
});
},
titlebarDispatch: {
"titlebar-min": {new: function(e){
firetray_log.debug(' titlebar-min clicked');
if (!firetray.Handler.onMinimize(firetrayChrome.winId))
firetrayChrome.applyDefaultCommand("titlebar-min");
}, old: null, type: null},
"titlebar-close": {new: function(e){
firetray_log.debug(' titlebar-close clicked');
if (!firetrayChrome.onClose(null)) {
firetrayChrome.applyDefaultCommand("titlebar-close");
}
}, old: null, type: null}
},
replaceCommand: function(eltId, func) {
let elt = document.getElementById(eltId);
if (!elt) {
firetray_log.info("Element '"+eltId+"' not found. Command not replaced.");
return null;
}
let command = elt.command;
let oncommand = elt.getAttribute("oncommand");
let old = null, type = null;
if (command) {
firetray_log.debug('command');
type = FIRETRAY_XUL_ATTRIBUTE_COMMAND;
old = elt.command;
elt.command = null;
elt.addEventListener('click', func, false);
} else if (oncommand) {
firetray_log.debug('oncommand');
type = FIRETRAY_XUL_ATTRIBUTE_ONCOMMAND;
let prev = elt.getAttribute("oncommand");
old = new Function(prev);
elt.setAttribute("oncommand", void(0));
elt.addEventListener('command', func, false);
} else {
firetray_log.warn('Could not replace oncommand on '+eltId);
}
return [old, type];
},
applyDefaultCommand: function(key) {
let callType = this.titlebarDispatch[key]['type'];
if (callType === FIRETRAY_XUL_ATTRIBUTE_COMMAND) {
let cmdName = firetrayChrome.titlebarDispatch[key]['old'];
let cmd = document.getElementById(cmdName);
cmd.doCommand();
} else if (callType === FIRETRAY_XUL_ATTRIBUTE_ONCOMMAND) {
firetrayChrome.titlebarDispatch[key]['old']();
} else {
firetray_log.error("Calling type undefined for "+key);
}
}

View File

@ -2,7 +2,7 @@
<?xml-stylesheet href="chrome://firetray/skin/overlay.css" type="text/css"?>
<!DOCTYPE overlay SYSTEM "chrome://firetray/locale/overlay.dtd">
<overlay id="firetray-overlay" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript;version=1.7" src="overlay.js"/>
<script type="application/javascript;version=1.8" src="overlay.js"/>
<stringbundleset id="stringbundleset">
<stringbundle id="firetray-strings" src="chrome://firetray/locale/overlay.properties"/>

View File

@ -57,8 +57,8 @@
<!ENTITY mail_notification_unread_count.accesskey "U">
<!ENTITY mail_notification_newmail_icon.label "display newmail icon">
<!ENTITY mail_notification_newmail_icon.accesskey "N">
<!ENTITY mail_notification_custom_mail_icon.label "display custom icon">
<!ENTITY mail_notification_custom_mail_icon.accesskey "I">
<!ENTITY mail_notification_mail_icon_custom.label "display custom icon">
<!ENTITY mail_notification_mail_icon_custom.accesskey "I">
<!ENTITY icon_text_color "Text color">
<!ENTITY icon_text_color.accesskey "T">
<!ENTITY choose "Choose">
@ -87,8 +87,8 @@
<!ENTITY mail_change_trigger.accesskey "L">
<!ENTITY mail_change_trigger.placeholder "/bin/notify-send">
<!ENTITY mail_change_trigger.tooltip "Absolute path of the program to run when the message count changes. This program will get the new message count as the first argument.">
<!ENTITY mail_urgency_hint.label "Set X11 urgency hint">
<!ENTITY mail_urgency_hint.accesskey "X">
<!ENTITY mail_get_attention.label "Window draws attention on new messages">
<!ENTITY mail_get_attention.accesskey "g">
<!ENTITY chat_icon_enable.label "Enable chat icon">
<!ENTITY chat_icon_enable.accesskey "E">

View File

@ -51,8 +51,8 @@
<!ENTITY mail_notification_unread_count.accesskey "U">
<!ENTITY mail_notification_newmail_icon.label "visualizar ícono de correo nuevo">
<!ENTITY mail_notification_newmail_icon.accesskey "n">
<!ENTITY mail_notification_custom_mail_icon.label "visualizar ícono personalizado">
<!ENTITY mail_notification_custom_mail_icon.accesskey "p">
<!ENTITY mail_notification_mail_icon_custom.label "visualizar ícono personalizado">
<!ENTITY mail_notification_mail_icon_custom.accesskey "p">
<!ENTITY icon_text_color "Color de texto">
<!ENTITY icon_text_color.accesskey "C">
<!ENTITY choose "Seleccionar">
@ -77,8 +77,8 @@
<!ENTITY mail_change_trigger.accesskey "L">
<!ENTITY mail_change_trigger.placeholder "/bin/notify-send">
<!ENTITY mail_change_trigger.tooltip "Ruta absoluta del programa para correr cuando la cuenta de mensajes cambia. Este programa obtendrá recibira el contador de nuevos mensajes como primer argumento.">
<!ENTITY mail_urgency_hint.label "Set X11 urgency hint">
<!ENTITY mail_urgency_hint.accesskey "X">
<!ENTITY mail_get_attention.label "Window draws attention on new messages">
<!ENTITY mail_get_attention.accesskey "g">
<!ENTITY chat_icon_enable.label "Habilitar ícono de chat">
<!ENTITY chat_icon_enable.accesskey "H">
<!ENTITY chat_icon_blink.label "Icono de chat parpadea con mensajes nuevos.">

View File

@ -51,8 +51,8 @@
<!ENTITY mail_notification_unread_count.accesskey "N">
<!ENTITY mail_notification_newmail_icon.label "afficher l'icône de nouveau message">
<!ENTITY mail_notification_newmail_icon.accesskey "f">
<!ENTITY mail_notification_custom_mail_icon.label "afficher l'icône personnalisée">
<!ENTITY mail_notification_custom_mail_icon.accesskey "i">
<!ENTITY mail_notification_mail_icon_custom.label "afficher l'icône personnalisée">
<!ENTITY mail_notification_mail_icon_custom.accesskey "i">
<!ENTITY icon_text_color "Couleur du texte">
<!ENTITY icon_text_color.accesskey "T">
<!ENTITY choose "Choisir">
@ -77,10 +77,10 @@
<!ENTITY mail_change_trigger.accesskey "L">
<!ENTITY mail_change_trigger.placeholder "/bin/notify-send">
<!ENTITY mail_change_trigger.tooltip "Chemin absolu du programme à lancer lorsque le compte des messages a changé. Ce programme prendra pour base le nouveau décompte de messages">
<!ENTITY mail_urgency_hint.label "Activer l'indice d'urgence X11">
<!ENTITY mail_urgency_hint.accesskey "X">
<!ENTITY mail_get_attention.label "La fenêtre attire l'attention lors de nouveaux messages">
<!ENTITY mail_get_attention.accesskey "e">
<!ENTITY chat_icon_enable.label "Activer l'icône du chat">
<!ENTITY chat_icon_enable.accesskey "A">
<!ENTITY chat_icon_enable.accesskey "h">
<!ENTITY chat_icon_blink.label "L'icône du chat clignote à l'arrivée de nouveaux messages">
<!ENTITY chat_icon_blink.accesskey "i">
<!ENTITY chat_icon_blink.tooltip "quand un message privé est cité sur un canal">

View File

@ -51,8 +51,8 @@
<!ENTITY mail_notification_unread_count.accesskey "U">
<!ENTITY mail_notification_newmail_icon.label "Visualizza l'icona per i nuovi messaggi">
<!ENTITY mail_notification_newmail_icon.accesskey "N">
<!ENTITY mail_notification_custom_mail_icon.label "Visualizza icona personalizzata">
<!ENTITY mail_notification_custom_mail_icon.accesskey "I">
<!ENTITY mail_notification_mail_icon_custom.label "Visualizza icona personalizzata">
<!ENTITY mail_notification_mail_icon_custom.accesskey "I">
<!ENTITY icon_text_color "Colore del testo">
<!ENTITY icon_text_color.accesskey "T">
<!ENTITY choose "Scegli">
@ -77,8 +77,8 @@
<!ENTITY mail_change_trigger.accesskey "L">
<!ENTITY mail_change_trigger.placeholder "/bin/notify-send">
<!ENTITY mail_change_trigger.tooltip "Percorso assoluto del programma da lanciare quando viene incrementato il contatore dei messaggi non letti. Il programma utilizzerà il contatore dei nuovi messaggi come primo argomento.">
<!ENTITY mail_urgency_hint.label "Set X11 urgency hint">
<!ENTITY mail_urgency_hint.accesskey "X">
<!ENTITY mail_get_attention.label "Window draws attention on new messages">
<!ENTITY mail_get_attention.accesskey "g">
<!ENTITY chat_icon_enable.label "Attiva icona della chat">
<!ENTITY chat_icon_enable.accesskey "E">
<!ENTITY chat_icon_blink.label "Lampeggiamento dell'icona della chat per nuovi messaggi">

View File

@ -51,8 +51,8 @@
<!ENTITY mail_notification_unread_count.accesskey "O">
<!ENTITY mail_notification_newmail_icon.label "pictogram nieuw bericht weergeven">
<!ENTITY mail_notification_newmail_icon.accesskey "N">
<!ENTITY mail_notification_custom_mail_icon.label "aangepast pictogram weergeven">
<!ENTITY mail_notification_custom_mail_icon.accesskey "i">
<!ENTITY mail_notification_mail_icon_custom.label "aangepast pictogram weergeven">
<!ENTITY mail_notification_mail_icon_custom.accesskey "i">
<!ENTITY icon_text_color "Tekstkleur">
<!ENTITY icon_text_color.accesskey "T">
<!ENTITY choose "Kiezen">
@ -77,8 +77,8 @@
<!ENTITY mail_change_trigger.accesskey "S">
<!ENTITY mail_change_trigger.placeholder "/bin/notify-send">
<!ENTITY mail_change_trigger.tooltip "Absoluut pad van het uit te voeren programma wanneer het aantal wijzigt. Dit programma krijgt het aantal nieuwe berichten als het eerste argument.">
<!ENTITY mail_urgency_hint.label "Set X11 urgency hint">
<!ENTITY mail_urgency_hint.accesskey "X">
<!ENTITY mail_get_attention.label "Window draws attention on new messages">
<!ENTITY mail_get_attention.accesskey "g">
<!ENTITY chat_icon_enable.label "Chatpictogram inschakelen">
<!ENTITY chat_icon_enable.accesskey "s">
<!ENTITY chat_icon_blink.label "Chatpictogram knippert bij nieuwe berichten">

View File

@ -57,8 +57,8 @@
<!ENTITY mail_notification_unread_count.accesskey "P">
<!ENTITY mail_notification_newmail_icon.label "zobraziť ikonu novej pošty">
<!ENTITY mail_notification_newmail_icon.accesskey "N">
<!ENTITY mail_notification_custom_mail_icon.label "zobraziť vlastnú ikonu">
<!ENTITY mail_notification_custom_mail_icon.accesskey "V">
<!ENTITY mail_notification_mail_icon_custom.label "zobraziť vlastnú ikonu">
<!ENTITY mail_notification_mail_icon_custom.accesskey "V">
<!ENTITY icon_text_color "Farba textu">
<!ENTITY icon_text_color.accesskey "T">
<!ENTITY choose "Vyberte">
@ -87,8 +87,8 @@
<!ENTITY mail_change_trigger.accesskey "S">
<!ENTITY mail_change_trigger.placeholder "/bin/notify-send">
<!ENTITY mail_change_trigger.tooltip "Absolútna cesta programu, ktorý bude spustený pri zmene počtu správ. Prvý argument je počet nových správ.">
<!ENTITY mail_urgency_hint.label "Set X11 urgency hint">
<!ENTITY mail_urgency_hint.accesskey "X">
<!ENTITY mail_get_attention.label "Window draws attention on new messages">
<!ENTITY mail_get_attention.accesskey "g">
<!ENTITY chat_icon_enable.label "Zapnúť ikonu chatu">
<!ENTITY chat_icon_enable.accesskey "E">

View File

@ -51,8 +51,8 @@
<!ENTITY mail_notification_unread_count.accesskey "U">
<!ENTITY mail_notification_newmail_icon.label "顯示新郵件圖示">
<!ENTITY mail_notification_newmail_icon.accesskey "N">
<!ENTITY mail_notification_custom_mail_icon.label "顯示自訂圖示">
<!ENTITY mail_notification_custom_mail_icon.accesskey "I">
<!ENTITY mail_notification_mail_icon_custom.label "顯示自訂圖示">
<!ENTITY mail_notification_mail_icon_custom.accesskey "I">
<!ENTITY icon_text_color "文字色彩">
<!ENTITY icon_text_color.accesskey "T">
<!ENTITY choose "選擇">
@ -77,8 +77,8 @@
<!ENTITY mail_change_trigger.accesskey "L">
<!ENTITY mail_change_trigger.placeholder "/bin/notify-send">
<!ENTITY mail_change_trigger.tooltip "要在訊息數改變時執行程式的絕對路徑。程式會將新的訊息數作為第一個參數。">
<!ENTITY mail_urgency_hint.label "Set X11 urgency hint">
<!ENTITY mail_urgency_hint.accesskey "X">
<!ENTITY mail_get_attention.label "Window draws attention on new messages">
<!ENTITY mail_get_attention.accesskey "g">
<!ENTITY chat_icon_enable.label "啟用聊天圖示">
<!ENTITY chat_icon_enable.accesskey "E">
<!ENTITY chat_icon_blink.label "有新訊息時閃爍聊天圖示">

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -16,7 +16,7 @@ pref("extensions.firetray.app_icon_type", 0);
pref("extensions.firetray.app_browser_icon_names", '["web-browser", "internet-web-browser"]');
pref("extensions.firetray.app_mail_icon_names", '["indicator-messages", "applications-email-panel"]');
pref("extensions.firetray.app_default_icon_names", '[]');
pref("extensions.firetray.app_icon_filename", "");
pref("extensions.firetray.app_icon_custom", "");
pref("extensions.firetray.new_mail_icon_names", '["indicator-messages-new", "mail-message-new"]');
pref("extensions.firetray.show_icon_on_hide", false);
pref("extensions.firetray.scroll_hides", true);
@ -25,12 +25,12 @@ pref("extensions.firetray.chat_icon_enable", true);
pref("extensions.firetray.chat_icon_blink", true);
pref("extensions.firetray.chat_icon_blink_style", 0);
pref("extensions.firetray.mail_urgency_hint", true);
pref("extensions.firetray.mail_get_attention", true);
pref("extensions.firetray.message_count_type", 0);
pref("extensions.firetray.mail_notification_enabled", true);
pref("extensions.firetray.mail_notification_type", 0);
pref("extensions.firetray.icon_text_color", "#000000");
pref("extensions.firetray.custom_mail_icon", "");
pref("extensions.firetray.mail_icon_custom", "");
pref("extensions.firetray.mail_change_trigger", "");
pref("extensions.firetray.folder_count_recursive", true);
// Ci.nsMsgFolderFlags.Archive|Drafts|Junk|Queue|SentMail|Trash|Virtual

View File

@ -6,7 +6,7 @@
<em:unpack>true</em:unpack> <!-- needed for embedded icons -->
<em:type>2</em:type>
<em:name>FireTray</em:name>
<em:version>0.4.8</em:version> <!-- change FIRETRAY_VERSION accordingly ! -->
<em:version>0.5.0b1</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>
@ -14,21 +14,22 @@
<em:optionsURL>chrome://firetray/content/options.xul</em:optionsURL>
<em:iconURL>chrome://firetray/skin/firetray48.png</em:iconURL>
<em:icon64URL>chrome://firetray/skin/firetray64.png</em:icon64URL>
<em:targetPlatform>Linux</em:targetPlatform> <!-- only Linux supported for now -->
<em:targetPlatform>Linux</em:targetPlatform>
<em:targetPlatform>WINNT</em:targetPlatform>
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> <!-- Firefox -->
<em:minVersion>7.0</em:minVersion>
<em:maxVersion>27.0</em:maxVersion>
<em:minVersion>27.0</em:minVersion>
<em:maxVersion>30.0</em:maxVersion>
</Description>
</em:targetApplication>
<em:targetApplication> <!-- Thunderbird -->
<Description>
<em:id>{3550f703-e582-4d05-9a08-453d09bdfdc6}</em:id>
<em:minVersion>7.0</em:minVersion>
<em:maxVersion>27.0</em:maxVersion>
<em:minVersion>27.0</em:minVersion>
<em:maxVersion>30.0</em:maxVersion>
</Description>
</em:targetApplication>

View File

@ -1,4 +1,5 @@
/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
"use strict";
var EXPORTED_SYMBOLS = [ "firetray" ];
@ -37,8 +38,14 @@ firetray.Handler = {
appHasChat: false,
appStarted: false,
windows: {},
windowsCount: 0,
visibleWindowsCount: 0,
get windowsCount() {return Object.keys(this.windows).length;},
get visibleWindowsCount() {
let count = 0;
for (let wid in firetray.Handler.windows) {
if (firetray.Handler.windows[wid].visible) count += 1;
}
return count;
},
observedTopics: {},
ctypesLibs: {}, // {"lib1": lib1, "lib2": lib2}
@ -46,7 +53,7 @@ firetray.Handler = {
appName: (function(){return Services.appinfo.name;})(),
xulVer: (function(){return Services.appinfo.platformVersion;})(), // Services.vc.compare(xulVer,"2.0a")>=0
runtimeABI: (function(){return Services.appinfo.XPCOMABI;})(),
runtimeOS: (function(){return Services.appinfo.OS;})(), // "WINNT", "Linux", "Darwin"
runtimeOS: (function(){return Services.appinfo.OS.toLowerCase();})(), // "WINNT", "Linux", "Darwin"
addonRootDir: (function(){
let uri = Services.io.newURI(Components.stack.filename, null, null);
if (uri instanceof Ci.nsIFileURL) {
@ -55,6 +62,7 @@ firetray.Handler = {
}
throw new Error("not resolved");
})(),
support: {chat: false, full_feat: false},
init: function() { // does creates icon
firetray.PrefListener.register(false);
@ -62,17 +70,20 @@ firetray.Handler = {
// version checked during install, so we shouldn't need to care
log.info("OS=" + this.runtimeOS + ", ABI=" + this.runtimeABI + ", XULrunner=" + this.xulVer);
switch (this.runtimeOS) {
case "Linux":
Cu.import("resource://firetray/linux/FiretrayStatusIcon.jsm");
log.debug('FiretrayStatusIcon imported');
Cu.import("resource://firetray/linux/FiretrayWindow.jsm");
log.debug('FiretrayWindow imported');
break;
default:
log.error("FIRETRAY: only Linux platform supported at this time. Firetray not loaded");
if (FIRETRAY_SUPPORTED_OS.indexOf(this.runtimeOS) < 0) {
let platforms = FIRETRAY_SUPPORTED_OS.join(", ");
log.error("Only "+platforms+" platform(s) supported at this time. Firetray not loaded");
return false;
}
Cu.import("resource://firetray/"+this.runtimeOS+"/FiretrayStatusIcon.jsm");
log.debug("FiretrayStatusIcon "+this.runtimeOS+" imported");
Cu.import("resource://firetray/"+this.runtimeOS+"/FiretrayWindow.jsm");
log.debug("FiretrayWindow "+this.runtimeOS+" imported");
this.support['chat'] = FIRETRAY_CHAT_SUPPORTED_OS
.indexOf(this.runtimeOS) > -1;
this.support['full_feat'] = FIRETRAY_FULL_FEAT_SUPPORTED_OS
.indexOf(firetray.Handler.runtimeOS) > -1;
if (this.appId === FIRETRAY_APP_DB['thunderbird']['id'] ||
this.appId === FIRETRAY_APP_DB['seamonkey']['id'])
@ -108,13 +119,18 @@ firetray.Handler = {
let chatIsProvided = this.isChatProvided();
log.info('isChatProvided='+chatIsProvided);
if (chatIsProvided) {
Cu.import("resource://firetray/FiretrayMessaging.jsm"); // needed for existsChatAccount
Cu.import("resource://firetray/FiretrayChat.jsm");
firetray.Utils.addObservers(firetray.Handler, [
"account-added", "account-removed"]);
if (firetray.Utils.prefService.getBoolPref("chat_icon_enable") &&
this.existsChatAccount())
firetray.Chat.init();
if (this.support['chat']) {
Cu.import("resource://firetray/FiretrayMessaging.jsm"); // needed for existsChatAccount
Cu.import("resource://firetray/"+this.runtimeOS+"/FiretrayChat.jsm");
firetray.Utils.addObservers(firetray.Handler, [
"account-added", "account-removed"]);
if (firetray.Utils.prefService.getBoolPref("chat_icon_enable") &&
this.existsChatAccount())
firetray.Chat.init();
} else {
let platforms = FIRETRAY_CHAT_SUPPORTED_OS.join(", ");
log.warn("Only "+platforms+" platform(s) supported at this time. Chat not loaded");
}
}
firetray.Utils.addObservers(firetray.Handler,
@ -153,7 +169,8 @@ firetray.Handler = {
shutdown: function() {
log.debug("Disabling Handler");
if (firetray.Handler.isChatProvided() && firetray.Chat.initialized)
if (firetray.Handler.isChatProvided() && firetray.Handler.support['chat']
&& firetray.Chat.initialized)
firetray.Chat.shutdown();
if (this.inMailApp)
@ -183,7 +200,7 @@ firetray.Handler = {
tryCloseLibs: function() {
try {
for (libName in this.ctypesLibs) {
for (let libName in this.ctypesLibs) {
let lib = this.ctypesLibs[libName];
if (lib.available())
lib.close();
@ -302,7 +319,7 @@ firetray.Handler = {
} else {
for (let winId in firetray.Handler.windows) {
firetray.Chat.detachSelectListeners(firetray.Handler.windows[winId].chromeWin);
firetray.ChatStatusIcon.detachOnFocusInCallback(winId);
firetray.ChatStatusIcon.detachOnFocusInCallback(winId); // FIXME: to be removed
}
firetray.Chat.shutdown();
}
@ -311,19 +328,21 @@ firetray.Handler = {
// these get overridden in OS-specific Icon/Window handlers
setIconImageDefault: function() {},
setIconImageNewMail: function() {},
setIconImageFromFile: function(filename) {},
setIconImageCustom: function(prefname) {},
setIconText: function(text, color) {},
setIconTooltip: function(localizedMessage) {},
setIconTooltipDefault: function() {},
setIconVisibility: function(visible) {},
registerWindow: function(win) {},
unregisterWindow: function(win) {},
getWindowIdFromChromeWindow: function(win) {},
hideWindow: function(winId) {},
showWindow: function(winId) {},
showHideAllWindows: function() {},
activateLastWindowCb: function(gtkStatusIcon, gdkEvent, userData) {},
getActiveWindow: function() {},
windowGetAttention: function(winId) {},
showHidePopupMenuItems: function() {}, // linux
addPopupWindowItemAndSeparatorMaybe: function(wid) {}, // winnt
removePopupWindowItemAndSeparatorMaybe: function(wid) {}, // winnt
showAllWindows: function() {
log.debug("showAllWindows");
@ -340,6 +359,34 @@ firetray.Handler = {
}
},
showHideAllWindows: function() {
log.debug("showHideAllWindows");
log.debug(" visibleWindowsCount="+firetray.Handler.visibleWindowsCount+" / windowsCount="+firetray.Handler.windowsCount);
let visibilityRate = firetray.Handler.visibleWindowsCount /
firetray.Handler.windowsCount;
log.debug(" visibilityRate="+visibilityRate);
if ((0.5 < visibilityRate) && (visibilityRate < 1)
|| visibilityRate === 0) { // TODO: should be configurable
firetray.Handler.showAllWindows();
} else {
firetray.Handler.hideAllWindows();
}
},
onMinimize: function(wid) {
let hidden = false;
let hides_on_minimize = firetray.Utils.prefService.getBoolPref('hides_on_minimize');
if (hides_on_minimize) {
let hides_single_window = firetray.Utils.prefService.getBoolPref('hides_single_window');
if (hides_single_window)
firetray.Handler.hideWindow(wid);
else
firetray.Handler.hideAllWindows();
hidden = true;
}
return hidden;
},
showHideIcon: function() {
if (firetray.Utils.prefService.getBoolPref('show_icon_on_hide'))
firetray.Handler.setIconVisibility(
@ -471,7 +518,7 @@ firetray.Handler = {
firetray.PrefListener = new PrefListener(
FIRETRAY_PREF_BRANCH,
function(branch, name) {
log.debug('Pref changed: '+name);
log.debug('____Pref changed: '+name);
switch (name) {
case 'hides_single_window':
firetray.Handler.showHidePopupMenuItems();
@ -488,19 +535,27 @@ firetray.PrefListener = new PrefListener(
firetray.Handler.setIconImageDefault();
}
break;
case 'mail_notification_type':
case 'icon_text_color':
firetray.Messaging.updateIcon();
break;
case 'new_mail_icon_names':
firetray.StatusIcon.loadThemedIcons();
case 'only_favorite_folders':
case 'message_count_type':
case 'excluded_folders_flags':
case 'folder_count_recursive':
case 'mail_accounts':
case 'message_count_type':
case 'only_favorite_folders':
firetray.Messaging.updateMsgCountWithCb();
break;
case 'app_mail_icon_names':
case 'app_browser_icon_names':
case 'app_default_icon_names':
firetray.StatusIcon.loadThemedIcons(); // linux
case 'app_icon_custom':
case 'mail_icon_custom':
firetray.StatusIcon.loadImageCustom(name);
case 'app_icon_type':
firetray.StatusIcon.loadThemedIcons();
case 'app_icon_filename':
firetray.Handler.setIconImageDefault();
if (firetray.Handler.inMailApp)
firetray.Messaging.updateMsgCountWithCb();
@ -542,13 +597,14 @@ firetray.MailChatPrefListener = new PrefListener(
case 'enabled':
let enableChatCond =
(firetray.Handler.appHasChat &&
firetray.Utils.prefService.getBoolPref("chat_icon_enable"));
firetray.Utils.prefService.getBoolPref("chat_icon_enable") &&
firetray.Handler.support['chat']);
if (!enableChatCond) return;
if (Services.prefs.getBoolPref("mail.chat.enabled")) {
if (!firetray.Chat) {
Cu.import("resource://firetray/FiretrayMessaging.jsm"); // needed for existsChatAccount
Cu.import("resource://firetray/FiretrayChat.jsm");
Cu.import("resource://firetray/linux/FiretrayChat.jsm");
firetray.Utils.addObservers(firetray.Handler, [
"account-added", "account-removed"]);
}
@ -642,7 +698,7 @@ firetray.VersionChangeHandler = {
},
tryEraseOldOptions: function() {
let v03Options = [
let v0_3_Opts = [
"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",
@ -651,12 +707,13 @@ firetray.VersionChangeHandler = {
"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);
let v0_4_0b2_Opts = [ 'mail_notification' ];
let v0_5_0b1_Opts = [ 'mail_urgency_hint', 'app_icon_filename', 'custom_mail_icon' ];
let oldOpts = v0_3_Opts.concat(v0_4_0b2_Opts).concat(v0_5_0b1_Opts);
for (let i = 0, length = oldOptions.length; i<length; ++i) {
for (let i = 0, length = oldOpts.length; i<length; ++i) {
try {
let option = oldOptions[i];
let option = oldOpts[i];
firetray.Utils.prefService.clearUserPref(option);
} catch (x) {}
}

View File

@ -13,14 +13,14 @@ Cu.import("resource://gre/modules/PluralForm.jsm");
Cu.import("resource://firetray/commons.js");
const FLDRS_UNINTERESTING = {
Archive: Ci.nsMsgFolderFlags.Archive,
Drafts: Ci.nsMsgFolderFlags.Drafts,
Junk: Ci.nsMsgFolderFlags.Junk,
Queue: Ci.nsMsgFolderFlags.Queue,
SentMail: Ci.nsMsgFolderFlags.SentMail,
Templates: Ci.nsMsgFolderFlags.Templates,
Trash: Ci.nsMsgFolderFlags.Trash,
Virtual: Ci.nsMsgFolderFlags.Virtual
Archive: Ci.nsMsgFolderFlags.Archive, // 0x00004000
Drafts: Ci.nsMsgFolderFlags.Drafts, // 0x00000400
Junk: Ci.nsMsgFolderFlags.Junk, // 0x40000000
Queue: Ci.nsMsgFolderFlags.Queue, // 0x00000800
SentMail: Ci.nsMsgFolderFlags.SentMail, // 0x00000200
Templates: Ci.nsMsgFolderFlags.Templates, // 0x00400000
Trash: Ci.nsMsgFolderFlags.Trash, // 0x00000100
Virtual: Ci.nsMsgFolderFlags.Virtual // 0x00000020
};
const ACCOUNTS_PREF_BRANCH = "mail.accountmanager.accounts";
@ -73,8 +73,7 @@ firetray.Messaging = {
/* could also use a PrefListener, but let's keep it simple for now */
observe: function(subject, topic, data) {
if (topic === "nsPref:changed" &&
data === ACCOUNTS_PREF_BRANCH) {
if (topic === "nsPref:changed" && data === ACCOUNTS_PREF_BRANCH) {
log.debug(ACCOUNTS_PREF_BRANCH+"="+subject.QueryInterface(Ci.nsIPrefBranch).getCharPref(ACCOUNTS_PREF_BRANCH));
this.cleanExcludedAccounts();
}
@ -183,10 +182,10 @@ firetray.Messaging = {
if (mailChangeTriggerFile)
firetray.Messaging.runProcess(mailChangeTriggerFile, [newMsgCount.toString()]);
let setUrgency = firetray.Utils.prefService.getBoolPref("mail_urgency_hint");
if (setUrgency && (newMsgCount > currentMsgCount))
let getAttention = firetray.Utils.prefService.getBoolPref("mail_get_attention");
if (getAttention && (newMsgCount > currentMsgCount))
for (let winId in firetray.Handler.windows)
firetray.Window.setUrgency(winId, true);
firetray.Handler.windowGetAttention(winId);
}
};
@ -206,6 +205,7 @@ firetray.Messaging = {
},
updateIcon: function(msgCount) {
log.debug("updateIcon");
if ("undefined" === typeof(msgCount)) msgCount = this.currentMsgCount;
let localizedTooltip;
@ -227,6 +227,7 @@ firetray.Messaging = {
} else if (msgCount > 0) {
let prefMailNotification = firetray.Utils.prefService.getIntPref('mail_notification_type');
log.debug("msgCount prefMailNotification="+prefMailNotification);
switch (prefMailNotification) {
case FIRETRAY_NOTIFICATION_MESSAGE_COUNT:
let prefIconTextColor = firetray.Utils.prefService.getCharPref("icon_text_color");
@ -236,8 +237,7 @@ firetray.Messaging = {
firetray.Handler.setIconImageNewMail();
break;
case FIRETRAY_NOTIFICATION_CUSTOM_ICON:
let prefCustomIconPath = firetray.Utils.prefService.getCharPref("custom_mail_icon");
firetray.Handler.setIconImageFromFile(prefCustomIconPath);
firetray.Handler.setIconImageCustom('mail_icon_custom');
break;
default:
log.error("Unknown notification mode: "+prefMailNotification);

View File

@ -0,0 +1,43 @@
/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
// https://developer.mozilla.org/en/Code_snippets/Preferences
var EXPORTED_SYMBOLS = [ "FiretrayWindow" ];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://firetray/commons.js");
let log = firetray.Logging.getLogger("firetray.FiretrayWindow");
if ("undefined" == typeof(firetray.Handler))
log.error("This module MUST be imported from/after FiretrayHandler !");
function FiretrayWindow () {}
FiretrayWindow.prototype = {
getRegisteredWinIdFromChromeWindow: function(win) {
for (let wid in firetray.Handler.windows)
if (firetray.Handler.windows[wid].chromeWin === win) return wid;
log.error("unknown window while lookup");
return null;
},
getWindowTitle: function(wid) {
let title = firetray.Handler.windows[wid].baseWin.title;
log.debug("|baseWin.title="+title+"|");
let tailIndex;
tailIndex = title.indexOf(" - Mozilla "+firetray.Handler.appName);
if (tailIndex === -1)
tailIndex = title.indexOf(" - "+firetray.Handler.appName);
if (tailIndex !== -1)
return title.substring(0, tailIndex);
else if (title === "Mozilla "+firetray.Handler.appName)
return title;
else
return null;
}
};

View File

@ -3,20 +3,20 @@
/* for now, logging facilities (imported from logging.jsm) and Services are
automatically provided by this module */
var EXPORTED_SYMBOLS =
[ "firetray", "FIRETRAY_ID", "FIRETRAY_VERSION", "FIRETRAY_PREF_BRANCH",
"FIRETRAY_SPLASH_PAGE", "FIRETRAY_APPLICATION_ICON_TYPE_THEMED",
[ "firetray", "FIRETRAY_VERSION", "FIRETRAY_SUPPORTED_OS",
"FIRETRAY_CHAT_SUPPORTED_OS", "FIRETRAY_FULL_FEAT_SUPPORTED_OS",
"FIRETRAY_ID", "FIRETRAY_PREF_BRANCH", "FIRETRAY_SPLASH_PAGE",
"FIRETRAY_APPLICATION_ICON_TYPE_THEMED",
"FIRETRAY_APPLICATION_ICON_TYPE_CUSTOM",
"FIRETRAY_NOTIFICATION_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_ACCOUNT_SERVER_TYPE_IM",
"FIRETRAY_DELAY_STARTUP_MILLISECONDS",
"FIRETRAY_DELAY_NOWAIT_MILLISECONDS",
"FIRETRAY_MESSAGE_COUNT_TYPE_UNREAD", "FIRETRAY_MESSAGE_COUNT_TYPE_NEW",
"FIRETRAY_CHAT_ICON_BLINK_STYLE_NORMAL",
"FIRETRAY_CHAT_ICON_BLINK_STYLE_FADE",
"FIRETRAY_APP_DB" ];
"FIRETRAY_ACCOUNT_SERVER_TYPE_IM", "FIRETRAY_DELAY_STARTUP_MILLISECONDS",
"FIRETRAY_DELAY_NOWAIT_MILLISECONDS", "FIRETRAY_MESSAGE_COUNT_TYPE_UNREAD",
"FIRETRAY_MESSAGE_COUNT_TYPE_NEW", "FIRETRAY_CHAT_ICON_BLINK_STYLE_NORMAL",
"FIRETRAY_CHAT_ICON_BLINK_STYLE_FADE", "FIRETRAY_APP_DB",
"FIRETRAY_XUL_ATTRIBUTE_COMMAND", "FIRETRAY_XUL_ATTRIBUTE_ONCOMMAND" ];
const Cc = Components.classes;
const Ci = Components.interfaces;
@ -25,10 +25,13 @@ const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://firetray/logging.jsm");
const FIRETRAY_VERSION = "0.4.8"; // needed for sync call of onVersionChange() :(
const FIRETRAY_ID = "{9533f794-00b4-4354-aa15-c2bbda6989f8}";
const FIRETRAY_PREF_BRANCH = "extensions.firetray.";
const FIRETRAY_SPLASH_PAGE = "http://foudfou.github.com/FireTray/";
const FIRETRAY_VERSION = "0.5.0b1"; // needed for sync call of onVersionChange() :(
const FIRETRAY_SUPPORTED_OS = ['linux', 'winnt']; // install.rdf sync :(
const FIRETRAY_CHAT_SUPPORTED_OS = ['linux'];
const FIRETRAY_FULL_FEAT_SUPPORTED_OS = FIRETRAY_CHAT_SUPPORTED_OS;
const FIRETRAY_ID = "{9533f794-00b4-4354-aa15-c2bbda6989f8}";
const FIRETRAY_PREF_BRANCH = "extensions.firetray.";
const FIRETRAY_SPLASH_PAGE = "http://foudfou.github.com/FireTray/";
const FIRETRAY_APPLICATION_ICON_TYPE_THEMED = 0;
const FIRETRAY_APPLICATION_ICON_TYPE_CUSTOM = 1;
@ -47,12 +50,15 @@ const FIRETRAY_IM_STATUS_OFFLINE = "user-offline";
const FIRETRAY_ACCOUNT_SERVER_TYPE_IM = "im";
const FIRETRAY_DELAY_STARTUP_MILLISECONDS = 500;
const FIRETRAY_DELAY_NOWAIT_MILLISECONDS = 0;
const FIRETRAY_DELAY_STARTUP_MILLISECONDS = 500;
const FIRETRAY_DELAY_NOWAIT_MILLISECONDS = 0;
const FIRETRAY_CHAT_ICON_BLINK_STYLE_NORMAL = 0;
const FIRETRAY_CHAT_ICON_BLINK_STYLE_FADE = 1;
const FIRETRAY_XUL_ATTRIBUTE_COMMAND = 0;
const FIRETRAY_XUL_ATTRIBUTE_ONCOMMAND = 1;
const FIRETRAY_APP_DB = {
firefox: {
@ -72,7 +78,7 @@ const FIRETRAY_APP_DB = {
},
sunbird: {
id: "718e30fb-e89b-41dd-9da7-e25a45638b28}",
id: "{718e30fb-e89b-41dd-9da7-e25a45638b28}",
},
chatzilla: {
@ -144,11 +150,13 @@ firetray.Utils = {
getArrayPref: function(prefStr) {
let arrayPref = this.getObjPref(prefStr);
if (!firetray.js.isArray(arrayPref)) throw new TypeError();
if (!firetray.js.isArray(arrayPref))
throw new TypeError("'"+prefStr+"' preference is not array.");
return arrayPref;
},
setArrayPref: function(prefStr, aArray) {
if (!firetray.js.isArray(aArray)) throw new TypeError();
if (!firetray.js.isArray(aArray))
throw new TypeError("'"+aArray+"' is not array.");
this.setObjPref(prefStr, aArray);
},
@ -189,11 +197,11 @@ firetray.Utils = {
dumpObj: function(obj) {
let str = "";
for(i in obj) {
for(let prop in firetray.js.listAllProperties(obj)) {
try {
str += "obj["+i+"]: " + obj[i] + "\n";
str += "obj["+prop+"]: " + obj[prop] + "\n";
} catch(e) {
str += "obj["+i+"]: Unavailable\n";
str += "obj["+prop+"]: Unavailable\n";
}
}
log.info(str);
@ -262,6 +270,17 @@ firetray.Utils = {
timer.initWithCallback({ notify: callback },
delay, timerType);
return timer;
},
/*
* Extracts statements from functions. Intended for feeding a 'oncommand'
* attribute.
* BUG: |let| assignations break oncommand inline callback under TB27
* The statements should probably be limited to a single function call.
*/
bodyToString: function(func) {
let matches = func.toSource().match(/\{([\s\S]*)\}/m);
return matches ? matches[1] : matches;
}
};
@ -291,7 +310,26 @@ firetray.js = {
// https://developer.mozilla.org/en/js-ctypes/Using_js-ctypes/Working_with_data#Quirks_in_equality
strEquals: function(obj1, obj2) {
return obj1.toString() === obj2.toString();
}
},
assert: function(condition, message) {
if (!condition) {
throw new Error(message || "Assertion failed");
}
},
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects#Enumerating_all_properties_of_an_object
listAllProperties: function(obj){
var objectToInspect;
var result = [];
for(objectToInspect = obj; objectToInspect !== null; objectToInspect = Object.getPrototypeOf(objectToInspect)){
result = result.concat(Object.getOwnPropertyNames(objectToInspect));
}
return result;
},
floatToInt: function(nb) { return nb >> 0; } // bitwise ops on signed int
};
// http://stackoverflow.com/questions/18912/how-to-find-keys-of-a-hash

View File

@ -36,13 +36,18 @@
*
* ***** END LICENSE BLOCK ***** */
var EXPORTED_SYMBOLS = [ "ctypes_library", "is64bit", "WinCbABI" ];
const Cu = Components.utils;
Cu.import("resource://gre/modules/ctypes.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://firetray/logging.jsm");
var EXPORTED_SYMBOLS = [ "ctypes_library" ];
const is64bit = ctypes.size_t.size == 8; // firetray.Handler.runtimeABI.indexOf('_64') > -1;
const WinABI = is64bit ? ctypes.default_abi : ctypes.winapi_abi;
const WinCbABI = is64bit ? ctypes.default_abi : ctypes.stdcall_abi;
let log = firetray.Logging.getLogger("firetray.ctypes-utils");
@ -111,7 +116,10 @@ function ctypes_library(aName, aABIs, aDefines, aGlobal) {
var library;
for each (let abi in aABIs) {
let soname = "lib" + aName + ".so." + abi.toString();
// FIXME: ABI is in fact SO_VER. Now we're mixing .so versions and the
// .dll extension :(
let soname = abi === 'dll' ? aName :
"lib" + aName + ".so." + abi.toString();
log.debug("Trying " + soname);
try {
library = ctypes.open(soname);
@ -155,7 +163,12 @@ function ctypes_library(aName, aABIs, aDefines, aGlobal) {
try {
args = [];
args.push(arguments[0]);
args.push(ctypes.default_abi);
// FIXME: ugly hack. We'll see when we need WinCbABI
if (this.ABI === 'dll') {
args.push(WinABI);
} else {
args.push(ctypes.default_abi);
}
for each (let arg in Array.prototype.slice.call(arguments, 1)) {
args.push(arg);
}

View File

@ -32,6 +32,10 @@ ctypesMap.prototype.get = function(key) {
return this.array[this.map[key]];
};
Object.defineProperties(ctypesMap.prototype, {
"keys": {get: function(){return Object.keys(this.map);} }
});
ctypesMap.prototype.insert = function(key, item) {
if (this.map.hasOwnProperty(key)) {
log.debug("REPLACE");

View File

@ -145,6 +145,7 @@ function gtk_defines(lib) {
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_get_visible", 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,182 @@
var EXPORTED_SYMBOLS = [ "gdi32" ];
const GDI32_LIBNAME = "gdi32";
const GDI32_ABIS = [ "dll" ];
const Cu = Components.utils;
Cu.import("resource://gre/modules/ctypes.jsm");
Cu.import("resource://firetray/ctypes/ctypes-utils.jsm");
Cu.import("resource://firetray/ctypes/winnt/win32.jsm");
function gdi32_defines(lib) {
this.BITMAP = ctypes.StructType("BITMAP", [
{ "bmType": win32.LONG },
{ "bmWidth": win32.LONG },
{ "bmHeight": win32.LONG },
{ "bmWidthBytes": win32.LONG },
{ "bmPlanes": win32.WORD },
{ "bmBitsPixel": win32.WORD },
{ "bmBits": win32.LPVOID }
]);
this.PBITMAP = this.BITMAP.ptr;
lib.lazy_bind("CreateCompatibleDC", win32.HDC, win32.HDC);
lib.lazy_bind("DeleteDC", win32.BOOL, win32.HDC);
lib.lazy_bind("BitBlt", win32.BOOL, win32.HDC, ctypes.int, ctypes.int, ctypes.int, ctypes.int, win32.HDC, ctypes.int, ctypes.int, win32.DWORD);
this.SRCCOPY = win32.DWORD(0x00CC0020); /* dest = source */
this.SRCPAINT = win32.DWORD(0x00EE0086); /* dest = source OR dest */
this.SRCAND = win32.DWORD(0x008800C6); /* dest = source AND dest */
this.SRCINVERT = win32.DWORD(0x00660046); /* dest = source XOR dest */
this.SRCERASE = win32.DWORD(0x00440328); /* dest = source AND (NOT dest ) */
this.NOTSRCCOPY = win32.DWORD(0x00330008); /* dest = (NOT source) */
this.NOTSRCERASE = win32.DWORD(0x001100A6); /* dest = (NOT src) AND (NOT dest) */
this.MERGECOPY = win32.DWORD(0x00C000CA); /* dest = (source AND pattern) */
this.MERGEPAINT = win32.DWORD(0x00BB0226); /* dest = (NOT source) OR dest */
this.PATCOPY = win32.DWORD(0x00F00021); /* dest = pattern */
this.PATPAINT = win32.DWORD(0x00FB0A09); /* dest = DPSnoo */
this.PATINVERT = win32.DWORD(0x005A0049); /* dest = pattern XOR dest */
this.DSTINVERT = win32.DWORD(0x00550009); /* dest = (NOT dest) */
this.BLACKNESS = win32.DWORD(0x00000042); /* dest = BLACK */
this.WHITENESS = win32.DWORD(0x00FF0062); /* dest = WHITE */
this.NOMIRRORBITMAP = win32.DWORD(0x80000000); /* Do not Mirror the bitmap in this call */
this.CAPTUREBLT = win32.DWORD(0x40000000); /* Include layered windows */
lib.lazy_bind("CreateCompatibleBitmap", win32.HBITMAP, win32.HDC, ctypes.int, ctypes.int);
lib.lazy_bind("CreateBitmapIndirect", win32.HBITMAP, win32.BITMAP.ptr);
lib.lazy_bind("GetObjectW", ctypes.int, win32.HGDIOBJ, ctypes.int, win32.LPVOID);
lib.lazy_bind("GetCurrentObject", win32.HGDIOBJ, win32.HDC, win32.UINT);
this.OBJ_PEN = 1;
this.OBJ_BRUSH = 2;
this.OBJ_DC = 3;
this.OBJ_METADC = 4;
this.OBJ_PAL = 5;
this.OBJ_FONT = 6;
this.OBJ_BITMAP = 7;
this.OBJ_REGION = 8;
this.OBJ_METAFILE = 9;
this.OBJ_MEMDC = 10;
this.OBJ_EXTPEN = 11;
this.OBJ_ENHMETADC = 12;
this.OBJ_ENHMETAFILE = 13;
this.OBJ_COLORSPACE = 14;
lib.lazy_bind("SelectObject", win32.HGDIOBJ, win32.HDC, win32.HGDIOBJ);
lib.lazy_bind("DeleteObject", win32.BOOL, win32.HGDIOBJ);
lib.lazy_bind("PatBlt", win32.BOOL, win32.HDC, ctypes.int, ctypes.int, ctypes.int, ctypes.int, win32.DWORD);
this.BLACKNESS = win32.DWORD(0x00000042); /* dest = BLACK */
this.WHITENESS = win32.DWORD(0x00FF0062); /* dest = WHITE */
lib.lazy_bind("CreateFontW", win32.HFONT, ctypes.int, ctypes.int, ctypes.int, ctypes.int, ctypes.int, win32.DWORD, win32.DWORD, win32.DWORD, win32.DWORD, win32.DWORD, win32.DWORD, win32.DWORD, win32.DWORD, win32.LPCWSTR);
this.FW_DONTCARE = 0;
this.FW_THIN = 100;
this.FW_EXTRALIGHT = 200;
this.FW_LIGHT = 300;
this.FW_NORMAL = 400;
this.FW_MEDIUM = 500;
this.FW_SEMIBOLD = 600;
this.FW_BOLD = 700;
this.FW_EXTRABOLD = 800;
this.FW_HEAVY = 900;
this.FF_DONTCARE = (0<<4); /* Don't care or don't know. */
this.FF_ROMAN = (1<<4); /* Variable stroke width, serifed. Times Roman, Century Schoolbook, etc. */
this.FF_SWISS = (2<<4); /* Variable stroke width, sans-serifed. Helvetica, Swiss, etc. */
this.FF_MODERN = (3<<4); /* Constant stroke width, serifed or sans-serifed. Pica, Elite, Courier, etc. */
this.FF_SCRIPT = (4<<4); /* Cursive, etc. */
this.FF_DECORATIVE = (5<<4); /* Old English, etc. */
this.DEFAULT_PITCH = 0;
this.FIXED_PITCH = 1;
this.VARIABLE_PITCH = 2;
this.MONO_FONT = 8;
this.ANSI_CHARSET = 0;
this.DEFAULT_CHARSET = 1;
this.SYMBOL_CHARSET = 2;
this.SHIFTJIS_CHARSET = 128;
this.HANGEUL_CHARSET = 129;
this.HANGUL_CHARSET = 129;
this.GB2312_CHARSET = 134;
this.CHINESEBIG5_CHARSET = 136;
this.OEM_CHARSET = 255;
this.JOHAB_CHARSET = 130;
this.HEBREW_CHARSET = 177;
this.ARABIC_CHARSET = 178;
this.GREEK_CHARSET = 161;
this.TURKISH_CHARSET = 162;
this.VIETNAMESE_CHARSET = 163;
this.THAI_CHARSET = 222;
this.EASTEUROPE_CHARSET = 238;
this.RUSSIAN_CHARSET = 204;
this.DEFAULT_QUALITY = 0;
this.DRAFT_QUALITY = 1;
this.PROOF_QUALITY = 2;
this.NONANTIALIASED_QUALITY = 3;
this.ANTIALIASED_QUALITY = 4;
this.CLEARTYPE_QUALITY = 5;
this.CLEARTYPE_NATURAL_QUALITY = 6;
this.OUT_DEFAULT_PRECIS = 0;
this.OUT_STRING_PRECIS = 1;
this.OUT_CHARACTER_PRECIS = 2;
this.OUT_STROKE_PRECIS = 3;
this.OUT_TT_PRECIS = 4;
this.OUT_DEVICE_PRECIS = 5;
this.OUT_RASTER_PRECIS = 6;
this.OUT_TT_ONLY_PRECIS = 7;
this.OUT_OUTLINE_PRECIS = 8;
this.OUT_SCREEN_OUTLINE_PRECIS = 9;
this.OUT_PS_ONLY_PRECIS = 10;
lib.lazy_bind("GetTextFaceW", ctypes.int, win32.HDC, ctypes.int, win32.LPTSTR);
lib.lazy_bind("SetTextColor", win32.COLORREF, win32.HDC, win32.COLORREF);
lib.lazy_bind("SetBkMode", ctypes.int, win32.HDC, ctypes.int);
this.TRANSPARENT = 1;
this.OPAQUE = 2;
this.BKMODE_LAST = 2;
lib.lazy_bind("TextOutW", win32.BOOL, win32.HDC, ctypes.int, ctypes.int, win32.LPCTSTR, ctypes.int);
this.SIZE = ctypes.StructType("SIZE", [
{ "cx": win32.LONG },
{ "cy": win32.LONG }
]);
this.LPSIZE = this.SIZE.ptr;
lib.lazy_bind("GetTextExtentPoint32W", win32.BOOL, win32.HDC, win32.LPCTSTR, ctypes.int, this.LPSIZE);
lib.lazy_bind("GetTextAlign", win32.UINT, win32.HDC);
lib.lazy_bind("SetTextAlign", win32.UINT, win32.HDC, win32.UINT);
this.TA_LEFT = 0;
this.TA_RIGHT = 2;
this.TA_CENTER = 6;
this.TA_TOP = 0;
this.TA_BOTTOM = 8;
this.TA_BASELINE = 24;
this.TA_RTLREADING = 256;
this.TA_MASK =(this.TA_BASELINE+this.TA_CENTER+this.TA_UPDATECP+this.TA_RTLREADING);
this.BITMAPINFOHEADER = ctypes.StructType("BITMAPINFOHEADER", [
{ "biSize": win32.DWORD },
{ "biWidth": win32.LONG },
{ "biHeight": win32.LONG },
{ "biPlanes": win32.WORD },
{ "biBitCount": win32.WORD },
{ "biCompression": win32.DWORD },
{ "biSizeImage": win32.DWORD },
{ "biXPelsPerMeter": win32.LONG },
{ "biYPelsPerMeter": win32.LONG },
{ "biClrUsed": win32.DWORD },
{ "biClrImportant": win32.DWORD }
]);
this.PBITMAPINFOHEADER = this.BITMAPINFOHEADER.ptr;
this.RGBQUAD = ctypes.StructType("RGBQUAD", [
{ "rgbBlue": win32.BYTE },
{ "rgbGreen": win32.BYTE },
{ "rgbRed": win32.BYTE },
{ "rgbReserved": win32.BYTE }
]);
this.BITMAPINFO = ctypes.StructType("BITMAPINFO", [
{ "bmiHeader": this.BITMAPINFOHEADER },
{ "bmiColors": this.RGBQUAD.array(1) }
]);
this.PBITMAPINFO = this.BITMAPINFO.ptr;
lib.lazy_bind("SetDIBits", ctypes.int, win32.HDC, win32.HBITMAP, win32.UINT, win32.UINT, ctypes.voidptr_t, this.BITMAPINFO.ptr, win32.UINT);
}
new ctypes_library(GDI32_LIBNAME, GDI32_ABIS, gdi32_defines, this);

View File

@ -0,0 +1,49 @@
var EXPORTED_SYMBOLS = [ "kernel32" ];
const KERNEL32_LIBNAME = "kernel32";
const KERNEL32_ABIS = [ "dll" ];
const Cu = Components.utils;
Cu.import("resource://gre/modules/ctypes.jsm");
Cu.import("resource://firetray/ctypes/ctypes-utils.jsm");
Cu.import("resource://firetray/ctypes/winnt/win32.jsm");
function kernel32_defines(lib) {
this.OSVERSIONINFOEXW = ctypes.StructType("OSVERSIONINFOEXW", [
{ "dwOSVersionInfoSize": win32.DWORD },
{ "dwMajorVersion": win32.DWORD },
{ "dwMinorVersion": win32.DWORD },
{ "dwBuildNumber": win32.DWORD },
{ "dwPlatformId": win32.DWORD },
{ "szCSDVersion": ctypes.ArrayType(win32.TCHAR, 128) },
{ "wServicePackMajor": win32.WORD },
{ "wServicePackMinor": win32.WORD },
{ "wSuiteMask": win32.WORD },
{ "wProductType": win32.BYTE },
{ "wReserved": win32.BYTE }
]);
// lib.lazy_bind("GetLastError", win32.DWORD); // use ctypes.winLastError instead
lib.lazy_bind("GetVersionExW", win32.BOOL, this.OSVERSIONINFOEXW.ptr);
lib.lazy_bind("GetConsoleWindow", win32.HWND);
lib.lazy_bind("GetConsoleTitleW", win32.DWORD, win32.LPTSTR, win32.DWORD);
lib.lazy_bind("GetModuleHandleW", win32.HMODULE, win32.LPCTSTR);
lib.lazy_bind("LoadLibraryW", win32.HMODULE, win32.LPCTSTR);
lib.lazy_bind("GetProcAddress", win32.FARPROC, win32.HMODULE, win32.LPCSTR);
lib.lazy_bind("GetCurrentThreadId", win32.DWORD);
}
new ctypes_library(KERNEL32_LIBNAME, KERNEL32_ABIS, kernel32_defines, this);
let osvi = new kernel32.OSVERSIONINFOEXW();
osvi.dwOSVersionInfoSize = kernel32.OSVERSIONINFOEXW.size;
if (kernel32.GetVersionExW(osvi.address())) {
win32.WINVER = (+osvi.dwMajorVersion)*10 + (+osvi.dwMinorVersion); // ctypes.UInt64 objects!
} else {
Cu.reportError("win version not found");
}

View File

@ -0,0 +1,96 @@
var EXPORTED_SYMBOLS = [ "shell32" ];
const SHELL32_LIBNAME = "shell32";
const SHELL32_ABIS = [ "dll" ];
const Cu = Components.utils;
Cu.import("resource://gre/modules/ctypes.jsm");
Cu.import("resource://firetray/ctypes/ctypes-utils.jsm");
Cu.import("resource://firetray/ctypes/winnt/win32.jsm");
function shell32_defines(lib) {
this.NOTIFYICONDATAW = ctypes.StructType("NOTIFYICONDATAW", [
{ "cbSize": win32.DWORD },
{ "hWnd": win32.HWND },
{ "uID": win32.UINT },
{ "uFlags": win32.UINT },
{ "uCallbackMessage": win32.UINT },
{ "hIcon": win32.HICON },
{ "szTip": ctypes.ArrayType(win32.TCHAR, 128) },
{ "dwState": win32.DWORD },
{ "dwStateMask": win32.DWORD },
{ "szInfo": ctypes.ArrayType(win32.TCHAR, 256) },
{ "uTimeoutOrVersion": win32.UINT }, // union
{ "szInfoTitle": ctypes.ArrayType(win32.TCHAR, 64) },
{ "dwInfoFlags": win32.DWORD },
{ "guidItem": win32.GUID },
{ "hBalloonIcon": win32.HICON }
]);
this.NOTIFY_VERSION = 3; // 2K+
this.NOTIFYICON_VERSION_4 = 4; // Vista+
// #define FIELD_OFFSET(t,f) ((LONG)&(((t*)0)->f))
function FIELD_OFFSET(aType, aField, aPos) {
function addr2nb(a) {
return ctypes.cast(a, ctypes.unsigned_long).value;
}
// 'would be nice to use aType.ptr(1) (0 raises null pointer error) but we
// can't access fields (or their size) from a StructType.
let s = new aType();
let addr_base = addr2nb(s.address());
let addr_field;
if (typeof(aPos) == "undefined") {
addr_field = addr2nb(s.addressOfField(aField)); // s[aField].address() also fine
} else {
addr_field = addr2nb(s[aField].addressOfElement(aPos)); // pfew! nice feature!
}
return addr_field - addr_base;
}
this.NOTIFYICONDATAW_V1_SIZE = FIELD_OFFSET(this.NOTIFYICONDATAW, 'szTip', 64); // FIELD_OFFSET(NOTIFYICONDATAW, szTip[64])
this.NOTIFYICONDATAW_V2_SIZE = FIELD_OFFSET(this.NOTIFYICONDATAW, 'guidItem'); // 2K
this.NOTIFYICONDATAW_V3_SIZE = FIELD_OFFSET(this.NOTIFYICONDATAW, 'hBalloonIcon'); // XP
this.NOTIFYICONDATAW_SIZE = function() {
let cbSize = this.NOTIFYICONDATAW.size;
if (!win32.WINVER) {
Cu.reportError("WINVER not defined! shell32 should be initialized before using WINVER.");
} else if (win32.WINVER >= win32.WIN_VERSIONS["Vista"]) {
cbSize = this.NOTIFYICONDATAW.size;
} else if (win32.WINVER >= win32.WIN_VERSIONS["XP"]) {
cbSize = this.NOTIFYICONDATAW_V3_SIZE;
} else if (win32.WINVER >= win32.WIN_VERSIONS["2K"]) {
cbSize = this.NOTIFYICONDATAW_V2_SIZE;
} else {
cbSize = this.NOTIFYICONDATAW_V1_SIZE;
}
return cbSize;
};
lib.lazy_bind("Shell_NotifyIconW", win32.BOOL, win32.DWORD, this.NOTIFYICONDATAW.ptr);
// notify icon message
this.NIM_ADD = 0x00000000;
this.NIM_MODIFY = 0x00000001;
this.NIM_DELETE = 0x00000002;
this.NIM_SETFOCUS = 0x00000003;
this.NIM_SETVERSION = 0x00000004;
// for NOTIFYICONDATAW.uFlags
this.NIF_MESSAGE = 0x00000001;
this.NIF_ICON = 0x00000002;
this.NIF_TIP = 0x00000004;
this.NIF_STATE = 0x00000008;
this.NIF_INFO = 0x00000010;
this.NIF_GUID = 0x00000020;
this.NIF_REALTIME = 0x00000040;
this.NIF_SHOWTIP = 0x00000080;
lib.lazy_bind("ExtractIconW", win32.HICON, win32.HINSTANCE, win32.LPCTSTR, win32.UINT);
lib.lazy_bind("ExtractIconExW", win32.UINT, win32.LPCTSTR, ctypes.int, win32.HICON.ptr, win32.HICON.ptr, win32.UINT);
}
new ctypes_library(SHELL32_LIBNAME, SHELL32_ABIS, shell32_defines, this);

View File

@ -0,0 +1,380 @@
var EXPORTED_SYMBOLS = [ "user32" ];
const USER32_LIBNAME = "user32";
const USER32_ABIS = [ "dll" ];
const Cu = Components.utils;
Cu.import("resource://gre/modules/ctypes.jsm");
Cu.import("resource://firetray/ctypes/ctypes-utils.jsm");
Cu.import("resource://firetray/ctypes/winnt/win32.jsm");
function user32_defines(lib) {
this.CHANGEFILTERSTRUCT = ctypes.StructType("CHANGEFILTERSTRUCT", [
{ "cbSize": win32.DWORD },
{ "ExtStatus": win32.DWORD }
]);
this.MSGFLTINFO_NONE = 0;
this.MSGFLTINFO_ALLOWED_HIGHER = 3;
this.MSGFLTINFO_ALREADYALLOWED_FORWND = 1;
this.MSGFLTINFO_ALREADYDISALLOWED_FORWND = 2;
lib.lazy_bind("ChangeWindowMessageFilter", win32.BOOL, win32.UINT, win32.DWORD);
this.MSGFLT_ADD = 1;
this.MSGFLT_REMOVE = 2;
lib.lazy_bind("ChangeWindowMessageFilterEx", win32.BOOL, win32.HWND, win32.UINT, win32.DWORD, this.CHANGEFILTERSTRUCT.ptr);
this.MSGFLT_ALLOW = 1;
this.MSGFLT_DISALLOW = 2;
this.MSGFLT_RESET = 0;
lib.lazy_bind("RegisterWindowMessageW", win32.UINT, win32.LPCTSTR);
lib.lazy_bind("GetWindowTextW", ctypes.int, win32.HWND, win32.LPTSTR, ctypes.int);
lib.lazy_bind("FindWindowW", win32.HWND, win32.LPCTSTR, win32.LPCTSTR);
lib.lazy_bind("SendMessageW", win32.LRESULT, win32.HWND, win32.UINT, win32.WPARAM, win32.LPARAM);
this.WM_GETICON = 0x007F;
this.WM_SETICON = 0x0080;
this.ICON_SMALL = 0;
this.ICON_BIG = 1;
this.ICON_SMALL2 = 2;
lib.lazy_bind("GetClassLongPtrW", win32.ULONG_PTR, win32.HWND, ctypes.int);
lib.lazy_bind("GetClassLongW", win32.DWORD, win32.HWND, ctypes.int); // 32-bits
this.GetClassLong = is64bit ? this.GetClassLongPtrW : this.GetClassLongW;
this.GCLP_HICONSM = -34;
lib.lazy_bind("LoadIconW", win32.HICON, win32.HINSTANCE, win32.LPCTSTR); // superseeded by LoadImage
this.IDI_APPLICATION = win32.MAKEINTRESOURCE(32512);
this.IDI_HAND = win32.MAKEINTRESOURCE(32513);
this.IDI_QUESTION = win32.MAKEINTRESOURCE(32514);
this.IDI_EXCLAMATION = win32.MAKEINTRESOURCE(32515);
this.IDI_ASTERISK = win32.MAKEINTRESOURCE(32516);
lib.lazy_bind("DestroyIcon", win32.BOOL, win32.HICON);
lib.lazy_bind("LoadImageW", win32.HANDLE, win32.HINSTANCE, win32.LPCTSTR, win32.UINT, ctypes.int, ctypes.int, win32.UINT);
this.IMAGE_BITMAP = 0;
this.IMAGE_ICON = 1;
this.IMAGE_CURSOR = 2;
this.LR_CREATEDIBSECTION = 0x00002000;
this.LR_DEFAULTCOLOR = 0x00000000;
this.LR_DEFAULTSIZE = 0x00000040;
this.LR_LOADFROMFILE = 0x00000010;
this.LR_LOADMAP3DCOLORS = 0x00001000;
this.LR_LOADTRANSPARENT = 0x00000020;
this.LR_MONOCHROME = 0x00000001;
this.LR_SHARED = 0x00008000;
this.LR_VGACOLOR = 0x00000080;
lib.lazy_bind("CopyImage", win32.HANDLE, win32.HANDLE, win32.UINT, ctypes.int, ctypes.int, win32.UINT);
lib.lazy_bind("GetPropW", win32.HANDLE, win32.HWND, win32.LPCTSTR);
lib.lazy_bind("SetPropW", win32.BOOL, win32.HWND, win32.LPCTSTR, win32.HANDLE);
lib.lazy_bind("RemovePropW", win32.HANDLE, win32.HWND, win32.LPCTSTR);
lib.lazy_bind("GetWindowLongW", win32.LONG_PTR, win32.HWND, ctypes.int);
lib.lazy_bind("SetWindowLongW", win32.LONG_PTR , win32.HWND, ctypes.int, win32.LONG_PTR);
// SetWindowLongPtrW aliases SetWindowLongW with the correct signature thank
// win32.LONG_PTR
this.GWLP_WNDPROC = -4;
this.GWLP_HINSTANCE = -6;
this.GWLP_ID = -12;
this.GWL_STYLE = -16;
this.GWL_EXSTYLE = -20;
this.GWLP_USERDATA = -21;
lib.lazy_bind("SetClassLongW", win32.DWORD , win32.HWND, ctypes.int, win32.LONG); // superseeded by SetClassLongPtrW
this.GCL_MENUNAME = -8;
this.GCL_HBRBACKGROUND = -10;
this.GCL_HCURSOR = -12;
this.GCL_HICON = -14;
this.GCL_HMODULE = -16;
this.GCL_CBWNDEXTRA = -18;
this.GCL_CBCLSEXTRA = -20;
this.GCL_WNDPROC = -24;
this.GCL_HICONSM = -34;
this.WNDPROC = ctypes.FunctionType(
WinCbABI, win32.LRESULT,
[win32.HWND, win32.UINT, win32.WPARAM, win32.LPARAM]).ptr;
// lib.lazy_bind("CallWindowProcW", win32.LRESULT, this.WNDPROC, win32.HWND, win32.UINT, win32.WPARAM, win32.LPARAM);
lib.lazy_bind("CallWindowProcW", win32.LRESULT, ctypes.voidptr_t, win32.HWND, win32.UINT, win32.WPARAM, win32.LPARAM);
lib.lazy_bind("DefWindowProcW", win32.LRESULT, win32.HWND, win32.UINT, win32.WPARAM, win32.LPARAM);
this.WNDCLASSEXW = ctypes.StructType("WNDCLASSEXW", [
{ "cbSize": win32.UINT },
{ "style": win32.UINT },
{ "lpfnWndProc": this.WNDPROC },
{ "cbClsExtra": ctypes.int },
{ "cbWndExtra": ctypes.int },
{ "hInstance": win32.HINSTANCE },
{ "hIcon": win32.HICON },
{ "hCursor": win32.HCURSOR },
{ "hbrBackground": win32.HBRUSH },
{ "lpszMenuName": win32.LPCTSTR },
{ "lpszClassName": win32.LPCTSTR },
{ "hIconSm": win32.HICON }
]);
lib.lazy_bind("RegisterClassExW", win32.ATOM, this.WNDCLASSEXW.ptr);
lib.lazy_bind("UnregisterClassW", win32.BOOL, win32.LPCTSTR, win32.HINSTANCE);
lib.lazy_bind("CreateWindowExW", win32.HWND, win32.DWORD, win32.LPCTSTR, win32.LPCTSTR, win32.DWORD, ctypes.int, ctypes.int, ctypes.int, ctypes.int, win32.HWND, win32.HMENU, win32.HINSTANCE, win32.LPVOID);
lib.lazy_bind("DestroyWindow", win32.BOOL, win32.HWND);
lib.lazy_bind("ShowWindow", win32.BOOL, win32.HWND, ctypes.int);
lib.lazy_bind("IsWindowVisible", win32.BOOL, win32.HWND);
this.SW_HIDE = 0;
this.SW_SHOWNORMAL = 1;
this.SW_NORMAL = 1;
this.SW_SHOWMINIMIZED = 2;
this.SW_SHOWMAXIMIZED = 3;
this.SW_MAXIMIZE = 3;
this.SW_SHOWNOACTIVATE = 4;
this.SW_SHOW = 5;
this.SW_MINIMIZE = 6;
this.SW_SHOWMINNOACTIVE = 7;
this.SW_SHOWNA = 8;
this.SW_RESTORE = 9;
this.SW_SHOWDEFAULT = 10;
this.SW_FORCEMINIMIZE = 11;
this.SW_MAX = 11;
this.CW_USEDEFAULT = ctypes.int(0x80000000); // -2147483648
this.HWND_BROADCAST = win32.HWND(0xffff);
this.HWND_MESSAGE = win32.HWND(-3); // WINVER >= 0x0500
// need to be win32.DWORD()'d after binray operations are applied !
this.WS_BORDER = 0x00800000;
this.WS_CAPTION = 0x00C00000;
this.WS_CHILD = 0x40000000;
this.WS_CHILDWINDOW = 0x40000000;
this.WS_CLIPCHILDREN = 0x02000000;
this.WS_CLIPSIBLINGS = 0x04000000;
this.WS_DISABLED = 0x08000000;
this.WS_DLGFRAME = 0x00400000;
this.WS_GROUP = 0x00020000;
this.WS_HSCROLL = 0x00100000;
this.WS_ICONIC = 0x20000000;
this.WS_MAXIMIZE = 0x01000000;
this.WS_MAXIMIZEBOX = 0x00010000;
this.WS_MINIMIZE = 0x20000000;
this.WS_MINIMIZEBOX = 0x00020000;
this.WS_OVERLAPPED = 0x00000000;
this.WS_POPUP = 0x80000000;
this.WS_SIZEBOX = 0x00040000;
this.WS_SYSMENU = 0x00080000;
this.WS_TABSTOP = 0x00010000;
this.WS_THICKFRAME = 0x00040000;
this.WS_TILED = 0x00000000;
this.WS_VISIBLE = 0x10000000;
this.WS_VSCROLL = 0x00200000;
this.WS_POPUPWINDOW = (this.WS_POPUP | this.WS_BORDER | this.WS_SYSMENU);
this.WS_OVERLAPPEDWINDOW = (this.WS_OVERLAPPED | this.WS_CAPTION | this.WS_SYSMENU | this.WS_THICKFRAME | this.WS_MINIMIZEBOX | this.WS_MAXIMIZEBOX);
this.WS_TILEDWINDOW = (this.WS_OVERLAPPED | this.WS_CAPTION | this.WS_SYSMENU | this.WS_THICKFRAME | this.WS_MINIMIZEBOX | this.WS_MAXIMIZEBOX);
this.CWPSTRUCT = ctypes.StructType("CWPSTRUCT", [
{ "lParam": win32.LPARAM },
{ "wParam": win32.WPARAM },
{ "message": win32.UINT },
{ "hwnd": win32.HWND }
]);
this.CWPRETSTRUCT = ctypes.StructType("CWPRETSTRUCT", [
{ "lResult": win32.LRESULT },
{ "lParam": win32.LPARAM },
{ "wParam": win32.WPARAM },
{ "message": win32.UINT },
{ "hwnd": win32.HWND }
]);
this.HOOKPROC = ctypes.FunctionType(
WinCbABI, win32.LRESULT,
[ctypes.int, win32.WPARAM, win32.LPARAM]).ptr;
lib.lazy_bind("SetWindowsHookExW", win32.HHOOK, ctypes.int, this.HOOKPROC, win32.HINSTANCE, win32.DWORD);
lib.lazy_bind("CallNextHookEx", win32.LRESULT, win32.HHOOK, ctypes.int, win32.WPARAM, win32.LPARAM);
lib.lazy_bind("UnhookWindowsHookEx", win32.BOOL, win32.HHOOK);
this.WH_MIN = (-1);
this.WH_MSGFILTER = (-1);
this.WH_JOURNALRECORD = 0;
this.WH_JOURNALPLAYBACK = 1;
this.WH_KEYBOARD = 2;
this.WH_GETMESSAGE = 3;
this.WH_CALLWNDPROC = 4;
this.WH_CBT = 5;
this.WH_SYSMSGFILTER = 6;
this.WH_MOUSE = 7;
this.WH_HARDWARE = 8;
this.WH_DEBUG = 9;
this.WH_SHELL = 10;
this.WH_FOREGROUNDIDLE = 11;
this.WH_CALLWNDPROCRET = 12;
this.WH_KEYBOARD_LL = 13;
this.WH_MOUSE_LL = 14;
this.HC_ACTION = 0;
this.HC_GETNEXT = 1;
this.HC_SKIP = 2;
this.HC_NOREMOVE = 3;
this.HC_NOREM = this.HC_NOREMOVE;
this.HC_SYSMODALON = 4;
this.HC_SYSMODALOFF = 5;
lib.lazy_bind("GetWindowThreadProcessId", win32.DWORD, win32.HWND, win32.LPDWORD);
this.FLASHWINFO = ctypes.StructType("FLASHWINFO", [
{ "cbSize": win32.UINT },
{ "hwnd": win32.HWND },
{ "dwFlags": win32.DWORD },
{ "uCount": win32.UINT },
{ "dwTimeout": win32.DWORD }
]);
this.PFLASHWINFO = this.FLASHWINFO.ptr;
lib.lazy_bind("FlashWindow", win32.BOOL, win32.HWND, win32.BOOL);
lib.lazy_bind("FlashWindowEx", win32.BOOL, this.PFLASHWINFO);
this.FLASHW_STOP = 0;
this.FLASHW_CAPTION = 0x00000001;
this.FLASHW_TRAY = 0x00000002;
this.FLASHW_ALL =(this.FLASHW_CAPTION | this.FLASHW_TRAY);
this.FLASHW_TIMER = 0x00000004;
this.FLASHW_TIMERNOFG = 0x0000000C;
lib.lazy_bind("SystemParametersInfoW", win32.BOOL, win32.UINT, win32.UINT, win32.PVOID, win32.UINT);
this.SPI_GETFOREGROUNDFLASHCOUNT = 0x2004;
lib.lazy_bind("GetForegroundWindow", win32.HWND);
lib.lazy_bind("GetDC", win32.HDC, win32.HWND);
lib.lazy_bind("ReleaseDC", ctypes.int, win32.HWND, win32.HDC);
lib.lazy_bind("CreateIconIndirect", win32.HICON, win32.PICONINFO);
lib.lazy_bind("GetClientRect", win32.BOOL, win32.HWND, win32.PRECT);
lib.lazy_bind("DrawTextW", ctypes.int, win32.HDC, win32.LPCTSTR, ctypes.int, win32.PRECT, win32.UINT);
this.DT_TOP = 0x00000000;
this.DT_LEFT = 0x00000000;
this.DT_CENTER = 0x00000001;
this.DT_RIGHT = 0x00000002;
this.DT_VCENTER = 0x00000004;
this.DT_BOTTOM = 0x00000008;
this.DT_WORDBREAK = 0x00000010;
this.DT_SINGLELINE = 0x00000020;
this.DT_EXPANDTABS = 0x00000040;
this.DT_TABSTOP = 0x00000080;
this.DT_NOCLIP = 0x00000100;
this.DT_EXTERNALLEADING = 0x00000200;
this.DT_CALCRECT = 0x00000400;
this.DT_NOPREFIX = 0x00000800;
this.DT_INTERNAL = 0x00001000;
lib.lazy_bind("CreatePopupMenu", win32.HMENU);
lib.lazy_bind("DestroyMenu", win32.BOOL, win32.HMENU);
this.MENUITEMINFOW = ctypes.StructType("MENUITEMINFOW", [
{ "cbSize": win32.UINT },
{ "fMask": win32.UINT },
{ "fType": win32.UINT },
{ "fState": win32.UINT },
{ "wID": win32.UINT },
{ "hSubMenu": win32.HMENU },
{ "hbmpChecked": win32.HBITMAP },
{ "hbmpUnchecked": win32.HBITMAP },
{ "dwItemData": win32.ULONG_PTR },
{ "dwTypeData": win32.LPWSTR },
{ "cch": win32.UINT },
{ "hbmpItem": win32.HBITMAP }
]);
this.LPCMENUITEMINFO = this.LPMENUITEMINFOW = this.MENUITEMINFOW.ptr;
lib.lazy_bind("InsertMenuItemW", win32.BOOL, win32.HMENU, win32.UINT, win32.BOOL, this.LPCMENUITEMINFO);
lib.lazy_bind("GetMenuItemInfoW", win32.BOOL, win32.HMENU, win32.UINT, win32.BOOL, this.LPCMENUITEMINFO);
this.MIIM_STATE = 0x00000001;
this.MIIM_ID = 0x00000002;
this.MIIM_SUBMENU = 0x00000004;
this.MIIM_CHECKMARKS = 0x00000008;
this.MIIM_TYPE = 0x00000010;
this.MIIM_DATA = 0x00000020;
this.MIIM_STRING = 0x00000040;
this.MIIM_BITMAP = 0x00000080;
this.MIIM_FTYPE = 0x00000100;
lib.lazy_bind("InsertMenuW", win32.BOOL, win32.HMENU, win32.UINT, win32.UINT, win32.UINT_PTR, win32.LPCTSTR);
lib.lazy_bind("DeleteMenu", win32.BOOL, win32.HMENU, win32.UINT, win32.UINT);
this.MF_INSERT = 0x00000000;
this.MF_CHANGE = 0x00000080;
this.MF_APPEND = 0x00000100;
this.MF_DELETE = 0x00000200;
this.MF_REMOVE = 0x00001000;
this.MF_BYCOMMAND = 0x00000000;
this.MF_BYPOSITION = 0x00000400;
this.MF_SEPARATOR = 0x00000800;
this.MF_ENABLED = 0x00000000;
this.MF_GRAYED = 0x00000001;
this.MF_DISABLED = 0x00000002;
this.MF_UNCHECKED = 0x00000000;
this.MF_CHECKED = 0x00000008;
this.MF_USECHECKBITMAPS = 0x00000200;
this.MF_STRING = 0x00000000;
this.MF_BITMAP = 0x00000004;
this.MF_OWNERDRAW = 0x00000100;
this.MF_POPUP = 0x00000010;
this.MF_MENUBARBREAK = 0x00000020;
this.MF_MENUBREAK = 0x00000040;
this.MF_UNHILITE = 0x00000000;
this.MF_HILITE = 0x00000080;
this.MF_DEFAULT = 0x00001000;
this.MF_RIGHTJUSTIFY = 0x00004000;
this.MFT_STRING = this.MF_STRING;
this.MFT_BITMAP = this.MF_BITMAP;
this.MFT_MENUBARBREAK = this.MF_MENUBARBREAK;
this.MFT_MENUBREAK = this.MF_MENUBREAK;
this.MFT_OWNERDRAW = this.MF_OWNERDRAW;
this.MFT_RADIOCHECK = 0x00000200;
this.MFT_SEPARATOR = this.MF_SEPARATOR;
this.MFT_RIGHTORDER = 0x00002000;
this.MFT_RIGHTJUSTIFY = this.MF_RIGHTJUSTIFY;
this.MFS_GRAYED = 0x00000003;
this.MFS_DISABLED = this.MFS_GRAYED;
this.MFS_CHECKED = this.MF_CHECKED;
this.MFS_HILITE = this.MF_HILITE;
this.MFS_ENABLED = this.MF_ENABLED;
this.MFS_UNCHECKED = this.MF_UNCHECKED;
this.MFS_UNHILITE = this.MF_UNHILITE;
this.MFS_DEFAULT = this.MF_DEFAULT;
this.TPM_LEFTBUTTON = 0x0000;
this.TPM_RIGHTBUTTON = 0x0002;
this.TPM_LEFTALIGN = 0x0000;
this.TPM_CENTERALIGN = 0x0004;
this.TPM_RIGHTALIGN = 0x0008;
this.TPM_TOPALIGN = 0x0000;
this.TPM_VCENTERALIGN = 0x0010;
this.TPM_BOTTOMALIGN = 0x0020;
this.TPM_HORIZONTAL = 0x0000;
this.TPM_VERTICAL = 0x0040;
lib.lazy_bind("GetMenuItemCount", ctypes.int, win32.HMENU);
lib.lazy_bind("CalculatePopupWindowPosition", win32.BOOL, win32.POINT.ptr, win32.SIZE, win32.UINT, win32.RECT.ptr, win32.RECT.ptr);
lib.lazy_bind("TrackPopupMenu", win32.BOOL, win32.HMENU, win32.UINT, ctypes.int, ctypes.int, ctypes.int, win32.HWND, win32.RECT.ptr);
lib.lazy_bind("SetForegroundWindow", win32.BOOL, win32.HWND);
lib.lazy_bind("GetCursorPos", win32.BOOL, win32.LPPOINT);
lib.lazy_bind("GetMessagePos", win32.DWORD);
this.WINDOWPLACEMENT = ctypes.StructType("WINDOWPLACEMENT", [
{ "length": win32.UINT },
{ "flags": win32.UINT },
{ "showCmd": win32.UINT },
{ "ptMinPosition": win32.POINT },
{ "ptMaxPosition": win32.POINT },
{ "rcNormalPosition": win32.RECT }
]);
this.PWINDOWPLACEMENT = this.LPWINDOWPLACEMENT = this.WINDOWPLACEMENT.ptr;
lib.lazy_bind("GetWindowPlacement", win32.BOOL, win32.HWND, this.WINDOWPLACEMENT.ptr);
lib.lazy_bind("SetWindowPlacement", win32.BOOL, win32.HWND, this.WINDOWPLACEMENT.ptr);
}
new ctypes_library(USER32_LIBNAME, USER32_ABIS, user32_defines, this);

View File

@ -0,0 +1,210 @@
var EXPORTED_SYMBOLS = [ "win32" ];
const Cu = Components.utils;
Cu.import("resource://gre/modules/ctypes.jsm");
Cu.import("resource://firetray/ctypes/ctypes-utils.jsm");
var win32 = new function() {
this.WIN_VERSIONS = { // maj*10 + min
'8': 62, // 2012
'7': 61, // 2009
'Vista': 60, // 2007
'XP': 51, // 2001
'2K': 50, // 2000
};
// could also parse Cc["@mozilla.org/network/protocol;1?name=http"].
// getService(Ci.nsIHttpProtocolHandler).oscpu
this.WINVER = null; // initialized in kernel32.jsm
this.BOOL = ctypes.bool;
this.BYTE = ctypes.unsigned_char;
this.INT_PTR = is64bit ? ctypes.int64_t : ctypes.int;
this.UINT = ctypes.unsigned_int;
this.UINT_PTR = is64bit ? ctypes.uint64_t : ctypes.unsigned_int;
this.WORD = ctypes.unsigned_short;
this.DWORD = ctypes.unsigned_long;
this.LPDWORD = this.DWORD.ptr;
this.PVOID = ctypes.voidptr_t;
this.LPVOID = ctypes.voidptr_t;
this.LONG = ctypes.long;
this.LONG_PTR = is64bit ? ctypes.int64_t : ctypes.long;
this.ULONG_PTR = is64bit ? ctypes.uint64_t : ctypes.unsigned_long;
this.SIZE_T = this.ULONG_PTR;
this.DWORD_PTR = this.ULONG_PTR;
this.ATOM = this.WORD;
this.HANDLE = ctypes.voidptr_t;
this.HWND = this.HANDLE;
this.HICON = this.HANDLE;
this.HINSTANCE = this.HANDLE;
this.HMODULE = this.HANDLE;
this.HMENU = this.HANDLE;
this.HBRUSH = this.HICON;
this.HCURSOR = this.HANDLE;
this.HHOOK = this.HANDLE;
this.HDC = this.HANDLE;
this.HGDIOBJ = this.HANDLE;
this.HBITMAP = this.HANDLE;
this.HFONT = this.HANDLE;
this.TCHAR = ctypes.jschar, // Mozilla compiled with UNICODE/_UNICODE macros and wchar_t = jschar
this.LPSTR = ctypes.char.ptr;
this.LPCSTR = ctypes.char.ptr;
this.LPTSTR = ctypes.jschar.ptr; // UNICODE
this.LPCTSTR = ctypes.jschar.ptr;
this.LPCWSTR = ctypes.jschar.ptr;
this.LPWSTR = ctypes.jschar.ptr; // WCHAR
this.LRESULT = this.LONG_PTR;
this.WPARAM = this.UINT_PTR;
this.LPARAM = this.LONG_PTR;
this.FARPROC = ctypes.voidptr_t; // typedef INT_PTR (FAR WINAPI *FARPROC)();
this.COLORREF = this.DWORD; // 0x00bbggrr
this.GUID = ctypes.StructType("GUID", [
{ "Data1": ctypes.unsigned_long },
{ "Data2": ctypes.unsigned_short },
{ "Data3": ctypes.unsigned_short },
{ "Data4": ctypes.char.array(8) }
]);
/*
* #define MAKEINTRESOURCEA(i) ((LPSTR)((ULONG_PTR)((WORD)(i))))
* #define MAKEINTRESOURCEW(i) ((LPWSTR)((ULONG_PTR)((WORD)(i))))
*/
this.MAKEINTRESOURCE = function(i) {return this.LPWSTR(i);};
this._T = function(str) {
return ctypes.jschar.array()(str);
};
/*
* #define LOWORD(l) ((WORD)((DWORD_PTR)(l) & 0xffff))
* #define HIWORD(l) ((WORD)((DWORD_PTR)(l) >> 16))
* #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
* #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
*/
this.LOWORD = function(l) {return l & 0x0000ffff;};
this.HIWORD = function(l) {return l >> 16;};
/* Although we shouldn't use LO-/HIWORD to get coords, because of negative
coords on multi-monitor displays, I'm not sure how to express the
GET_?_LPARAM macros with ctypes. */
this.GET_X_LPARAM = this.LOWORD;
this.GET_Y_LPARAM = this.HIWORD;
this.ERROR_INVALID_PARAMETER = 87;
this.ERROR_INVALID_WINDOW_HANDLE = 1400;
this.ERROR_RESOURCE_TYPE_NOT_FOUND = 1813;
// WinUser.h
this.WM_NULL = 0x0000;
this.WM_CREATE = 0x0001;
this.WM_DESTROY = 0x0002;
this.WM_MOVE = 0x0003;
this.WM_SIZE = 0x0005;
this.WM_ACTIVATE = 0x0006;
this.WA_INACTIVE = 0;
this.WA_ACTIVE = 1;
this.WA_CLICKACTIVE = 2;
this.WM_SETFOCUS = 0x0007;
this.WM_KILLFOCUS = 0x0008;
this.WM_ENABLE = 0x000A;
this.WM_SETREDRAW = 0x000B;
this.WM_SETTEXT = 0x000C;
this.WM_GETTEXT = 0x000D;
this.WM_GETTEXTLENGTH = 0x000E;
this.WM_PAINT = 0x000F;
this.WM_CLOSE = 0x0010;
this.WM_QUIT = 0x0012;
this.WM_ERASEBKGND = 0x0014;
this.WM_SYSCOLORCHANGE = 0x0015;
this.WM_SHOWWINDOW = 0x0018;
this.WM_WININICHANGE = 0x001A;
this.WM_SETTINGCHANGE = this.WM_WININICHANGE;
this.WM_DEVMODECHANGE = 0x001B;
this.WM_ACTIVATEAPP = 0x001C;
this.WM_FONTCHANGE = 0x001D;
this.WM_TIMECHANGE = 0x001E;
this.WM_CANCELMODE = 0x001F;
this.WM_SETCURSOR = 0x0020;
this.WM_MOUSEACTIVATE = 0x0021;
this.WM_CHILDACTIVATE = 0x0022;
this.WM_QUEUESYNC = 0x0023;
this.WM_COMMAND = 0x0111;
this.WM_SYSCOMMAND = 0x0112;
this.WM_HSCROLL = 0x0114;
this.WM_VSCROLL = 0x0115;
this.WM_MOUSEWHEEL = 0x020A;
this.WM_USER = 0x0400;
this.WM_APP = 0x8000;
this.WM_CONTEXTMENU = 0x007B;
this.WM_MOUSEFIRST = 0x0200;
this.WM_MOUSEMOVE = 0x0200;
this.WM_LBUTTONDOWN = 0x0201;
this.WM_LBUTTONUP = 0x0202;
this.WM_LBUTTONDBLCLK = 0x0203;
this.WM_RBUTTONDOWN = 0x0204;
this.WM_RBUTTONUP = 0x0205;
this.WM_RBUTTONDBLCLK = 0x0206;
this.WM_MBUTTONDOWN = 0x0207;
this.WM_MBUTTONUP = 0x0208;
this.WM_MBUTTONDBLCLK = 0x0209;
this.WM_MOUSEWHEEL = 0x020A;
this.WM_XBUTTONDOWN = 0x020B;
this.WM_XBUTTONUP = 0x020C;
this.WM_XBUTTONDBLCLK = 0x020D;
this.WM_MOUSELAST = 0x020D;
this.WM_MOUSELAST = 0x020A;
this.SC_MINIMIZE = 0xF020;
this.SC_CLOSE = 0xF060;
this.SIZE_RESTORED = 0;
this.SIZE_MINIMIZED = 1;
this.SIZE_MAXIMIZED = 2;
this.SIZE_MAXSHOW = 3;
this.SIZE_MAXHIDE = 4;
this.BITMAP = ctypes.StructType("BITMAP", [
{ "bmType": this.LONG },
{ "bmWidth": this.LONG },
{ "bmHeight": this.LONG },
{ "bmWidthBytes": this.LONG },
{ "bmPlanes": this.WORD },
{ "bmBitsPixel": this.WORD },
{ "bmBits": this.LPVOID }
]);
this.ICONINFO = ctypes.StructType("ICONINFO", [
{ "fIcon": this.BOOL },
{ "xHotspot": this.DWORD },
{ "yHotspot": this.DWORD },
{ "hbmMask": this.HBITMAP },
{ "hbmColor": this.HBITMAP }
]);
this.PICONINFO = this.ICONINFO.ptr;
this.POINT = ctypes.StructType("POINT", [
{ "x": this.LONG },
{ "y": this.LONG }
]);
this.PPOINT = this.LPPOINT =this.POINT.ptr;
this.RECT = ctypes.StructType("RECT", [
{ "left": this.LONG },
{ "top": this.LONG },
{ "right": this.LONG },
{ "bottom": this.LONG }
]);
this.PRECT = this.RECT.ptr;
};
// ShellAPI.h
let nin_select = win32.WM_USER + 0;
win32.NIN_SELECT = nin_select;
win32.NINF_KEY = 0x1;
win32.NIN_KEYSELECT = (win32.NIN_SELECT | win32.NINF_KEY);

View File

@ -24,7 +24,7 @@ firetray.Chat = {
init: function() {
if (this.initialized) {
log.warn("Chat already initialized");
return;
return true;
}
log.debug("Enabling Chat");
@ -42,10 +42,11 @@ firetray.Chat = {
this.updateIcon();
this.initialized = true;
return true;
},
shutdown: function() {
if (!this.initialized) return;
if (!this.initialized) return false;
log.debug("Disabling Chat");
if (firetray.Chat.convsToAcknowledge.length())
@ -55,6 +56,7 @@ firetray.Chat = {
firetray.Utils.removeAllObservers(firetray.Chat);
this.initialized = false;
return true;
},
// FIXME: the listener should probably attached on the conv entry in the

View File

@ -204,7 +204,6 @@ firetray.ChatStatusIcon = {
{ notify: firetray.ChatStatusIcon.fadeStep },
ALPHA_STEP_SLEEP_MILLISECONDS, Ci.nsITimer.TYPE_ONE_SHOT);
} catch (e if e instanceof StopIteration) {
if (firetray.ChatStatusIcon.events['stop-fade']) {

View File

@ -172,8 +172,7 @@ firetray.PopupMenu = {
},
hideWindowItemAndSeparatorMaybe: function(xid) {
if (!this.windowItemsHandled())
return;
if (!this.windowItemsHandled()) return;
this.hideWindowItem(xid);
if (firetray.Handler.visibleWindowsCount === firetray.Handler.windowsCount)
@ -212,4 +211,5 @@ firetray.PopupMenu = {
}; // firetray.PopupMenu
firetray.Handler.showHidePopupMenuItems = firetray.PopupMenu.showHideWindowItems;
firetray.Handler.showHidePopupMenuItems =
firetray.PopupMenu.showHideWindowItems.bind(firetray.PopupMenu);

View File

@ -97,6 +97,8 @@ firetray.StatusIcon = {
this.themedIconApp = this.initThemedIcon(appIconNames);
},
loadImageCustom: function() { }, // done in setIconImageCustom
getAppIconNames: function() {
let appIconNames = firetray.Utils.getArrayPref(this.prefAppIconNames);
appIconNames.push(this.defaultAppIconName);
@ -135,7 +137,7 @@ firetray.StatusIcon = {
log.debug("showHideAllWindows: "+firetray.Handler.hasOwnProperty("showHideAllWindows"));
this.callbacks.iconActivate = gtk.GCallbackStatusIconActivate_t(
firetray.Handler.showHideAllWindows);
firetray.StatusIcon.onClick);
let handlerId = gobject.g_signal_connect(firetray.StatusIcon.trayIcon,
"activate", firetray.StatusIcon.callbacks.iconActivate, null);
log.debug("g_connect activate="+handlerId);
@ -176,12 +178,18 @@ firetray.StatusIcon = {
}
},
onClick: function(gtkStatusIcon, userData) {
firetray.Handler.showHideAllWindows();
let stopPropagation = true;
return stopPropagation;
},
setIconImageFromFile: function(filename) {
if (!firetray.StatusIcon.trayIcon)
log.error("Icon missing");
log.debug(filename);
gtk.gtk_status_icon_set_from_file(firetray.StatusIcon.trayIcon,
filename);
filename);
},
setIconImageFromGIcon: function(gicon) {
@ -200,17 +208,18 @@ firetray.Handler.setIconImageDefault = function() {
let appIconType = firetray.Utils.prefService.getIntPref("app_icon_type");
if (appIconType === FIRETRAY_APPLICATION_ICON_TYPE_THEMED)
firetray.StatusIcon.setIconImageFromGIcon(firetray.StatusIcon.themedIconApp);
else if (appIconType === FIRETRAY_APPLICATION_ICON_TYPE_CUSTOM) {
let appIconFilename = firetray.Utils.prefService.getCharPref("app_icon_filename");
firetray.StatusIcon.setIconImageFromFile(appIconFilename);
}
else if (appIconType === FIRETRAY_APPLICATION_ICON_TYPE_CUSTOM)
firetray.Handler.setIconImageCustom("app_icon_custom");
};
firetray.Handler.setIconImageNewMail = function() {
firetray.StatusIcon.setIconImageFromGIcon(firetray.StatusIcon.themedIconNewMail);
};
firetray.Handler.setIconImageFromFile = firetray.StatusIcon.setIconImageFromFile;
firetray.Handler.setIconImageCustom = function(prefname) {
let prefCustomIconPath = firetray.Utils.prefService.getCharPref(prefname);
firetray.StatusIcon.setIconImageFromFile(prefCustomIconPath);
};
// GTK bug: Gdk-CRITICAL **: IA__gdk_window_get_root_coords: assertion `GDK_IS_WINDOW (window)' failed
firetray.Handler.setIconTooltip = function(toolTipStr) {

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,9 @@ const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const FIRETRAY_LOG_LEVEL = "Warn"; // "All" for debugging
Cu.import("resource://gre/modules/Services.jsm");
const FIRETRAY_LOG_LEVEL = "All"; // "All" for debugging
const COLOR_NORMAL = "";
const COLOR_RESET = "\033[m";
@ -45,6 +47,7 @@ var colorTermLogColors = {
if ("undefined" == typeof(firetray)) {
var firetray = {};
};
var LogMod;
// https://wiki.mozilla.org/Labs/JS_Modules#Logging
firetray.Logging = {
@ -53,17 +56,22 @@ firetray.Logging = {
init: function() {
if (this.initialized) return;
["resource://services-common/log4moz.js", // FF
"resource:///app/modules/gloda/log4moz.js", // TB
"resource://firetray/log4moz.js"] // default
["resource://gre/modules/Log.jsm", // FF 27+
"resource://services-common/log4moz.js", // FF
"resource:///app/modules/gloda/log4moz.js", // TB
"resource://firetray/log4moz.js"] // default
.forEach(function(file){
try {Cu.import(file);} catch(x) {}
}, this);
if ("undefined" == typeof(Log4Moz)) {
let errMsg = "log4moz.js not found";
if ("undefined" != typeof(Log)) {
LogMod = Log;
} else if ("undefined" != typeof(Log4Moz)) {
LogMod = Log4Moz;
} else {
let errMsg = "Log module not found";
dump(errMsg+"\n");
Cu.ReportError(errMsg);
Cu.reportError(errMsg);
};
this.setupLogging("firetray");
@ -77,24 +85,9 @@ firetray.Logging = {
setupLogging: function(loggerName) {
// lifted from log4moz.js
function SimpleFormatter(dateFormat) {
if (dateFormat)
this.dateFormat = dateFormat;
}
function SimpleFormatter() {}
SimpleFormatter.prototype = {
__proto__: Log4Moz.Formatter.prototype,
_dateFormat: null,
get dateFormat() {
if (!this._dateFormat)
this._dateFormat = "%Y-%m-%d %H:%M:%S";
return this._dateFormat;
},
set dateFormat(format) {
this._dateFormat = format;
},
__proto__: LogMod.Formatter.prototype,
format: function(message) {
let messageString = "";
@ -108,7 +101,9 @@ firetray.Logging = {
([,mo] in Iterator(message.messageObjects))].join(" ");
let date = new Date(message.time);
let stringLog = date.toLocaleFormat(this.dateFormat) + " " +
let dateStr = date.getHours() + ":" + date.getMinutes() + ":" +
date.getSeconds() + "." + date.getMilliseconds();
let stringLog = dateStr + " " +
message.levelDesc + " " + message.loggerName + " " +
messageString + "\n";
@ -119,10 +114,7 @@ firetray.Logging = {
}
};
function ColorTermFormatter(dateFormat) {
if (dateFormat)
this.dateFormat = dateFormat;
}
function ColorTermFormatter() {}
ColorTermFormatter.prototype = {
__proto__: SimpleFormatter.prototype,
@ -136,26 +128,30 @@ firetray.Logging = {
};
// Loggers are hierarchical, affiliation is handled by a '.' in the name.
this._logger = Log4Moz.repository.getLogger(loggerName);
this._logger = LogMod.repository.getLogger(loggerName);
// Lowering this log level will affect all of our addon output
this._logger.level = Log4Moz.Level[FIRETRAY_LOG_LEVEL];
this._logger.level = LogMod.Level[FIRETRAY_LOG_LEVEL];
// A console appender outputs to the JS Error Console
let dateFormat = "%T";
let simpleFormatter = new SimpleFormatter(dateFormat);
let capp = new Log4Moz.ConsoleAppender(simpleFormatter);
capp.level = Log4Moz.Level["Debug"];
let simpleFormatter = new SimpleFormatter();
let capp = new LogMod.ConsoleAppender(simpleFormatter);
capp.level = LogMod.Level["Debug"];
this._logger.addAppender(capp);
// A dump appender outputs to standard out
let colorFormatter = new ColorTermFormatter(dateFormat);
let dapp = new Log4Moz.DumpAppender(colorFormatter);
dapp.level = Log4Moz.Level["Debug"];
let dumpFormatter;
if (Services.appinfo.OS.match(/(^Linux|^Darwin|BSD$)/)) {
dumpFormatter = new ColorTermFormatter();
} else {
dumpFormatter = new SimpleFormatter();
}
let dapp = new LogMod.DumpAppender(dumpFormatter);
dapp.level = LogMod.Level["Debug"];
this._logger.addAppender(dapp);
},
getLogger: function(loggerName){
return Log4Moz.repository.getLogger(loggerName);
return LogMod.repository.getLogger(loggerName);
}
}; // firetray.Logging

View File

@ -0,0 +1,160 @@
/* -*- 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/winnt/win32.jsm");
Cu.import("resource://firetray/ctypes/winnt/user32.jsm");
Cu.import("resource://firetray/commons.js");
firetray.Handler.subscribeLibsForClosing([user32]);
let log = firetray.Logging.getLogger("firetray.PopupMenu");
if ("undefined" == typeof(firetray.StatusIcon))
log.error("This module MUST be imported from/after StatusIcon !");
// popupmenu items
const IDM_PREF = 100;
const IDM_QUIT = 200;
const IDM_NEW_MSG = 300;
const IDM_NEW_WND = 400;
const IDM_RESET = 500;
firetray.PopupMenu = {
initialized: false,
menu: null,
init: function() {
this.create();
this.initialized = true;
return true;
},
shutdown: function() {
this.destroy();
log.debug("Disabling PopupMenu");
this.initialized = false;
},
create: function() {
this.menu = user32.CreatePopupMenu();
log.debug("menu="+this.menu);
this.insertMenuItem('Quit', 'quit', IDM_QUIT);
this.insertSeparator();
this.insertMenuItem('Preferences', 'prefs', IDM_PREF);
let menuSeparatorAdded = false;
if (firetray.Handler.inBrowserApp) {
this.insertSeparator();
menuSeparatorAdded = true;
this.insertMenuItem('NewWindow', 'new-wnd', IDM_NEW_WND);
}
if (firetray.Handler.inMailApp) {
if (!menuSeparatorAdded) {
this.insertSeparator();
}
this.insertMenuItem('NewMessage', 'new-msg', IDM_NEW_MSG);
this.insertMenuItem('ResetIcon', 'reset', IDM_RESET);
}
log.debug("PopupMenu created");
},
destroy: function() {
user32.DestroyMenu(this.menu);
log.debug("PopupMenu destroyed");
},
insertMenuItem: function(itemName, iconName, actionId) {
var menuItemLabel = firetray.Utils.strings.GetStringFromName("popupMenu.itemLabel."+itemName);
let mii = new user32.MENUITEMINFOW();
// BUG: ctypes doesn't detect wrong field assignments mii.size = ... ('size' undefined)
mii.cbSize = user32.MENUITEMINFOW.size;
mii.fMask = user32.MIIM_ID | user32.MIIM_STRING | user32.MIIM_DATA;
mii.wID = actionId;
// mii.dwItemData = win32.ULONG_PTR(actionId);
mii.dwTypeData = win32._T(menuItemLabel);
/* Under XP, putting a bitmap into hbmpItem results in ugly icons. We
should probably use HBMMENU_CALLBACK as explained in
http://www.nanoant.com/programming/themed-menus-icons-a-complete-vista-xp-solution.
But for now, we just don't display icons in XP-. */
if (win32.WINVER >= win32.WIN_VERSIONS["Vista"]) {
mii.fMask |= user32.MIIM_BITMAP;
mii.hbmpItem = firetray.StatusIcon.bitmaps.get(iconName);
}
log.debug("mii="+mii);
if (!user32.InsertMenuItemW(this.menu, 0, true, mii.address())) {
log.error("InsertMenuItemW failed winLastError="+ctypes.winLastError);
}
},
insertSeparator: function() {
user32.InsertMenuW(this.menu, 0, user32.MF_BYPOSITION|user32.MF_SEPARATOR,
0, null);
},
// FIXME: need to handle hides_single_window=false
addWindowItemAndSeparatorMaybe: function(wid) {
if (firetray.Handler.visibleWindowsCount === firetray.Handler.windowsCount)
this.insertSeparator();
this.addWindowItem(wid);
},
removeWindowItemAndSeparatorMaybe: function(wid) {
this.removeWindowItem(wid);
if (firetray.Handler.visibleWindowsCount === firetray.Handler.windowsCount - 1)
user32.DeleteMenu(this.menu, 0, user32.MF_BYPOSITION);
},
addWindowItem: function(wid) {
let title = firetray.Window.getWindowTitle(wid);
let mii = new user32.MENUITEMINFOW();
mii.cbSize = user32.MENUITEMINFOW.size;
mii.fMask = user32.MIIM_ID | user32.MIIM_STRING | user32.MIIM_DATA;
mii.wID = ctypes.UInt64(wid);
mii.dwTypeData = win32._T(title);
if (!user32.InsertMenuItemW(this.menu, 0, true, mii.address())) {
log.error("InsertMenuItemW failed winLastError="+ctypes.winLastError);
}
},
removeWindowItem: function(wid) {
let itemId = ctypes.UInt64(wid);
if (!user32.DeleteMenu(this.menu, itemId, user32.MF_BYCOMMAND)) {
log.error("DeleteMenu failed winLastError="+ctypes.winLastError);
}
},
processMenuItem: function(itemId) {
let wid = firetray.Win32.hwndToHexStr(win32.HWND(itemId));
if (firetray.Handler.windows[wid]) {
firetray.Handler.showWindow(wid);
return;
}
switch (itemId) {
case IDM_PREF: firetray.Handler.openPrefWindow(); break;
case IDM_QUIT: firetray.Handler.quitApplication(); break;
case IDM_NEW_MSG: firetray.Handler.openMailMessage(); break;
case IDM_NEW_WND: firetray.Handler.openBrowserWindow(); break;
case IDM_RESET: firetray.Handler.setIconImageDefault(); break;
default:
log.error("no action for itemId ("+itemId+")");
}
}
}; // firetray.PopupMenu
firetray.Handler.addPopupMenuWindowItemAndSeparatorMaybe =
firetray.PopupMenu.addWindowItemAndSeparatorMaybe.bind(firetray.PopupMenu);
firetray.Handler.removePopupMenuWindowItemAndSeparatorMaybe =
firetray.PopupMenu.removeWindowItemAndSeparatorMaybe.bind(firetray.PopupMenu);

View File

@ -0,0 +1,467 @@
/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* The tray icon for the main app. We need a hidden proxy window as (1) we want
a unique icon, (2) the icon sends notifications to a single window. */
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/ctypesMap.jsm");
Cu.import("resource://firetray/ctypes/winnt/win32.jsm");
Cu.import("resource://firetray/ctypes/winnt/gdi32.jsm");
Cu.import("resource://firetray/ctypes/winnt/kernel32.jsm");
Cu.import("resource://firetray/ctypes/winnt/shell32.jsm");
Cu.import("resource://firetray/ctypes/winnt/user32.jsm");
Cu.import("resource://firetray/winnt/FiretrayWin32.jsm");
Cu.import("resource://firetray/commons.js");
firetray.Handler.subscribeLibsForClosing([gdi32, kernel32, shell32, user32]);
let log = firetray.Logging.getLogger("firetray.StatusIcon");
if ("undefined" == typeof(firetray.Handler))
log.error("This module MUST be imported from/after FiretrayHandler !");
const ICON_CHROME_PATHS = {
'blank-icon': "chrome://firetray/skin/winnt/blank-icon.bmp",
'mail-unread': "chrome://firetray/skin/winnt/mail-unread.ico",
// these are for the popup menu:
'prefs': "chrome://firetray/skin/winnt/gtk-preferences.bmp",
'quit': "chrome://firetray/skin/winnt/application-exit.bmp",
'new-wnd': "chrome://firetray/skin/winnt/document-new.bmp",
'new-msg': "chrome://firetray/skin/winnt/gtk-edit.bmp",
'reset': "chrome://firetray/skin/winnt/gtk-apply.bmp"
};
firetray.StatusIcon = {
initialized: false,
callbacks: {}, // pointers to JS functions. MUST LIVE DURING ALL THE EXECUTION
notifyIconData: null,
hwndProxy: null,
WNDCLASS_NAME: "FireTrayHiddenWindowClass",
WNDCLASS_ATOM: null,
icons: (function(){return new ctypesMap(win32.HICON);})(),
bitmaps: (function(){return new ctypesMap(win32.HBITMAP);})(),
IMG_TYPES: {
ico: { win_t: win32.HICON, load_const: user32.IMAGE_ICON, map: 'icons' },
bmp: { win_t: win32.HBITMAP, load_const: user32.IMAGE_BITMAP, map: 'bitmaps' }
},
PREF_TO_ICON_NAME: {
app_icon_custom: 'app-custom',
mail_icon_custom: 'mail-custom'
},
init: function() {
this.loadImages();
this.create();
firetray.Handler.setIconImageDefault();
Cu.import("resource://firetray/winnt/FiretrayPopupMenu.jsm");
if (!firetray.PopupMenu.init())
return false;
this.initialized = true;
return true;
},
shutdown: function() {
log.debug("Disabling StatusIcon");
firetray.PopupMenu.shutdown();
this.destroy();
this.destroyImages();
this.initialized = false;
return true;
},
loadThemedIcons: function() { },
loadImages: function() {
// the Mozilla hidden window has the default Mozilla icon
let hwnd_hidden_moz = user32.FindWindowW("MozillaHiddenWindowClass", null);
log.debug("=== hwnd_hidden_moz="+hwnd_hidden_moz);
this.icons.insert('app', this.getIconFromWindow(hwnd_hidden_moz));
['app_icon_custom', 'mail_icon_custom'].forEach(function(elt) {
firetray.StatusIcon.loadImageCustom(elt);
});
/* we'll take the first icon in the .ico file. To get the icon count in the
file, pass ctypes.cast(ctypes.int(-1), win32.UINT); */
for (let imgName in ICON_CHROME_PATHS) {
let path = firetray.Utils.chromeToPath(ICON_CHROME_PATHS[imgName]);
let img = this.loadImageFromFile(path);
if (img)
this[this.IMG_TYPES[img['type']]['map']].insert(imgName, img['himg']);
}
},
loadImageCustom: function(prefname) {
log.debug("loadImageCustom pref="+prefname);
let filename = firetray.Utils.prefService.getCharPref(prefname);
if (!filename) return;
let img = this.loadImageFromFile(filename);
if (!img) return;
log.debug("loadImageCustom img type="+img['type']+" himg="+img['himg']);
let hicon = img['himg'];
if (img['type'] === 'bmp')
hicon = this.HBITMAPToHICON(img['himg']);
let name = this.PREF_TO_ICON_NAME[prefname];
log.debug(" name="+name);
this.icons.insert(name, hicon);
},
loadImageFromFile: function(path) {
let imgType = path.substr(-3, 3).toLowerCase();
if (!(imgType in this.IMG_TYPES)) {
throw Error("Unrecognized type '"+imgType+"'");
}
let imgTypeRec = this.IMG_TYPES[imgType];
let himg = ctypes.cast(
user32.LoadImageW(null, path, imgTypeRec['load_const'], 0, 0,
user32.LR_LOADFROMFILE|user32.LR_SHARED),
imgTypeRec['win_t']);
if (himg.isNull()) {
log.error("Could not load '"+path+"'="+himg+" winLastError="+ctypes.winLastError);
return null;
}
return {type:imgType, himg:himg};
},
HBITMAPToHICON: function(hBitmap) {
log.debug("HBITMAPToHICON hBitmap="+hBitmap);
let hWnd = null; // firetray.StatusIcon.hwndProxy;
let hdc = user32.GetDC(hWnd);
let bitmap = new win32.BITMAP();
let err = gdi32.GetObjectW(hBitmap, win32.BITMAP.size, bitmap.address()); // get bitmap info
let hBitmapMask = gdi32.CreateCompatibleBitmap(hdc, bitmap.bmWidth, bitmap.bmHeight);
user32.ReleaseDC(hWnd, hdc);
let iconInfo = win32.ICONINFO();
iconInfo.fIcon = true;
iconInfo.xHotspot = 0;
iconInfo.yHotspot = 0;
iconInfo.hbmMask = hBitmapMask;
iconInfo.hbmColor = hBitmap;
let hIcon = user32.CreateIconIndirect(iconInfo.address());
log.debug(" CreateIconIndirect hIcon="+hIcon+" lastError="+ctypes.winLastError);
gdi32.DeleteObject(hBitmap);
gdi32.DeleteObject(hBitmapMask);
return hIcon;
},
// images loaded with LR_SHARED need't be destroyed
destroyImages: function() {
[this.icons, this.bitmaps].forEach(function(map, idx, ary) {
let keys = map.keys;
for (let i=0, len=keys.length; i<len; ++i) {
let imgName = keys[i];
map.remove(imgName);
}
});
log.debug("Icons destroyed");
},
create: function() {
let hwnd_hidden = this.createProxyWindow();
nid = new shell32.NOTIFYICONDATAW();
nid.cbSize = shell32.NOTIFYICONDATAW_SIZE();
log.debug("SIZE="+nid.cbSize);
nid.szTip = firetray.Handler.appName;
nid.hIcon = this.icons.get('app');
nid.hWnd = hwnd_hidden;
nid.uCallbackMessage = firetray.Win32.WM_TRAYMESSAGE;
nid.uFlags = shell32.NIF_ICON | shell32.NIF_MESSAGE | shell32.NIF_TIP;
nid.uVersion = shell32.NOTIFYICON_VERSION_4;
// Install the icon
rv = shell32.Shell_NotifyIconW(shell32.NIM_ADD, nid.address());
log.debug("Shell_NotifyIcon ADD="+rv+" winLastError="+ctypes.winLastError); // ERROR_INVALID_WINDOW_HANDLE(1400)
rv = shell32.Shell_NotifyIconW(shell32.NIM_SETVERSION, nid.address());
log.debug("Shell_NotifyIcon SETVERSION="+rv+" winLastError="+ctypes.winLastError);
this.notifyIconData = nid;
this.hwndProxy = hwnd_hidden;
},
createProxyWindow: function() {
this.registerWindowClass();
let hwnd_hidden = user32.CreateWindowExW(
0, win32.LPCTSTR(this.WNDCLASS_ATOM), // lpClassName can also be _T(WNDCLASS_NAME)
"Firetray Message Window", 0,
user32.CW_USEDEFAULT, user32.CW_USEDEFAULT, user32.CW_USEDEFAULT, user32.CW_USEDEFAULT,
null, null, firetray.Win32.hInstance, null);
log.debug("CreateWindow="+!hwnd_hidden.isNull()+" winLastError="+ctypes.winLastError);
this.callbacks.proxyWndProc = user32.WNDPROC(firetray.StatusIcon.proxyWndProc);
let procPrev = user32.SetWindowLongW(hwnd_hidden, user32.GWLP_WNDPROC,
ctypes.cast(this.callbacks.proxyWndProc, win32.LONG_PTR));
log.debug("procPrev="+procPrev+" winLastError="+ctypes.winLastError);
firetray.Win32.acceptAllMessages(hwnd_hidden);
return hwnd_hidden;
},
registerWindowClass: function() {
let wndClass = new user32.WNDCLASSEXW();
wndClass.cbSize = user32.WNDCLASSEXW.size;
wndClass.lpfnWndProc = ctypes.cast(user32.DefWindowProcW, user32.WNDPROC);
wndClass.hInstance = firetray.Win32.hInstance;
wndClass.lpszClassName = win32._T(this.WNDCLASS_NAME);
this.WNDCLASS_ATOM = user32.RegisterClassExW(wndClass.address());
log.debug("WNDCLASS_ATOM="+this.WNDCLASS_ATOM);
},
proxyWndProc: function(hWnd, uMsg, wParam, lParam) {
// log.debug("ProxyWindowProc CALLED: hWnd="+hWnd+", uMsg="+uMsg+", wParam="+wParam+", lParam="+lParam);
// FIXME: WM_TASKBARCREATED is needed in case of explorer crash
// http://twigstechtips.blogspot.fr/2011/02/c-detect-when-windows-explorer-has.html
if (uMsg === firetray.Win32.WM_TASKBARCREATED) {
log.info("____________TASKBARCREATED");
} else if (uMsg === firetray.Win32.WM_TRAYMESSAGE) {
switch (win32.LOWORD(lParam)) {
case win32.WM_LBUTTONUP:
log.debug("WM_LBUTTONUP");
firetray.Handler.showHideAllWindows();
break;
case win32.WM_RBUTTONUP:
log.debug("WM_RBUTTONUP");
case win32.WM_CONTEXTMENU:
log.debug("WM_CONTEXTMENU");
/* Can't determine tray icon position precisely: the mouse cursor can
move between WM_RBUTTONDOWN and WM_RBUTTONUP, or the icon can have
been moved inside the notification area... so we opt for the easy
solution. */
let pos = user32.GetMessagePos();
let xPos = win32.GET_X_LPARAM(pos), yPos = win32.GET_Y_LPARAM(pos);
log.debug(" x="+xPos+" y="+yPos);
user32.SetForegroundWindow(hWnd);
user32.TrackPopupMenu(firetray.PopupMenu.menu, user32.TPM_RIGHTALIGN|user32.TPM_BOTTOMALIGN, xPos, yPos, 0, hWnd, null);
break;
default:
}
} else {
switch (uMsg) {
case win32.WM_SYSCOMMAND:
log.debug("WM_SYSCOMMAND wParam="+wParam+", lParam="+lParam);
break;
case win32.WM_COMMAND:
log.debug("WM_COMMAND wParam="+wParam+", lParam="+lParam);
firetray.PopupMenu.processMenuItem(wParam);
break;
case win32.WM_MENUCOMMAND:
log.debug("WM_MENUCOMMAND wParam="+wParam+", lParam="+lParam);
break;
case win32.WM_MENUCHAR:
log.debug("WM_MENUCHAR wParam="+wParam+", lParam="+lParam);
break;
}
}
return user32.DefWindowProcW(hWnd, uMsg, wParam, lParam);
},
getIconFromWindow: function(hwnd) {
let rv = user32.SendMessageW(hwnd, user32.WM_GETICON, user32.ICON_SMALL, 0);
log.debug("SendMessageW winLastError="+ctypes.winLastError);
// result is a ctypes.Int64. So we need to create a CData from it before
// casting it to a HICON.
let icon = ctypes.cast(win32.LRESULT(rv), win32.HICON);
if (icon.isNull()) { // from the window class
rv = user32.GetClassLong(hwnd, user32.GCLP_HICONSM);
icon = ctypes.cast(win32.ULONG_PTR(rv), win32.HICON);
log.debug("GetClassLong winLastError="+ctypes.winLastError);
}
if (icon.isNull()) { // from the first resource -> ERROR_RESOURCE_TYPE_NOT_FOUND(1813)
icon = user32.LoadIconW(firetray.Win32.hInstance, win32.MAKEINTRESOURCE(0));
log.debug("LoadIconW module winLastError="+ctypes.winLastError);
}
if (icon.isNull()) { // OS default icon
icon = user32.LoadIconW(null, win32.MAKEINTRESOURCE(user32.IDI_APPLICATION));
log.debug("LoadIconW default winLastError="+ctypes.winLastError);
}
log.debug("=== icon="+icon);
return icon;
},
destroyProxyWindow: function() {
let rv = user32.DestroyWindow(this.hwndProxy);
rv = this.unregisterWindowClass();
log.debug("Hidden window removed");
},
unregisterWindowClass: function() {
return user32.UnregisterClassW(win32.LPCTSTR(this.WNDCLASS_ATOM), firetray.Win32.hInstance);
},
destroy: function() {
let rv = shell32.Shell_NotifyIconW(shell32.NIM_DELETE, this.notifyIconData.address());
log.debug("Shell_NotifyIcon DELETE="+rv+" winLastError="+ctypes.winLastError);
this.destroyProxyWindow();
},
setIcon: function(iconinfo) {
let nid = firetray.StatusIcon.notifyIconData;
if (iconinfo.hicon)
nid.hIcon = iconinfo.hicon;
if (iconinfo.tip)
nid.szTip = iconinfo.tip;
rv = shell32.Shell_NotifyIconW(shell32.NIM_MODIFY, nid.address());
log.debug("Shell_NotifyIcon MODIFY="+rv+" winLastError="+ctypes.winLastError);
},
// rgb colors encoded in *bbggrr*
cssColorToCOLORREF: function(csscolor) {
let rgb = csscolor.substr(1);
let rr = rgb.substr(0, 2);
let gg = rgb.substr(2, 2);
let bb = rgb.substr(4, 2);
return parseInt("0x"+bb+gg+rr);
},
// http://stackoverflow.com/questions/457050/how-to-display-text-in-system-tray-icon-with-win32-api
createTextIcon: function(hWnd, text, color) {
log.debug("createTextIcon hWnd="+hWnd+" text="+text+" color="+color);
let blank = this.bitmaps.get('blank-icon');
let bitmap = new win32.BITMAP();
let err = gdi32.GetObjectW(blank, win32.BITMAP.size, bitmap.address()); // get bitmap info
let width = bitmap.bmWidth, height = bitmap.bmHeight;
let hdc = user32.GetDC(hWnd); // get device context (DC) for hWnd
let hdcMem = gdi32.CreateCompatibleDC(hdc); // creates a memory device context (DC) compatible with hdc (need a bitmap)
let hBitmap = user32.CopyImage(blank, user32.IMAGE_BITMAP, width, height, 0);
let hBitmapMask = gdi32.CreateCompatibleBitmap(hdc, width, height);
user32.ReleaseDC(hWnd, hdc);
let hBitmapOrig = gdi32.SelectObject(hdcMem, hBitmap);
function getFont(fnHeight) {
return gdi32.CreateFontW(
fnHeight, 0, 0, 0, gdi32.FW_SEMIBOLD, 0, 0, 0,
gdi32.ANSI_CHARSET, gdi32.OUT_OUTLINE_PRECIS, 0, gdi32.ANTIALIASED_QUALITY,
gdi32.FIXED_PITCH|gdi32.FF_SWISS, "Arial"
);
}
let fnHeight = firetray.js.floatToInt(height);
log.debug(" fnHeight initial="+fnHeight);
let hFont = getFont(fnHeight);
gdi32.SelectObject(hdcMem, hFont); // replace font in bitmap by hFont
{ let bufLen = 32, faceName = ctypes.jschar.array()(bufLen); gdi32.GetTextFaceW(hdcMem, bufLen, faceName); log.debug(" font="+faceName); }
let size = new gdi32.SIZE();
gdi32.GetTextExtentPoint32W(hdcMem, text, text.length, size.address()); // more reliable than DrawText(DT_CALCRECT)
while (size.cx > width - 6 || size.cy > height - 4) {
fnHeight -= 1;
hFont = getFont(fnHeight);
gdi32.SelectObject(hdcMem, hFont);
gdi32.GetTextExtentPoint32W(hdcMem, text, text.length, size.address());
log.debug(" fnHeight="+fnHeight+" width="+size.cx);
}
gdi32.SetTextColor(hdcMem, win32.COLORREF(this.cssColorToCOLORREF(color)));
gdi32.SetBkMode(hdcMem, gdi32.TRANSPARENT); // VERY IMPORTANT
gdi32.SetTextAlign(hdcMem, gdi32.TA_TOP|gdi32.TA_CENTER);
log.debug(" ___ALIGN=(winLastError="+ctypes.winLastError+") "+gdi32.GetTextAlign(hdcMem));
let nXStart = firetray.js.floatToInt((width - size.cx)/2),
nYStart = firetray.js.floatToInt((height - size.cy)/2);
gdi32.TextOutW(hdcMem, width/2, nYStart+2, text, text.length); // ref point for alignment
gdi32.SelectObject(hdcMem, hBitmapOrig);
let iconInfo = win32.ICONINFO();
iconInfo.fIcon = true;
iconInfo.xHotspot = 0;
iconInfo.yHotspot = 0;
iconInfo.hbmMask = hBitmapMask;
iconInfo.hbmColor = hBitmap;
let hIcon = user32.CreateIconIndirect(iconInfo.address());
log.debug(" CreateIconIndirect hIcon="+hIcon+" lastError="+ctypes.winLastError);
gdi32.DeleteObject(gdi32.SelectObject(hdcMem, hFont));
gdi32.DeleteDC(hdcMem);
// gdi32.DeleteDC(hdc); // already ReleaseDC's
gdi32.DeleteObject(hBitmap);
gdi32.DeleteObject(hBitmapMask);
return hIcon; // to be destroyed (DestroyIcon)
},
getIconSafe: function(name) {
let hicon = null;
try {
hicon = firetray.StatusIcon.icons.get(name);
} catch(error) {
log.error("icon '"+name+"' not defined.");
}
return hicon;
}
}; // firetray.StatusIcon
firetray.Handler.setIconImageDefault = function() {
log.debug("setIconImageDefault");
let appIconType = firetray.Utils.prefService.getIntPref("app_icon_type");
if (appIconType === FIRETRAY_APPLICATION_ICON_TYPE_THEMED)
firetray.StatusIcon.setIcon({hicon:firetray.StatusIcon.icons.get('app')});
else if (appIconType === FIRETRAY_APPLICATION_ICON_TYPE_CUSTOM) {
firetray.StatusIcon.setIcon({hicon:firetray.StatusIcon.getIconSafe('app-custom')});
}
};
firetray.Handler.setIconImageNewMail = function() {
log.debug("setIconImageDefault");
firetray.StatusIcon.setIcon({hicon:firetray.StatusIcon.icons.get('mail-unread')});
};
firetray.Handler.setIconImageCustom = function(prefname) {
log.debug("setIconImageCustom pref="+prefname);
let name = firetray.StatusIcon.PREF_TO_ICON_NAME[prefname];
firetray.StatusIcon.setIcon({hicon:firetray.StatusIcon.getIconSafe(name)});
};
// firetray.Handler.setIconImageFromFile = firetray.StatusIcon.setIconImageFromFile;
firetray.Handler.setIconTooltip = function(toolTipStr) {
log.debug("setIconTooltip");
firetray.StatusIcon.setIcon({tip:toolTipStr});
};
firetray.Handler.setIconTooltipDefault = function() {
log.debug("setIconTooltipDefault");
firetray.StatusIcon.setIcon({tip:this.appName});
};
firetray.Handler.setIconText = function(text, color) {
let hicon = firetray.StatusIcon.createTextIcon(
firetray.StatusIcon.hwndProxy, text, color);
log.debug("setIconText icon="+hicon);
if (hicon.isNull())
log.error("Could not create hicon");
firetray.StatusIcon.setIcon({hicon:hicon});
};
firetray.Handler.setIconVisibility = function(visible) {
};

View File

@ -0,0 +1,61 @@
/* -*- 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/ctypes.jsm");
Cu.import("resource://firetray/ctypes/winnt/win32.jsm");
Cu.import("resource://firetray/ctypes/winnt/kernel32.jsm");
Cu.import("resource://firetray/ctypes/winnt/user32.jsm");
Cu.import("resource://firetray/commons.js");
firetray.Handler.subscribeLibsForClosing([kernel32, user32]);
let log = firetray.Logging.getLogger("firetray.Win32");
const kMessageTray = "_FIRETRAY_Tray";
if ("undefined" == typeof(firetray.Handler))
log.error("This module MUST be imported from/after FiretrayHandler !");
function Win32Env() {
this.hInstance = kernel32.GetModuleHandleW("xul"); // ordinary windows are created from xul.dll
log.debug("hInstance="+this.hInstance);
// we use our own messages because we create a different window class than Moz
this.WM_TASKBARCREATED = user32.RegisterWindowMessageW("TaskbarCreated");
this.WM_TRAYMESSAGE = user32.RegisterWindowMessageW(kMessageTray);
log.debug("WM_*="+this.WM_TASKBARCREATED+" "+this.WM_TRAYMESSAGE+" "+this.WM_TRAYCALLBACK);
/* if Administrator, accept messages from applications running in a lower
privilege level */
this.acceptAllMessages = function(hwnd) {
let rv = null;
log.debug(win32.WINVER+" >= "+win32.WIN_VERSIONS["7"]);
if (win32.WINVER >= win32.WIN_VERSIONS["7"]) {
rv = user32.ChangeWindowMessageFilterEx(hwnd, firetray.Win32.WM_TASKBARCREATED, user32.MSGFLT_ALLOW, null);
log.debug("ChangeWindowMessageFilterEx res="+rv+" winLastError="+ctypes.winLastError);
} else if (win32.WINVER >= win32.WINVER["Vista"]) {
rv = user32.ChangeWindowMessageFilter(firetray.Win32.WM_TASKBARCREATED, user32.MSGFLT_ADD);
log.debug("ChangeWindowMessageFilter res="+rv+" winLastError="+ctypes.winLastError);
} else {
// no UIPI
}
return rv;
};
// wid will be used as a string most of the time (through f.Handler.windows mainly)
this.hwndToHexStr = function(hWnd) {
return "0x" + ctypes.cast(hWnd, ctypes.uintptr_t).value.toString(16);
};
this.hexStrToHwnd = function(wid) {
return win32.HWND(ctypes.UInt64(wid));
};
}
firetray.Win32 = new Win32Env();

View File

@ -0,0 +1,294 @@
/* -*- 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/ctypes.jsm");
Cu.import("resource://firetray/ctypes/ctypesMap.jsm");
Cu.import("resource://firetray/ctypes/winnt/kernel32.jsm");
Cu.import("resource://firetray/ctypes/winnt/user32.jsm");
Cu.import("resource://firetray/ctypes/winnt/win32.jsm");
Cu.import("resource://firetray/winnt/FiretrayWin32.jsm");
Cu.import("resource://firetray/FiretrayWindow.jsm");
Cu.import("resource://firetray/commons.js");
firetray.Handler.subscribeLibsForClosing([user32, kernel32]);
let log = firetray.Logging.getLogger("firetray.Window");
if ("undefined" == typeof(firetray.Handler))
log.error("This module MUST be imported from/after FiretrayHandler !");
const FIRETRAY_XWINDOW_HIDDEN = 1 << 0; // when minimized also
const FIRETRAY_XWINDOW_MAXIMIZED = 1 << 1;
// We need to keep long-living references to wndProcs callbacks. As they also
// happen to be ctypes pointers, we store them into real ctypes arrays.
firetray.Handler.wndProcs = new ctypesMap(user32.WNDPROC);
firetray.Handler.wndProcsOrig = new ctypesMap(user32.WNDPROC);
firetray.Handler.procHooks = new ctypesMap(win32.HHOOK);
firetray.Handler.procHooksRegistred = new ctypesMap(win32.HHOOK);
firetray.Window = new FiretrayWindow();
firetray.Window.init = function() {
this.initialized = true;
};
firetray.Window.shutdown = function() {
this.initialized = false;
};
firetray.Window.getVisibility = function(wid) {
let hwnd = firetray.Win32.hexStrToHwnd(wid);
let style = user32.GetWindowLongW(hwnd, user32.GWL_STYLE);
return ((style & user32.WS_VISIBLE) != 0); // user32.IsWindowVisible(hwnd);
};
// firetray.Window.{show,hide} useless as we don't need to restore position and size
firetray.Window.setVisibility = function(wid, visible) {
log.debug("setVisibility="+visible);
let hwnd = firetray.Win32.hexStrToHwnd(wid);
let ret = user32.ShowWindow(hwnd, visible ? user32.SW_SHOW : user32.SW_HIDE);
log.debug(" ShowWindow="+ret+" winLastError="+ctypes.winLastError);
};
firetray.Window.wndProc = function(hWnd, uMsg, wParam, lParam) { // filterWindow
// log.debug("wndProc CALLED: hWnd="+hWnd+", uMsg=0x"+uMsg.toString(16)+", wParam="+wParam+", lParam="+lParam);
let wid = firetray.Win32.hwndToHexStr(hWnd);
if (uMsg === win32.WM_SYSCOMMAND) {
log.debug("wndProc CALLED with WM_SYSCOMMAND wParam="+wParam);
if (wParam === win32.SC_MINIMIZE) {
log.debug("GOT ICONIFIED");
if (firetray.Handler.hideOnMinimizeMaybe(wid)) {
return 0; // processed => preventDefault
}
}
}
let procPrev = firetray.Handler.wndProcsOrig.get(wid);
return user32.CallWindowProcW(procPrev, hWnd, uMsg, wParam, lParam); // or DefWindowProcW
};
// We could chain wndProcs, but adding a hook looks simpler.
firetray.Window.showCount = 0;
firetray.Window.startupHook = function(nCode, wParam, lParam) { // WH_CALLWNDPROC, WH_GETMESSAGE
// log.debug("startupHook CALLED: nCode="+nCode+", wParam="+wParam+", lParam="+lParam);
if (nCode < 0) return user32.CallNextHookEx(null, nCode, wParam, lParam); // user32.HC_ACTION
let cwpstruct = ctypes.cast(win32.LPARAM(lParam), user32.CWPSTRUCT.ptr).contents;
let uMsg = cwpstruct.message;
let hwnd = cwpstruct.hwnd;
let wid = firetray.Win32.hwndToHexStr(hwnd);
let wparam = cwpstruct.wParam;
let lparam = cwpstruct.lParam;
if (uMsg === win32.WM_SHOWWINDOW && wparam == 1 && lparam == 0) { // shown and ShowWindow called
log.debug("startupHook CALLED with WM_SHOWWINDOW wparam="+wparam+" lparam="+lparam);
firetray.Window.showCount += 1;
if (firetray.Utils.prefService.getBoolPref('start_hidden')) {
log.debug("start_hidden");
/* Compared to ShowWindow, SetWindowPlacement seems to bypass window
animations. http://stackoverflow.com/a/6087214 */
let placement = new user32.WINDOWPLACEMENT;
let ret = user32.GetWindowPlacement(hwnd, placement.address());
log.debug(" GetWindowPlacement="+ret+" winLastError="+ctypes.winLastError);
log.debug(" PLACEMENT="+placement);
if (firetray.Window.showCount < 2) {
// we can't prevent ShowWindow, so we mitigate the effect by minimizing
// it before. This is why we'll have to restore it when unhidden.
placement.showCmd = user32.SW_SHOWMINNOACTIVE;
ret = user32.SetWindowPlacement(hwnd, placement.address());
log.debug(" SetWindowPlacement="+ret+" winLastError="+ctypes.winLastError);
firetray.Utils.timer(
FIRETRAY_DELAY_NOWAIT_MILLISECONDS,
Ci.nsITimer.TYPE_ONE_SHOT, function(){firetray.Handler.hideWindow(wid);}
); // looks like CData (hwnd) cannot be closured
} else { // restore
firetray.Window.detachHook(wid);
placement.showCmd = user32.SW_RESTORE;
user32.SetWindowPlacement(hwnd, placement.address());
}
} else {
firetray.Window.detachHook(wid);
}
}
return user32.CallNextHookEx(null, nCode, wParam, lParam);
};
firetray.Window.attachWndProc = function(wid, hwnd) {
try {
let wndProc = user32.WNDPROC(firetray.Window.wndProc);
log.debug("proc="+wndProc);
firetray.Handler.wndProcs.insert(wid, wndProc);
let procPrev = user32.WNDPROC(
user32.SetWindowLongW(hwnd, user32.GWLP_WNDPROC,
ctypes.cast(wndProc, win32.LONG_PTR))
);
log.debug("procPrev="+procPrev+" winLastError="+ctypes.winLastError);
/* we can't store WNDPROC callbacks (JS ctypes objects) with SetPropW(), as
we need long-living refs. */
firetray.Handler.wndProcsOrig.insert(wid, procPrev);
} catch (x) {
if (x.name === "RangeError") { // instanceof not working :-(
let msg = x+"\n\nYou seem to have more than "+FIRETRAY_WINDOW_COUNT_MAX
+" windows open. This breaks FireTray and most probably "
+firetray.Handler.appName+".";
log.error(msg);
Cu.reportError(msg);
}else {
log.error(x);
Cu.reportError(x);
}
}
};
firetray.Window.detachWndProc = function(wid) {
let procPrev = firetray.Handler.wndProcsOrig.get(wid);
let hwnd = firetray.Win32.hexStrToHwnd(wid);
log.debug("hwnd="+hwnd);
let proc = user32.WNDPROC(
user32.SetWindowLongW(hwnd, user32.GWLP_WNDPROC,
ctypes.cast(procPrev, win32.LONG_PTR))
);
firetray.js.assert(firetray.js.strEquals(proc, firetray.Handler.wndProcs.get(wid)),
"Wrong WndProc replaced.");
firetray.Handler.wndProcs.remove(wid);
firetray.Handler.wndProcsOrig.remove(wid);
};
firetray.Window.attachHook = function(wid) { // detaches itself alone
let startupHook = user32.HOOKPROC(firetray.Window.startupHook);
log.debug("callhk="+startupHook);
firetray.Handler.procHooks.insert(wid, startupHook);
// Global hooks must reside in a dll (hence hInst). This is important for
// the scope of variables.
let hhook = user32.SetWindowsHookExW(
user32.WH_CALLWNDPROC, startupHook, null, kernel32.GetCurrentThreadId());
log.debug(" hhook="+hhook+" winLastError="+ctypes.winLastError);
firetray.Handler.procHooksRegistred.insert(wid, hhook);
};
firetray.Window.detachHook = function(wid) { // detaches itself alone
let hook = firetray.Handler.procHooksRegistred.get(wid);
if (!user32.UnhookWindowsHookEx(hook)) {
log.error("UnhookWindowsHookEx for window "+wid+" failed: winLastError="+ctypes.winLastError);
return;
}
firetray.Handler.procHooks.remove(wid);
firetray.Handler.procHooksRegistred.remove(wid);
};
///////////////////////// firetray.Handler overriding /////////////////////////
/** debug facility */
firetray.Handler.dumpWindows = function() {
let dumpStr = ""+firetray.Handler.windowsCount;
for (let wid in firetray.Handler.windows) {
dumpStr += " "+wid;
}
log.info(dumpStr);
};
firetray.Handler.registerWindow = function(win) {
log.debug("register window");
let baseWin = firetray.Handler.getWindowInterface(win, "nsIBaseWindow");
let nativeHandle = baseWin.nativeHandle;
let hwnd = nativeHandle ?
firetray.Win32.hexStrToHwnd(nativeHandle) :
user32.FindWindowW("MozillaWindowClass", win.document.title);
let wid = firetray.Win32.hwndToHexStr(hwnd);
log.debug("=== hwnd="+hwnd+" wid="+wid+" win.document.title: "+win.document.title);
if (this.windows.hasOwnProperty(wid)) {
let msg = "Window ("+wid+") already registered.";
log.error(msg);
Cu.reportError(msg);
return false;
}
this.windows[wid] = {};
this.windows[wid].chromeWin = win;
this.windows[wid].baseWin = baseWin;
Object.defineProperties(this.windows[wid], {
"visible": { get: function(){return firetray.Window.getVisibility(wid);} }
});
log.debug("window "+wid+" registered");
firetray.Window.attachWndProc(wid, hwnd);
if (!firetray.Handler.appStarted) {
firetray.Window.attachHook(wid);
}
firetray.Win32.acceptAllMessages(hwnd);
log.debug("AFTER"); firetray.Handler.dumpWindows();
return wid;
};
firetray.Handler.unregisterWindow = function(win) {
log.debug("unregister window");
let wid = firetray.Window.getRegisteredWinIdFromChromeWindow(win);
if (!firetray.Handler.windows.hasOwnProperty(wid)) {
log.error("can't unregister unknown window "+wid);
return false;
}
firetray.Window.detachWndProc(wid);
if (!delete firetray.Handler.windows[wid])
throw new DeleteError();
firetray.Handler.dumpWindows();
log.debug("window "+wid+" unregistered");
return true;
};
firetray.Handler.showWindow = function(wid) {
firetray.Handler.removePopupMenuWindowItemAndSeparatorMaybe(wid);
return firetray.Window.setVisibility(wid, true);
};
firetray.Handler.hideWindow = function(wid) {
firetray.Handler.addPopupMenuWindowItemAndSeparatorMaybe(wid);
return firetray.Window.setVisibility(wid, false);
};
firetray.Handler.windowGetAttention = function(wid) { // see nsWindow.cpp
for (var first in this.windows) break;
wid = wid || first;
let hwnd = firetray.Win32.hexStrToHwnd(wid);
let fgWnd = user32.GetForegroundWindow();
log.debug(hwnd+" === "+fgWnd);
if (firetray.js.strEquals(hwnd, fgWnd) ||
!this.windows[wid].visible)
return;
let defaultCycleCount = new win32.DWORD;
user32.SystemParametersInfoW(user32.SPI_GETFOREGROUNDFLASHCOUNT, 0,
defaultCycleCount.address(), 0);
log.debug("defaultCycleCount="+defaultCycleCount);
let flashInfo = new user32.FLASHWINFO;
flashInfo.cbSize = user32.FLASHWINFO.size;
flashInfo.hwnd = hwnd;
flashInfo.dwFlags = user32.FLASHW_ALL;
flashInfo.uCount = defaultCycleCount;
flashInfo.dwTimeout = 0;
user32.FlashWindowEx(flashInfo.address());
};

View File

@ -7,6 +7,7 @@
int main(int argc, char **argv) {
printf("sizeof(void*)=%d\n",sizeof(void*));
printf("sizeof(char)=%d\n",sizeof(char));
printf("sizeof(short)=%d\n",sizeof(short));
printf("sizeof(int)=%d\n",sizeof(int));
printf("sizeof(long)=%d\n",sizeof(long));
printf("sizeof(unsigned_long)=%d\n",sizeof(unsigned long));