FireTray/src/modules/linux/FiretrayPopupMenu.jsm

227 lines
8.1 KiB
JavaScript

/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
var EXPORTED_SYMBOLS = [ "firetray" ];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/ctypes.jsm");
Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
Cu.import("resource://firetray/commons.js");
firetray.Handler.subscribeLibsForClosing([gobject, gtk]);
let log = firetray.Logging.getLogger("firetray.PopupMenu");
if ("undefined" == typeof(firetray.StatusIcon))
log.error("This module MUST be imported from/after StatusIcon !");
firetray.PopupMenu = {
initialized: false,
callbacks: {menuItemWindowActivate: {}}, // FIXME: try to store them into a ctypes array/struct.
menu: null,
menuShell: null,
menuSeparatorWindows: null,
init: function() {
this.menu = gtk.gtk_menu_new();
this.menuShell = ctypes.cast(this.menu, gtk.GtkMenuShell.ptr);
var addMenuSeparator = false;
if (firetray.Handler.inMailApp) {
this.addItem("ResetIcon", "gtk-apply", "activate",
firetray.Handler.setIconImageDefault);
this.addItem("NewMessage", "gtk-edit", "activate",
firetray.Handler.openMailMessage);
addMenuSeparator = true;
}
if (firetray.Handler.inBrowserApp) {
this.addItem("NewWindow", "gtk-new", "activate",
firetray.Handler.openBrowserWindow);
addMenuSeparator = true;
}
var menuSeparator;
if (addMenuSeparator) {
menuSeparator = gtk.gtk_separator_menu_item_new();
gtk.gtk_menu_shell_append(this.menuShell, ctypes.cast(menuSeparator,
gtk.GtkWidget.ptr));
}
this.addItem("Preferences", "gtk-preferences", "activate",
firetray.Handler.openPrefWindow);
menuSeparator = gtk.gtk_separator_menu_item_new();
gtk.gtk_menu_shell_append(this.menuShell, ctypes.cast(menuSeparator,
gtk.GtkWidget.ptr));
this.addItem("Quit", "gtk-quit", "activate",
firetray.Handler.quitApplication);
var menuWidget = ctypes.cast(this.menu, gtk.GtkWidget.ptr);
gtk.gtk_widget_show_all(menuWidget);
var menuSeparatorWindows = gtk.gtk_separator_menu_item_new();
gtk.gtk_menu_shell_prepend(this.menuShell, ctypes.cast(menuSeparatorWindows,
gtk.GtkWidget.ptr));
this.menuSeparatorWindows = menuSeparatorWindows;
this.initialized = true;
return true;
},
shutdown: function() {
log.debug("Disabling PopupMenu");
this.initialized = false;
},
addItem: function(itemName, iconName, action, callback) {
var menuItemLabel = firetray.Utils.strings.GetStringFromName("popupMenu.itemLabel."+itemName); // shouldn't need to convert to utf8 later thank to js-ctypes
var menuItem = gtk.gtk_image_menu_item_new_with_label(menuItemLabel);
var menuItemIcon = gtk.gtk_image_new_from_stock(iconName, gtk.GTK_ICON_SIZE_MENU);
gtk.gtk_image_menu_item_set_image(menuItem, menuItemIcon);
gtk.gtk_menu_shell_append(this.menuShell, ctypes.cast(menuItem, gtk.GtkWidget.ptr));
function capitalizeFirst(str) {
return str.charAt(0).toUpperCase() + str.substring(1);
}
let cbName = "menuItem"+capitalizeFirst(itemName)+capitalizeFirst(action);
log.debug("cbName="+cbName);
this.callbacks[cbName] = gobject.GCallback_t(callback); // void return, no sentinel
gobject.g_signal_connect(menuItem, action,
firetray.PopupMenu.callbacks[cbName], null);
},
popup: function(icon, button, activateTime, menu) {
log.debug("menu-popup");
log.debug("ARGS="+icon+", "+button+", "+activateTime+", "+menu);
var gtkMenuPtr = ctypes.cast(menu, gtk.GtkMenu.ptr);
var iconGpointer = ctypes.cast(icon, gobject.gpointer);
gtk.gtk_menu_popup(
gtkMenuPtr, null, null, gtk.gtk_status_icon_position_menu,
iconGpointer, button, activateTime);
let stopPropagation = false;
return stopPropagation;
},
// we'll be creating menuItems for windows (and not showing them) even if
// hides_single_window is false, because if hides_single_window becomes true,
// we'll just have to show the menuItems
addWindowItem: function(xid) { // on registerWindow
log.debug("addWindowItem");
var menuItemWindow = this.createAndAddItemToMenu();
firetray.Handler.gtkPopupMenuWindowItems.insert(xid, menuItemWindow);
this.setWindowItemLabel(menuItemWindow, xid.toString()); // default to xid
let callback = gobject.GCallback_t(
function(){firetray.Handler.showWindow(xid);}, null, FIRETRAY_CB_SENTINEL); // void return, no sentinel
this.callbacks.menuItemWindowActivate[xid] = callback,
gobject.g_signal_connect(menuItemWindow, "activate", callback, null);
log.debug("added gtkPopupMenuWindowItems: "+firetray.Handler.gtkPopupMenuWindowItems.count);
},
createAndAddItemToMenu: function() {
var menuItem = gtk.gtk_image_menu_item_new();
gtk.gtk_menu_shell_prepend(this.menuShell, ctypes.cast(menuItem,
gtk.GtkWidget.ptr));
return menuItem;
},
removeWindowItem: function(xid) { // on unregisterWindow
let menuItemWindow = firetray.Handler.gtkPopupMenuWindowItems.get(xid);
firetray.Handler.gtkPopupMenuWindowItems.remove(xid);
this.removeItem(menuItemWindow);
log.debug("remove gtkPopupMenuWindowItems: "+firetray.Handler.gtkPopupMenuWindowItems.count);
},
removeItem: function(item) {
gtk.gtk_widget_destroy(ctypes.cast(item, gtk.GtkWidget.ptr));
},
showAllWindowItemsOnlyVisibleWindows: function() {
for (let xid in firetray.Handler.windows)
if (!firetray.Handler.windows[xid].visible)
this.showWindowItem(xid);
},
showWindowItem: function(xid) {
if (!this.windowItemsHandled())
return;
log.debug("showWindowItem");
let menuItemWindow = firetray.Handler.gtkPopupMenuWindowItems.get(xid);
this.showItem(menuItemWindow);
this.setWindowItemLabel(menuItemWindow, firetray.Window.getWindowTitle(xid));
this.showWindowSeparator();
},
showItem: function(menuItem) {
gtk.gtk_widget_show(ctypes.cast(menuItem, gtk.GtkWidget.ptr));
},
setWindowItemLabel: function(menuItem, label) {
log.debug("about to set title: "+label);
if (label)
gtk.gtk_menu_item_set_label(ctypes.cast(menuItem, gtk.GtkMenuItem.ptr), label);
},
hideAllWindowItems: function() {
for (let xid in firetray.Handler.windows)
this.hideWindowItemAndSeparator(xid);
},
hideWindowItemAndSeparator: function(xid) {
this.hideWindowItem(xid);
this.hideWindowSeparator();
},
hideWindowItemAndSeparatorMaybe: function(xid) {
if (!this.windowItemsHandled()) return;
this.hideWindowItem(xid);
if (firetray.Handler.visibleWindowsCount === firetray.Handler.windowsCount)
this.hideWindowSeparator();
},
hideWindowItem: function(xid) {
log.debug("hideWindowItem");
let menuItemWindow = firetray.Handler.gtkPopupMenuWindowItems.get(xid);
this.hideItem(menuItemWindow);
},
hideItem: function(menuItem) {
gtk.gtk_widget_hide(ctypes.cast(menuItem, gtk.GtkWidget.ptr));
},
showWindowSeparator: function() {
log.debug("showing menuSeparatorWindows");
gtk.gtk_widget_show(ctypes.cast(this.menuSeparatorWindows, gtk.GtkWidget.ptr));
},
hideWindowSeparator: function() {
log.debug("hiding menuSeparatorWindows");
gtk.gtk_widget_hide(ctypes.cast(this.menuSeparatorWindows, gtk.GtkWidget.ptr));
},
showHideWindowItems: function() {
if (this.windowItemsHandled())
this.showAllWindowItemsOnlyVisibleWindows();
else
this.hideAllWindowItems();
},
windowItemsHandled: function() {
return firetray.Utils.prefService.getBoolPref('hides_single_window');
}
}; // firetray.PopupMenu
firetray.Handler.showHidePopupMenuItems =
firetray.PopupMenu.showHideWindowItems.bind(firetray.PopupMenu);