diff options
Diffstat (limited to 'comm/calendar/base/content/preferences')
13 files changed, 1709 insertions, 0 deletions
diff --git a/comm/calendar/base/content/preferences/alarms.inc.xhtml b/comm/calendar/base/content/preferences/alarms.inc.xhtml new file mode 100644 index 0000000000..a2524983d0 --- /dev/null +++ b/comm/calendar/base/content/preferences/alarms.inc.xhtml @@ -0,0 +1,188 @@ +# 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/. + <html:div data-category="paneCalendar"> + <html:fieldset data-category="paneCalendar"> + <html:legend data-l10n-id="reminder-legend"></html:legend> + <vbox id="alarm-sound-box"> + <hbox align="center"> + <checkbox id="alarmSoundCheckbox" + preference="calendar.alarms.playsound" + data-l10n-id="reminder-play-checkbox"/> + <spacer flex="1"/> + <button is="highlightable-button" id="calendar.prefs.alarm.sound.play" + data-l10n-id="reminder-play-alarm-button" + oncommand="gAlarmsPane.previewAlarm()"/> + </hbox> + <radiogroup id="alarmSoundType" + class="indent" + orient="vertical" + preference="calendar.alarms.soundType" + aria-labelledby="alarmSoundCheckbox"> + <hbox> + <radio id="alarmSoundSystem" + value="0" + data-l10n-id="reminder-default-sound-label"/> + </hbox> + <hbox> + <radio id="alarmSoundCustom" + value="1" + data-l10n-id="reminder-custom-sound-label"/> + </hbox> + </radiogroup> + <hbox align="center" class="input-container"> + <html:input id="alarmSoundFileField" + type="text" + class="input-filefield indent" + readonly="readonly" + preference="calendar.alarms.soundURL" + preference-editable="true" + aria-labelledby="alarmSoundCustom"/> + <button is="highlightable-button" id="calendar.prefs.alarm.sound.browse" + data-l10n-id="reminder-browse-sound-label" + oncommand="gAlarmsPane.browseAlarm()"/> + </hbox> + </vbox> + <hbox align="center" flex="1"> + <checkbox id="alarmshow" + preference="calendar.alarms.show" + data-l10n-id="reminder-dialog-label"/> + </hbox> + <hbox align="center" flex="1"> + <checkbox id="missedalarms" + preference="calendar.alarms.showmissed" + data-l10n-id="missed-reminder-label"/> + </hbox> + </html:fieldset> + </html:div> + + <html:div data-category="paneCalendar"> + <html:fieldset data-category="paneCalendar"> + <html:legend data-l10n-id="reminder-default-legend"></html:legend> + <hbox align="center"> + <label data-l10n-id="default-snooze-label" + control="defaultsnoozelength"/> + <html:input id="defaultsnoozelength" type="number" class="size3" + min="0" + preference="calendar.alarms.defaultsnoozelength" + onselect="updateUnitLabelPlural('defaultsnoozelength', 'defaultsnoozelengthunit', 'minutes')" + oninput="updateUnitLabelPlural('defaultsnoozelength', 'defaultsnoozelengthunit', 'minutes')"/> + <label id="defaultsnoozelengthunit"/> + </hbox> + <hbox> + <html:table id="alarm-defaults-table"> + <html:tr> + <html:td> + <label data-l10n-id="event-alarm-label" + control="eventdefalarm"/> + </html:td> + <html:td> + <hbox> + <menulist id="eventdefalarm" + crop="none" + preference="calendar.alarms.onforevents"> + <menupopup id="eventdefalarmpopup"> + <menuitem id="eventdefalarmon" + data-l10n-id="alarm-on-label" + value="1"/> + <menuitem id="eventdefalarmoff" + data-l10n-id="alarm-off-label" + value="0" + selected="true"/> + </menupopup> + </menulist> + </hbox> + </html:td> + </html:tr> + <html:tr> + <html:td> + <label data-l10n-id="task-alarm-label" + control="tododefalarm"/> + </html:td> + <html:td> + <hbox> + <menulist id="tododefalarm" + crop="none" + preference="calendar.alarms.onfortodos"> + <menupopup id="tododefalarmpopup"> + <menuitem id="tododefalarmon" + data-l10n-id="alarm-on-label" + value="1"/> + <menuitem id="tododefalarmoff" + data-l10n-id="alarm-off-label" + value="0" + selected="true"/> + </menupopup> + </menulist> + </hbox> + </html:td> + </html:tr> + <html:tr> + <html:td> + <label data-l10n-id="event-alarm-time-label" + control="eventdefalarmlen"/> + </html:td> + <html:td> + <hbox class="defaultTimeBox" + align="center" + flex="1"> + <html:input id="eventdefalarmlen" type="number" class="size3" min="0" + preference="calendar.alarms.eventalarmlen" + onselect="updateMenuLabelsPlural('eventdefalarmlen', 'eventdefalarmunit')" + oninput="updateMenuLabelsPlural('eventdefalarmlen', 'eventdefalarmunit')"/> + <hbox> + <menulist id="eventdefalarmunit" + flex="1" + crop="none" + preference="calendar.alarms.eventalarmunit"> + <menupopup id="eventdefalarmunitpopup"> + <menuitem id="eventdefalarmunitmin" + value="minutes" + selected="true"/> + <menuitem id="eventdefalarmunithour" + value="hours"/> + <menuitem id="eventdefalarmunitday" + value="days"/> + </menupopup> + </menulist> + </hbox> + </hbox> + </html:td> + </html:tr> + <html:tr> + <html:td> + <label data-l10n-id="task-alarm-time-label" + control="tododefalarmlen"/> + </html:td> + <html:td> + <hbox class="defaultTimeBox" + align="center" + flex="1"> + <html:input id="tododefalarmlen" type="number" class="size3" min="0" + preference="calendar.alarms.todoalarmlen" + onselect="updateMenuLabelsPlural('tododefalarmlen', 'tododefalarmunit')" + oninput="updateMenuLabelsPlural('tododefalarmlen', 'tododefalarmunit')"/> + <hbox> + <menulist id="tododefalarmunit" + flex="1" + crop="none" + preference="calendar.alarms.todoalarmunit"> + <menupopup id="tododefalarmunitpopup"> + <menuitem id="tododefalarmunitmin" + value="minutes" + selected="true"/> + <menuitem id="tododefalarmunithour" + value="hours"/> + <menuitem id="tododefalarmunitday" + value="days"/> + </menupopup> + </menulist> + </hbox> + </hbox> + </html:td> + </html:tr> + </html:table> + <spacer flex="1"/> + </hbox> + </html:fieldset> + </html:div> diff --git a/comm/calendar/base/content/preferences/alarms.js b/comm/calendar/base/content/preferences/alarms.js new file mode 100644 index 0000000000..29b5df30ab --- /dev/null +++ b/comm/calendar/base/content/preferences/alarms.js @@ -0,0 +1,165 @@ +/* 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 gAlarmsPane */ + +/* import-globals-from ../calendar-ui-utils.js */ +/* globals Preferences */ + +Preferences.addAll([ + { id: "calendar.alarms.playsound", type: "bool" }, + { id: "calendar.alarms.soundURL", type: "string" }, + { id: "calendar.alarms.soundType", type: "int" }, + { id: "calendar.alarms.show", type: "bool" }, + { id: "calendar.alarms.showmissed", type: "bool" }, + { id: "calendar.alarms.onforevents", type: "int" }, + { id: "calendar.alarms.onfortodos", type: "int" }, + { id: "calendar.alarms.eventalarmlen", type: "int" }, + { id: "calendar.alarms.eventalarmunit", type: "string" }, + { id: "calendar.alarms.todoalarmlen", type: "int" }, + { id: "calendar.alarms.todoalarmunit", type: "string" }, + { id: "calendar.alarms.defaultsnoozelength", type: "int" }, +]); + +/** + * Global Object to hold methods for the alarms pref pane + */ +var gAlarmsPane = { + /** + * Initialize the alarms pref pane. Sets up dialog controls to match the + * values set in prefs. + */ + init() { + // Enable/disable the alarm sound URL box and buttons + this.alarmsPlaySoundPrefChanged(); + + // Set the correct singular/plural for the time units + updateMenuLabelsPlural("eventdefalarmlen", "eventdefalarmunit"); + updateMenuLabelsPlural("tododefalarmlen", "tododefalarmunit"); + updateUnitLabelPlural("defaultsnoozelength", "defaultsnoozelengthunit", "minutes"); + + Preferences.addSyncFromPrefListener(document.getElementById("alarmSoundFileField"), () => + this.readSoundLocation() + ); + }, + + /** + * Converts the given file url to a nsIFile + * + * @param aFileURL A string with a file:// url. + * @returns The corresponding nsIFile. + */ + convertURLToLocalFile(aFileURL) { + // Convert the file url into a nsIFile + if (aFileURL) { + let fph = Services.io.getProtocolHandler("file").QueryInterface(Ci.nsIFileProtocolHandler); + return fph.getFileFromURLSpec(aFileURL); + } + return null; + }, + + /** + * Handler function to be called when the calendar.alarms.soundURL pref has + * changed. Updates the label in the dialog. + */ + readSoundLocation() { + let soundUrl = document.getElementById("alarmSoundFileField"); + soundUrl.value = Preferences.get("calendar.alarms.soundURL").value; + if (soundUrl.value.startsWith("file://")) { + soundUrl.label = gAlarmsPane.convertURLToLocalFile(soundUrl.value).leafName; + } else { + soundUrl.label = soundUrl.value; + } + soundUrl.style.backgroundImage = "url(moz-icon://" + soundUrl.label + "?size=16)"; + return undefined; + }, + + /** + * Causes the default sound to be selected in the dialog controls + */ + useDefaultSound() { + let defaultSoundUrl = "chrome://calendar/content/sound.wav"; + Preferences.get("calendar.alarms.soundURL").value = defaultSoundUrl; + document.getElementById("alarmSoundCheckbox").checked = true; + this.readSoundLocation(); + }, + + /** + * Opens a filepicker to open a local sound for the alarm. + */ + browseAlarm() { + let picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); + + // If we already have a sound file, then use the path for that sound file + // as the initial path in the dialog. + let currentValue = Preferences.get("calendar.alarms.soundURL").value; + if (currentValue && currentValue.startsWith("file://")) { + let localFile = Services.io.newURI(currentValue).QueryInterface(Ci.nsIFileURL).file; + picker.displayDirectory = localFile.parent; + } + + let title = document.getElementById("bundlePreferences").getString("soundFilePickerTitle"); + + picker.init(window, title, Ci.nsIFilePicker.modeOpen); + picker.appendFilters(Ci.nsIFilePicker.filterAudio); + picker.appendFilters(Ci.nsIFilePicker.filterAll); + + picker.open(rv => { + if (rv != Ci.nsIFilePicker.returnOK || !picker.file) { + return; + } + Preferences.get("calendar.alarms.soundURL").value = picker.fileURL.spec; + document.getElementById("alarmSoundCheckbox").checked = true; + this.readSoundLocation(); + }); + }, + + /** + * Plays the alarm sound currently selected. + */ + previewAlarm() { + let soundUrl; + if (Preferences.get("calendar.alarms.soundType").value == 0) { + soundUrl = "chrome://calendar/content/sound.wav"; + } else { + soundUrl = Preferences.get("calendar.alarms.soundURL").value; + } + let soundIfc = Cc["@mozilla.org/sound;1"].createInstance(Ci.nsISound); + let url; + try { + soundIfc.init(); + if (soundUrl && soundUrl.length && soundUrl.length > 0) { + url = Services.io.newURI(soundUrl); + soundIfc.play(url); + } else { + soundIfc.beep(); + } + } catch (ex) { + dump("alarms.js previewAlarm Exception caught! " + ex + "\n"); + } + }, + + /** + * Handler function to call when the calendar.alarms.playsound preference + * has been changed. Updates the disabled state of fields that depend on + * playing a sound. + */ + alarmsPlaySoundPrefChanged() { + let alarmsPlaySoundPref = Preferences.get("calendar.alarms.playsound"); + let alarmsSoundType = Preferences.get("calendar.alarms.soundType"); + + for (let item of ["alarmSoundType", "calendar.prefs.alarm.sound.play"]) { + document.getElementById(item).disabled = !alarmsPlaySoundPref.value; + } + + for (let item of ["alarmSoundFileField", "calendar.prefs.alarm.sound.browse"]) { + document.getElementById(item).disabled = + alarmsSoundType.value != 1 || !alarmsPlaySoundPref.value; + } + }, +}; + +Preferences.get("calendar.alarms.playsound").on("change", gAlarmsPane.alarmsPlaySoundPrefChanged); +Preferences.get("calendar.alarms.soundType").on("change", gAlarmsPane.alarmsPlaySoundPrefChanged); +Preferences.get("calendar.alarms.soundURL").on("change", gAlarmsPane.readSoundLocation); diff --git a/comm/calendar/base/content/preferences/calendar-preferences.inc.xhtml b/comm/calendar/base/content/preferences/calendar-preferences.inc.xhtml new file mode 100644 index 0000000000..d07f2b812b --- /dev/null +++ b/comm/calendar/base/content/preferences/calendar-preferences.inc.xhtml @@ -0,0 +1,55 @@ +# 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/. + +# This file requires the following localization files: +# chrome://lightning/locale/lightning.dtd +# chrome://calendar/locale/global.dtd +# chrome://calendar/locale/calendar-event-dialog.dtd + <linkset> + <html:link rel="localization" href="calendar/preferences.ftl"/> + <html:link rel="localization" href="calendar/category-dialog.ftl"/> + </linkset> + <html:template id="paneCalendar" + flex="1" + insertbefore="paneChat"> + + <hbox id="calendarPaneCategory" + class="subcategory" + data-category="paneCalendar"> + <html:h1 data-l10n-id="calendar-title"></html:h1> + </hbox> + +#include views.inc.xhtml +#include general.inc.xhtml + + <hbox id="calendarReminderCategory" + class="subcategory" + data-category="paneCalendar"> + <html:h1 data-l10n-id="calendar-title-reminder"></html:h1> + </hbox> + +#include alarms.inc.xhtml + + <hbox id="calendarNotificationCategory" + class="subcategory" + data-category="paneCalendar"> + <html:h1 data-l10n-id="calendar-title-notification"></html:h1> + </hbox> + + <html:div data-category="paneCalendar"> + <html:fieldset data-category="paneCalendar"> + <calendar-notifications-setting id="calendar-notifications-setting"/> + <label data-l10n-id="calendar-notifications-customize-label" + class="indent tip-caption"></label> + </html:fieldset> + </html:div> + + <hbox id="calendarCategoriesCategory" + class="subcategory" + data-category="paneCalendar"> + <html:h1 data-l10n-id="calendar-title-category"></html:h1> + </hbox> + +#include categories.inc.xhtml + </html:template> diff --git a/comm/calendar/base/content/preferences/calendar-preferences.js b/comm/calendar/base/content/preferences/calendar-preferences.js new file mode 100644 index 0000000000..4b42fa2300 --- /dev/null +++ b/comm/calendar/base/content/preferences/calendar-preferences.js @@ -0,0 +1,28 @@ +/* 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 gCalendarPane */ + +/* import-globals-from alarms.js */ +/* import-globals-from categories.js */ +/* import-globals-from general.js */ +/* import-globals-from notifications.js */ +/* import-globals-from views.js */ +/* globals Preferences */ + +Preferences.add({ id: "calendar.preferences.lightning.selectedTabIndex", type: "int" }); + +var gCalendarPane = { + init() { + let elements = document.querySelectorAll("#paneCalendar preference"); + for (let element of elements) { + element.updateElements(); + } + gCalendarGeneralPane.init(); + gAlarmsPane.init(); + gNotificationsPane.init(); + gCategoriesPane.init(); + gViewsPane.init(); + }, +}; diff --git a/comm/calendar/base/content/preferences/categories.inc.xhtml b/comm/calendar/base/content/preferences/categories.inc.xhtml new file mode 100644 index 0000000000..d6f37be9df --- /dev/null +++ b/comm/calendar/base/content/preferences/categories.inc.xhtml @@ -0,0 +1,32 @@ +# 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/. + <html:div data-category="paneCalendar"> + <html:fieldset data-category="paneCalendar"> + <separator class="thin"/> + <hbox> + <richlistbox id="categorieslist" + flex="1" + seltype="multiple" + onselect="gCategoriesPane.updateButtons()" + ondblclick="gCategoriesPane.listOnDblClick(event)"/> + <vbox id="categoriesButtons"> + <hbox> + <button is="highlightable-button" id="newCButton" + data-l10n-id="new-tag-button" + oncommand="gCategoriesPane.addCategory()" + search-l10n-ids="category-name-label,category-color-label.label"/> + </hbox> + <hbox> + <button is="highlightable-button" id="editCButton" + data-l10n-id="edit-tag-button" + oncommand="gCategoriesPane.editCategory()" + search-l10n-ids="category-name-label,category-color-label.label"/> + </hbox> + <button is="highlightable-button" id="deleteCButton" + data-l10n-id="delete-tag-button" + oncommand="gCategoriesPane.deleteCategory()"/> + </vbox> + </hbox> + </html:fieldset> + </html:div> diff --git a/comm/calendar/base/content/preferences/categories.js b/comm/calendar/base/content/preferences/categories.js new file mode 100644 index 0000000000..ba7453b447 --- /dev/null +++ b/comm/calendar/base/content/preferences/categories.js @@ -0,0 +1,297 @@ +/* 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 gCategoriesPane */ + +/* globals gSubDialog, Preferences */ + +var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm"); +var { AppConstants } = ChromeUtils.importESModule("resource://gre/modules/AppConstants.sys.mjs"); + +Preferences.add({ id: "calendar.categories.names", type: "string" }); + +var gCategoryList; +var categoryPrefBranch = Services.prefs.getBranch("calendar.category.color."); + +/** + * Global Object to hold methods for the categories pref pane + */ +var gCategoriesPane = { + mCategoryDialog: null, + mWinProp: null, + + /** + * Initialize the categories pref pane. Sets up dialog controls to show the + * categories saved in preferences. + */ + init() { + // On non-instant-apply platforms, once this pane has been loaded, + // attach our "revert all changes" function to the parent prefwindow's + // "ondialogcancel" event. + let parentPrefWindow = document.documentElement; + if (!parentPrefWindow.instantApply) { + let existingOnDialogCancel = parentPrefWindow.getAttribute("ondialogcancel"); + parentPrefWindow.setAttribute( + "ondialogcancel", + "gCategoriesPane.panelOnCancel(); " + existingOnDialogCancel + ); + } + + // A list of preferences to be reverted when the dialog is cancelled. + // It needs to be a property of the parent to be visible onCancel + if (!("backupPrefList" in parent)) { + parent.backupPrefList = []; + } + + gCategoryList = cal.category.fromPrefs(); + + this.updateCategoryList(); + + this.mCategoryDialog = "chrome://calendar/content/preferences/editCategory.xhtml"; + + // Workaround for Bug 1151440 - the HTML color picker won't work + // in linux when opened from modal dialog + this.mWinProp = "centerscreen, chrome, resizable=no"; + if (AppConstants.platform != "linux") { + this.mWinProp += ", modal"; + } + }, + + /** + * Updates the listbox containing the categories from the categories saved + * in preferences. + */ + + updatePrefs() { + cal.l10n.sortArrayByLocaleCollator(gCategoryList); + Preferences.get("calendar.categories.names").value = cal.category.arrayToString(gCategoryList); + }, + + updateCategoryList() { + this.updatePrefs(); + let listbox = document.getElementById("categorieslist"); + + listbox.clearSelection(); + this.updateButtons(); + + while (listbox.lastElementChild) { + listbox.lastChild.remove(); + } + + for (let i = 0; i < gCategoryList.length; i++) { + let newListItem = document.createXULElement("richlistitem"); + let categoryName = document.createXULElement("label"); + categoryName.setAttribute("id", gCategoryList[i]); + categoryName.setAttribute("flex", "1"); + categoryName.setAttribute("value", gCategoryList[i]); + let categoryNameFix = cal.view.formatStringForCSSRule(gCategoryList[i]); + + let categoryColor = document.createXULElement("box"); + categoryColor.style.width = "150px"; + let colorCode = categoryPrefBranch.getCharPref(categoryNameFix, ""); + if (colorCode) { + categoryColor.style.backgroundColor = colorCode; + } + + newListItem.appendChild(categoryName); + newListItem.appendChild(categoryColor); + listbox.appendChild(newListItem); + } + }, + + /** + * Adds a category, opening the edit category dialog to prompt the user to + * set up the category. + */ + async addCategory() { + let listbox = document.getElementById("categorieslist"); + listbox.clearSelection(); + this.updateButtons(); + let params = { + title: await document.l10n.formatValue("category-new-label"), + category: "", + color: null, + }; + gSubDialog.open(this.mCategoryDialog, { features: "resizable=no" }, params); + }, + + /** + * Edits the currently selected category using the edit category dialog. + */ + async editCategory() { + let list = document.getElementById("categorieslist"); + let categoryNameFix = cal.view.formatStringForCSSRule(gCategoryList[list.selectedIndex]); + let currentColor = categoryPrefBranch.getCharPref(categoryNameFix, ""); + + let params = { + title: await document.l10n.formatValue("category-edit-label"), + category: gCategoryList[list.selectedIndex], + color: currentColor, + }; + if (list.selectedItem) { + gSubDialog.open(this.mCategoryDialog, { features: "resizable=no" }, params); + } + }, + + /** + * Removes the selected category. + */ + deleteCategory() { + let list = document.getElementById("categorieslist"); + if (list.selectedCount < 1) { + return; + } + + let categoryNameFix = cal.view.formatStringForCSSRule(gCategoryList[list.selectedIndex]); + this.backupData(categoryNameFix); + try { + categoryPrefBranch.clearUserPref(categoryNameFix); + } catch (ex) { + // If the pref doesn't exist, don't bail out here. + } + + // Remove category entry from listbox and gCategoryList. + let newSelection = + list.selectedItem.nextElementSibling || list.selectedItem.previousElementSibling; + let selectedItems = Array.from(list.selectedItems); + for (let i = list.selectedCount - 1; i >= 0; i--) { + let item = selectedItems[i]; + if (item == newSelection) { + newSelection = newSelection.nextElementSibling || newSelection.previousElementSibling; + } + gCategoryList.splice(list.getIndexOfItem(item), 1); + item.remove(); + } + list.selectedItem = newSelection; + this.updateButtons(); + + // Update the prefs from gCategoryList + this.updatePrefs(); + }, + + /** + * Saves the given category to the preferences. + * + * @param categoryName The name of the category. + * @param categoryColor The color of the category + */ + async saveCategory(categoryName, categoryColor) { + let list = document.getElementById("categorieslist"); + // Check to make sure another category doesn't have the same name + let toBeDeleted = -1; + for (let i = 0; i < gCategoryList.length; i++) { + if (i == list.selectedIndex) { + continue; + } + + if (categoryName.toLowerCase() == gCategoryList[i].toLowerCase()) { + let [title, description] = await document.l10n.formatValues([ + { id: "category-overwrite-title" }, + { id: "category-overwrite" }, + ]); + + if (Services.prompt.confirm(null, title, description)) { + if (list.selectedIndex != -1) { + // Don't delete the old category yet. It will mess up indices. + toBeDeleted = list.selectedIndex; + } + list.selectedIndex = i; + } else { + return; + } + } + } + + if (categoryName.length == 0) { + let warning = await document.l10n.formatValue("category-blank-warning"); + Services.prompt.alert(null, null, warning); + return; + } + + let categoryNameFix = cal.view.formatStringForCSSRule(categoryName); + if (list.selectedIndex == -1) { + this.backupData(categoryNameFix); + gCategoryList.push(categoryName); + if (categoryColor) { + categoryPrefBranch.setCharPref(categoryNameFix, categoryColor); + } + } else { + this.backupData(categoryNameFix); + gCategoryList.splice(list.selectedIndex, 1, categoryName); + categoryPrefBranch.setCharPref(categoryNameFix, categoryColor || ""); + } + + // If 'Overwrite' was chosen, delete category that was being edited + if (toBeDeleted != -1) { + list.selectedIndex = toBeDeleted; + this.deleteCategory(); + } + + this.updateCategoryList(); + + let updatedCategory = gCategoryList.indexOf(categoryName); + list.ensureIndexIsVisible(updatedCategory); + list.selectedIndex = updatedCategory; + }, + + /** + * Enable the edit and delete category buttons. + */ + updateButtons() { + let categoriesList = document.getElementById("categorieslist"); + document.getElementById("deleteCButton").disabled = categoriesList.selectedCount <= 0; + document.getElementById("editCButton").disabled = categoriesList.selectedCount != 1; + }, + + /** + * Backs up the category name in case the dialog is canceled. + * + * @see formatStringForCSSRule + * @param categoryNameFix The formatted category name. + */ + backupData(categoryNameFix) { + let currentColor = categoryPrefBranch.getCharPref(categoryNameFix, "##NEW"); + + for (let i = 0; i < parent.backupPrefList.length; i++) { + if (categoryNameFix == parent.backupPrefList[i].name) { + return; + } + } + parent.backupPrefList[parent.backupPrefList.length] = { + name: categoryNameFix, + color: currentColor, + }; + }, + + /** + * Event Handler function to be called on doubleclick of the categories + * list. If the edit function is enabled and the user doubleclicked on a + * list item, then edit the selected category. + */ + listOnDblClick(event) { + if (event.target.localName == "listitem" && !document.getElementById("editCButton").disabled) { + this.editCategory(); + } + }, + + /** + * Reverts category preferences in case the cancel button is pressed. + */ + panelOnCancel() { + for (let i = 0; i < parent.backupPrefList.length; i++) { + if (parent.backupPrefList[i].color == "##NEW") { + try { + categoryPrefBranch.clearUserPref(parent.backupPrefList[i].name); + } catch (ex) { + dump("Exception caught in 'panelOnCancel': " + ex + "\n"); + } + } else { + categoryPrefBranch.setCharPref( + parent.backupPrefList[i].name, + parent.backupPrefList[i].color + ); + } + } + }, +}; diff --git a/comm/calendar/base/content/preferences/editCategory.js b/comm/calendar/base/content/preferences/editCategory.js new file mode 100644 index 0000000000..c3479b88c0 --- /dev/null +++ b/comm/calendar/base/content/preferences/editCategory.js @@ -0,0 +1,112 @@ +/* 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 editCategoryLoad, categoryNameChanged, clickColor, delay */ + +var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm"); + +window.addEventListener("DOMContentLoaded", editCategoryLoad); + +// Global variable, set to true if the user has picked a custom color. +var customColorSelected = false; + +/** + * Load Handler, called when the edit category dialog is loaded + */ +function editCategoryLoad() { + let winArg = window.arguments[0]; + let color = winArg.color || cal.view.hashColor(winArg.category); + let hasColor = !!winArg.color; + document.getElementById("categoryName").value = winArg.category; + document.getElementById("categoryColor").value = color; + document.getElementById("useColor").checked = hasColor; + customColorSelected = hasColor; + document.title = winArg.title; + + toggleColor(); +} + +/** + * Handler function to be called when the category dialog is accepted and + * the opener should further process the selected name and color + */ +document.addEventListener("dialogaccept", () => { + let color = document.getElementById("useColor").checked + ? document.getElementById("categoryColor").value + : null; + + let categoryName = document.getElementById("categoryName").value; + window.opener.gCategoriesPane.saveCategory(categoryName, color); +}); + +/** + * Handler function to be called when the category name changed + */ +function categoryNameChanged() { + let newValue = document.getElementById("categoryName").value; + + // The user removed the category name, assign the color automatically again. + if (newValue == "") { + customColorSelected = false; + } + + if (!customColorSelected && document.getElementById("useColor").checked) { + // Color is wanted, choose the color based on the category name's hash. + document.getElementById("categoryColor").value = cal.view.hashColor(newValue); + } +} + +/** + * Handler function to be called when the color picker's color has been changed. + */ +function colorPickerChanged() { + document.getElementById("useColor").checked = true; + customColorSelected = true; +} + +/** + * Handler called when the use color checkbox is toggled. + */ +function toggleColor() { + let useColor = document.getElementById("useColor").checked; + let categoryColor = document.getElementById("categoryColor"); + + if (useColor) { + categoryColor.removeAttribute("disabled"); + if (toggleColor.lastColor) { + categoryColor.value = toggleColor.lastColor; + } + } else { + categoryColor.setAttribute("disabled", "disabled"); + toggleColor.lastColor = categoryColor.value; + categoryColor.value = ""; + } +} + +/** + * Click handler for the color picker. Turns the button back into a colorpicker + * when clicked. + */ +function clickColor() { + let categoryColor = document.getElementById("categoryColor"); + if (categoryColor.hasAttribute("disabled")) { + colorPickerChanged(); + toggleColor(); + categoryColor.click(); + } +} + +/** + * Call the function after the given timeout, resetting the timer if delay is + * called again with the same function. + * + * @param timeout The timeout interval. + * @param func The function to call after the timeout. + */ +function delay(timeout, func) { + if (func.timer) { + clearTimeout(func.timer); + } + func.timer = setTimeout(func, timeout); +} diff --git a/comm/calendar/base/content/preferences/editCategory.xhtml b/comm/calendar/base/content/preferences/editCategory.xhtml new file mode 100644 index 0000000000..5128a03249 --- /dev/null +++ b/comm/calendar/base/content/preferences/editCategory.xhtml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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/. --> + +<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?> + +<!DOCTYPE html> +<html + xmlns="http://www.w3.org/1999/xhtml" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:html="http://www.w3.org/1999/xhtml" + scrolling="false" +> + <head> + <title><!-- category-new-label / category-edit-label --></title> + <link rel="localization" href="calendar/category-dialog.ftl" /> + <script defer="defer" src="chrome://messenger/content/globalOverlay.js"></script> + <script defer="defer" src="chrome://global/content/editMenuOverlay.js"></script> + <script defer="defer" src="chrome://calendar/content/preferences/editCategory.js"></script> + </head> + <html:body xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <dialog id="editCategory" buttons="accept,cancel" style="width: 100vw; height: 100vh"> + <label id="categoryNameLabel" data-l10n-id="category-name-label" control="categoryName" /> + <html:input + id="categoryName" + type="text" + onchange="categoryNameChanged()" + oninput="delay(500, categoryNameChanged)" + aria-labelledby="categoryNameLabel" + /> + <hbox id="colorSelectRow"> + <checkbox + id="useColor" + data-l10n-id="category-color-label" + oncommand="toggleColor(); categoryNameChanged()" + /> + <html:input + id="categoryColor" + type="color" + style="width: 64px; height: 23px" + onclick="clickColor()" + onchange="colorPickerChanged()" + aria-labelledby="useColor" + /> + </hbox> + </dialog> + </html:body> +</html> diff --git a/comm/calendar/base/content/preferences/general.inc.xhtml b/comm/calendar/base/content/preferences/general.inc.xhtml new file mode 100644 index 0000000000..f6b00ad29c --- /dev/null +++ b/comm/calendar/base/content/preferences/general.inc.xhtml @@ -0,0 +1,185 @@ +# 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/. + <html:div data-category="paneCalendar"> + <html:fieldset data-category="paneCalendar"> + <html:legend data-l10n-id="todaypane-legend"></html:legend> + <hbox align="center"> + <label data-l10n-id="agenda-days" + control="agenda-days-menulist"/> + <hbox> + <menulist id="agenda-days-menulist" + preference="calendar.agenda.days"> + <menupopup id="agenda-days-popup"> + <menuitem value="1"/> + <menuitem value="2"/> + <menuitem value="3"/> + <menuitem value="4"/> + <menuitem value="5"/> + <menuitem value="6"/> + <menuitem value="7"/> + <menuitem value="14"/> + <menuitem value="21"/> + <menuitem value="28"/> + </menupopup> + </menulist> + </hbox> + </hbox> + </html:fieldset> + </html:div> + + <html:div data-category="paneCalendar"> + <html:fieldset id="defaults-itemtype-groupbox" data-category="paneCalendar"> + <html:legend data-l10n-id="event-task-legend"></html:legend> + <vbox id="defaults-itemtype-box"> + <hbox id="defaults-event-grid-box" align="center"> + <label id="default-event-length-label" + data-l10n-id="default-length-label" + control="defaultlength"/> + <html:input id="defaultlength" type="number" + class="size3" + min="0" + preference="calendar.event.defaultlength" + onselect="updateUnitLabelPlural('defaultlength', 'defaultlengthunit', 'minutes')" + oninput="updateUnitLabelPlural('defaultlength', 'defaultlengthunit', 'minutes')"/> + <label id="defaultlengthunit"/> + </hbox> + <html:table id="defaults-task-table"> + <html:tr id="defaults-task-start-row"> + <html:td> + <label id="default-task-start-label" + data-l10n-id="task-start-label" + control="default_task_start"/> + </html:td> + <html:td> + <hbox> + <menulist id="default_task_start" + crop="none" + oncommand="gCalendarGeneralPane.updateDefaultTodoDates()" + preference="calendar.task.defaultstart"> + <menupopup id="default_task_start_popup"> + <menuitem id="default_task_start_none" + data-l10n-id="task-start-1-label" + value="none" + selected="true"/> + <menuitem id="default_task_start_start_of_day" + data-l10n-id="task-start-2-label" + value="startofday"/> + <menuitem id="default_task_start_tomorrow" + data-l10n-id="task-start-4-label" + value="tomorrow"/> + <menuitem id="default_task_start_next_week" + data-l10n-id="task-start-5-label" + value="nextweek"/> + <menuitem id="default_task_start_offset_current" + data-l10n-id="task-start-6-label" + value="offsetcurrent"/> + <menuitem id="default_task_start_offset_next_hour" + data-l10n-id="task-start-8-label" + value="offsetnexthour"/> + </menupopup> + </menulist> + </hbox> + </html:td> + <html:td> + <hbox id="default_task_start_offset" align="center"> + <html:input id="default_task_start_offset_text" type="number" + class="size3" + min="0" + preference="calendar.task.defaultstartoffset" + onselect="updateMenuLabelsPlural('default_task_start_offset_text', 'default_task_start_offset_units')" + oninput="updateMenuLabelsPlural('default_task_start_offset_text', 'default_task_start_offset_units')"/> + <hbox> + <menulist id="default_task_start_offset_units" + crop="none" + preference="calendar.task.defaultstartoffsetunits"> + <menupopup id="default_task_start_offset_units_popup"> + <menuitem id="default_task_start_offset_units_minutes" + value="minutes" + selected="true"/> + <menuitem id="default_task_start_offset_units_hours" + value="hours"/> + <menuitem id="default_task_start_offset_units_days" + value="days"/> + </menupopup> + </menulist> + </hbox> + </hbox> + </html:td> + </html:tr> + <html:tr id="defaults-task-due-row"> + <html:td> + <label id="default-task-due-label" + data-l10n-id="task-due-label" + control="default_task_due"/> + </html:td> + <html:td> + <hbox> + <menulist id="default_task_due" + crop="none" + oncommand="gCalendarGeneralPane.updateDefaultTodoDates()" + preference="calendar.task.defaultdue"> + <menupopup id="default_task_due_popup"> + <menuitem id="default_task_due_none" + data-l10n-id="task-start-1-label" + value="none" + selected="true"/> + <menuitem id="default_task_due_end_of_day" + data-l10n-id="task-start-3-label" + value="endofday"/> + <menuitem id="default_task_due_tomorrow" + data-l10n-id="task-start-4-label" + value="tomorrow"/> + <menuitem id="default_task_due_next_week" + data-l10n-id="task-start-5-label" + value="nextweek"/> + <menuitem id="default_task_due_offset_current" + data-l10n-id="task-start-7-label" + value="offsetcurrent"/> + <menuitem id="default_task_due_offset_next_hour" + data-l10n-id="task-start-8-label" + value="offsetnexthour"/> + </menupopup> + </menulist> + </hbox> + </html:td> + <html:td> + <hbox id="default_task_due_offset" align="center"> + <html:input id="default_task_due_offset_text" type="number" + class="size3" + min="0" + preference="calendar.task.defaultdueoffset" + onselect="updateMenuLabelsPlural('default_task_due_offset_text', 'default_task_due_offset_units')" + oninput="updateMenuLabelsPlural('default_task_due_offset_text', 'default_task_due_offset_units')"/> + <hbox> + <menulist id="default_task_due_offset_units" + crop="none" + preference="calendar.task.defaultdueoffsetunits"> + <menupopup id="default_task_due_offset_units_popup"> + <menuitem id="default_task_due_offset_units_minutes" + value="minutes" + selected="true"/> + <menuitem id="default_task_due_offset_units_hours" + value="hours"/> + <menuitem id="default_task_due_offset_units_days" + value="days"/> + </menupopup> + </menulist> + </hbox> + </hbox> + </html:td> + </html:tr> + </html:table> + </vbox> + <hbox align="center"> + <checkbox id="tabedit" pack="end" + data-l10n-id="edit-intab-label" + preference="calendar.item.editInTab"/> + </hbox> + <hbox align="center"> + <checkbox id="promptDelete" pack="end" + data-l10n-id="prompt-delete-label" + preference="calendar.item.promptDelete"/> + </hbox> + </html:fieldset> + </html:div> diff --git a/comm/calendar/base/content/preferences/general.js b/comm/calendar/base/content/preferences/general.js new file mode 100644 index 0000000000..8991193e05 --- /dev/null +++ b/comm/calendar/base/content/preferences/general.js @@ -0,0 +1,148 @@ +/* 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 gCalendarGeneralPane */ + +/* import-globals-from ../calendar-ui-utils.js */ +/* globals Preferences */ + +var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm"); + +Preferences.addAll([ + { id: "calendar.date.format", type: "int" }, + { id: "calendar.event.defaultlength", type: "int" }, + { id: "calendar.timezone.local", type: "string" }, + { id: "calendar.timezone.useSystemTimezone", type: "bool" }, + { id: "calendar.task.defaultstart", type: "string" }, + { id: "calendar.task.defaultstartoffset", type: "int" }, + { id: "calendar.task.defaultstartoffsetunits", type: "string" }, + { id: "calendar.task.defaultdue", type: "string" }, + { id: "calendar.task.defaultdueoffset", type: "int" }, + { id: "calendar.task.defaultdueoffsetunits", type: "string" }, + { id: "calendar.agenda.days", type: "int" }, + { id: "calendar.item.editInTab", type: "bool" }, + { id: "calendar.item.promptDelete", type: "bool" }, +]); + +/** + * Global Object to hold methods for the general pref pane + */ +var gCalendarGeneralPane = { + /** + * Initialize the general pref pane. Sets up dialog controls to match the + * values set in prefs. + */ + init() { + this.onChangedUseSystemTimezonePref(); + + let formatter = cal.dtz.formatter; + let dateFormattedLong = formatter.formatDateLong(cal.dtz.now()); + let dateFormattedShort = formatter.formatDateShort(cal.dtz.now()); + + // menu items include examples of current date formats. + document.l10n.setAttributes( + document.getElementById("dateformat-long-menuitem"), + "dateformat-long", + { date: dateFormattedLong } + ); + + document.l10n.setAttributes( + document.getElementById("dateformat-short-menuitem"), + "dateformat-short", + { date: dateFormattedShort } + ); + + // deselect and reselect to update visible item title + updateUnitLabelPlural("defaultlength", "defaultlengthunit", "minutes"); + this.updateDefaultTodoDates(); + + let tzMenuList = document.getElementById("calendar-timezone-menulist"); + let tzMenuPopup = document.getElementById("calendar-timezone-menupopup"); + + let tzids = {}; + let displayNames = []; + // don't rely on what order the timezone-service gives you + for (let timezoneId of cal.timezoneService.timezoneIds) { + let timezone = cal.timezoneService.getTimezone(timezoneId); + if (timezone && !timezone.isFloating && !timezone.isUTC) { + let displayName = timezone.displayName; + displayNames.push(displayName); + tzids[displayName] = timezone.tzid; + } + } + // the display names need to be sorted + displayNames.sort((a, b) => a.localeCompare(b)); + for (let displayName of displayNames) { + addMenuItem(tzMenuPopup, displayName, tzids[displayName]); + } + + let prefValue = Preferences.get("calendar.timezone.local").value; + if (!prefValue) { + prefValue = cal.dtz.defaultTimezone.tzid; + } + tzMenuList.value = prefValue; + + // Set the agenda length menulist. + this.initializeTodaypaneMenu(); + }, + + updateDefaultTodoDates() { + let defaultDue = document.getElementById("default_task_due").value; + let defaultStart = document.getElementById("default_task_start").value; + let offsetValues = ["offsetcurrent", "offsetnexthour"]; + + document.getElementById("default_task_due_offset").style.visibility = offsetValues.includes( + defaultDue + ) + ? "" + : "hidden"; + document.getElementById("default_task_start_offset").style.visibility = offsetValues.includes( + defaultStart + ) + ? "" + : "hidden"; + + updateMenuLabelsPlural("default_task_start_offset_text", "default_task_start_offset_units"); + updateMenuLabelsPlural("default_task_due_offset_text", "default_task_due_offset_units"); + }, + + initializeTodaypaneMenu() { + // Assign the labels for the menuitem + let menulist = document.getElementById("agenda-days-menulist"); + let items = menulist.getElementsByTagName("menuitem"); + for (let menuItem of items) { + let menuitemValue = Number(menuItem.value); + if (menuitemValue > 7) { + menuItem.label = unitPluralForm(menuitemValue / 7, "weeks"); + } else { + menuItem.label = unitPluralForm(menuitemValue, "days"); + } + } + + let pref = Preferences.get("calendar.agenda.days"); + let value = pref.value; + + // Check if the preference has been edited with a wrong value. + if (value > 0 && value <= 28) { + if (value % 7 != 0) { + let intValue = Math.floor(value / 7) * 7; + value = intValue == 0 ? value : intValue; + pref.value = value; + } + } else { + pref.value = 14; + } + }, + + onChangedUseSystemTimezonePref() { + let useSystemTimezonePref = Preferences.get("calendar.timezone.useSystemTimezone"); + + document.getElementById("calendar-timezone-menulist").disabled = useSystemTimezonePref.value; + }, +}; + +Preferences.get("calendar.timezone.useSystemTimezone").on( + "change", + gCalendarGeneralPane.onChangedUseSystemTimezonePref +); diff --git a/comm/calendar/base/content/preferences/notifications.js b/comm/calendar/base/content/preferences/notifications.js new file mode 100644 index 0000000000..11725b8309 --- /dev/null +++ b/comm/calendar/base/content/preferences/notifications.js @@ -0,0 +1,24 @@ +/* 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 gNotificationsPane */ +/* globals Preferences */ + +Preferences.add({ id: "calendar.notifications.times", type: "string" }); + +/** + * Global Object to hold methods for the notifications pref pane. + */ +var gNotificationsPane = { + /** + * Initialize <calendar-notifications-setting> and listen to the change event. + */ + init() { + var calendarNotificationsSetting = document.getElementById("calendar-notifications-setting"); + calendarNotificationsSetting.value = Preferences.get("calendar.notifications.times").value; + calendarNotificationsSetting.addEventListener("change", () => { + Preferences.get("calendar.notifications.times").value = calendarNotificationsSetting.value; + }); + }, +}; diff --git a/comm/calendar/base/content/preferences/views.inc.xhtml b/comm/calendar/base/content/preferences/views.inc.xhtml new file mode 100644 index 0000000000..d37d183d8b --- /dev/null +++ b/comm/calendar/base/content/preferences/views.inc.xhtml @@ -0,0 +1,311 @@ +# 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/. + <html:div data-category="paneCalendar"> + <html:fieldset data-category="paneCalendar"> + <separator/> + <box id="datePrefsBox"> + <hbox align="center"> + <label data-l10n-id="dateformat-label" + control="dateformat"/> + </hbox> + <hbox align="center"> + <menulist id="dateformat" crop="none" + flex="1" + preference="calendar.date.format"> + <menupopup id="dateformatpopup"> + <menuitem id="dateformat-long-menuitem" + value="0"/> + <menuitem id="dateformat-short-menuitem" + value="1"/> + </menupopup> + </menulist> + </hbox> + <spacer/> +#if defined(XP_WIN) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GTK) && defined(MOZ_ENABLE_DBUS) + <radiogroup id="timezone-source-radio-group" + orient="vertical" + preference="calendar.timezone.useSystemTimezone"> + <hbox align="center"> + <radio id="use-system-timezone-radio-button" + value="true" + data-l10n-id="use-system-timezone-radio-button"/> + </hbox> + <hbox> + <radio id="set-timezone-manually-radio-button" + value="false" + data-l10n-id="set-timezone-manually-radio-button"/> + </hbox> + </radiogroup> + <spacer/><spacer/> + <hbox align="center" class="indent"> + <label data-l10n-id="timezone-label" + control="calendar-timezone-menulist"/> + </hbox> + <hbox align="center"> + <menulist id="calendar-timezone-menulist" + flex="1" + preference="calendar.timezone.local"> + <menupopup id="calendar-timezone-menupopup"/> + </menulist> + </hbox> +#else + <hbox align="center" class="indent"> + <label data-l10n-id="timezone-label" + control="calendar-timezone-menulist"/> + </hbox> + <hbox align="center"> + <menulist id="calendar-timezone-menulist" + flex="1" + preference="calendar.timezone.local"> + <menupopup id="calendar-timezone-menupopup"/> + </menulist> + </hbox> +#endif + <spacer/> + <hbox align="center" flex="1"> + <label data-l10n-id="weekstart-label" + control="weekstarts"/> + </hbox> + <hbox align="center"> + <menulist id="weekstarts" + flex="1" + preference="calendar.week.start" + oncommand="gViewsPane.updateViewWorkDayCheckboxes(this.value)"> + <menupopup id="weekstartspopup"> + <menuitem data-l10n-id="day-1-name" value="0"/> + <menuitem data-l10n-id="day-2-name" value="1"/> + <menuitem data-l10n-id="day-3-name" value="2"/> + <menuitem data-l10n-id="day-4-name" value="3"/> + <menuitem data-l10n-id="day-5-name" value="4"/> + <menuitem data-l10n-id="day-6-name" value="5"/> + <menuitem data-l10n-id="day-7-name" value="6"/> + </menupopup> + </menulist> + </hbox> + <spacer/> + </box> + <hbox align="center" flex="1"> + <checkbox id="weekNumber" + crop="end" + data-l10n-id="show-weeknumber-label" + preference="calendar.view-minimonth.showWeekNumber"/> + </hbox> + <separator/> + <hbox> + <vbox> + <label data-l10n-id="workdays-label"/> + </vbox> + <vbox> + <hbox> + <checkbox id="dayoff0" + class="dayOffCheckbox" + data-l10n-id="day-1-checkbox" + orient="vertical" + preference="calendar.week.d0sundaysoff"/> + <checkbox id="dayoff1" + class="dayOffCheckbox" + data-l10n-id="day-2-checkbox" + orient="vertical" + preference="calendar.week.d1mondaysoff"/> + <checkbox id="dayoff2" + class="dayOffCheckbox" + data-l10n-id="day-3-checkbox" + orient="vertical" + preference="calendar.week.d2tuesdaysoff"/> + <checkbox id="dayoff3" + class="dayOffCheckbox" + data-l10n-id="day-4-checkbox" + orient="vertical" + preference="calendar.week.d3wednesdaysoff"/> + <checkbox id="dayoff4" + class="dayOffCheckbox" + data-l10n-id="day-5-checkbox" + orient="vertical" + preference="calendar.week.d4thursdaysoff"/> + <checkbox id="dayoff5" + class="dayOffCheckbox" + data-l10n-id="day-6-checkbox" + orient="vertical" + preference="calendar.week.d5fridaysoff"/> + <checkbox id="dayoff6" + class="dayOffCheckbox" + data-l10n-id="day-7-checkbox" + orient="vertical" + preference="calendar.week.d6saturdaysoff"/> + </hbox> + </vbox> + </hbox> + </html:fieldset> + </html:div> + + <html:div data-category="paneCalendar"> + <html:fieldset data-category="paneCalendar"> + <html:legend data-l10n-id="dayweek-legend"></html:legend> + <html:table id="dayAndWeekViewsTable"> + <html:tr> + <html:th> + <label data-l10n-id="visible-hours-label" + control="visiblehours"/> + </html:th> + <html:td> + <hbox align="center"> + <hbox> + <menulist id="visiblehours" + preference="calendar.view.visiblehours"> + <menupopup id="visiblehourspopup"> + <menuitem label="1" value="1"/> + <menuitem label="2" value="2"/> + <menuitem label="3" value="3"/> + <menuitem label="4" value="4"/> + <menuitem label="5" value="5"/> + <menuitem label="6" value="6"/> + <menuitem label="7" value="7"/> + <menuitem label="8" value="8"/> + <menuitem label="9" value="9"/> + <menuitem label="10" value="10"/> + <menuitem label="11" value="11"/> + <menuitem label="12" value="12"/> + <menuitem label="13" value="13"/> + <menuitem label="14" value="14"/> + <menuitem label="15" value="15"/> + <menuitem label="16" value="16"/> + <menuitem label="17" value="17"/> + <menuitem label="18" value="18"/> + <menuitem label="19" value="19"/> + <menuitem label="20" value="20"/> + <menuitem label="21" value="21"/> + <menuitem label="22" value="22"/> + <menuitem label="23" value="23"/> + <menuitem label="24" value="24"/> + </menupopup> + </menulist> + </hbox> + <label data-l10n-id="visible-hours-end-label"/> + </hbox> + </html:td> + </html:tr> + <html:tr> + <html:th> + <label data-l10n-id="day-start-label" + control="daystarthour"/> + </html:th> + <html:td> + <hbox> + <menulist id="daystarthour" + oncommand="gViewsPane.updateViewEndMenu(this.value);" + preference="calendar.view.daystarthour"> + <menupopup id="daystarthourpopup"> + <menuitem id="timeStart0" value="0" data-l10n-id="midnight-label"/> + <menuitem id="timeStart1" value="1"/> + <menuitem id="timeStart2" value="2"/> + <menuitem id="timeStart3" value="3"/> + <menuitem id="timeStart4" value="4"/> + <menuitem id="timeStart5" value="5"/> + <menuitem id="timeStart6" value="6"/> + <menuitem id="timeStart7" value="7"/> + <menuitem id="timeStart8" value="8"/> + <menuitem id="timeStart9" value="9"/> + <menuitem id="timeStart10" value="10"/> + <menuitem id="timeStart11" value="11"/> + <menuitem id="timeStart12" value="12" data-l10n-id="noon-label"/> + <menuitem id="timeStart13" value="13"/> + <menuitem id="timeStart14" value="14"/> + <menuitem id="timeStart15" value="15"/> + <menuitem id="timeStart16" value="16"/> + <menuitem id="timeStart17" value="17"/> + <menuitem id="timeStart18" value="18"/> + <menuitem id="timeStart19" value="19"/> + <menuitem id="timeStart20" value="20"/> + <menuitem id="timeStart21" value="21"/> + <menuitem id="timeStart22" value="22"/> + <menuitem id="timeStart23" value="23"/> + </menupopup> + </menulist> + </hbox> + </html:td> + </html:tr> + <html:tr> + <html:th> + <label data-l10n-id="day-end-label" + control="dayendhour"/> + </html:th> + <html:td> + <hbox> + <menulist id="dayendhour" + oncommand="gViewsPane.updateViewStartMenu(this.value);" + preference="calendar.view.dayendhour"> + <menupopup id="dayendhourpopup"> + <menuitem id="timeEnd1" value="1"/> + <menuitem id="timeEnd2" value="2"/> + <menuitem id="timeEnd3" value="3"/> + <menuitem id="timeEnd4" value="4"/> + <menuitem id="timeEnd5" value="5"/> + <menuitem id="timeEnd6" value="6"/> + <menuitem id="timeEnd7" value="7"/> + <menuitem id="timeEnd8" value="8"/> + <menuitem id="timeEnd9" value="9"/> + <menuitem id="timeEnd10" value="10"/> + <menuitem id="timeEnd11" value="11"/> + <menuitem id="timeEnd12" value="12" data-l10n-id="noon-label"/> + <menuitem id="timeEnd13" value="13"/> + <menuitem id="timeEnd14" value="14"/> + <menuitem id="timeEnd15" value="15"/> + <menuitem id="timeEnd16" value="16"/> + <menuitem id="timeEnd17" value="17"/> + <menuitem id="timeEnd18" value="18"/> + <menuitem id="timeEnd19" value="19"/> + <menuitem id="timeEnd20" value="20"/> + <menuitem id="timeEnd21" value="21"/> + <menuitem id="timeEnd22" value="22"/> + <menuitem id="timeEnd23" value="23"/> + <menuitem id="timeEnd24" value="24" data-l10n-id="midnight-label"/> + </menupopup> + </menulist> + </hbox> + </html:td> + </html:tr> + </html:table> + <checkbox id="showLocation" pack="end" + data-l10n-id="location-checkbox" + preference="calendar.view.showLocation"/> + <spacer/> + </html:fieldset> + </html:div> + + <html:div data-category="paneCalendar"> + <html:fieldset id="viewsMultiweekGroupbox" data-category="paneCalendar"> + <html:legend data-l10n-id="multiweek-legend"></html:legend> + <hbox align="center"> + <label data-l10n-id="number-of-weeks-label" + control="viewsMultiweekTotalWeeks"/> + <hbox> + <menulist id="viewsMultiweekTotalWeeks" + preference="calendar.weeks.inview"> + <menupopup> + <menuitem data-l10n-id="week-1-label" value="1"/> + <menuitem data-l10n-id="week-2-label" value="2"/> + <menuitem data-l10n-id="week-3-label" value="3"/> + <menuitem data-l10n-id="week-4-label" value="4"/> + <menuitem data-l10n-id="week-5-label" value="5"/> + <menuitem data-l10n-id="week-6-label" value="6"/> + </menupopup> + </menulist> + </hbox> + </hbox> + <hbox align="center" id="previousWeeksBox"> + <label data-l10n-id="previous-weeks-label" + control="viewsMultiweekPreviousWeeks"/> + <hbox> + <menulist id="viewsMultiweekPreviousWeeks" + preference="calendar.previousweeks.inview"> + <menupopup> + <menuitem data-l10n-id="week-0-label" value="0"/> + <menuitem data-l10n-id="week-1-label" value="1"/> + <menuitem data-l10n-id="week-2-label" value="2"/> + </menupopup> + </menulist> + </hbox> + </hbox> + </html:fieldset> + </html:div> diff --git a/comm/calendar/base/content/preferences/views.js b/comm/calendar/base/content/preferences/views.js new file mode 100644 index 0000000000..7c1a7384a1 --- /dev/null +++ b/comm/calendar/base/content/preferences/views.js @@ -0,0 +1,115 @@ +/* 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 gViewsPane */ + +/* globals Preferences */ + +Preferences.addAll([ + { id: "calendar.week.start", type: "int" }, + { id: "calendar.view-minimonth.showWeekNumber", type: "bool" }, + { id: "calendar.week.d0sundaysoff", type: "bool", inverted: "true" }, + { id: "calendar.week.d1mondaysoff", type: "bool", inverted: "true" }, + { id: "calendar.week.d2tuesdaysoff", type: "bool", inverted: "true" }, + { id: "calendar.week.d3wednesdaysoff", type: "bool", inverted: "true" }, + { id: "calendar.week.d4thursdaysoff", type: "bool", inverted: "true" }, + { id: "calendar.week.d5fridaysoff", type: "bool", inverted: "true" }, + { id: "calendar.week.d6saturdaysoff", type: "bool", inverted: "true" }, + { id: "calendar.view.daystarthour", type: "int" }, + { id: "calendar.view.dayendhour", type: "int" }, + { id: "calendar.view.visiblehours", type: "int" }, + { id: "calendar.weeks.inview", type: "int" }, + { id: "calendar.previousweeks.inview", type: "int" }, + { id: "calendar.view.showLocation", type: "bool" }, +]); + +/** + * Global Object to hold methods for the views pref pane + */ +var gViewsPane = { + /** + * Initialize the views pref pane. Sets up dialog controls to match the + * values set in prefs. + */ + init() { + this.updateViewEndMenu(Preferences.get("calendar.view.daystarthour").value); + this.updateViewStartMenu(Preferences.get("calendar.view.dayendhour").value); + this.updateViewWorkDayCheckboxes(Preferences.get("calendar.week.start").value); + this.initializeViewStartEndMenus(); + }, + + /** + * Initialize the strings for the "day starts at" and "day ends at" + * menulists. This is needed to respect locales that use AM/PM. + */ + initializeViewStartEndMenus() { + const { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm"); + + let formatter = cal.dtz.formatter; + let calTime = cal.createDateTime(); + calTime.minute = 0; + + // 1 to 23 instead of 0 to 24 to keep midnight & noon as the localized strings + for (let theHour = 1; theHour <= 23; theHour++) { + calTime.hour = theHour; + let time = formatter.formatTime(calTime); + + let labelIdStart = "timeStart" + theHour; + let labelIdEnd = "timeEnd" + theHour; + // This if block to keep Noon as the localized string, instead of as a number. + if (theHour != 12) { + document.getElementById(labelIdStart).setAttribute("label", time); + document.getElementById(labelIdEnd).setAttribute("label", time); + } + } + }, + + /** + * Updates the view end menu to only display hours after the selected view + * start. + * + * @param aStartValue The value selected for view start. + */ + updateViewEndMenu(aStartValue) { + let endMenuKids = document.getElementById("dayendhourpopup").children; + for (let i = 0; i < endMenuKids.length; i++) { + if (Number(endMenuKids[i].value) <= Number(aStartValue)) { + endMenuKids[i].setAttribute("hidden", true); + } else { + endMenuKids[i].removeAttribute("hidden"); + } + } + }, + + /** + * Updates the view start menu to only display hours before the selected view + * end. + * + * @param aEndValue The value selected for view end. + */ + updateViewStartMenu(aEndValue) { + let startMenuKids = document.getElementById("daystarthourpopup").children; + for (let i = 0; i < startMenuKids.length; i++) { + if (Number(startMenuKids[i].value) >= Number(aEndValue)) { + startMenuKids[i].setAttribute("hidden", true); + } else { + startMenuKids[i].removeAttribute("hidden"); + } + } + }, + + /** + * Update the workday checkboxes based on the start of the week. + * + * @Param weekStart The (0-based) index of the weekday the week + * should start at. + */ + updateViewWorkDayCheckboxes(weekStart) { + weekStart = Number(weekStart); + for (let i = weekStart; i < weekStart + 7; i++) { + let checkbox = document.getElementById("dayoff" + (i % 7)); + checkbox.parentNode.appendChild(checkbox); + } + }, +}; |