diff --git a/chrome/content/zotero/itemTree.jsx b/chrome/content/zotero/itemTree.jsx
index 349a00bba3..92a600eb2e 100644
--- a/chrome/content/zotero/itemTree.jsx
+++ b/chrome/content/zotero/itemTree.jsx
@@ -2463,6 +2463,182 @@ var ItemTree = class ItemTree extends LibraryTree {
}
};
+ // //////////////////////////////////////////////////////////////////////////////
+ //
+ // Menu utilities for ZoteroPane
+ //
+ // //////////////////////////////////////////////////////////////////////////////
+
+ buildColumnPickerMenu(menupopup) {
+ const prefix = 'zotero-column-picker-';
+ // Filter out ignored columns
+ const columns = this._getColumns();
+ let columnMenuitemElements = {};
+ for (let i = 0; i < columns.length; i++) {
+ const column = columns[i];
+ if (column.showInColumnPicker === false) continue;
+ let label = formatColumnName(column);
+ let menuitem = document.createXULElement('menuitem');
+ menuitem.setAttribute('type', 'checkbox');
+ menuitem.setAttribute('label', label);
+ menuitem.setAttribute('colindex', i);
+ menuitem.addEventListener('command', () => this.tree._columns.toggleHidden(i));
+ if (!column.hidden) {
+ menuitem.setAttribute('checked', true);
+ }
+ if (column.disabledIn && column.disabledIn.includes(this.collectionTreeRow.visibilityGroup)) {
+ menuitem.setAttribute('disabled', true);
+ }
+ columnMenuitemElements[column.dataKey] = menuitem;
+ menupopup.appendChild(menuitem);
+ }
+
+ try {
+ // More Columns menu
+ let id = prefix + 'more-menu';
+
+ let moreMenu = document.createXULElement('menu');
+ moreMenu.setAttribute('label', Zotero.getString('pane.items.columnChooser.moreColumns'));
+ moreMenu.setAttribute('anonid', id);
+
+ let moreMenuPopup = document.createXULElement('menupopup');
+ moreMenuPopup.setAttribute('anonid', id + '-popup');
+
+ let moreItems = [];
+ for (let i = 0; i < columns.length; i++) {
+ const column = columns[i];
+ if (column.columnPickerSubMenu) {
+ moreItems.push(columnMenuitemElements[column.dataKey]);
+ }
+ }
+
+ // Sort fields and move to submenu
+ var collation = Zotero.getLocaleCollation();
+ moreItems.sort(function (a, b) {
+ return collation.compareString(1, a.getAttribute('label'), b.getAttribute('label'));
+ });
+ moreItems.forEach(function (elem) {
+ moreMenuPopup.appendChild(menupopup.removeChild(elem));
+ });
+
+ let sep = document.createXULElement('menuseparator');
+ menupopup.appendChild(sep);
+ moreMenu.appendChild(moreMenuPopup);
+ menupopup.appendChild(moreMenu);
+ }
+ catch (e) {
+ Zotero.logError(e);
+ Zotero.debug(e, 1);
+ }
+
+ //
+ // Secondary Sort menu
+ //
+ if (!this.collectionTreeRow.isFeedsOrFeed()) {
+ try {
+ const id = prefix + 'sort-menu';
+ const primaryField = this.getSortField();
+ const sortFields = this.getSortFields();
+ let secondaryField = false;
+ if (sortFields[1]) {
+ secondaryField = sortFields[1];
+ }
+
+ const primaryFieldLabel = formatColumnName(columns.find(c => c.dataKey == primaryField));
+
+ const sortMenu = document.createXULElement('menu');
+ sortMenu.setAttribute('label',
+ Zotero.getString('pane.items.columnChooser.secondarySort', primaryFieldLabel));
+ sortMenu.setAttribute('anonid', id);
+
+ const sortMenuPopup = document.createXULElement('menupopup');
+ sortMenuPopup.setAttribute('anonid', id + '-popup');
+
+ // Generate menuitems
+ const sortOptions = [
+ 'title',
+ 'firstCreator',
+ 'itemType',
+ 'date',
+ 'year',
+ 'publisher',
+ 'publicationTitle',
+ 'dateAdded',
+ 'dateModified'
+ ];
+ for (let field of sortOptions) {
+ // Hide current primary field, and don't show Year for Date, since it would be a no-op
+ if (field == primaryField || (primaryField == 'date' && field == 'year')) {
+ continue;
+ }
+ let column = columns.find(c => c.dataKey == field);
+ let label = formatColumnName(column);
+
+ let sortMenuItem = document.createXULElement('menuitem');
+ sortMenuItem.setAttribute('fieldName', field);
+ sortMenuItem.setAttribute('label', label);
+ sortMenuItem.setAttribute('type', 'checkbox');
+ if (field == secondaryField) {
+ sortMenuItem.setAttribute('checked', 'true');
+ }
+ sortMenuItem.addEventListener('command', async () => {
+ if (this._setSecondarySortField(field)) {
+ await this.sort();
+ }
+ })
+ sortMenuPopup.appendChild(sortMenuItem);
+ }
+
+ sortMenu.appendChild(sortMenuPopup);
+ menupopup.appendChild(sortMenu);
+ }
+ catch (e) {
+ Zotero.logError(e);
+ Zotero.debug(e, 1);
+ }
+ }
+
+ let sep = document.createXULElement('menuseparator');
+ // sep.setAttribute('anonid', prefix + 'sep');
+ menupopup.appendChild(sep);
+
+ //
+ // Restore Default Column Order
+ //
+ let menuitem = document.createXULElement('menuitem');
+ menuitem.setAttribute('label', Zotero.getString('zotero.items.restoreColumnOrder.label'));
+ menuitem.setAttribute('anonid', prefix + 'restore-order');
+ menuitem.addEventListener('command', () => this.tree._columns.restoreDefaultOrder());
+ menupopup.appendChild(menuitem);
+ }
+
+ buildSortMenu(menupopup) {
+ this._getColumns()
+ .filter(column => !column.hidden)
+ .forEach((column, i) => {
+ let menuItem = document.createXULElement('menuitem');
+ menuItem.setAttribute('type', 'checkbox');
+ menuItem.setAttribute('checked', this.getSortField() == column.dataKey);
+ menuItem.setAttribute('label', formatColumnName(column));
+ menuItem.addEventListener('command', () => {
+ this.toggleSort(i, true);
+ });
+ menupopup.append(menuItem);
+ });
+ }
+
+ toggleSort(sortIndex, countVisible = false) {
+ if (countVisible) {
+ let cols = this._getColumns();
+ sortIndex = cols.indexOf(cols.filter(col => !col.hidden)[sortIndex]);
+ if (sortIndex == -1) {
+ return;
+ }
+ }
+ this.tree._columns.toggleSort(sortIndex);
+ }
+
+
// //////////////////////////////////////////////////////////////////////////////
//
// Private methods
@@ -3532,16 +3708,14 @@ var ItemTree = class ItemTree extends LibraryTree {
_displayColumnPickerMenu = (event) => {
if (!this.props.columnPicker) return;
- const prefix = 'zotero-column-picker-';
- const doc = document;
let popupset = document.querySelector('#zotero-column-picker-popupset');
if (!popupset) {
- popupset = doc.createXULElement('popupset');
+ popupset = document.createXULElement('popupset');
popupset.id = 'zotero-column-picker-popupset';
document.children[0].appendChild(popupset);
}
- const menupopup = doc.createXULElement('menupopup');
+ const menupopup = document.createXULElement('menupopup');
menupopup.id = 'zotero-column-picker';
menupopup.addEventListener('popuphiding', (event) => {
if (event.target.id == menupopup.id) {
@@ -3549,145 +3723,7 @@ var ItemTree = class ItemTree extends LibraryTree {
}
});
- // Filter out ignored columns
- const columns = this._getColumns();
- let columnMenuitemElements = {};
- for (let i = 0; i < columns.length; i++) {
- const column = columns[i];
- if (column.showInColumnPicker === false) continue;
- let label = formatColumnName(column);
- let menuitem = doc.createXULElement('menuitem');
- menuitem.setAttribute('type', 'checkbox');
- menuitem.setAttribute('label', label);
- menuitem.setAttribute('colindex', i);
- menuitem.addEventListener('command', () => this.tree._columns.toggleHidden(i));
- if (!column.hidden) {
- menuitem.setAttribute('checked', true);
- }
- if (column.disabledIn && column.disabledIn.includes(this.collectionTreeRow.visibilityGroup)) {
- menuitem.setAttribute('disabled', true);
- }
- columnMenuitemElements[column.dataKey] = menuitem;
- menupopup.appendChild(menuitem);
- }
-
- try {
- // More Columns menu
- let id = prefix + 'more-menu';
-
- let moreMenu = doc.createXULElement('menu');
- moreMenu.setAttribute('label', Zotero.getString('pane.items.columnChooser.moreColumns'));
- moreMenu.setAttribute('anonid', id);
-
- let moreMenuPopup = doc.createXULElement('menupopup');
- moreMenuPopup.setAttribute('anonid', id + '-popup');
-
- let moreItems = [];
- for (let i = 0; i < columns.length; i++) {
- const column = columns[i];
- if (column.columnPickerSubMenu) {
- moreItems.push(columnMenuitemElements[column.dataKey]);
- }
- }
-
- // Sort fields and move to submenu
- var collation = Zotero.getLocaleCollation();
- moreItems.sort(function (a, b) {
- return collation.compareString(1, a.getAttribute('label'), b.getAttribute('label'));
- });
- moreItems.forEach(function (elem) {
- moreMenuPopup.appendChild(menupopup.removeChild(elem));
- });
-
- let sep = doc.createXULElement('menuseparator');
- menupopup.appendChild(sep);
- moreMenu.appendChild(moreMenuPopup);
- menupopup.appendChild(moreMenu);
- }
- catch (e) {
- Zotero.logError(e);
- Zotero.debug(e, 1);
- }
-
- //
- // Secondary Sort menu
- //
- if (!this.collectionTreeRow.isFeedsOrFeed()) {
- try {
- const id = prefix + 'sort-menu';
- const primaryField = this.getSortField();
- const sortFields = this.getSortFields();
- let secondaryField = false;
- if (sortFields[1]) {
- secondaryField = sortFields[1];
- }
-
- const primaryFieldLabel = formatColumnName(columns.find(c => c.dataKey == primaryField));
-
- const sortMenu = doc.createXULElement('menu');
- sortMenu.setAttribute('label',
- Zotero.getString('pane.items.columnChooser.secondarySort', primaryFieldLabel));
- sortMenu.setAttribute('anonid', id);
-
- const sortMenuPopup = doc.createXULElement('menupopup');
- sortMenuPopup.setAttribute('anonid', id + '-popup');
-
- // Generate menuitems
- const sortOptions = [
- 'title',
- 'firstCreator',
- 'itemType',
- 'date',
- 'year',
- 'publisher',
- 'publicationTitle',
- 'dateAdded',
- 'dateModified'
- ];
- for (let field of sortOptions) {
- // Hide current primary field, and don't show Year for Date, since it would be a no-op
- if (field == primaryField || (primaryField == 'date' && field == 'year')) {
- continue;
- }
- let column = columns.find(c => c.dataKey == field);
- let label = formatColumnName(column);
-
- let sortMenuItem = doc.createXULElement('menuitem');
- sortMenuItem.setAttribute('fieldName', field);
- sortMenuItem.setAttribute('label', label);
- sortMenuItem.setAttribute('type', 'checkbox');
- if (field == secondaryField) {
- sortMenuItem.setAttribute('checked', 'true');
- }
- sortMenuItem.addEventListener('command', async () => {
- if (this._setSecondarySortField(field)) {
- await this.sort();
- }
- })
- sortMenuPopup.appendChild(sortMenuItem);
- }
-
- sortMenu.appendChild(sortMenuPopup);
- menupopup.appendChild(sortMenu);
- }
- catch (e) {
- Zotero.logError(e);
- Zotero.debug(e, 1);
- }
- }
-
- let sep = doc.createXULElement('menuseparator');
- // sep.setAttribute('anonid', prefix + 'sep');
- menupopup.appendChild(sep);
-
- //
- // Restore Default Column Order
- //
- let menuitem = doc.createXULElement('menuitem');
- menuitem.setAttribute('label', Zotero.getString('zotero.items.restoreColumnOrder.label'));
- menuitem.setAttribute('anonid', prefix + 'restore-order');
- menuitem.addEventListener('command', () => this.tree._columns.restoreDefaultOrder());
- menupopup.appendChild(menuitem);
+ this.buildColumnPickerMenu(menupopup);
popupset.appendChild(menupopup);
menupopup.openPopupAtScreen(
diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js
index aaa55a75da..374dcee358 100644
--- a/chrome/content/zotero/zoteroPane.js
+++ b/chrome/content/zotero/zoteroPane.js
@@ -1624,6 +1624,16 @@ var ZoteroPane = new function()
});
ZoteroPane.itemsView.onRefresh.addListener(() => ZoteroPane.setTagScope());
ZoteroPane.itemsView.waitForLoad().then(() => Zotero.uiIsReady());
+
+ let sortSubmenuKeys = document.getElementById('sortSubmenuKeys');
+ for (let i = 0; i < 10; i++) {
+ let key = document.createElement('key');
+ key.id = 'key_sortCol' + i;
+ key.setAttribute('modifiers', Zotero.isMac ? 'accel alt control' : 'accel alt');
+ key.setAttribute('key', (i + 1) % 10);
+ key.addEventListener('command', () => ZoteroPane.itemsView.toggleSort(i, true));
+ sortSubmenuKeys.append(key);
+ }
}
catch (e) {
Zotero.logError(e);
@@ -6166,6 +6176,31 @@ var ZoteroPane = new function()
this.itemPane.handleResize();
}
+
+ this.onColumnPickerPopupShowing = function (event) {
+ let menuPopup = document.getElementById('column-picker-submenu').menupopup;
+ if (event.target !== menuPopup) {
+ return;
+ }
+ menuPopup.replaceChildren();
+ this.itemsView?.buildColumnPickerMenu(menuPopup);
+ };
+
+ this.onSortPopupShowing = function (event) {
+ let menuPopup = document.getElementById('sort-submenu').menupopup;
+ if (event.target !== menuPopup) {
+ return;
+ }
+ menuPopup.replaceChildren();
+ this.itemsView?.buildSortMenu(menuPopup);
+
+ for (let i = 0; i < 10; i++) {
+ if (!menuPopup.children[i]) {
+ break;
+ }
+ menuPopup.children[i].setAttribute('key', 'key_sortCol' + i);
+ }
+ };
/**
* Opens the about dialog
diff --git a/chrome/content/zotero/zoteroPane.xhtml b/chrome/content/zotero/zoteroPane.xhtml
index 9db9132618..88a22fbd2d 100644
--- a/chrome/content/zotero/zoteroPane.xhtml
+++ b/chrome/content/zotero/zoteroPane.xhtml
@@ -191,6 +191,8 @@
+
+
@@ -624,6 +626,19 @@
oncommand="ZoteroStandalone.onViewMenuItemClick(event); event.stopPropagation();"/>
+
+
+