Mercurial > snowl
changeset 344:62557df25569
make the River menuitem toggle the river view open and closed; fix the Wide Message layout problem (chrome gets messed up in Wide Message mode, particularly when using extensions like Sidebar Tabs)
author | alta88 <alta88@gmail.com> |
---|---|
date | Fri, 24 Oct 2008 14:13:17 -0700 |
parents | af1ecc9327e0 |
children | 1bbcbc4d95d2 |
files | content/browser.css content/browser.js content/browser.xul content/collections.js content/list.js content/list.xul locale/en-US/browser.dtd modules/service.js |
diffstat | 8 files changed, 235 insertions(+), 47 deletions(-) [+] |
line wrap: on
line diff
--- a/content/browser.css Tue Oct 21 18:05:30 2008 -0700 +++ b/content/browser.css Fri Oct 24 14:13:17 2008 -0700 @@ -61,3 +61,11 @@ #palette-box toolbarbutton.snowlToolbarMenuButton { -moz-box-orient: vertical; } + +tab[snowl] { + background: white; +} + +tab[snowl] > image { + list-style-image: url("chrome://snowl/content/icons/snowl-16.png"); +}
--- a/content/browser.js Tue Oct 21 18:05:30 2008 -0700 +++ b/content/browser.js Fri Oct 24 14:13:17 2008 -0700 @@ -56,6 +56,11 @@ return this._version = addon.version; }, + get _mainWindow() { + delete this._mainWindow; + return this._mainWindow = document.getElementById("main-window"); + }, + init: function() { let lastVersion = this._prefs.get("lastVersion"); @@ -74,10 +79,29 @@ this._prefs.set("lastVersion", this._version); - // Listen for tab selected, to toggle preferred header view - gBrowser.tabContainer.addEventListener("TabSelect", - function() { Snowl._toggleHeader("TabSelect"); }, false); + // Init tab listeners + this._initTabListeners(); + + // Init river tab + setTimeout(function() { Snowl._initSnowlRiverTab() }, 100); + + }, + _snowlRiverTab: function() { + // Could be null if none else a reference to the tab + let gBrowser = document.getElementById("content"); + let snowlTab = null; + let snowlTabOpen = false; + + for (let index = 0; index < gBrowser.mTabs.length && !snowlTabOpen; index++) { + // Get the next tab + let currentTab = gBrowser.mTabs[index]; + if (currentTab.hasAttribute("snowl")) { + snowlTabOpen = true; + snowlTab = currentTab; + } + } + return snowlTab; }, //**************************************************************************// @@ -104,15 +128,35 @@ statusbarButton.appendChild(menuPopup); }, + onPopupShowing: function(event) { + // River view menuitem checkstate is off if its tab is not selected+focused + let rivermenuitem = document.getElementById("viewSnowlRiver"); + let isRiverTab = gBrowser.selectedTab.hasAttribute("snowl"); + rivermenuitem.setAttribute("checked", isRiverTab); + + // Header checked state + let menuitems = document.getElementsByAttribute("name", "snowlHeaderMenuitemGroup"); + let selectedIndex = this._prefs.get("message.headerView"); + if (menuitems) + menuitems[selectedIndex].setAttribute("checked", true); + }, + + layoutName: ["classic", "vertical", "widemessage", "widethread", "stacked"], + onLayoutPopupShowing: function(event) { let layoutmenu = document.getElementById("snowlLayoutMenu"); let lchecked = document.getElementById("viewSnowlList").hasAttribute("checked"); let schecked = document.getElementById("viewSnowlStream").hasAttribute("checked"); let layoutmenuitems = document.getElementsByAttribute("name", "snowlLayoutMenuitemGroup"); + let layout = this._mainWindow.getAttribute("snowllayout"); + let layoutIndex = this.layoutName.indexOf(layout); if (layoutmenuitems) { - for (var i = 0; i < layoutmenuitems.length; i++) + for (var i = 0; i < layoutmenuitems.length; i++) { layoutmenuitems[i].setAttribute("disabled", !lchecked); + if (i == layoutIndex) + layoutmenuitems[i].setAttribute("checked", true); + } } document.getElementById("snowlToolbarMenuitem").setAttribute("disabled", (!lchecked && !schecked) ? true : false); @@ -138,7 +182,44 @@ // Event Handlers onRiverView: function() { - gBrowser.selectedTab = gBrowser.addTab("chrome://snowl/content/river.xul"); + // Unchecking river menuitem, if current tab is snowl river tab, close it + let snowlRiverTab = this._snowlRiverTab(); + if (gBrowser.selectedTab == snowlRiverTab) { + this.closeRiverView(gBrowser.selectedTab); + return; + } + + // Handle unchecked menuitem + if (snowlRiverTab != null) { + // Snowl River tab is already open, focus it + gBrowser.selectedTab = snowlRiverTab; + gBrowser.focus(); + } + else { + // River tab not open, create a new one, toggle other views in sidebar 'off' +// let lchecked = document.getElementById("viewSnowlList").hasAttribute("checked"); +// let schecked = document.getElementById("viewSnowlStream").hasAttribute("checked"); +// if (lchecked) +// toggleSidebar('viewSnowlList'); +// if (schecked) +// toggleSidebar('viewSnowlStream'); + + gBrowser.selectedTab = gBrowser.addTab("chrome://snowl/content/river.xul"); + let tabIndex = gBrowser.mTabContainer.selectedIndex; + this._mainWindow.setAttribute("snowltabindex", tabIndex); + gBrowser.mTabs[tabIndex].setAttribute("snowl", "river"); + } + }, + + closeRiverView: function(aTab) { + gBrowser.removeTab(aTab); + document.getElementById("viewSnowlRiver").setAttribute("checked", false); + }, + + onTabSelect: function() { + // Make sure desired header view showing.. + this._toggleHeader("TabSelect"); + // others.. }, onCheckForNewMessages: function() { @@ -159,8 +240,22 @@ SnowlOPML.export(window); }, + _initTabListeners: function() { + // TabSelect - make sure header state correct + gBrowser.tabContainer.addEventListener("TabSelect", + function() { Snowl.onTabSelect("TabSelect"); }, false); + + // TabOpen, TabClose, TabMove - make sure snowl River tab index is correct + gBrowser.tabContainer.addEventListener("TabOpen", + function() { Snowl._resetSnowlRiverTabIndex(); }, false); + gBrowser.tabContainer.addEventListener("TabClose", + function() { Snowl._resetSnowlRiverTabIndex(); }, false); + gBrowser.tabContainer.addEventListener("TabMove", + function() { Snowl._resetSnowlRiverTabIndex(); }, false); + }, + //**************************************************************************// - // Buttons + // Buttons, menuitems, commands.. // Header toggle kNoHeader: 0, @@ -212,7 +307,6 @@ "none" : (selectedIndex == 1 ? "brief" : "full")); if (menuitems) { menuitems[selectedIndex].setAttribute("checked", true); -//alert("id: checked "+menuitems[selectedIndex].id+":"+menuitems[selectedIndex].getAttribute("checked")); } }, @@ -239,6 +333,29 @@ menuitem.setAttribute("checked", !toolbar.hidden); } }, + + // Need to init snowl River tab, if exists + _initSnowlRiverTab: function() { + let tabIndex = parseInt(this._mainWindow.getAttribute("snowltabindex")); + if (tabIndex >= 0 && tabIndex <= gBrowser.mTabs.length) + gBrowser.mTabs[tabIndex].setAttribute("snowl", "river"); + }, + + // Need to reset snowl River tab index + _resetSnowlRiverTabIndex: function () { + setTimeout(function() { + let snowlRiverTab = Snowl._snowlRiverTab(); + if (snowlRiverTab) { + // River tab exists + let newIndex = snowlRiverTab._tPos; + Snowl._mainWindow.setAttribute("snowltabindex", newIndex); + } + else + // Tab closed or none, remove it + Snowl._mainWindow.removeAttribute("snowltabindex"); + }, 200) + }, + }; Cu.import("resource://snowl/modules/Preferences.js", Snowl);
--- a/content/browser.xul Tue Oct 21 18:05:30 2008 -0700 +++ b/content/browser.xul Fri Oct 24 14:13:17 2008 -0700 @@ -74,14 +74,22 @@ <menu id="snowlMenu" class="menu-iconic" label="&snowlMenu.label;" image="chrome://snowl/content/icons/snowl-16.png" accesskey="&snowlMenu.accesskey;" insertafter="menu_openAddons"> - <menupopup id="snowlMenuPopup" onpopuphiding="Snowl.onPopupHiding(event)"> + <menupopup id="snowlMenuPopup" + onpopupshowing="Snowl.onPopupShowing(event)" + onpopuphiding="Snowl.onPopupHiding(event)"> <menuitem observes="viewSnowlList" label="&listView.label;" accesskey="&listView.accesskey;"/> <menuitem observes="viewSnowlStream" label="&streamView.label;" accesskey="&streamView.accesskey;"/> <menuseparator/> - <menuitem label="&riverView.label;" accesskey="&riverView.accesskey;" + <menuitem id="viewSnowlRiver" + label="&riverView.label;" + accesskey="&riverView.accesskey;" + autoCheck="false" + type="checkbox" + persist="checked" oncommand="Snowl.onRiverView()"/> + <menuseparator/> <menuitem label="&checkForNewMessages.label;" accesskey="&checkForNewMessages.accesskey;" oncommand="Snowl.onCheckForNewMessages()"/> @@ -97,35 +105,30 @@ checked="true" accesskey="&layoutClassic.accesskey;" name="snowlLayoutMenuitemGroup" - persist="checked" oncommand="SnowlMessageView.switchLayout(SnowlMessageView.kClassicLayout)"/> <menuitem id="snowlLayoutMenuitemVertical" label="&layoutVertical.label;" type="radio" accesskey="&layoutVertical.accesskey;" name="snowlLayoutMenuitemGroup" - persist="checked" oncommand="SnowlMessageView.switchLayout(SnowlMessageView.kVerticalLayout)"/> <menuitem id="snowlLayoutMenuitemWideMessage" label="&layoutWideMessage.label;" type="radio" accesskey="&layoutWideMessage.accesskey;" name="snowlLayoutMenuitemGroup" - persist="checked" oncommand="SnowlMessageView.switchLayout(SnowlMessageView.kWideMessageLayout)"/> <menuitem id="snowlLayoutMenuitemWideThread" label="&layoutWideThread.label;" type="radio" accesskey="&layoutWideThread.accesskey;" name="snowlLayoutMenuitemGroup" - persist="checked" oncommand="SnowlMessageView.switchLayout(SnowlMessageView.kWideThreadLayout)"/> <menuitem id="snowlLayoutMenuitemStacked" label="&layoutStacked.label;" type="radio" accesskey="&layoutStacked.accesskey;" name="snowlLayoutMenuitemGroup" - persist="checked" oncommand="SnowlMessageView.switchLayout(SnowlMessageView.kStackedLayout)"/> <menuseparator/> <menuitem id="snowlHeaderMenuitemNone" @@ -133,7 +136,6 @@ type="radio" accesskey="&headerNone.accesskey;" name="snowlHeaderMenuitemGroup" - persist="checked" headerType="Snowl.kNoHeader" oncommand="Snowl._toggleHeader(event)"/> <menuitem id="snowlHeaderMenuitemBrief" @@ -141,7 +143,6 @@ type="radio" accesskey="&headerBrief.accesskey;" name="snowlHeaderMenuitemGroup" - persist="checked" headerType="Snowl.kBriefHeader" oncommand="Snowl._toggleHeader(event)"/> <menuitem id="snowlHeaderMenuitemFull" @@ -149,27 +150,22 @@ type="radio" accesskey="&headerFull.accesskey;" name="snowlHeaderMenuitemGroup" - persist="checked" headerType="Snowl.kFullHeader" oncommand="Snowl._toggleHeader(event)"/> <menuseparator/> <menuitem id="snowlToolbarMenuitem" label="&toolbar.label;" type="checkbox" - checked="true" - autoCheck="false" accesskey="&toolbar.accesskey;" name="snowlToolbar" - persist="checked" oncommand="Snowl._toggleToolbar(event)"/> <menuitem id="snowlViewToolbarMenuitem" label="&viewtoolbar.label;" type="checkbox" checked="true" - autoCheck="false" + persist="checked" accesskey="&viewtoolbar.accesskey;" name="snowlViewToolbar" - persist="checked" oncommand="Snowl._toggleToolbar(event)"/> </menupopup> </menu>
--- a/content/collections.js Tue Oct 21 18:05:30 2008 -0700 +++ b/content/collections.js Fri Oct 24 14:13:17 2008 -0700 @@ -79,6 +79,11 @@ this._getCollections(); this._tree.view = this; + // Ensure collection selection maintained, if in List sidebar + if (document.getElementById("snowlSidebar")) + this._tree.view.selection.select( + gMessageViewWindow.SnowlMessageView._listCollectionIndex); + // Add a capturing click listener to the tree so we can find out if the user // clicked on a row that is already selected (in which case we let them edit // the collection name). @@ -305,7 +310,8 @@ return; let collection = this._rows[this._tree.currentIndex]; - gMessageViewWindow.SnowlMessageView.setCollection(collection); + let index = this._tree.currentIndex; + gMessageViewWindow.SnowlMessageView.setCollection(collection, index); }, onClick: function(aEvent) {
--- a/content/list.js Tue Oct 21 18:05:30 2008 -0700 +++ b/content/list.js Fri Oct 24 14:13:17 2008 -0700 @@ -108,6 +108,10 @@ return this._unreadButton = document.getElementById("snowlUnreadButton"); }, + // Always maintain selected collection within a session, only for List view + // XXX store on document for restore on restart?? + _listCollectionIndex: null, + // Maps XUL tree column IDs to collection properties. _columnProperties: { "snowlAuthorCol": "author", @@ -175,6 +179,10 @@ let sidebarBox = document.getElementById("sidebar-box"); this._snowlSidebar.appendChild(sidebarBox); + // Save position of sidebar/splitter (for wide message layout change) + let sidebarSplitter = document.getElementById("sidebar-splitter"); + this.gSidebarSplitterSiblingID = sidebarSplitter.nextSibling.id; + // Listen for sidebar-box hidden attr change, to toggle properly sidebarBox.addEventListener("DOMAttrModified", function(aEvent) { @@ -182,12 +190,11 @@ SnowlMessageView._snowlSidebar.hidden = (aEvent.newValue == "true"); }, false); - // Restore previous layout view and set menuitem checked - let mainWindow = document.getElementById("main-window"); - let layout = mainWindow.getAttribute("snowlLayout"); + // Restore previous layout view + let layout = Snowl._mainWindow.getAttribute("snowllayout"); // If error or first time default to 'classic' view - let layoutIndex = this.layoutName.indexOf(layout) < 0 ? - 0 : this.layoutName.indexOf(layout); + let layoutIndex = Snowl.layoutName.indexOf(layout) < 0 ? + this.kClassicLayout : Snowl.layoutName.indexOf(layout); this.layout(layoutIndex); }, @@ -288,8 +295,9 @@ this._rebuildView(); }, - setCollection: function(collection) { + setCollection: function(collection, index) { this._collection = collection; + this._listCollectionIndex = index; this._rebuildView(); }, @@ -302,7 +310,8 @@ // by reinitializing it instead of merely invalidating the box object // (which wouldn't accommodate changes to the number of rows). // XXX Is there a better way to do this? - this._tree.view = this; + // this._tree.view = this; <- doesn't work for all DOM moves.. + this._tree.boxObject.QueryInterface(Ci.nsITreeBoxObject).view = this; // Scroll back to the top of the tree. this._tree.boxObject.scrollToRow(this._tree.boxObject.getFirstVisibleRow()); @@ -315,7 +324,7 @@ // Because we've moved the tree, we have to reattach the view to it, // or we will get the error: "this._tree.boxObject.invalidate is not // a function" when we switch sources. - this._tree.view = this; + this._tree.boxObject.QueryInterface(Ci.nsITreeBoxObject).view = this; }, // Layout views @@ -325,18 +334,25 @@ kWideThreadLayout: 3, kStackedLayout: 4, gCurrentLayout: null, - layoutName: ["classic", "vertical", "widemessage", "widethread", "stacked"], + gSidebarSplitterSiblingID: null, layout: function(layout) { - let mainWindow = document.getElementById("main-window"); + if (layout == this.gCurrentLayout) + return; + let browser = document.getElementById("browser"); let appcontent = document.getElementById("appcontent"); let content = document.getElementById("content"); let sidebarSplitter = document.getElementById("sidebar-splitter"); + let snowlSidebar = this._snowlSidebar; let snowlThreadContainer = this._snowlViewContainer; let snowlThreadSplitter = this._snowlViewSplitter; - let layoutThreadPaneParent = ["appcontent", "browser", "snowlSidebar", "main-window", "sidebar-box"]; + let layoutThreadPaneParent = ["appcontent", + "browser", + "snowlSidebar", + "main-window", + "sidebar-box"]; // A 'null' is an effective appendChild, code is nice and reusable.. let layoutThreadPaneInsertBefore = [content, appcontent, null, browser, null]; // 0=horizontal, 1=vertical for orient arrays.. @@ -350,25 +366,46 @@ case this.kClassicLayout: case this.kVerticalLayout: case this.kWideThreadLayout: - desiredParent.insertBefore(snowlThreadContainer, layoutThreadPaneInsertBefore[layout]); - desiredParent.insertBefore(snowlThreadSplitter, layoutThreadPaneInsertBefore[layout]); + case this.kStackedLayout: + // Restore sidebar if coming from wide mess + if (this.gCurrentLayout == this.kWideMessageLayout) { + browser.insertBefore(snowlSidebar, + document.getElementById(this.gSidebarSplitterSiblingID)); + browser.insertBefore(sidebarSplitter, + document.getElementById(this.gSidebarSplitterSiblingID)); + } + if (layout == this.kStackedLayout) + desiredParent.insertBefore(snowlThreadSplitter, + layoutThreadPaneInsertBefore[layout]); + desiredParent.insertBefore(snowlThreadContainer, + layoutThreadPaneInsertBefore[layout]); + if (layout != this.kStackedLayout) + desiredParent.insertBefore(snowlThreadSplitter, + layoutThreadPaneInsertBefore[layout]); break; - case this.kStackedLayout: + case this.kWideMessageLayout: - desiredParent.insertBefore(snowlThreadSplitter, layoutThreadPaneInsertBefore[layout]); - desiredParent.insertBefore(snowlThreadContainer, layoutThreadPaneInsertBefore[layout]); + // Move sidebar for wide mess + Snowl._mainWindow.insertBefore(snowlSidebar, browser); + Snowl._mainWindow.insertBefore(sidebarSplitter, browser); + + desiredParent.insertBefore(snowlThreadSplitter, + layoutThreadPaneInsertBefore[layout]); + desiredParent.insertBefore(snowlThreadContainer, + layoutThreadPaneInsertBefore[layout]); break; } } // Adjust orient and flex for all layouts - browser.orient = sidebarSplitterOrient[layout] ? "vertical" : "horizontal"; - snowlThreadSplitter.orient = layoutsnowlThreadSplitterOrient[layout] ? "vertical" : "horizontal"; - sidebarSplitter.orient = sidebarSplitterOrient[layout] ? "vertical" : "horizontal"; + snowlThreadSplitter.orient = layoutsnowlThreadSplitterOrient[layout] ? + "vertical" : "horizontal"; + sidebarSplitter.orient = sidebarSplitterOrient[layout] ? + "vertical" : "horizontal"; snowlThreadContainer.setAttribute("flex", layoutSnowlBoxFlex[layout]); // Store the layout - mainWindow.setAttribute("snowlLayout", this.layoutName[layout]); + Snowl._mainWindow.setAttribute("snowllayout", Snowl.layoutName[layout]); this.gCurrentLayout = layout; },
--- a/content/list.xul Tue Oct 21 18:05:30 2008 -0700 +++ b/content/list.xul Fri Oct 24 14:13:17 2008 -0700 @@ -47,8 +47,9 @@ <script type="application/x-javascript" src="list.js"/> <window id="main-window" - persist="screenX screenY width height sizemode snowlLayout" - snowlLayout="classic"/> + persist="screenX screenY width height sizemode + snowllayout snowltabindex snowlcollectionindex" + snowllayout="classic"/> <hbox id="browser"> <hbox id="snowlSidebar"
--- a/locale/en-US/browser.dtd Tue Oct 21 18:05:30 2008 -0700 +++ b/locale/en-US/browser.dtd Fri Oct 24 14:13:17 2008 -0700 @@ -10,7 +10,7 @@ <!ENTITY listView.label "List"> <!ENTITY listView.accesskey "l"> -<!ENTITY riverView.label "Open River in New Tab"> +<!ENTITY riverView.label "River"> <!ENTITY riverView.accesskey "r"> <!ENTITY streamView.label "Stream"> <!ENTITY streamView.accesskey "t">
--- a/modules/service.js Tue Oct 21 18:05:30 2008 -0700 +++ b/modules/service.js Fri Oct 24 14:13:17 2008 -0700 @@ -96,6 +96,14 @@ return this._converterSvc; }, + get _promptSvc() { + let promptSvc = + Cc["@mozilla.org/embedcomp/prompt-service;1"]. + getService(Ci.nsIPromptService); + this.__defineGetter__("_promptSvc", function() { return promptSvc }); + return this._promptSvc; + }, + _log: null, _init: function() { @@ -296,8 +304,23 @@ */ hasMessage: function(aExternalID) { return SnowlDatastore.selectHasMessage(aExternalID); + }, + + _restartApp: function() { + // Notify all windows that an application quit has been requested. + var os = Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService); + var cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]. + createInstance(Ci.nsISupportsPRBool); + os.notifyObservers(cancelQuit, "quit-application-requested", "restart"); + + // Something aborted the quit process. + if (cancelQuit.data) + return; + + Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci.nsIAppStartup). + quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit); } - }; SnowlService._init();