Bug 1475817 - Part 5: Convert simple <listbox> to <richlistbox> in calendar/. r=philipp
authorGeoff Lankow <geoff@darktrojan.net>
Tue, 17 Jul 2018 20:00:40 +1200
changeset 31767 1e7bb39d56f31b05be87943821a5112531c9b8d3
parent 31766 630c57d2b41283e904a7bf62dea5b20b24efa3dc
child 31768 c038f663b745ad6488576daaa49119062d164b12
push id2308
push userclokep@gmail.com
push dateWed, 05 Sep 2018 00:34:58 +0000
treeherdercomm-beta@e326b2dcd127 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersphilipp
bugs1475817
Bug 1475817 - Part 5: Convert simple <listbox> to <richlistbox> in calendar/. r=philipp
calendar/base/content/dialogs/calendar-event-dialog-reminder.js
calendar/base/content/dialogs/calendar-event-dialog-reminder.xul
calendar/base/content/dialogs/calendar-migration-dialog.js
calendar/base/content/dialogs/calendar-migration-dialog.xul
calendar/base/content/dialogs/chooseCalendarDialog.js
calendar/base/content/dialogs/chooseCalendarDialog.xul
calendar/base/content/preferences/categories.js
calendar/base/content/preferences/categories.xul
calendar/base/jar.mn
calendar/lightning/content/lightning-item-iframe.js
calendar/lightning/content/lightning-item-iframe.xul
calendar/lightning/content/messenger-overlay-preferences.xul
--- a/calendar/base/content/dialogs/calendar-event-dialog-reminder.js
+++ b/calendar/base/content/dialogs/calendar-event-dialog-reminder.js
@@ -203,28 +203,43 @@ function setupMaxReminders() {
  *
  * @param aListItem     (optional) A reference listitem to set up. If not
  *                                   passed, a new listitem will be created.
  * @param aReminder     The calIAlarm to display in this listitem
  * @param aItem         The item the alarm is set up on.
  * @return              The  XUL listitem node showing the passed reminder.
  */
 function setupListItem(aListItem, aReminder, aItem) {
-    let listitem = aListItem || createXULElement("listitem");
+    let listitem = aListItem || createXULElement("richlistitem");
 
     // Create a random id to be used for accessibility
     let reminderId = cal.getUUID();
     let ariaLabel = "reminder-action-" + aReminder.action + " " + reminderId;
 
     listitem.reminder = aReminder;
     listitem.setAttribute("id", reminderId);
-    listitem.setAttribute("label", aReminder.toString(aItem));
+    listitem.setAttribute("align", "center");
     listitem.setAttribute("aria-labelledby", ariaLabel);
-    listitem.setAttribute("class", "reminder-icon listitem-iconic");
     listitem.setAttribute("value", aReminder.action);
+
+    let image = listitem.querySelector("image");
+    if (!image) {
+        image = createXULElement("image");
+        image.setAttribute("class", "reminder-icon");
+        listitem.appendChild(image);
+    }
+    image.setAttribute("value", aReminder.action);
+
+    let label = listitem.querySelector("label");
+    if (!label) {
+        label = createXULElement("label");
+        listitem.appendChild(label);
+    }
+    label.setAttribute("value", aReminder.toString(aItem));
+
     return listitem;
 }
 
 /**
  * Handler function to be called when a reminder is selected in the listbox.
  * Sets up remaining controls to show the selected alarm.
  */
 function onReminderSelected() {
@@ -287,17 +302,18 @@ function onReminderSelected() {
 
 /**
  * Handler function to be called when an aspect of the alarm has been changed
  * using the dialog controls.
  *
  * @param event         The DOM event caused by the change.
  */
 function updateReminder(event) {
-    if (event.explicitOriginalTarget.localName == "listitem" ||
+    if (event.explicitOriginalTarget.localName == "richlistitem" ||
+        event.explicitOriginalTarget.parentNode.localName == "richlistitem" ||
         event.explicitOriginalTarget.id == "reminder-remove-button" ||
         !document.commandDispatcher.focusedElement) {
         // Do not set things if the select came from selecting or removing an
         // alarm from the list, or from setting when the dialog initially loaded.
         // XXX Quite fragile hack since radio/radiogroup doesn't have the
         // supressOnSelect stuff.
         return;
     }
--- a/calendar/base/content/dialogs/calendar-event-dialog-reminder.xul
+++ b/calendar/base/content/dialogs/calendar-event-dialog-reminder.xul
@@ -24,21 +24,21 @@
   <!-- Javascript includes -->
   <script type="application/javascript" src="chrome://calendar/content/calendar-event-dialog-reminder.js"/>
   <script type="application/javascript" src="chrome://calendar/content/calendar-ui-utils.js"/>
 
   <notificationbox id="reminder-notifications"/>
 
   <!-- Listbox with custom reminders -->
   <vbox flex="1">
-    <listbox id="reminder-listbox"
-             seltype="single"
-             class="event-dialog-listbox"
-             onselect="onReminderSelected()"
-             flex="1"/>
+    <richlistbox id="reminder-listbox"
+                 seltype="single"
+                 class="event-dialog-listbox"
+                 onselect="onReminderSelected()"
+                 flex="1"/>
     <hbox id="reminder-action-buttons-box" pack="end">
       <button id="reminder-new-button"
               label="&reminder.add.label;"
               accesskey="&reminder.add.accesskey;"
               oncommand="onNewReminder()"/>
       <button id="reminder-remove-button"
               label="&reminder.remove.label;"
               accesskey="&reminder.remove.accesskey;"
--- a/calendar/base/content/dialogs/calendar-migration-dialog.js
+++ b/calendar/base/content/dialogs/calendar-migration-dialog.js
@@ -31,22 +31,21 @@ var gMigrateWizard = {
                                                   ["Lightning"],
                                                   1);
         desc.textContent = props.formatStringFromName("migrationDescription",
                                                       ["Lightning"],
                                                       1);
 
         migLOG("migrators: " + window.arguments.length);
         for (let migrator of window.arguments[0]) {
-            let listItem = document.createElement("listitem");
-            listItem.setAttribute("type", "checkbox");
-            listItem.setAttribute("checked", true);
-            listItem.setAttribute("label", migrator.title);
-            listItem.migrator = migrator;
-            listbox.appendChild(listItem);
+            let checkbox = document.createElement("checkbox");
+            checkbox.setAttribute("checked", true);
+            checkbox.setAttribute("label", migrator.title);
+            checkbox.migrator = migrator;
+            listbox.appendChild(checkbox);
         }
     },
 
     /**
      * Called from the second page of the wizard.  Finds all of the migrators
      * that were checked and begins migrating their data.  Also controls the
      * progress dialog so the user can see what is happening. (somewhat)
      */
--- a/calendar/base/content/dialogs/calendar-migration-dialog.xul
+++ b/calendar/base/content/dialogs/calendar-migration-dialog.xul
@@ -25,18 +25,17 @@
     <script type="application/javascript" src="chrome://calendar/content/calendar-migration-dialog.js"/>
     <script type="application/javascript" src="chrome://calendar/content/import-export.js"/>
 
     <wizardpage id="wizardPage1"
                 pageid="initialPage"
                 next="progressPage"
                 label="&migration.welcome;">
         <label id="wizard-desc" control="datasource-list">&migration.list.description;</label>
-        <listbox id="datasource-list" flex="1">
-        </listbox>
+        <vbox id="datasource-list" flex="1"/>
     </wizardpage>
 
     <wizardpage id="wizardPage2"
                 pageid="progressPage"
                 label="&migration.importing;"
                 onpageshow="gMigrateWizard.migrateChecked()">
         <label control="migrate-progressmeter">&migration.progress.description;</label>
         <vbox flex="1">
new file mode 100644
--- /dev/null
+++ b/calendar/base/content/dialogs/chooseCalendarDialog.js
@@ -0,0 +1,90 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* exported loadCalendars, doOK, doExtra1 */
+ChromeUtils.import("resource://calendar/modules/calUtils.jsm");
+
+function loadCalendars() {
+    const calendarManager = Components.classes["@mozilla.org/calendar/manager;1"]
+                                    .getService(Components.interfaces.calICalendarManager);
+    let listbox = document.getElementById("calendar-list");
+    let composite = cal.view.getCompositeCalendar(window.opener);
+    let selectedIndex = 0;
+    let calendars;
+
+    if (window.arguments[0].calendars) {
+        calendars = window.arguments[0].calendars;
+    } else {
+        calendars = calendarManager.getCalendars({});
+    }
+    calendars = sortCalendarArray(calendars);
+
+    for (let i = 0; i < calendars.length; i++) {
+        let calendar = calendars[i];
+        let listItem = document.createElement("richlistitem");
+
+        let colorCell = document.createElement("box");
+        try {
+            let calColor = calendar.getProperty("color");
+            colorCell.style.background = calColor || "#a8c2e1";
+        } catch (e) {}
+        colorCell.style.width = "17px";
+        listItem.appendChild(colorCell);
+
+        let nameCell = document.createElement("label");
+        nameCell.setAttribute("value", calendar.name);
+        nameCell.setAttribute("flex", "1");
+        listItem.appendChild(nameCell);
+
+        listItem.calendar = calendar;
+        listbox.appendChild(listItem);
+
+        // Select the default calendar of the opening calendar window.
+        if (calendar.id == composite.defaultCalendar.id) {
+            selectedIndex = i;
+        }
+    }
+    document.getElementById("prompt").textContent = window.arguments[0].promptText;
+    if (window.arguments[0].promptNotify) {
+        document.getElementById("promptNotify").textContent = window.arguments[0].promptNotify;
+    }
+
+    // this button is the default action
+    let accept = document.getAnonymousElementByAttribute(document.documentElement, "dlgtype", "accept");
+    if (window.arguments[0].labelOk) {
+        accept.setAttribute("label", window.arguments[0].labelOk);
+        accept.removeAttribute("hidden");
+    }
+
+    let extra1 = document.getAnonymousElementByAttribute(document.documentElement, "dlgtype", "extra1");
+    if (window.arguments[0].labelExtra1) {
+        extra1.setAttribute("label", window.arguments[0].labelExtra1);
+        extra1.removeAttribute("hidden");
+    } else {
+        extra1.setAttribute("hidden", "true");
+    }
+
+    if (calendars.length) {
+        listbox.ensureIndexIsVisible(selectedIndex);
+        listbox.timedSelect(listbox.getItemAtIndex(selectedIndex), 0);
+    } else {
+        // If there are no calendars, then disable the accept button
+        document.documentElement.getButton("accept").setAttribute("disabled", "true");
+    }
+
+    window.sizeToContent();
+}
+
+function doOK() {
+    let listbox = document.getElementById("calendar-list");
+    window.arguments[0].onOk(listbox.selectedItem.calendar);
+    return true;
+}
+
+function doExtra1() {
+    let listbox = document.getElementById("calendar-list");
+    window.arguments[0].onExtra1(listbox.selectedItem.calendar);
+    window.close();
+    return true;
+}
--- a/calendar/base/content/dialogs/chooseCalendarDialog.xul
+++ b/calendar/base/content/dialogs/chooseCalendarDialog.xul
@@ -17,107 +17,16 @@
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         buttons="accept,cancel"
         onload="setTimeout('loadCalendars()',0);"
         ondialogaccept="return doOK();"
         ondialogextra1="return doExtra1();"
         persist="screenX screenY height width">
 
     <script type="application/javascript" src="chrome://calendar/content/calendar-ui-utils.js"/>
-    <script type="application/javascript"><![CDATA[
-        ChromeUtils.import("resource://calendar/modules/calUtils.jsm");
-
-        function loadCalendars() {
-            const calendarManager = Components.classes["@mozilla.org/calendar/manager;1"]
-                                            .getService(Components.interfaces.calICalendarManager);
-            let listbox = document.getElementById("calendar-list");
-            let composite = cal.view.getCompositeCalendar(window.opener);
-            let selectedIndex = 0;
-            let calendars;
-
-            if (window.arguments[0].calendars) {
-                calendars = window.arguments[0].calendars;
-            } else {
-                calendars = calendarManager.getCalendars({});
-            }
-            calendars = sortCalendarArray(calendars);
-
-            for (let i = 0; i < calendars.length; i++) {
-                let calendar = calendars[i];
-                let listItem = document.createElement("listitem");
-
-                let colorCell = document.createElement("listcell");
-                try {
-                    let calColor = calendar.getProperty('color');
-                    colorCell.style.background = calColor || "#a8c2e1";
-                } catch(e) {}
-                listItem.appendChild(colorCell);
-
-                let nameCell = document.createElement("listcell");
-                nameCell.setAttribute("label", calendar.name);
-                listItem.appendChild(nameCell);
-
-                listItem.calendar = calendar;
-                listbox.appendChild(listItem);
-                listItem.setAttribute("flex", "1");
-
-                // Select the default calendar of the opening calendar window.
-                if (calendar.id == composite.defaultCalendar.id) {
-                    selectedIndex = i;
-                }
-            }
-            document.getElementById("prompt").textContent = window.arguments[0].promptText;
-            if (window.arguments[0].promptNotify) {
-                document.getElementById("promptNotify").textContent = window.arguments[0].promptNotify;
-            }
-
-            // this button is the default action
-            let accept = document.getAnonymousElementByAttribute(document.documentElement, "dlgtype", "accept");
-            if (window.arguments[0].labelOk) {
-                accept.setAttribute("label", window.arguments[0].labelOk);
-                accept.removeAttribute("hidden");
-            }
-
-            let extra1 = document.getAnonymousElementByAttribute(document.documentElement, "dlgtype", "extra1");
-            if (window.arguments[0].labelExtra1) {
-                extra1.setAttribute("label", window.arguments[0].labelExtra1);
-                extra1.removeAttribute("hidden");
-            } else {
-                extra1.setAttribute("hidden", "true");
-            }
-
-            if (calendars.length) {
-                listbox.ensureIndexIsVisible(selectedIndex);
-                var selItem = listbox.getItemAtIndex(selectedIndex);
-                listbox.timedSelect(selItem, 0);
-            } else {
-                // If there are no calendars, then disable the accept button
-                document.documentElement.getButton("accept").setAttribute("disabled", "true");
-            }
-
-            window.sizeToContent();
-        }
-
-        function doOK() {
-            let listbox = document.getElementById("calendar-list");
-            window.arguments[0].onOk(listbox.selectedItem.calendar);
-            return true;
-        }
-
-        function doExtra1() {
-            let listbox = document.getElementById("calendar-list");
-            window.arguments[0].onExtra1(listbox.selectedItem.calendar);
-            window.close();
-            return true;
-        }
-    ]]></script>
+    <script type="application/javascript" src="chrome://calendar/content/chooseCalendarDialog.js"/>
 
     <vbox id="dialog-box" flex="1">
         <label id="prompt" control="calendar-list"/>
-        <listbox id="calendar-list" rows="5" flex="1" seltype="single">
-            <listcols>
-                <listcol/>
-                <listcol flex="1"/>
-            </listcols>
-        </listbox>
+        <richlistbox id="calendar-list" flex="1" seltype="single"/>
         <description id="promptNotify"/>
     </vbox>
 </dialog>
--- a/calendar/base/content/preferences/categories.js
+++ b/calendar/base/content/preferences/categories.js
@@ -77,27 +77,30 @@ var gCategoriesPane = {
     updateCategoryList: function() {
         this.updatePrefs();
         let listbox = document.getElementById("categorieslist");
 
         listbox.clearSelection();
         this.updateButtons();
 
 
-        while (listbox.lastChild.id != "categoryColumns") {
+        while (listbox.lastChild) {
             listbox.lastChild.remove();
         }
 
         for (let i = 0; i < gCategoryList.length; i++) {
-            let newListItem = document.createElement("listitem");
-            let categoryName = document.createElement("listcell");
+            let newListItem = document.createElement("richlistitem");
+            let categoryName = document.createElement("label");
             categoryName.setAttribute("id", gCategoryList[i]);
-            categoryName.setAttribute("label", gCategoryList[i]);
+            categoryName.setAttribute("flex", "1");
+            categoryName.setAttribute("value", gCategoryList[i]);
             let categoryNameFix = cal.view.formatStringForCSSRule(gCategoryList[i]);
-            let categoryColor = document.createElement("listcell");
+
+            let categoryColor = document.createElement("box");
+            categoryColor.setAttribute("width", "150");
             try {
                 let colorCode = categoryPrefBranch.getCharPref(categoryNameFix);
                 categoryColor.setAttribute("id", colorCode);
                 categoryColor.setAttribute("style", "background-color: " + colorCode + ";");
             } catch (ex) {
                 categoryColor.setAttribute("label", noneLabel);
             }
 
--- a/calendar/base/content/preferences/categories.xul
+++ b/calendar/base/content/preferences/categories.xul
@@ -16,37 +16,28 @@
         var noneLabel  = "&pref.categories.none.label;";
         var newTitle   = "&pref.categories.new.title;";
         var editTitle  = "&pref.categories.edit.title;";
         var overwrite  = "&pref.categories.overwrite;";
         var overwriteTitle = "&pref.categories.overwrite.title;";
         var noBlankCategories = "&pref.categories.noBlankCategories;";
     </script>
 
-    <vbox id="calPreferencesBoxCategories">
+    <vbox id="calPreferencesBoxCategories" flex="1">
         <preferences>
             <preference id="calendar.categories.names"
                         name="calendar.categories.names"
                         type="string"/>
         </preferences>
 
-        <listbox id="categorieslist"
-                 flex="1"
-                 seltype="multiple"
-                 onselect="gCategoriesPane.updateButtons()"
-                 ondblclick="gCategoriesPane.listOnDblClick(event)">
-            <listhead>
-                <listheader label="&pref.categories.name.label;"/>
-                <listheader label="&pref.categories.color.label;"/>
-            </listhead>
-            <listcols id="categoryColumns">
-                <listcol flex="3"/>
-                <listcol flex="1"/>
-            </listcols>
-        </listbox>
+        <richlistbox id="categorieslist"
+                     flex="1"
+                     seltype="multiple"
+                     onselect="gCategoriesPane.updateButtons()"
+                     ondblclick="gCategoriesPane.listOnDblClick(event)"/>
         <hbox pack="end">
             <button label="&pref.categories.newButton.label;"
                     accesskey="&pref.categories.newButton.accesskey;"
                     oncommand="gCategoriesPane.addCategory()"/>
             <button id="editCButton"
                     label="&pref.categories.editButton.label;"
                     accesskey="&pref.categories.editButton.accesskey;"
                     oncommand="gCategoriesPane.editCategory()"/>
--- a/calendar/base/jar.mn
+++ b/calendar/base/jar.mn
@@ -76,16 +76,17 @@ calendar.jar:
     content/calendar/calendar-properties-dialog.js         (content/dialogs/calendar-properties-dialog.js)
     content/calendar/calendar-providerUninstall-dialog.xul (content/dialogs/calendar-providerUninstall-dialog.xul)
     content/calendar/calendar-providerUninstall-dialog.js  (content/dialogs/calendar-providerUninstall-dialog.js)
     content/calendar/calendar-subscriptions-dialog.css     (content/dialogs/calendar-subscriptions-dialog.css)
     content/calendar/calendar-subscriptions-dialog.js      (content/dialogs/calendar-subscriptions-dialog.js)
     content/calendar/calendar-subscriptions-dialog.xul     (content/dialogs/calendar-subscriptions-dialog.xul)
     content/calendar/calendar-summary-dialog.js            (content/dialogs/calendar-summary-dialog.js)
     content/calendar/calendar-summary-dialog.xul           (content/dialogs/calendar-summary-dialog.xul)
+    content/calendar/chooseCalendarDialog.js               (content/dialogs/chooseCalendarDialog.js)
     content/calendar/chooseCalendarDialog.xul              (content/dialogs/chooseCalendarDialog.xul)
     content/calendar/preferences/alarms.xul                (content/preferences/alarms.xul)
     content/calendar/preferences/alarms.js                 (content/preferences/alarms.js)
     content/calendar/preferences/categories.xul            (content/preferences/categories.xul)
     content/calendar/preferences/categories.js             (content/preferences/categories.js)
     content/calendar/preferences/editCategory.xul          (content/preferences/editCategory.xul)
     content/calendar/preferences/editCategory.js           (content/preferences/editCategory.js)
     content/calendar/preferences/general.js                (content/preferences/general.js)
--- a/calendar/lightning/content/lightning-item-iframe.js
+++ b/calendar/lightning/content/lightning-item-iframe.js
@@ -2135,51 +2135,52 @@ function addAttachment(attachment, cloud
         !attachment.hashId ||
         attachment.hashId in gAttachMap) {
         return;
     }
 
     // We currently only support uri attachments
     if (attachment.uri) {
         let documentLink = document.getElementById("attachment-link");
-        let listItem = createXULElement("listitem");
-
-        // Set listitem attributes
-        listItem.setAttribute("label", makePrettyName(attachment.uri));
-        listItem.setAttribute("crop", "end");
-        listItem.setAttribute("class", "listitem-iconic");
+        let listItem = createXULElement("richlistitem");
+        let image = document.createElement("image");
+        listItem.appendChild(image);
+        let label = document.createElement("label");
+        label.setAttribute("value", makePrettyName(attachment.uri));
+        label.setAttribute("crop", "end");
+        listItem.appendChild(label);
         listItem.setAttribute("tooltiptext", attachment.uri.spec);
         if (cloudProvider) {
             if (attachment.uri.schemeIs("file")) {
                 // Its still a local url, needs to be uploaded
-                listItem.setAttribute("image", "chrome://messenger/skin/icons/connecting.png");
+                image.setAttribute("src", "chrome://messenger/skin/icons/connecting.png");
                 uploadCloudAttachment(attachment, cloudProvider, listItem);
             } else {
                 let leafName = attachment.getParameter("FILENAME");
-                listItem.setAttribute("image", cloudProvider.iconClass);
+                image.setAttribute("src", cloudProvider.iconClass);
                 if (leafName) {
                     listItem.setAttribute("label", leafName);
                 }
             }
         } else if (attachment.uri.schemeIs("file")) {
-            listItem.setAttribute("image", "moz-icon://" + attachment.uri);
+            image.setAttribute("src", "moz-icon://" + attachment.uri);
         } else {
             let leafName = attachment.getParameter("FILENAME");
             let providerType = attachment.getParameter("PROVIDER");
             let cloudFileEnabled = Preferences.get("mail.cloud_files.enabled", false);
 
             if (leafName) {
                 // TODO security issues?
                 listItem.setAttribute("label", leafName);
             }
             if (providerType && cloudFileEnabled) {
                 let provider = cloudFileAccounts.getProviderForType(providerType);
-                listItem.setAttribute("image", provider.iconClass);
+                image.setAttribute("src", provider.iconClass);
             } else {
-                listItem.setAttribute("image", "moz-icon://dummy.html");
+                image.setAttribute("src", "moz-icon://dummy.html");
             }
         }
 
         // Now that everything is set up, add it to the attachment box.
         documentLink.appendChild(listItem);
 
         // full attachment object is stored here
         listItem.attachment = attachment;
@@ -2254,79 +2255,86 @@ function deleteAllAttachments() {
 
 /**
  * Opens the selected attachment using the external protocol service.
  * @see nsIExternalProtocolService
  */
 function openAttachment() {
     // Only one file has to be selected and we don't handle base64 files at all
     let documentLink = document.getElementById("attachment-link");
-    if (documentLink.selectedItems.length == 1) {
-        let attURI = documentLink.getSelectedItem(0).attachment.uri;
+    if (documentLink.selectedItem) {
+        let attURI = documentLink.selectedItem.attachment.uri;
         let externalLoader = Components.classes["@mozilla.org/uriloader/external-protocol-service;1"]
                                        .getService(Components.interfaces.nsIExternalProtocolService);
         // TODO There should be a nicer dialog
         externalLoader.loadURI(attURI);
     }
 }
 
 /**
  * Copies the link location of the first selected attachment to the clipboard
  */
 function copyAttachment() {
     let documentLink = document.getElementById("attachment-link");
-    let attURI = documentLink.getSelectedItem(0).attachment.uri.spec;
-    let clipboard = Components.classes["@mozilla.org/widget/clipboardhelper;1"]
-                              .getService(Components.interfaces.nsIClipboardHelper);
-    clipboard.copyString(attURI);
+    if (documentLink.selectedItem) {
+        let attURI = documentLink.selectedItem.attachment.uri.spec;
+        let clipboard = Components.classes["@mozilla.org/widget/clipboardhelper;1"]
+                                  .getService(Components.interfaces.nsIClipboardHelper);
+        clipboard.copyString(attURI);
+    }
 }
 
 /**
  * Handler function to handle pressing keys in the attachment listbox.
  *
  * @param aEvent     The DOM event caused by the key press.
  */
 function attachmentLinkKeyPress(aEvent) {
     switch (aEvent.key) {
         case "Backspace":
         case "Delete":
             deleteAttachment();
             break;
         case "Enter":
             openAttachment();
+            aEvent.preventDefault();
             break;
     }
 }
 
 /**
  * Handler function to take care of double clicking on an attachment
  *
  * @param aEvent     The DOM event caused by the clicking.
  */
 function attachmentDblClick(aEvent) {
+    let item = aEvent.originalTarget;
+    while (item && item.localName != "richlistbox" && item.localName != "richlistitem") {
+        item = item.parentNode;
+    }
+
     // left double click on a list item
-    if (aEvent.originalTarget.localName == "listitem" && aEvent.button == 0) {
+    if (item.localName == "richlistitem" && aEvent.button == 0) {
         openAttachment();
     }
 }
 
 /**
  * Handler function to take care of right clicking on an attachment or the attachment list
  *
  * @param aEvent     The DOM event caused by the clicking.
  */
 function attachmentClick(aEvent) {
-    // we take only care about right clicks
-    if (aEvent.button != 2) {
-        return;
+    let item = document.popupNode;
+    while (item && item.localName != "richlistbox" && item.localName != "richlistitem") {
+        item = item.parentNode;
     }
-    let attachmentPopup = document.getElementById("attachment-popup");
-    for (let node of attachmentPopup.childNodes) {
-        if (aEvent.originalTarget.localName == "listitem" ||
-            node.id == "attachment-popup-attachPage") {
+
+    for (let node of event.target.children) {
+        if (item.localName == "richlistitem" || node.id == "attachment-popup-attachPage") {
             showElement(node);
         } else {
             hideElement(node);
         }
     }
 }
 
 /**
--- a/calendar/lightning/content/lightning-item-iframe.xul
+++ b/calendar/lightning/content/lightning-item-iframe.xul
@@ -574,24 +574,24 @@
             <textbox id="item-description"
                      disable-on-readonly="true"
                      flex="1"
                      multiline="true"
                      rows="12"/>
           </tabpanel>
           <tabpanel id="event-grid-tabpanel-attachements">
             <vbox flex="1">
-              <listbox id="attachment-link"
-                       context="attachment-popup"
-                       rows="3"
-                       flex="1"
-                       disable-on-readonly="true"
-                       onkeypress="attachmentLinkKeyPress(event)"
-                       onclick="attachmentClick(event);"
-                       ondblclick="attachmentDblClick(event);"/>
+              <richlistbox id="attachment-link"
+                           seltype="single"
+                           context="attachment-popup"
+                           rows="3"
+                           flex="1"
+                           disable-on-readonly="true"
+                           onkeypress="attachmentLinkKeyPress(event)"
+                           ondblclick="attachmentDblClick(event);"/>
             </vbox>
           </tabpanel>
           <tabpanel id="event-grid-tabpanel-attendees"
                     collapsed="true">
             <vbox flex="1">
               <hbox id="item-organizer-row"
                     collapsed="true"
                     align="top"
@@ -679,17 +679,17 @@
                 label="&event.email.tentative.attendees.label;"
                 accesskey="&event.email.tentative.attendees.accesskey;"
                 command="cmd_email_undecided"/>
       <menuseparator id="attendee-popup-second-separator"/>
       <menuitem id="attendee-popup-emailattendee-menuitem"
                 oncommand="sendMailToAttendees([event.target.attendee])"
                 crop="end"/>
     </menupopup>
-    <menupopup id="attachment-popup">
+    <menupopup id="attachment-popup" onpopupshowing="attachmentClick(event)">
       <menuitem id="attachment-popup-open"
                 label="&event.attachments.popup.open.label;"
                 accesskey="&event.attachments.popup.open.accesskey;"
                 command="cmd_openAttachment"/>
       <menuitem id="attachment-popup-copy"
                 label="&calendar.copylink.label;"
                 accesskey="&calendar.copylink.accesskey;"
                 command="cmd_copyAttachment"/>
--- a/calendar/lightning/content/messenger-overlay-preferences.xul
+++ b/calendar/lightning/content/messenger-overlay-preferences.xul
@@ -12,16 +12,17 @@
 ]>
 
 <?xml-stylesheet href="chrome://lightning/skin/lightning.css"?>
 
 <overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
     <prefwindow id="MailPreferences">
         <prefpane id="paneLightning"
+                  flex="1"
                   insertbefore="paneAdvanced"
                   label="&lightning.preferencesLabel;">
             <preferences>
                 <preference id="calendar.preferences.lightning.selectedTabIndex"
                             name="calendar.preferences.lightning.selectedTabIndex"
                             type="int"/>
             </preferences>
             <tabbox id="calPreferencesTabbox"