diff -r b0dd75e285d2 -r 0f2326c2a325 ginebra2/chrome/bedrockchrome/contextmenu.snippet/contextmenu.js --- 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 = - '
'+ - '
'+ - '
'+ - '
'+ - '
'+ - '
'+ + // 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, + // }, + // ], + // }, + // ] + // }; + // - ''+ + 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(); + } - ''; - - 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