view content/browser.js @ 349:f95f6f24516f

menuitem to show flat or hierarchical collections, fix stream to unload/remove observer, wiring for menuitems and view states.
author alta88
date Tue, 28 Oct 2008 18:33:20 -0600
parents b8fbfd7ecfdf
children 88287a633755
line wrap: on
line source

/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Snowl.
 *
 * The Initial Developer of the Original Code is Mozilla.
 * Portions created by the Initial Developer are Copyright (C) 2008
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Myk Melez <myk@mozilla.org>
 *   alta88 <alta88@gmail.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

Cu.import("resource://snowl/modules/service.js");
Cu.import("resource://snowl/modules/opml.js");

let Snowl = {
  // The Preferences service that is imported from the Preferences module below.
  // XXX Could we import it lazily when this property is first accessed?
  Preferences: null,

  get _prefs() {
    delete this._prefs;
    return this._prefs = new this.Preferences("extensions.snowl.");
  },

  get _version() {
    let em = Cc["@mozilla.org/extensions/manager;1"].
             getService(Ci.nsIExtensionManager);
    let addon = em.getItemForID("snowl@mozilla.org");
    delete this._version;
    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");

    if (!lastVersion) {
      let url = "chrome://snowl/content/firstrun.html";
      setTimeout(function() { window.openUILinkIn(url, "tab") }, 500);
    }
    else if (lastVersion != this._version) {
      // We don't use this code yet because we haven't yet set up update.html
      // to properly list changes yet.
      // FIXME: make this work.
      //let url = "chrome://snowl/content/update.html?old=" + lastVersion +
      //          "&new=" + this._version;
      //setTimeout(function() { window.openUILinkIn(url, "tab"); }, 500);
    }

    this._prefs.set("lastVersion", this._version);

    // Init tab listeners
    this._initTabListeners();

    // Init river tab 
    setTimeout(function() { Snowl._initSnowlRiverTab() }, 100);

  },


  //**************************************************************************//
  // Menu Popups

  onToolsMenuPopupShowing: function(event) {
    // Reuse popup
    let popup = document.getElementById("snowlMenuPopup");
    let element = document.getElementById("snowlMenu");
    document.popupNode = element;
    popup.hidden = false;
    popup.openPopup(element, "end_before", -3);
  },

  onToolsMenuPopupHiding: function(event) {
    // Hide it manually, no idea why .hidePopup doesn't work..
    let popup = document.getElementById("snowlMenuPopup");
    //popup.hidePopup();
    popup.hidden = true;
  },

  onSnowlMenuPopupShowing: 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);

    if (event.target.id == "snowlMenuPopup")
      if (document.popupNode.localName == "toolbarbutton")
        document.popupNode.setAttribute("open", true);
  },

  onSnowlMenuPopupHiding: function(event) {
    event.target.hidden = false;
    if (event.target.id == "snowlMenuPopup")
      if (document.popupNode.localName == "toolbarbutton")
        document.popupNode.removeAttribute("open");
  },

  layoutName: ["classic", "vertical", "widemessage", "widethread", "stacked"],

  onLayoutPopupShowing: function(event) {
    // Layout checked state
    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++) {
        layoutmenuitems[i].setAttribute("disabled", !lchecked);
        if (i == layoutIndex)
          layoutmenuitems[i].setAttribute("checked", true);
      }
    }

    // Header checked state
    let menuitems = document.getElementsByAttribute("name", "snowlHeaderMenuitemGroup");
    let selectedIndex = this._prefs.get("message.headerView");
    if (menuitems) {
      for (var i = 0; i < menuitems.length; i++) {
        menuitems[i].setAttribute("disabled", !this._listMessageHeader());
        if (i == selectedIndex)
          menuitems[i].setAttribute("checked", true);
      }
    }

    // Hierarchy init 
    let hmenuitems = document.getElementsByAttribute("name", "snowlHierarchyMenuitemGroup");
    let isHierarchical = this._prefs.get("collection.hierarchicalView");
    let rivertab = this._snowlRiverTab();
    if (hmenuitems) {
      for (var i = 0; i < hmenuitems.length; i++) {
        hmenuitems[i].setAttribute("disabled", !lchecked && !(rivertab));
        if (i == isHierarchical)
          hmenuitems[i].setAttribute("checked", true);
      }
    }

    // Toolbars
    document.getElementById("snowlToolbarMenuitem").setAttribute("disabled",
        (!lchecked && !schecked) ? true : false);
    document.getElementById("snowlViewToolbarMenuitem").setAttribute("disabled",
        (!lchecked) ? true : false)
  },

  onSnowlButtonMouseDown: function(event) {
    // Jumping thru hoops to reuse popup for menupopup and button..
    let popup = document.getElementById("snowlMenuPopup");
    popup.hidden = false;
  },

  // Correct state of button based on message in current tab
  // XXX better to add url change listener?
  onSnowlToggleHeaderButtonMouseover: function(event) {
    event.target.setAttribute("disabled", !this._listMessageHeader());
  },


  //**************************************************************************//
  // Event Handlers

  onRiverView: function() {
    // 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");

      let riverbroadcaster = document.getElementById("viewSnowlRiver");
      let isRiverTab = gBrowser.selectedTab.hasAttribute("snowl");
      if (riverbroadcaster)
        riverbroadcaster.setAttribute("checked", isRiverTab);
    }
  },

  closeRiverView: function(aTab) {
    gBrowser.removeTab(aTab);
    document.getElementById("viewSnowlRiver").setAttribute("checked", false);
  },

  onTabSelect: function() {
    // Make sure desired header view showing..
    this._toggleHeader("TabSelect");

    // Set checkstate of River broadcaster
    let riverbroadcaster = document.getElementById("viewSnowlRiver");
    let isRiverTab = gBrowser.selectedTab.hasAttribute("snowl");
    if (riverbroadcaster)
      riverbroadcaster.setAttribute("checked", isRiverTab);
  },

  onCheckForNewMessages: function() {
    SnowlService.refreshAllSources();
  },

  onSubscribe: function() {
    gBrowser.selectedTab =
      gBrowser.addTab("chrome://snowl/content/subscribe.xul");
  },

  onImportOPML: function() {
    gBrowser.selectedTab =
      gBrowser.addTab("chrome://snowl/content/subscribe.xul?tab=opml");
  },

  onExportOPML: function() {
    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, menuitems, commands..

  // Header toggle
  kNoHeader: 0,
  kBriefHeader: 1,
  kFullHeader: 2,

  _toggleHeader: function(val) {
    let contentWindowDoc = gBrowser.selectedBrowser.contentDocument;
    let selectedIndex = null;
    let headerDeck = this._listMessageHeader();
    let button = document.getElementById("snowlToggleHeaderButton");
    if (button)
      button.setAttribute("disabled", !headerDeck ? true : false);

    // Not a snowl message in the tab..
    if (!headerDeck)
      return;

    let briefHeader = new XPCNativeWrapper(contentWindowDoc, "getElementById()")
        .getElementById("briefHeader");
    let fullHeader = new XPCNativeWrapper(contentWindowDoc, "getElementById()")
        .getElementById("fullHeader");
    let menuitems = document.getElementsByAttribute("name", "snowlHeaderMenuitemGroup");

    if (val == "TabSelect")
      // Make sure tab switch reflects header state
      selectedIndex = this._prefs.get("message.headerView");
    else if (val == "Toggle") {
      // Toggled to next in 3 way
      selectedIndex = parseInt(headerDeck.selectedIndex);
      selectedIndex = ++selectedIndex > 2 ? 0 : selectedIndex++;
      this._prefs.set("message.headerView", selectedIndex);
    }
    else {
      // Passed an event from menuitem choice
      selectedIndex = eval(val.target.getAttribute("headerType"));
      val.target.setAttribute("checked", true);
      this._prefs.set("message.headerView", selectedIndex);
    }

    headerDeck.setAttribute("selectedIndex", selectedIndex);
    briefHeader.setAttribute("collapsed", selectedIndex == 1 ? false : true);
    fullHeader.setAttribute("collapsed", selectedIndex == 2 ? false : true);

    if (button)
      button.setAttribute("snowlHeader", selectedIndex == 0 ?
          "none" : (selectedIndex == 1 ? "brief" : "full"));
    if (menuitems) {
      menuitems[selectedIndex].setAttribute("checked", true);
    }
  },

  _listMessageHeader: function() {
    let contentWindowDoc = gBrowser.selectedBrowser.contentDocument;
    let headerDeck = new XPCNativeWrapper(contentWindowDoc, "getElementById()")
        .getElementById("headerDeck");
    return headerDeck;
  },

  // Collections hierarchy toggle
  kHierarchyOff: 0,
  kHierarchyOn: 1,

  _toggleHierarchy: function(val) {
    let sidebarDoc = document.getElementById("sidebar").contentWindow;
    let lchecked = document.getElementById("viewSnowlList").hasAttribute("checked");
    if (lchecked) {
      sidebarDoc.CollectionsView.isHierarchical = val;
      sidebarDoc.CollectionsView._buildCollectionTree();
    }

    let rivertab = this._snowlRiverTab();
    if (rivertab) {
      let tabWindowDoc = gBrowser.getBrowserAtIndex(rivertab._tPos).contentWindow;
      let tabDoc = new XPCNativeWrapper(tabWindowDoc).wrappedJSObject;
      tabDoc.CollectionsView.isHierarchical = val;
      tabDoc.CollectionsView._buildCollectionTree();
    }

    this._prefs.set("collection.hierarchicalView", val);
  },

  // Need to init onLoad due to xul structure, toolbar exists in list and stream
  _initSnowlToolbar: function() {
    let menuitem = document.getElementById("snowlToolbarMenuitem");
    let doc = document.getElementById("sidebar").contentDocument;
    let toolbar = doc.getElementById("snowlToolbar");

    if (toolbar) {
      menuitem.setAttribute("checked", !toolbar.hidden);
    }
  },

  _toggleToolbar: function(event) {
    let name = event.target.getAttribute("name");
    let menuitem = document.getElementById(name+"Menuitem");
    let doc, toolbar, rtoolbar = null;

    if (name == "snowlToolbar") {
      doc = document.getElementById("sidebar").contentDocument;
      let rivertab = this._snowlRiverTab();
      if (rivertab)
        rtoolbar = gBrowser.getBrowserAtIndex(rivertab._tPos).
                   contentDocument.getElementById(name);
    }
    else 
      doc = document;

    toolbar = doc.getElementById(name);
    if (toolbar) {
      toolbar.hidden = !toolbar.hidden;
      menuitem.setAttribute("checked", !toolbar.hidden);
    }
    if (rtoolbar)
      rtoolbar.hidden = !rtoolbar.hidden;


  },

  // See if River tab exists
  _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;
  },

  // 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");

    let riverbroadcaster = document.getElementById("viewSnowlRiver");
    let isRiverTab = gBrowser.selectedTab.hasAttribute("snowl");
    if (riverbroadcaster)
      riverbroadcaster.setAttribute("checked", isRiverTab);
  },

  // 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);

window.addEventListener("load", function() { Snowl.init() }, false);