Skip to content

Commit 24d542c

Browse files
committed
add: duplicate action
resolve: #205
1 parent cde633a commit 24d542c

File tree

5 files changed

+78
-38
lines changed

5 files changed

+78
-38
lines changed

addon/chrome/content/preferences.xhtml

+21-7
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,21 @@
66
<html:link rel="localization" href="__addonRef__-preferences.ftl" />
77
</linkset>
88
<html:style>
9-
.action-button { min-width: auto; }
9+
.action-button { min-width:auto; width:30px; height:24px;
10+
-moz-context-properties: fill, fill-opacity; fill: currentColor;}
11+
#__addonRef__-action-add {
12+
list-style-image:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNzAxMzEzNjg5MjIxIiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9Ijg5NDgiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiPjxwYXRoIGQ9Ik02OTYgNDgwSDU0NFYzMjhjMC00LjQtMy42LTgtOC04aC00OGMtNC40IDAtOCAzLjYtOCA4djE1MkgzMjhjLTQuNCAwLTggMy42LTggOHY0OGMwIDQuNCAzLjYgOCA4IDhoMTUydjE1MmMwIDQuNCAzLjYgOCA4IDhoNDhjNC40IDAgOC0zLjYgOC04VjU0NGgxNTJjNC40IDAgOC0zLjYgOC04di00OGMwLTQuNC0zLjYtOC04LTh6IiBwLWlkPSI4OTQ5IiBmaWxsPSJjb250ZXh0LWZpbGwiPjwvcGF0aD48cGF0aCBkPSJNNTEyIDY0QzI2NC42IDY0IDY0IDI2NC42IDY0IDUxMnMyMDAuNiA0NDggNDQ4IDQ0OCA0NDgtMjAwLjYgNDQ4LTQ0OFM3NTkuNCA2NCA1MTIgNjR6IG0wIDgyMGMtMjA1LjQgMC0zNzItMTY2LjYtMzcyLTM3MnMxNjYuNi0zNzIgMzcyLTM3MiAzNzIgMTY2LjYgMzcyIDM3Mi0xNjYuNiAzNzItMzcyIDM3MnoiIHAtaWQ9Ijg5NTAiIGZpbGw9ImNvbnRleHQtZmlsbCI+PC9wYXRoPjwvc3ZnPg==");
13+
} #__addonRef__-action-remove {
14+
list-style-image:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNzAxMzEzNjk3NjY1IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjkxMjMiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiPjxwYXRoIGQ9Ik02OTYgNDgwSDMyOGMtNC40IDAtOCAzLjYtOCA4djQ4YzAgNC40IDMuNiA4IDggOGgzNjhjNC40IDAgOC0zLjYgOC04di00OGMwLTQuNC0zLjYtOC04LTh6IiBwLWlkPSI5MTI0IiBmaWxsPSJjb250ZXh0LWZpbGwiPjwvcGF0aD48cGF0aCBkPSJNNTEyIDY0QzI2NC42IDY0IDY0IDI2NC42IDY0IDUxMnMyMDAuNiA0NDggNDQ4IDQ0OCA0NDgtMjAwLjYgNDQ4LTQ0OFM3NTkuNCA2NCA1MTIgNjR6IG0wIDgyMGMtMjA1LjQgMC0zNzItMTY2LjYtMzcyLTM3MnMxNjYuNi0zNzIgMzcyLTM3MiAzNzIgMTY2LjYgMzcyIDM3Mi0xNjYuNiAzNzItMzcyIDM3MnoiIHAtaWQ9IjkxMjUiIGZpbGw9ImNvbnRleHQtZmlsbCI+PC9wYXRoPjwvc3ZnPg==");
15+
} #__addonRef__-action-edit {
16+
list-style-image:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNzAxMzEzNDAwMTg2IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9Ijg1OTgiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiPjxwYXRoIGQ9Ik05MDQgNTEyaC01NmMtNC40IDAtOCAzLjYtOCA4djMyMEgxODRWMTg0aDMyMGM0LjQgMCA4LTMuNiA4LTh2LTU2YzAtNC40LTMuNi04LTgtOEgxNDRjLTE3LjcgMC0zMiAxNC4zLTMyIDMydjczNmMwIDE3LjcgMTQuMyAzMiAzMiAzMmg3MzZjMTcuNyAwIDMyLTE0LjMgMzItMzJWNTIwYzAtNC40LTMuNi04LTgtOHoiIHAtaWQ9Ijg1OTkiIGZpbGw9ImNvbnRleHQtZmlsbCI+PC9wYXRoPjxwYXRoIGQ9Ik0zNTUuOSA1MzQuOUwzNTQgNjUzLjhjLTAuMSA4LjkgNy4xIDE2LjIgMTYgMTYuMmgwLjRsMTE4LTIuOWMyLTAuMSA0LTAuOSA1LjQtMi4zbDQxNS45LTQxNWMzLjEtMy4xIDMuMS04LjIgMC0xMS4zTDc4NS40IDExNC4zYy0xLjYtMS42LTMuNi0yLjMtNS43LTIuM3MtNC4xIDAuOC01LjcgMi4zbC00MTUuOCA0MTVjLTEuNCAxLjUtMi4zIDMuNS0yLjMgNS42eiBtNjMuNSAyMy42TDc3OS43IDE5OWw0NS4yIDQ1LjEtMzYwLjUgMzU5LjctNDUuNyAxLjEgMC43LTQ2LjR6IiBwLWlkPSI4NjAwIiBmaWxsPSJjb250ZXh0LWZpbGwiPjwvcGF0aD48L3N2Zz4=");
17+
} #__addonRef__-action-duplicate {
18+
list-style-image:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNzAxMzE1MTE1MTc3IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjkyOTgiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiPjxwYXRoIGQ9Ik04MzIgNjRIMjk2Yy00LjQgMC04IDMuNi04IDh2NTZjMCA0LjQgMy42IDggOCA4aDQ5NnY2ODhjMCA0LjQgMy42IDggOCA4aDU2YzQuNCAwIDgtMy42IDgtOFY5NmMwLTE3LjctMTQuMy0zMi0zMi0zMnoiIHAtaWQ9IjkyOTkiIGZpbGw9ImNvbnRleHQtZmlsbCI+PC9wYXRoPjxwYXRoIGQ9Ik03MDQgMTkySDE5MmMtMTcuNyAwLTMyIDE0LjMtMzIgMzJ2NTMwLjdjMCA4LjUgMy40IDE2LjYgOS40IDIyLjZsMTczLjMgMTczLjNjMi4yIDIuMiA0LjcgNCA3LjQgNS41djEuOWg0LjJjMy41IDEuMyA3LjIgMiAxMSAySDcwNGMxNy43IDAgMzItMTQuMyAzMi0zMlYyMjRjMC0xNy43LTE0LjMtMzItMzItMzJ6TTM1MCA4NTYuMkwyNjMuOSA3NzBIMzUwdjg2LjJ6TTY2NCA4ODhINDE0Vjc0NmMwLTIyLjEtMTcuOS00MC00MC00MEgyMzJWMjY0aDQzMnY2MjR6IiBwLWlkPSI5MzAwIiBmaWxsPSJjb250ZXh0LWZpbGwiPjwvcGF0aD48L3N2Zz4=");
19+
} #__addonRef__-action-export {
20+
list-style-image:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNzAxMzEzNTc5MTE2IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9Ijg3NzMiIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB3aWR0aD0iMTYiIGhlaWdodD0iMTYiPjxwYXRoIGQ9Ik04OTMuMyAyOTMuM0w3MzAuNyAxMzAuN2MtNy41LTcuNS0xNi43LTEzLTI2LjctMTZWMTEySDE0NGMtMTcuNyAwLTMyIDE0LjMtMzIgMzJ2NzM2YzAgMTcuNyAxNC4zIDMyIDMyIDMyaDczNmMxNy43IDAgMzItMTQuMyAzMi0zMlYzMzguNWMwLTE3LTYuNy0zMy4yLTE4LjctNDUuMnpNMzg0IDE4NGgyNTZ2MTA0SDM4NFYxODR6IG00NTYgNjU2SDE4NFYxODRoMTM2djEzNmMwIDE3LjcgMTQuMyAzMiAzMiAzMmgzMjBjMTcuNyAwIDMyLTE0LjMgMzItMzJWMjA1LjhsMTM2IDEzNlY4NDB6IiBwLWlkPSI4Nzc0IiBmaWxsPSJjb250ZXh0LWZpbGwiPjwvcGF0aD48cGF0aCBkPSJNNTEyIDQ0MmMtNzkuNSAwLTE0NCA2NC41LTE0NCAxNDRzNjQuNSAxNDQgMTQ0IDE0NCAxNDQtNjQuNSAxNDQtMTQ0LTY0LjUtMTQ0LTE0NC0xNDR6IG0wIDIyNGMtNDQuMiAwLTgwLTM1LjgtODAtODBzMzUuOC04MCA4MC04MCA4MCAzNS44IDgwIDgwLTM1LjggODAtODAgODB6IiBwLWlkPSI4Nzc1IiBmaWxsPSJjb250ZXh0LWZpbGwiPjwvcGF0aD48L3N2Zz4=");
21+
} #__addonRef__-action-import {
22+
list-style-image:url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB0PSIxNzAxMzEyMzc1ODY3IiBjbGFzcz0iaWNvbiIgdmlld0JveD0iMCAwIDEwMjQgMTAyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHAtaWQ9IjgzODkiIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxwYXRoIGQ9Ik04ODguMyA3NTcuNGgtNTMuOGMtNC4yIDAtNy43IDMuNS03LjcgNy43djYxLjhIMTk3LjFWMTk3LjFoNjI5Ljh2NjEuOGMwIDQuMiAzLjUgNy43IDcuNyA3LjdoNTMuOGM0LjIgMCA3LjctMy40IDcuNy03LjdWMTU4LjdjMC0xNy0xMy43LTMwLjctMzAuNy0zMC43SDE1OC43Yy0xNyAwLTMwLjcgMTMuNy0zMC43IDMwLjd2NzA2LjZjMCAxNyAxMy43IDMwLjcgMzAuNyAzMC43aDcwNi42YzE3IDAgMzAuNy0xMy43IDMwLjctMzAuN1Y3NjUuMWMwLTQuMy0zLjUtNy43LTcuNy03Ljd6IiBwLWlkPSI4MzkwIiBmaWxsPSJjb250ZXh0LWZpbGwiPjwvcGF0aD48cGF0aCBkPSJNOTAyIDQ3Nkg1ODh2LTc2YzAtNi43LTcuOC0xMC41LTEzLTYuM2wtMTQxLjkgMTEyYy00LjEgMy4yLTQuMSA5LjQgMCAxMi42bDE0MS45IDExMmM1LjMgNC4yIDEzIDAuNCAxMy02LjN2LTc2aDMxNGM0LjQgMCA4LTMuNiA4LTh2LTU2YzAtNC40LTMuNi04LTgtOHoiIHAtaWQ9IjgzOTEiIGZpbGw9ImNvbnRleHQtZmlsbCI+PC9wYXRoPjwvc3ZnPg==");
23+
}
1024
</html:style>
1125
<groupbox style="width: -moz-available">
1226
<label><html:h2 data-l10n-id="action"></html:h2></label>
@@ -16,31 +30,31 @@
1630
<hbox>
1731
<button
1832
id="__addonRef__-action-add"
19-
label="+"
2033
class="action-button"
2134
data-l10n-id="action-add"
2235
></button>
2336
<button
2437
id="__addonRef__-action-remove"
25-
label="-"
2638
class="action-button action-selection"
2739
data-l10n-id="action-remove"
2840
></button>
2941
<button
3042
id="__addonRef__-action-edit"
31-
label=""
32-
class="action-button action-selection"
43+
class="action-button action-selection action-selection-single"
3344
data-l10n-id="action-edit"
3445
></button>
46+
<button
47+
id="__addonRef__-action-duplicate"
48+
class="action-button action-selection action-selection-single"
49+
data-l10n-id="action-duplicate"
50+
></button>
3551
<button
3652
id="__addonRef__-action-export"
37-
label=""
3853
class="action-button action-selection"
3954
data-l10n-id="action-export"
4055
></button>
4156
<button
4257
id="__addonRef__-action-import"
43-
label=""
4458
class="action-button"
4559
data-l10n-id="action-import"
4660
></button>

