--- a/ginebra2/chrome/bedrockchrome/contextmenu.snippet/contextmenu.js Fri May 14 15:40:36 2010 +0300
+++ b/ginebra2/chrome/bedrockchrome/contextmenu.snippet/contextmenu.js Wed Jun 23 17:59:43 2010 +0300
@@ -1,125 +1,443 @@
-function printProp(x) {
- window.app.debug(x + ":");
- for (property in x) {
- window.chrome.alert(" " + property + ": " + x[property]);
- }
-}
+var cm_TheContextMenu;
+
+function ContextMenu(snippetId, contentView) {
+ this.snippetId = snippetId;
+ this.mainDiv = undefined;
+ this.tailEl = undefined;
+ this.contentView = contentView;
+ this.showTimeoutId = 0;
+ // Width of a tab with no text, just the icon. Icons must all be the same width.
+ // Update this if icon size or tab border width etc. changes -- or better yet, determine it dynamically.
+ this.normalTabWidth = 64;
-function historyViewContextEvent(e) {
- window.chrome.alert("history context: " + e + " " + e.itemIndex +
- " x=" + e.pos.x + " y=" + e.pos.y);
- printProp(e);
-}
+ // ContextMenu is a singleton to avoid problems with scope-chaining in some of the
+ // callbacks that it uses. See handleTabActivate.
+ if (cm_TheContextMenu != undefined) app.debug("ERROR: cm_TheContextMenu must be a singleton");
+ cm_TheContextMenu = this;
-window.chrome.chromeComplete.connect(chromeLoadComplete);
+ // Create tabs and their corresponding menus based on JSON data.
+ this.createTabsElement = function(data) {
+ this.mainDiv = document.createElement("div");
+ this.mainDiv.setAttribute("class", "ContextMenuDiv");
+ this.mainDiv.setAttribute("id", "cm_mainDivId");
+ var tabsDiv = document.createElement("div");
+ tabsDiv.setAttribute("class", "TabsDiv");
+ tabsDiv.setAttribute("id", "cm_tabsDivId");
-function chromeLoadComplete() {
-
- window.snippets.ContextMenuId.externalMouseEvent.connect(
- function(type, name, description) {
-
- if ((name == "QGraphicsSceneMouseReleaseEvent") || (name == "QGraphicsSceneResizeEvent")){
- window.snippets.ContextMenuId.hide();
- window.snippets.ContextMenuId.dontShow = true;
- setTimeout ( 'window.snippets.ContextMenuId.dontShow = false', 500 ); // reset the flag in 0.5 sec.
- }
- }
- );
- }
+ var menuDiv = document.createElement("div");
+ menuDiv.setAttribute("class", "MenuDiv");
+ menuDiv.setAttribute("id", "cm_menuDivId");
+
+ var tabsUl = document.createElement("ul");
+ tabsUl.setAttribute("class", "TabsUl");
+ tabsUl.setAttribute("id", "cm_tabsUlId");
+ tabsDiv.appendChild(tabsUl);
+
+ var currentTabFound = false;
-function ContextMenu()
-{
- // attach internal funcs
- this.write = writeContextMenu;
+ // Iterate through the list of tabs.
+ for (var i=0; i < data.tabs.length; i++) {
+ var tab = data.tabs[i];
+ if (tab.visible != undefined) {
+ if (!tab.visible()) {
+ continue;
+ }
+ }
- // do setup
- this.write();
-}
-////
+ // Create the tab.
+ var tabEl = document.createElement("li");
+ tabsUl.appendChild(tabEl);
+
+ var tabDiv = document.createElement("div");
+ tabEl.appendChild(tabDiv);
-function showBookmarkView()
-{
- if(window.views.WebView.BookmarkTreeView == undefined) {
- window.views.WebView.createSuperPage("BookmarkTreeView", true);
- }
- window.views.WebView.BookmarkTreeView.load(chrome.baseDirectory + "bookmarkview.superpage/BookmarkView.html");
+ var iconEl = undefined;
+ var iconHighlightedEl = undefined;
+
+ // Create the tab's icons.
+ if (tab.icon != undefined) {
+ iconEl = document.createElement("img");
+ iconEl.setAttribute("id", "icon");
+ iconEl.setAttribute("src", tab.icon);
+ tabDiv.appendChild(iconEl);
+ }
+ if (tab.iconHighlighted != undefined) {
+ iconHighlightedEl = document.createElement("img");
+ iconHighlightedEl.setAttribute("id", "iconHighlighted");
+ iconHighlightedEl.setAttribute("src", tab.iconHighlighted);
+ tabDiv.appendChild(iconHighlightedEl);
+ }
- // Show it.
- window.views.WebView.zoomFactor = 1.0;
- window.views.WebView.showSuperPage("BookmarkTreeView");
-}
+ // Create the tab's text.
+ if (tab.text != undefined) {
+ var anchorEl = document.createElement("a");
+ tabDiv.appendChild(anchorEl);
+ var textEl = document.createTextNode(tab.text);
+ anchorEl.appendChild(textEl);
+ }
-function showSettingsView()
-{
- if(window.views.WebView.SettingsView == undefined) {
- window.views.WebView.createSuperPage("SettingsView", true);
- }
- window.views.WebView.SettingsView.load(chrome.baseDirectory + "settingsview.superpage/SettingsView.html");
+ // Create the menu for this tab.
+ var menuEl = this.createMenuElement(tab);
+ menuDiv.appendChild(menuEl);
- // Show it.
- window.views.WebView.zoomFactor = 1.0;
- window.views.WebView.showSuperPage("SettingsView");
-}
+ var tabClassName;
+ if (tab.current == "true") {
+ // This is the current, or selected, tab.
+ tabClassName = "ViewContext_HighlightedTab";
+ tabDiv.className = "ViewContext_HighlightedTabDiv";
+ if(iconEl != undefined) {
+ iconEl.setAttribute("style", "display: none;");
+ }
+ currentTabFound = true;
+ }
+ else {
+ // Not selected.
+ tabClassName = "ViewContext_NormalTab";
+ tabDiv.className = "ViewContext_NormalTabDiv";
+ if(iconHighlightedEl != undefined) {
+ iconHighlightedEl.setAttribute("style", "display: none;");
+ }
+
+ // Hide its menu.
+ menuEl.style.display = "none";
+ }
+
+ if (tab.disabled == "true") {
+ // The tab is disabled, add the appropriate CSS class to it.
+ tabClassName += " ViewContext_DisabledTab";
+ }
-function showHistoryView()
-{
- if(window.views.WebView.BookmarkHistoryView == undefined) {
- window.views.WebView.createSuperPage("BookmarkHistoryView", true);
- }
- window.views.WebView.BookmarkHistoryView.load(chrome.baseDirectory + "historyview.superpage/historyView.html");
-
- // Show it.
- window.views.WebView.zoomFactor = 1.0;
- window.views.WebView.showSuperPage("BookmarkHistoryView");
-}
+ tabEl.className = tabClassName;
+
+ // Set up callback to show the menu that corresponds to this tab.
+ tabEl.onmouseup = this.handleTabActivate;
+ tabEl.cm_menu = menuEl;
+
+ // Remember that this tab is disabled.
+ tabEl.cm_disabled = tab.disabled == "true";
+ }
-function goToRecentUrlView () {
- showHistoryView();
- window.ViewStack.switchView("BookmarkHistoryView", "WebView");
+ // If a "current" tab was not specified, highlight the first tab and display its menu.
+ if (!currentTabFound) {
+ var firstTabEl = tabsUl.firstChild;
+ firstTabEl.className = "ViewContext_HighlightedTab";
+ firstTabEl.firstChild.className = "ViewContext_HighlightedTabDiv";
+ firstTabEl.cm_menu.style.display = "";
+ }
+
+// this.tailEl = document.createElement("img");
+// this.mainDiv.appendChild(this.tailEl);
+// this.tailEl.setAttribute("id", "cm_tailId");
+// this.tailEl.setAttribute("src", "contextmenu.snippet/icons/menu_tail.png");
+
+ this.mainDiv.appendChild(tabsDiv);
+ this.mainDiv.appendChild(menuDiv);
+ return this.mainDiv;
}
-function goToBookmarkView () {
- showBookmarkView();
- window.ViewStack.switchView("BookmarkTreeView", "WebView");
+ // Create a single menu based on the given data structure.
+ this.createMenuElement = function(data) {
+ // Create menu list.
+ var menuUl = document.createElement("ul");
+ menuUl.setAttribute("class", "MenuUl");
+
+ for (var i=0; i < data.menuItems.length; i++) {
+ var menuItem = data.menuItems[i];
+
+ // Create the item.
+ var itemLi = document.createElement("li");
+ itemLi.setAttribute("class", "MenuLi");
+ var itemSpan = document.createElement("div");
+
+ // Is it a row if items? enumerate that as a ul inside of this outer li
+ if(menuItem.menuRow != undefined) {
+ var menuRowUl = document.createElement("ul");
+ menuRowUl.setAttribute("class", "MenuRowUl");
+ itemSpan.appendChild(menuRowUl);
+
+ for(var j=0; j < menuItem.menuRow.length; j++)
+ {
+ var menuRowItem = menuItem.menuRow[j];
+
+ var rowItemLi = document.createElement("li");
+ rowItemLi.setAttribute("class", "MenuRowLi");
+ menuRowUl.appendChild(rowItemLi);
+
+ // bind to mouseup
+ rowItemLi.onmouseup = (function(handler) {
+ return function() {
+ if (handler != undefined)
+ handler();
+ this.hide();
+ }.bind(this)
+ }.bind(this))(menuRowItem.onclick);
+
+ if (menuRowItem.text != undefined) {
+ var textEl = document.createTextNode(menuRowItem.text);
+ rowItemLi.appendChild(textEl);
+ }
+ }
+ }
+ else {
+ itemLi.className += " RegularMenuLi";
+ if (menuItem.disabled == "true" || data.disabled == "true") {
+ // Disabled item.
+ itemLi.className += " ViewContext_DisabledMenuItem";
+ }
+ else {
+ // Enabled item. Set up the onmouseup handler.
+ itemLi.onmouseup = (function(handler) {
+ return function() {
+ if (handler != undefined)
+ handler();
+ this.hide();
+ }.bind(this)
+ }.bind(this))(menuItem.onclick);
+ }
+
+ // Create the item's icon.
+ if (menuItem.icon != undefined) {
+ var iconEl = document.createElement("img");
+ itemSpan.appendChild(iconEl);
+ iconEl.setAttribute("src", menuItem.icon);
+ }
+
+ // Create the item's text.
+ if (menuItem.text != undefined) {
+ var anchorEl = document.createElement("a");
+ itemSpan.appendChild(anchorEl);
+ var textEl = document.createTextNode(menuItem.text);
+ anchorEl.appendChild(textEl);
+ }
+ }
+
+ menuUl.appendChild(itemLi);
+ itemLi.appendChild(itemSpan);
+ }
+ return menuUl;
}
-function goToSettingsView () {
- showSettingsView();
- window.ViewStack.switchView("SettingsView", "WebView");
- //window.snippets.UrlSearchChromeId.hide(false);
-}
+ // Handle mouse clicks on tab elements.
+ // Note: "this" refers to the element that was clicked on, ie. the tab item.
+ this.handleTabActivate = function() {
+ var tabsDivChildren = document.getElementById("cm_tabsUlId").childNodes;
+ var otherTabsWidth = 0;
+ // Set the class for each tab.
+ for (var i = 0; i < tabsDivChildren.length; i++) {
+ var tabEl = tabsDivChildren[i];
+ var iconHighlighted = getChildById(tabEl, "iconHighlighted");
+ var icon = getChildById(tabEl, "icon");
+ if (tabEl == this) {
+ // Activate the tab.
+ tabEl.className = "ViewContext_HighlightedTab";
+ //tabEl.firstChild.className = "ViewContext_HighlightedTabDiv";
+
+ // Show the highlighted icon, if one exists.
+ if(iconHighlighted != undefined) {
+ iconHighlighted.style.display = "";
+
+ // Hide the normal icon.
+ if (icon != undefined) {
+ icon.style.display = "none";
+ }
+ }
+ }
+ else {
+ // Deactivate the tab.
+ tabEl.className = "ViewContext_NormalTab";
+ //tabEl.firstChildclassName = "ViewContext_NormalTabDiv";
+
+ // If a highlighted icon exists, switch to the normal one, otherwise leave
+ // the normal one alone.
+ if(iconHighlighted != undefined) {
+ iconHighlighted.style.display = "none";
+ if (icon != undefined) {
+ icon.style.display = "";
+ }
+ }
+ }
+ if (tabEl.cm_disabled) {
+ tabEl.className += " ViewContext_DisabledTab";
+ //tabEl.firstClassName += " ViewContext_DisabledTabDiv";
+ }
+ }
+
+ // Show the menu of the tab that was just clicked.
+ var menuDivChildren = document.getElementById("cm_menuDivId").childNodes;
+ for (var i = 0; i < menuDivChildren.length; i++) {
+ var menuEl = menuDivChildren[i];
+ if (menuEl == this.cm_menu) {
+ menuDivChildren[i].style.display = "";
+ }
+ else {
+ menuDivChildren[i].style.display = "none";
+ }
+ }
+ //cm_TheContextMenu.positionTail();
+ //document.getElementById(this.snippetId).clientHeight = this.mainDiv.clientHeight;
+ //document.getElementById("ContextMenuId").setAttribute("style", "height: " + document.getElementById("cm_mainDivId").clientHeight + "px;")
+
+ cm_TheContextMenu.updateTabSizes();
+ snippets[cm_TheContextMenu.snippetId].updateOwnerArea();
+ }
+
+ // Return the width of the "non-content" part of the element box, ie. thickness of
+ // the padding, the border and the margin.
+ this.getNonContentWidth = function(element) {
+ var tabStyle = document.defaultView.getComputedStyle(element, null);
+ return parseInt(tabStyle["margin-left"]) + parseInt(tabStyle["margin-right"]) +
+ parseInt(tabStyle["padding-left"]) + parseInt(tabStyle["padding-right"]) +
+ parseInt(tabStyle["border-left-width"]) + parseInt(tabStyle["border-right-width"]);
+ }
-// "Private" methods
-function writeContextMenu() {
- var html =
- '<div class="loadImagesTop">'+
- '</div>'+
- '<div class="loadImagesCenter">'+
- '</div>'+
- '<div class="loadImagesBottom">'+
- '</div>'+
+ // Update the tab widths. Expand the highlighted tab to its maximum width and shrink the
+ // normal tabs to their minimum widths. Note: it would be preferable to have this done
+ // by CSS.
+ this.updateTabSizes = function() {
+ var tabsUl = document.getElementById("cm_tabsUlId");
+ var tabsDivChildren = tabsUl.childNodes;
+ var otherTabsWidth = 0;
+ var highlightedTab;
+ for (var i = 0; i < tabsDivChildren.length; i++) {
+ var tabEl = tabsDivChildren[i];
+ if (tabEl.className.indexOf("ViewContext_HighlightedTab") != -1) {
+ highlightedTab = tabEl;
+ }
+ else {
+ var newTabWidth = cm_TheContextMenu.normalTabWidth - cm_TheContextMenu.getNonContentWidth(tabEl);
+ tabEl.style.width = newTabWidth;
+ otherTabsWidth += tabEl.offsetWidth;
+ }
+ }
+ if (highlightedTab != undefined) {
+ var newWidth = tabsUl.offsetWidth - otherTabsWidth;
+ newWidth -= cm_TheContextMenu.getNonContentWidth(highlightedTab) + 2;
+ highlightedTab.style.width = newWidth;
+ }
+ }
+
+ this.positionTail = function() {
+ // Move the "tail" into position.
+ var tailEl = document.getElementById("cm_tailId");
+ if (tailEl != undefined) {
+ //var mainDiv = document.getElementById("cm_mainDivId");
+ var tailX = (this.mainDiv.clientWidth - tailEl.clientWidth) / 2;
+ var tailY = this.mainDiv.clientHeight;
+ tailEl.setAttribute("style", "position:absolute; top: " + tailY + "px; left: " + tailX);
+ }
+ }
+
+ // Create all the DOM elements of the window.
+ this.create = function(menuData) {
+ var snippetEl = document.getElementById(this.snippetId);
+ var el = this.createTabsElement(menuData);
+ snippetEl.appendChild(el);
+ }
+
+ // Show the content menu. The menuData must contain an object tree describing the structure of the
+ // tabs and sub-menus.
+ //
+ // Example menu data in JSON format:
+ // var MenuData = {
+ // "tabs": [ {
+ // "text": "Tab 1",
+ // "icon": "tab1.png",
+ // "current": "true",
+ // "menuItems": [ {
+ // "text": Menu item 1,
+ // "onclick": handleMenu1,
+ // "icon": "menu1.png",
+ // },
+ // {
+ // "text": Menu item 2,
+ // "onclick": function() { alert("hello"); },
+ // "icon": "menu2.png",
+ // },
+ // ],
+ // },
+ // {
+ // "text": "Tab 2",
+ // "icon": "tab2.png",
+ // "menuItems": [ {
+ // "text": Menu item 1,
+ // "onclick": handleMenu21,
+ // },
+ // {
+ // "text": Menu item 2,
+ // "onclick": handleMenu22,
+ // },
+ // ],
+ // },
+ // ]
+ // };
+ //
- '<div class="menuItem menuTop">'+
- '<div class="menuItemContents" onmouseup="goToBookmarkView(); window.snippets.ContextMenuId.hide()">'+
- '<img STYLE="vertical-align: middle; padding-left: 20px; padding-right: 15px;" src="contextmenu.snippet/icons/menu_icon_bookmarks.png">'+
- '<span class="menuItemLabel">'+
- window.localeDelegate.translateText("content_view_menu_bookmarks")+
- '</span>'+
- '</div>'+
- '</div>'+
+ this.cancel = function() {
+ //app.debug("CM: cancel " + this.showTimeoutId);
+ clearTimeout(this.showTimeoutId);
+ this.showTimeoutId = 0;
+ this.cleanUp();
+ }
+
+ this.show = function(menuData) {
+ this.cleanUp();
+ this.create(menuData);
+ this.showTimeoutId = setTimeout('cm_TheContextMenu.showIt()', 10);
+ }
+
+ this.cleanUp = function() {
+ // Remove elements from DOM to save memory.
+ var oldEl = document.getElementById("cm_mainDivId");
+ if (oldEl) {
+ var snippetEl = document.getElementById(cm_TheContextMenu.snippetId);
+ snippetEl.removeChild(oldEl);
+ }
+ }
+
+ // Hide this window.
+ this.hide = function() {
+ snippets[cm_TheContextMenu.snippetId].hide();
+ }
+
+ this.onHide = function() {
+ this.cleanUp();
+ }
- '<div class="menuItem menuBot">'+
- '<div class="menuItemContents" onmouseup="goToRecentUrlView();window.snippets.ContextMenuId.hide()">'+
- '<img STYLE="vertical-align: middle; padding-left: 20px; padding-right: 15px;" src="contextmenu.snippet/icons/menu_icon_settings.png">'+
- '<span class="menuItemLabel">'+
- window.localeDelegate.translateText("content_view_menu_history")+
- '</span>'+
- '</div>'+
- '</div>';
-
- document.write(html);
-}
+ this.showIt = function() {
+ cm_TheContextMenu.updateTabSizes();
+ // Use a timer to actually show the window to allow the page re-layout
+ // to finish. We don't know when this really happens but 50ms seems to
+ // be enough on the N97. Without this delay the bottom of the window
+ // often gets clipped.
+ setTimeout("cm_TheContextMenu.showIt2()", 50);
+ }
+
+ this.showIt2 = function() {
+ var snippet = snippets[cm_TheContextMenu.snippetId];
+
+ snippet.updateOwnerArea();
+ snippet.setZValue(2);
+
+ centerSnippet(snippet);
+// if (showTail) {
+// cm_TheContextMenu.positionTail();
+// }
+ snippet.show();
+ }
+ chrome.chromeComplete.connect(createDelegate(this,
+ function() {
+ var snippet = snippets[this.snippetId];
+
+ chrome.aspectChanged.connect(createDelegate(this,
+ function(a) {
+ centerSnippet(snippets[this.snippetId]);
+ }));
+
+ snippet.hidden.connect(createDelegate(this, this.onHide));
+
+ }));
+} // End ContextMenu class