1//=============================================================================
2//  MuseScore
3//  Music Composition & Notation
4//
5//  Copyright (C) 2019 Werner Schweer and others
6//
7//  This program is free software; you can redistribute it and/or modify
8//  it under the terms of the GNU General Public License version 2.
9//
10//  This program is distributed in the hope that it will be useful,
11//  but WITHOUT ANY WARRANTY; without even the implied warranty of
12//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13//  GNU General Public License for more details.
14//
15//  You should have received a copy of the GNU General Public License
16//  along with this program; if not, write to the Free Software
17//  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18//=============================================================================
19
20import QtQuick 2.8
21import QtQuick.Controls 2.1
22import QtQml.Models 2.2
23import MuseScore.Palette 3.3
24
25import "utils.js" as Utils
26
27StyledPopup {
28    id: moreElementsPopup
29
30    property var poolPalette : null
31    property var poolPaletteRootIndex: null
32    property PaletteController poolPaletteController: null
33
34    property var customPalette : null
35    property var customPaletteRootIndex: null
36    property PaletteController customPaletteController: null
37
38    property PaletteElementEditor elementEditor: customPaletteRootIndex && customPaletteController ? customPaletteController.elementEditor(customPaletteRootIndex) : null
39
40    property string paletteName
41    readonly property string libraryPaletteName: (poolPalette && poolPaletteRootIndex) ? poolPalette.data(poolPaletteRootIndex, Qt.DisplayRole) : ""
42    property bool paletteIsCustom: false
43    property bool paletteEditingEnabled: true
44
45    property size cellSize
46    property bool drawGrid
47
48    property int maxHeight: 400
49    implicitHeight: column.height + topPadding + bottomPadding
50
51    property bool enablePaletteAnimations: false // disabled by default to avoid unnecessary "add" animations on opening this popup at first time
52
53    signal addElementsRequested(var mimeDataList)
54
55    Column {
56        id: column
57        width: parent.width
58        spacing: 8
59
60        StyledButton {
61            id: addToPaletteButton
62            width: parent.width
63
64            text: qsTr("Add to %1").arg(paletteName)
65            enabled: moreElementsPopup.paletteEditingEnabled && (masterPaletteSelectionModel.hasSelection || customPaletteSelectionModel.hasSelection)
66
67            onClicked: {
68                function collectMimeData(palette, selection) {
69                    const selectedList = selection.selectedIndexes;
70                    var mimeArr = [];
71                    for (var i = 0; i < selectedList.length; i++) {
72                        const mimeData = palette.paletteModel.data(selectedList[i], PaletteTreeModel.MimeDataRole);
73                        mimeArr.push(mimeData);
74                    }
75                    return mimeArr;
76                }
77
78                const mimeMasterPalette = collectMimeData(masterPalette, masterPaletteSelectionModel);
79                const mimeCustomPalette = collectMimeData(customPalette, customPaletteSelectionModel);
80
81                masterPaletteSelectionModel.clear();
82                customPaletteSelectionModel.clear();
83
84                if (mimeMasterPalette.length)
85                    addElementsRequested(mimeMasterPalette);
86                if (mimeCustomPalette.length)
87                    addElementsRequested(mimeCustomPalette);
88            }
89        }
90
91        Item {
92            id: masterIndexControls
93            enabled: moreElementsPopup.paletteIsCustom && poolPalette && poolPaletteRootIndex
94            visible: enabled
95            anchors { left: parent.left; right: parent.right }
96            implicitHeight: prevButton.implicitHeight
97            StyledButton {
98                id: prevButton
99                width: height
100                anchors.left: parent.left
101                flat: true
102                text: "<" // TODO: replace?
103
104                property var prevIndex: {
105                    if (!masterIndexControls.enabled)
106                        return null;
107
108                    var idx = poolPalette.sibling(poolPaletteRootIndex.row - 1, 0, poolPaletteRootIndex);
109                    if (!idx.valid) {
110                        const nrows = poolPalette.rowCount(poolPaletteRootIndex.parent);
111                        idx = poolPalette.sibling(nrows - 1, 0, poolPaletteRootIndex)
112                    }
113                    return idx;
114                }
115
116                enabled: prevIndex && prevIndex.valid
117
118                onClicked: poolPaletteRootIndex = prevIndex;
119            }
120            Text {
121                anchors.centerIn: parent
122                text: moreElementsPopup.libraryPaletteName
123                font: globalStyle.font
124                color: globalStyle.windowText
125            }
126            StyledButton {
127                width: height
128                anchors.right: parent.right
129                flat: true
130                text: ">" // TODO: replace?
131
132                property var nextIndex: {
133                    if (!masterIndexControls.enabled)
134                        return null;
135
136                    var idx = poolPalette.sibling(poolPaletteRootIndex.row + 1, 0, poolPaletteRootIndex);
137                    if (!idx.valid)
138                        idx = poolPalette.sibling(0, 0, poolPaletteRootIndex)
139                    return idx;
140                }
141
142                enabled: nextIndex && nextIndex.valid
143
144                onClicked: poolPaletteRootIndex = nextIndex
145            }
146        }
147
148        Rectangle {
149            id: paletteContainer
150            width: parent.width
151            height: childrenRect.height
152            border { width: 1; color: "black" }
153            color: mscore.paletteBackground
154
155            readonly property int availableHeight: moreElementsPopup.maxHeight - addToPaletteButton.height - (masterIndexControls ? masterIndexControls.height : 0) - bottomText.height - (elementEditorButton.visible ? elementEditorButton.height : 0) - 40
156
157            Column {
158                width: parent.width
159                padding: 8
160                property real contentWidth: width - 2 * padding
161
162                ItemSelectionModel {
163                    id: masterPaletteSelectionModel
164                    model: masterPalette.paletteModel
165                }
166
167                Palette {
168                    id: masterPalette
169                    height: Math.max(
170                                cellSize.height,
171                                Math.min(
172                                    implicitHeight,
173                                    paletteContainer.availableHeight - (customPalette.visible ? (customPalette.height + customPaletteLabel.height) : 0)
174                                    )
175                                )
176                    width: parent.contentWidth
177
178                    ScrollBar.vertical: ScrollBar { enabled: masterPalette.height < masterPalette.implicitHeight }
179
180                    // TODO: change settings to "hidden" model?
181                    cellSize: moreElementsPopup.cellSize
182                    drawGrid: moreElementsPopup.drawGrid
183
184                    paletteModel: moreElementsPopup.poolPalette
185                    paletteRootIndex: moreElementsPopup.poolPaletteRootIndex
186                    paletteController: moreElementsPopup.poolPaletteController
187                    selectionModel: masterPaletteSelectionModel
188
189                    enableAnimations: moreElementsPopup.enablePaletteAnimations
190                }
191
192                ToolSeparator {
193                    id: separator
194                    visible: !customPalette.empty
195                    orientation: Qt.Horizontal
196                    width: parent.contentWidth
197                }
198
199                Item {
200                    width: separator.width
201                    implicitHeight: deleteButton.implicitHeight
202                    visible: !customPalette.empty
203
204                    Text {
205                        id: customPaletteLabel
206                        height: deleteButton.height
207                        verticalAlignment: Text.AlignVCenter
208                        text: qsTr("Custom")
209                    }
210
211                    StyledToolButton {
212                        id: deleteButton
213                        width: height
214                        anchors.right: parent.right
215                        text: qsTr("Delete element(s)")
216                        enabled: customPaletteSelectionModel.hasSelection
217
218                        ToolTip.text: text
219
220                        onHoveredChanged: {
221                            if (hovered) {
222                                mscore.tooltip.item = deleteButton;
223                                mscore.tooltip.text = deleteButton.text;
224                            } else if (mscore.tooltip.item == deleteButton)
225                                mscore.tooltip.item = null;
226                        }
227
228                        padding: 4
229
230                        contentItem: StyledIcon {
231                            source: "icons/TrashCan.svg"
232                            color: "black"
233                            opacity: deleteButton.enabled ? 1.0 : 0.1
234                        }
235
236                        onClicked: Utils.removeSelectedItems(moreElementsPopup.customPaletteController, customPaletteSelectionModel, moreElementsPopup.customPaletteRootIndex);
237                    }
238                }
239
240                ItemSelectionModel {
241                    id: customPaletteSelectionModel
242                    model: customPalette.paletteModel
243                }
244
245                Palette {
246                    id: customPalette
247                    visible: !empty
248                    width: parent.contentWidth
249
250                    cellSize: control.cellSize
251                    drawGrid: control.drawGrid
252
253                    paletteModel: moreElementsPopup.customPalette
254                    paletteRootIndex: moreElementsPopup.customPaletteRootIndex
255                    paletteController: moreElementsPopup.customPaletteController
256                    selectionModel: customPaletteSelectionModel
257
258                    enableAnimations: moreElementsPopup.enablePaletteAnimations
259                }
260            }
261        }
262
263        Item {
264            // spacer item, adds extra spacing before "drag items..." text
265            width: 1
266            height: 2 - column.spacing
267        }
268
269        Text {
270            id: bottomText
271            width: parent.width
272            text: qsTr("Drag items to the palette or directly on your score")
273            color: globalStyle.windowText
274            horizontalAlignment: Text.AlignHCenter
275            wrapMode: Text.WordWrap
276            font.family: globalStyle.font.family
277            // make this label's font slightly smaller than other popup text
278            font.pointSize: globalStyle.font.pointSize * 0.8
279        }
280
281        Item {
282            // spacer item, adds extra spacing after "drag items..." text
283            width: 1
284            height: 2 - column.spacing
285        }
286
287        StyledButton {
288            id: elementEditorButton
289            visible: moreElementsPopup.elementEditor && moreElementsPopup.elementEditor.valid
290            enabled: moreElementsPopup.paletteEditingEnabled
291            width: parent.width
292            text: moreElementsPopup.elementEditor ? moreElementsPopup.elementEditor.actionName : ""
293            onClicked: moreElementsPopup.elementEditor.open()
294        }
295    }
296}
297