addon/locale/en-US/preferences.ftl

+2-5
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,16 @@ action-operation-script = Script
2222
action-operation-triggerAction = Trigger Another Action
2323
2424
action-add =
25-
.label = +
2625
.tooltiptext = Add a new action
2726
action-remove =
28-
.label = -
2927
.tooltiptext = Delete selected action
3028
action-edit =
31-
.label =
3229
.tooltiptext = Edit selected action
30+
action-duplicate =
31+
.tooltiptext = Duplicate selected action
3332
action-export =
34-
.label =
3533
.tooltiptext = Export selected actions to file
3634
action-import =
37-
.label =
3835
.tooltiptext = Import actions from file
3936
4037
menu = Menu

addon/locale/it-IT/preferences.ftl

+2-5
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,16 @@ action-operation-script = Script
2222
action-operation-triggerAction = Innesca altra azione
2323
2424
action-add =
25-
.label = +
2625
.tooltiptext = Add a new action
2726
action-remove =
28-
.label = -
2927
.tooltiptext = Delete selected action
3028
action-edit =
31-
.label =
3229
.tooltiptext = Edit selected action
30+
action-duplicate =
31+
.tooltiptext = Duplicate selected action
3332
action-export =
34-
.label =
3533
.tooltiptext = Export selected actions to file
3634
action-import =
37-
.label =
3835
.tooltiptext = Import actions from file
3936
4037
menu = Menu

