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 24import MuseScore.Views 3.3 25import MuseScore.Utils 3.3 26 27import "utils.js" as Utils 28 29GridView { 30 id: paletteView 31 clip: true 32 33 interactive: height < contentHeight // TODO: check if it helps on Mac 34 boundsBehavior: Flickable.StopAtBounds 35 36 property size cellSize 37 property bool drawGrid: false 38 property bool showMoreButton: false 39 40 property var paletteModel 41 property var paletteRootIndex 42 property PaletteController paletteController 43 property var selectionModel: null 44 45 property int ncells: paletteCellDelegateModel.count 46 property bool empty: !ncells 47 48 property bool externalDropBlocked: false 49 50 property bool enableAnimations: true 51 52 states: [ 53 State { 54 name: "default" 55 }, 56 State { 57 name: "drag" 58 PropertyChanges { 59 target: paletteView 60 // unbind width stretching from the delegate model's state while dragging 61 explicit: true 62 stretchWidth: paletteCellDelegateModel.stretchWidth 63 restoreEntryValues: true 64 } 65 } 66 ] 67 68 state: "default" 69 70 // internal property: whether the palette fits to one row 71 readonly property bool oneRow: cellDefaultWidth * ncells + moreButton.implicitWidth < width 72 73 implicitHeight: { 74 if (!ncells) 75 return cellHeight; 76 if (!showMoreButton || oneRow) 77 return contentHeight; 78 79 const moreButtonCells = Math.ceil(moreButton.implicitWidth / cellWidth); 80 const rowCells = Math.floor(width / cellWidth); 81 const lastRowCells = ncells % rowCells; 82 const freeCells = lastRowCells ? rowCells - lastRowCells : 0; 83 84 if (freeCells >= moreButtonCells) 85 return contentHeight; 86 return contentHeight + cellHeight; 87 } 88 89 readonly property int cellDefaultWidth: cellSize.width 90 property bool stretchWidth: !(oneRow && showMoreButton) 91 cellWidth: stretchWidth ? Math.floor(Utils.stretched(cellDefaultWidth, width)) : cellDefaultWidth 92 cellHeight: cellSize.height 93 94 readonly property real ncolumns: Math.floor(width / cellWidth); 95 readonly property real lastColumnCellWidth : cellWidth + (width % cellWidth) // width of last cell in a row: might be stretched to avoid a gap at row end 96 97 signal moreButtonClicked() 98 99 MouseArea { 100 // Dummy MouseArea to prevent propagation of clicks on empty place to palette's parent 101 z: -1000 102 anchors.fill: parent 103 } 104 105 StyledButton { 106 id: moreButton 107 visible: showMoreButton 108 activeFocusOnTab: this === paletteTree.currentTreeItem 109 110 highlighted: visualFocus || hovered 111 112 background: Rectangle { 113 color: mscore.paletteBackground 114 Rectangle { 115 anchors.fill: parent 116 color: globalStyle.voice1Color 117 opacity: moreButton.down ? 0.4 : (moreButton.highlighted ? 0.2 : 0.0) 118 } 119 border.color: moreButton.activeFocus ? "lightblue" : "transparent" // show current item 120 border.width: 2 121 } 122 123 onActiveFocusChanged: { 124 if (activeFocus) { 125 paletteTree.currentTreeItem = this; 126 127 if (mscore.keyboardModifiers() === Qt.NoModifier) 128 paletteView.selectionModel.clearSelection(); 129 } 130 } 131 132 anchors.bottom: parent.bottom 133 anchors.right: parent.right 134 width: { 135 if (paletteView.empty) 136 return implicitWidth; 137 138 // align to the left border of some palette cell 139 var addition = (parent.width - implicitWidth) % cellWidth - 1; // -1 allows to fit into a cell if palette grid is visible 140 if (addition < 0) 141 addition += cellWidth; 142 143 return implicitWidth + addition; 144 } 145 height: cellHeight - (paletteView.oneRow ? 0 : 1) 146 147 text: qsTr("More") 148 textColor: down ? globalStyle.buttonText : "black"// palette background has white or light color 149 visualFocusTextColor: "darkblue" 150 151 onClicked: paletteView.moreButtonClicked() 152 153 Keys.onShortcutOverride: { 154 // Intercept all keys that we want to use with Keys.onPressed 155 // in case they are assigned as shortcuts in Preferences. 156 event.accepted = true; // intercept everything 157 switch (event.key) { 158 case Qt.Key_Up: 159 case Qt.Key_Down: 160 return; 161 } 162 event.accepted = false; // allow key to function as shortcut (don't intercept) 163 } 164 165 Keys.onPressed: { 166 // NOTE: All keys must be intercepted with Keys.onShortcutOverride. 167 switch (event.key) { 168 case Qt.Key_Up: 169 focusPreviousItem(); 170 break; 171 case Qt.Key_Down: 172 paletteTree.focusNextItem(false); 173 break; 174 default: 175 return; // don't accept event 176 } 177 event.accepted = true; 178 } 179 180 function focusPreviousItem() { 181 if (paletteView.count == 0) { 182 paletteTree.currentItem.forceActiveFocus(); 183 } else { 184 paletteView.currentIndex = paletteView.count - 1 185 paletteView.currentItem.forceActiveFocus(); 186 } 187 } 188 } 189 190 PlaceholderManager { 191 id: placeholder 192 delegateModel: paletteCellDelegateModel 193 } 194 195 Timer { 196 id: dragDropReorderTimer 197 interval: 400 198 } 199 200 PaletteBackground { 201 z: -1 202 anchors.fill: parent 203 drawGrid: parent.drawGrid && !parent.empty 204 offsetX: parent.contentX 205 offsetY: parent.contentY 206 cellWidth: parent.cellWidth 207 cellHeight: parent.cellHeight 208 209 DropArea { 210 id: paletteDropArea 211 anchors { fill: parent/*; margins: 10*/ } 212 213// keys: [ "application/musescore/symbol", "application/musescore/palette/cell" ] 214 215 property var action 216 property var proposedAction: Qt.IgnoreAction 217 property bool internal: false 218 219 function onDrag(drag) { 220 if (drag.proposedAction != proposedAction) { 221 onEntered(drag); 222 return; 223 } 224 225 if (drag.source.dragged) { 226 drag.source.internalDrag = internal; 227 drag.source.dragCopy = action == Qt.CopyAction; 228 paletteView.state = "drag"; 229 drag.source.paletteDrag = true; 230 } else if (typeof drag.source.paletteDrag !== "undefined") // if this is a palette and not, e.g., scoreview 231 return; 232 233 drag.accept(action); // confirm we accept the action we determined inside onEntered 234 235 var idx = paletteView.indexAt(drag.x, drag.y); 236 if (idx == -1) 237 idx = paletteView.paletteModel.rowCount(paletteView.paletteRootIndex) - (internal ? 1 : 0); 238 239 if (placeholder.active && placeholder.index == idx) 240 return; 241 placeholder.makePlaceholder(idx, { decoration: "#eeeeee", toolTip: "placeholder", accessibleText: "", cellActive: false, mimeData: {} }); 242 } 243 244 onEntered: { 245 onDragOverPaletteFinished(); 246 247 // first check if controller allows dropping this item here 248 const mimeData = Utils.dropEventMimeData(drag); 249 internal = (drag.source.parentModelIndex == paletteView.paletteRootIndex); 250 action = paletteView.paletteController.dropAction(mimeData, drag.proposedAction, paletteView.paletteRootIndex, internal); 251 proposedAction = drag.proposedAction; 252 253 if (action != Qt.MoveAction) 254 internal = false; 255 256 const accept = (action & drag.supportedActions) && (internal || !externalDropBlocked); 257 258 if (accept) 259 drag.accept(action); 260 else 261 drag.accepted = false; 262 263 // If event is accepted, process the drag in a usual way 264 if (drag.accepted) 265 onDrag(drag); 266 } 267 268 onPositionChanged: onDrag(drag) 269 270 function onDragOverPaletteFinished() { 271 if (placeholder.active) { 272 placeholder.removePlaceholder(); 273 paletteView.state = "default"; 274 } 275 if (drag.source && drag.source.parentModelIndex == paletteView.paletteRootIndex) 276 drag.source.internalDrag = false; 277 } 278 279 onExited: onDragOverPaletteFinished(); 280 281 onDropped: { 282 if (!action) { 283 onDragOverPaletteFinished(); 284 return; 285 } 286 287 const destIndex = placeholder.active ? placeholder.index : paletteView.paletteModel.rowCount(paletteView.paletteRootIndex); 288 onDragOverPaletteFinished(); 289 290 // Moving cells here causes Drag.onDragFinished be not called properly. 291 // Therefore record the necessary information to move cells later. 292 const data = { 293 action: action, 294 srcParentModelIndex: drag.source.parentModelIndex, 295 srcRowIndex: drag.source.rowIndex, 296 paletteView: paletteView, 297 destIndex: destIndex, 298 mimeData: Utils.dropEventMimeData(drop) 299 }; 300 301 if (typeof data.srcParentModelIndex !== "undefined") 302 drag.source.dropData = data; 303 else 304 data.paletteView.insertCell(data.destIndex, data.mimeData, data.action); 305 306 drop.accept(action); 307 } 308 } 309 } 310 311 Text { 312 anchors { 313 top: parent.top 314 bottom: parent.bottom 315 left: parent.left; leftMargin: 8 316 right: moreButton.left 317 } 318 visible: parent.empty 319 font: globalStyle.font 320 text: paletteController && paletteController.canDropElements 321 ? qsTr("Drag and drop any element here\n(Use %1+Shift to add custom element from the score)").arg(Qt.platform.os === "osx" ? "Cmd" : "Ctrl") 322 : qsTr("No elements") 323 verticalAlignment: Text.AlignVCenter 324 color: "grey" 325 wrapMode: Text.WordWrap 326 elide: Text.ElideRight 327 } 328 329 add: Transition { 330 id: addTransition 331 enabled: paletteView.enableAnimations 332 readonly property bool unresolvedItem: ViewTransition.item && ViewTransition.item.DelegateModel.isUnresolved; 333 ParallelAnimation { 334 NumberAnimation { property: "scale"; from: 0; to: 1; duration: addTransition.unresolvedItem ? 0 : 150; easing.type: Easing.InOutQuad } 335 NumberAnimation { property: "opacity"; from: 0; to: 1; duration: addTransition.unresolvedItem ? 0 : 250 } 336 } 337 } 338 339 remove: Transition { 340 id: removeTransition 341 enabled: paletteView.enableAnimations && !paletteDropArea.containsDrag 342 readonly property bool unresolvedItem: ViewTransition.item && ViewTransition.item.DelegateModel.isUnresolved; 343 ParallelAnimation { 344 NumberAnimation { property: "scale"; to: 0; duration: removeTransition.unresolvedItem ? 0 : 150; easing.type: Easing.InOutQuad } 345 NumberAnimation { property: "opacity"; to: 0; duration: removeTransition.unresolvedItem ? 0 : 250 } 346 } 347 } 348 349 displaced: Transition { 350 enabled: paletteView.enableAnimations 351 NumberAnimation { properties: "x,y"; duration: 150 } 352 } 353 354 function isSelected(modelIndex) { 355 if (!selectionModel) 356 return false; 357 return selectionModel.isSelected(modelIndex); 358 } 359 360 function moveCell(srcRow, destRow) { 361 return paletteController.move( 362 paletteRootIndex, srcRow, 363 paletteRootIndex, destRow 364 ); 365 } 366 367 function insertCell(row, mimeData, action) { 368 return paletteController.insert(paletteRootIndex, row, mimeData, action); 369 } 370 371 function drop(row, mimeData, supportedActions) { 372 // TODO 373 } 374 375 function removeCell(row) { 376 return paletteController.remove(model.modelIndex(row)); 377 } 378 379 function removeSelectedCells() { 380 Utils.removeSelectedItems(paletteController, selectionModel, paletteRootIndex); 381 } 382 383 function focusNextItem(flags) { 384 if (flags === undefined) 385 flags = ItemSelectionModel.ClearAndSelect; 386 387 if (currentIndex == count - 1) { 388 if (moreButton.visible) 389 moreButton.forceActiveFocus(); 390 else 391 paletteTree.focusNextItem(false); 392 } else { 393 currentIndex++; // next grid item 394 } 395 } 396 397 function focusPreviousItem(flags) { 398 if (flags === undefined) 399 flags = ItemSelectionModel.ClearAndSelect; 400 401 if (currentIndex == 0) 402 paletteTree.currentItem.forceActiveFocus(); 403 else 404 currentIndex--; // previous grid item 405 } 406 407 function focusFirstItem() { 408 if (count == 0 && moreButton.visible) { 409 moreButton.forceActiveFocus(); 410 } else { 411 currentIndex = 0; 412 currentItem.forceActiveFocus(); 413 } 414 } 415 416 function focusLastItem() { 417 if (moreButton.visible) { 418 moreButton.forceActiveFocus(); 419 } else { 420 currentIndex = count - 1; 421 currentItem.forceActiveFocus(); 422 } 423 } 424 425 function focusNextMatchingItem(str, startIndex) { 426 const modelIndex = paletteModel.index(startIndex, 0, paletteRootIndex); 427 const matchedIndexList = paletteModel.match(modelIndex, Qt.ToolTipRole, str); 428 if (matchedIndexList.length) { 429 currentIndex = matchedIndexList[0].row; 430 currentItem.forceActiveFocus(); 431 return true; 432 } 433 return false; 434 } 435 436 function typeAheadFind(chr) { 437 if (paletteTree.typeAheadStr.length) { 438 // continue search on current item 439 const sameChr = chr === paletteTree.typeAheadStr; 440 paletteTree.typeAheadStr += chr; 441 const found = focusNextMatchingItem(paletteTree.typeAheadStr, currentIndex); 442 if (found || !sameChr) 443 return; 444 } 445 // start new search on next item 446 paletteTree.typeAheadStr = chr; 447 const nextIndex = (currentIndex === count - 1) ? 0 : currentIndex + 1; 448 focusNextMatchingItem(chr, nextIndex); 449 } 450 451 function updateSelection(itemPressed) { 452 if (itemPressed === undefined) 453 itemPressed = false; // reason function was called 454 455 const modifiers = mscore.keyboardModifiers(); 456 const shiftHeld = modifiers & Qt.ShiftModifier; 457 const ctrlHeld = modifiers & Qt.ControlModifier; 458 const herePreviously = selectionModel.currentIndex.parent === paletteRootIndex; 459 460 if (!ctrlHeld || !herePreviously) 461 selectionModel.clearSelection(); 462 463 if (shiftHeld && herePreviously) 464 selectionModel.selectRange(currentItem.modelIndex); 465 else if (ctrlHeld) 466 selectionModel.setCurrentIndex(currentItem.modelIndex, itemPressed ? ItemSelectionModel.Toggle : ItemSelectionModel.NoUpdate); 467 else 468 selectionModel.setCurrentIndex(currentItem.modelIndex, ItemSelectionModel.Select); 469 } 470 471 Keys.onShortcutOverride: { 472 // Intercept all keys that we want to use with Keys.onPressed 473 // in case they are assigned as shortcuts in Preferences. 474 event.accepted = true; // intercept everything 475 switch (event.key) { 476 case Qt.Key_Up: 477 case Qt.Key_Down: 478 case Qt.Key_Left: 479 case Qt.Key_Right: 480 case Qt.Key_Backspace: 481 case Qt.Key_Delete: 482 return; 483 } 484 event.accepted = false; // allow key to function as shortcut (don't intercept) 485 } 486 487 Keys.onPressed: { 488 // NOTE: All keys must be intercepted with Keys.onShortcutOverride. 489 switch (event.key) { 490 case Qt.Key_Up: 491 focusPreviousItem(); 492 break; 493 case Qt.Key_Down: 494 focusNextItem(); 495 break; 496 case Qt.Key_Left: 497 paletteTree.currentItem.forceActiveFocus(); 498 break; 499 case Qt.Key_Right: 500 if (moreButton.visible) 501 moreButton.forceActiveFocus(); 502 break; 503 case Qt.Key_Backspace: 504 case Qt.Key_Delete: 505 removeSelectedCells(); 506 break; 507 default: 508 return; // don't accept event 509 } 510 event.accepted = true; 511 } 512 513 model: DelegateModel { 514 id: paletteCellDelegateModel 515// model: paletteView.visible ? paletteView.paletteModel : null // TODO: use this optimization? TODO: apply it manually where appropriate (Custom palette breaks) 516 model: paletteView.paletteModel 517 rootIndex: paletteView.paletteRootIndex 518 519 delegate: ItemDelegate { 520 id: paletteCell 521 property int rowIndex: index 522 property var modelIndex: paletteView.model.modelIndex(index) 523 property var parentModelIndex: paletteView.paletteRootIndex 524 525 onActiveFocusChanged: { 526 if (activeFocus) { 527 paletteTree.currentTreeItem = this; 528 paletteView.updateSelection(false); 529 } 530 } 531 532 opacity: enabled ? 1.0 : 0.3 533 534 readonly property bool dragged: Drag.active && !dragDropReorderTimer.running 535 property bool paletteDrag: false 536 property bool internalDrag: false 537 property bool dragCopy: false 538 539 property bool selected: (paletteView.selectionModel && paletteView.selectionModel.hasSelection) ? paletteView.isSelected(modelIndex) : false // hasSelection is to trigger property bindings if selection changes, see https://doc.qt.io/qt-5/qml-qtqml-models-itemselectionmodel.html#hasSelection-prop 540 541 highlighted: visualFocus || hovered || !!model.cellActive 542 543 width: paletteView.cellWidth 544 height: paletteView.cellHeight 545 546 activeFocusOnTab: this === paletteTree.currentTreeItem 547 548 contentItem: QmlIconView { 549 id: icon 550 visible: !parent.paletteDrag || parent.dragCopy 551 anchors.fill: parent 552 icon: model.decoration 553 selected: false // TODO: remove properties? 554 active: false // TODO: remove properties? 555 } 556 557 background: Rectangle { 558 color: "transparent" 559 border.color: paletteCell.activeFocus ? "lightblue" : "transparent" // show current item 560 border.width: 2 561 width: ((paletteCell.rowIndex + 1) % paletteView.ncolumns) ? paletteView.cellWidth : paletteView.lastColumnCellWidth 562 563 Rectangle { 564 id: cellBackground 565 anchors.fill: parent 566 color: globalStyle.voice1Color 567 opacity: 0.0 568 } 569 } 570 571 onStateChanged: { 572 console.debug("STATE CHANGED " + state) 573 } 574 575 states: [ 576 // Note: if "when" is true for multiple states then 577 // the first state listed here takes precendence. 578 579 State { 580 name: "PRESSED" 581 when: leftClickArea.pressed 582 583 PropertyChanges { target: cellBackground; opacity: 0.75 } 584 }, 585 586 State { 587 name: "SELECTED" 588 when: selected 589 590 PropertyChanges { target: cellBackground; opacity: 0.5 } 591 }, 592 593 State { 594 name: "HOVERED" 595 when: highlighted 596 597 PropertyChanges { target: cellBackground; opacity: 0.2 } 598 } 599 ] 600 601 readonly property var toolTip: model.toolTip 602 603 onHoveredChanged: { 604 if (hovered) { 605 mscore.tooltip.item = paletteCell; 606 mscore.tooltip.text = paletteCell.toolTip ? paletteCell.toolTip : ""; 607 } else if (mscore.tooltip.item == paletteCell) 608 mscore.tooltip.item = null; 609 } 610 611 text: model.accessibleText; // Accessible.name is ignored for some reason 612 Accessible.selectable: true; 613 Accessible.selected: selected; 614 615 Keys.onShortcutOverride: { 616 // Intercept all keys that we want to use with Keys.onPressed 617 // in case they are assigned as shortcuts in Preferences. 618 event.accepted = true; // intercept everything 619 switch (event.key) { 620 case Qt.Key_Space: 621 case Qt.Key_Enter: 622 case Qt.Key_Return: 623 case Qt.Key_Menu: 624 case Qt.Key_Asterisk: 625 return; 626 } 627 if (event.key === Qt.Key_F10 && event.modifiers & Qt.ShiftModifier) 628 return; 629 if (event.text.match(/[^\x00-\x20\x7F]+$/) !== null) 630 return; 631 event.accepted = false; // allow key to function as shortcut (don't intercept) 632 } 633 634 Keys.onPressed: { 635 // NOTE: All keys must be intercepted with Keys.onShortcutOverride. 636 const shiftHeld = event.modifiers & Qt.ShiftModifier; 637 const ctrlHeld = event.modifiers & Qt.ControlModifier; 638 switch (event.key) { 639 case Qt.Key_Space: 640 if (paletteTree.typeAheadStr.length) 641 paletteView.typeAheadFind(' '); 642 else 643 paletteView.updateSelection(true); 644 break; 645 case Qt.Key_Enter: 646 case Qt.Key_Return: 647 paletteView.selectionModel.setCurrentIndex(modelIndex, ItemSelectionModel.ClearAndSelect); 648 paletteView.paletteController.applyPaletteElement(modelIndex, mscore.keyboardModifiers()); 649 break; 650 case Qt.Key_F10: 651 if (!shiftHeld) 652 return; 653 // fallthrough 654 case Qt.Key_Menu: 655 showCellMenu(); 656 break; 657 case Qt.Key_Asterisk: 658 if (paletteTree.typeAheadStr.length) 659 paletteView.typeAheadFind('*'); 660 else if (!paletteTree.expandCollapseAll(null)) 661 paletteTree.currentItem.forceActiveFocus(); 662 break; 663 default: 664 if (event.text.match(/[^\x00-\x20\x7F]+$/) !== null) { 665 // Pressed non-control character(s) (e.g. "D") so go 666 // to matching item (e.g. "D Major" in keysig palette) 667 paletteView.typeAheadFind(event.text); 668 } 669 else { 670 return; // don't accept event 671 } 672 } 673 event.accepted = true; 674 } 675 676 MouseArea { 677 id: leftClickArea 678 anchors.fill: parent 679 drag.target: this 680 681 onPressed: { 682 paletteView.currentIndex = paletteCell.rowIndex; 683 paletteCell.forceActiveFocus(); 684 paletteView.updateSelection(true); 685 paletteCell.beginDrag(); 686 } 687 688 onClicked: { 689 if (paletteView.paletteController.applyPaletteElement(paletteCell.modelIndex, mscore.keyboardModifiers())) 690 paletteView.selectionModel.setCurrentIndex(paletteCell.modelIndex, ItemSelectionModel.Current); 691 } 692 693 onDoubleClicked: { 694 const index = paletteCell.modelIndex; 695 paletteView.selectionModel.setCurrentIndex(index, ItemSelectionModel.Current); 696 paletteView.paletteController.applyPaletteElement(index, mouse.modifiers); 697 } 698 } 699 700 MouseArea { 701 id: rightClickArea 702 anchors.fill: parent 703 acceptedButtons: Qt.RightButton 704 705 onClicked: showCellMenu(true) 706 } 707 708 Drag.active: leftClickArea.drag.active 709 Drag.dragType: Drag.Automatic 710 Drag.supportedActions: Qt.CopyAction | (model.editable ? Qt.MoveAction : 0) 711 Drag.mimeData: Drag.active ? mimeData : {} 712 713 onInternalDragChanged: { 714 if (internalDrag && dragDropReorderTimer.running) 715 return; 716 DelegateModel.inItems = !internalDrag; 717 } 718 onDraggedChanged: DelegateModel.inItems = !internalDrag; 719 720 property var dropData: null 721 722 Drag.onDragStarted: { 723 paletteView.state = "drag"; 724 DelegateModel.inPersistedItems = true; 725 } 726 727 Drag.onDragFinished: { 728 paletteView.state = "default"; 729 paletteDrag = false; 730 internalDrag = false; 731 DelegateModel.inPersistedItems = false; 732 733 if (dropData) { 734 var data = dropData; 735 if (data.action == Qt.MoveAction && data.srcParentModelIndex == data.paletteView.paletteRootIndex) 736 data.paletteView.moveCell(data.srcRowIndex, data.destIndex); 737 else 738 data.paletteView.insertCell(data.destIndex, data.mimeData, data.action); 739 740 dropData = null; 741 } 742 } 743// Drag.hotSpot: Qt.point(64, 0) // TODO 744 745 function beginDrag() { 746 icon.grabToImage(function(result) { 747 Drag.imageSource = result.url 748 dragDropReorderTimer.restart(); 749 }) 750 } 751 752 function showCellMenu(useCursorPos) { 753 if (useCursorPos === undefined) 754 useCursorPos = false; 755 contextMenu.modelIndex = modelIndex; 756 contextMenu.canEdit = paletteView.paletteController.canEdit(paletteView.paletteRootIndex); 757 if (useCursorPos) 758 contextMenu.popup(); 759 else { 760 contextMenu.x = x + width; 761 contextMenu.y = y; 762 contextMenu.open(); 763 } 764 } 765 766 Connections { 767 // force not hiding palette cell if it is being dragged to a score 768 enabled: paletteCell.paletteDrag 769 target: mscore 770 onElementDraggedToScoreView: paletteCell.paletteDrag = false 771 } 772 } // end ItemDelegate 773 } // end DelegateModel 774 775 Menu { 776 id: contextMenu 777 property var modelIndex: null 778 property bool canEdit: true 779 780 MenuItem { 781 enabled: contextMenu.canEdit 782 text: qsTr("Delete") 783 onTriggered: paletteView.paletteController.remove(contextMenu.modelIndex) 784 } 785 MenuItem { 786 enabled: contextMenu.canEdit 787 text: qsTr("Properties…") 788 onTriggered: paletteView.paletteController.editCellProperties(contextMenu.modelIndex) 789 } 790 } 791} 792