addon/locale/zh-CN/preferences.ftl

+2-5
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,16 @@ action-operation-script = 自定义脚本
2222
action-operation-triggerAction = 触发另一个动作
2323
2424
action-add =
25-
.label = +
2625
.tooltiptext = 创建新动作
2726
action-remove =
28-
.label = -
2927
.tooltiptext = 移除选中的动作
3028
action-edit =
31-
.label =
3229
.tooltiptext = 编辑选中的动作
30+
action-duplicate =
31+
.tooltiptext = 复制选中的动作
3332
action-export =
34-
.label =
3533
.tooltiptext = 导出选中的动作到文件
3634
action-import =
37-
.label =
3835
.tooltiptext = 从文件批量导入动作
3936
4037
menu = 菜单

src/modules/preferenceWindow.ts

+51-16
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ async function initUI() {
1919
const renderLock = Zotero.Promise.defer();
2020
if (!isWindowAlive(addon.data.prefs.window)) return;
2121
addon.data.prefs.tableHelper = new ztoolkit.VirtualizedTable(
22-
addon.data.prefs.window!,
22+
addon.data.prefs.window!
2323
)
2424
.setContainerId(`${config.addonRef}-table-container`)
2525
.setProp({
@@ -34,19 +34,20 @@ async function initUI() {
3434
.setProp("getRowData", getRowData as any)
3535
// Update selected key when selection changes
3636
.setProp("onSelectionChange", (selection) => {
37-
const switchButtons =
38-
addon.data.prefs.window?.document.querySelectorAll(".action-selection");
39-
for (let i = 0; i < addon.data.actions.cachedKeys.length; i++) {
40-
if (selection.isSelected(i)) {
41-
addon.data.actions.selectedKey = addon.data.actions.cachedKeys[i];
42-
switchButtons &&
43-
switchButtons.forEach((e) => e.removeAttribute("disabled"));
44-
return;
45-
}
46-
}
47-
addon.data.actions.selectedKey = undefined;
48-
switchButtons &&
49-
switchButtons.forEach((e) => e.setAttribute("disabled", "true"));
37+
const selectedKeys = getSelection();
38+
addon.data.actions.selectedKey = selectedKeys[0];
39+
40+
addon.data.prefs.window?.document
41+
.querySelectorAll(".action-selection")
42+
?.forEach((e) =>
43+
setButtonDisabled(e as XUL.Button, selectedKeys.length === 0)
44+
);
45+
46+
addon.data.prefs.window?.document
47+
.querySelectorAll(".action-selection-single")
48+
?.forEach((e) =>
49+
setButtonDisabled(e as XUL.Button, selectedKeys.length !== 1)
50+
);
5051
})
5152
// When pressing delete, delete selected line and refresh table.
5253
// Returning false to prevent default event.
@@ -96,7 +97,7 @@ function initEvents() {
9697
.querySelector(`#${config.addonRef}-action-add`)
9798
?.addEventListener("command", (e) => {
9899
const key = addon.api.actionManager.updateAction(
99-
Object.assign({}, emptyAction),
100+
Object.assign({}, emptyAction)
100101
);
101102
updateUI();
102103
editAndUpdate(key);
@@ -117,6 +118,32 @@ function initEvents() {
117118
await editAndUpdate();
118119
});
119120

121+
doc
122+
.querySelector(`#${config.addonRef}-action-duplicate`)
123+
?.addEventListener("command", async (e) => {
124+
const newAction = Object.assign(
125+
{},
126+
addon.data.actions.map.get(addon.data.actions.selectedKey!)
127+
);
128+
if (newAction.name) {
129+
// Add (1) (2) ... to the end of the name
130+
if (newAction.name.match(/\(\d+\)$/)) {
131+
newAction.name = newAction.name.replace(
132+
/\((\d+)\)$/,
133+
(_, num) => `(${parseInt(num) + 1})`
134+
);
135+
} else {
136+
newAction.name += " (1)";
137+
}
138+
} else {
139+
// Use time as name
140+
newAction.name = new Date().toLocaleString();
141+
}
142+
const key = addon.api.actionManager.updateAction(newAction);
143+
updateUI();
144+
await editAndUpdate(key);
145+
});
146+
120147
doc
121148
.querySelector(`#${config.addonRef}-action-export`)
122149
?.addEventListener("command", async (e) => {
@@ -154,7 +181,7 @@ function getRowData(index: number) {
154181
return {
155182
event: getString(`prefs-action-event-${ActionEventTypes[action.event]}`),
156183
operation: getString(
157-
`prefs-action-operation-${ActionOperationTypes[action.operation]}`,
184+
`prefs-action-operation-${ActionOperationTypes[action.operation]}`
158185
),
159186
data: action.data,
160187
shortcut: action.shortcut,
@@ -172,3 +199,11 @@ function getSelection() {
172199
const keys = addon.data.actions.cachedKeys;
173200
return Array.from(indices).map((i) => keys[i]);
174201
}
202+
203+
function setButtonDisabled(button: XUL.Button, disabled: boolean = true) {
204+
if (disabled) {
205+
button.setAttribute("disabled", "true");
206+
} else {
207+
button.removeAttribute("disabled");
208+
}
209+
}

0 commit comments

Comments
 (0)