|
15 | 15 | using System.Windows.Media;
|
16 | 16 | using System.Windows.Threading;
|
17 | 17 | using Dynamo.Configuration;
|
| 18 | +using Dynamo.Controls; |
18 | 19 | using Dynamo.Core;
|
19 | 20 | using Dynamo.Engine;
|
20 | 21 | using Dynamo.Exceptions;
|
21 | 22 | using Dynamo.Graph;
|
22 | 23 | using Dynamo.Graph.Annotations;
|
23 | 24 | using Dynamo.Graph.Connectors;
|
24 | 25 | using Dynamo.Graph.Nodes;
|
| 26 | +using Dynamo.Graph.Nodes.CustomNodes; |
25 | 27 | using Dynamo.Graph.Workspaces;
|
26 | 28 | using Dynamo.Interfaces;
|
27 | 29 | using Dynamo.Logging;
|
@@ -1394,6 +1396,148 @@ private void Paste(object parameter)
|
1394 | 1396 | RaiseCanExecuteUndoRedo();
|
1395 | 1397 | }
|
1396 | 1398 |
|
| 1399 | + internal bool CanUpdatePythonNodeEngine(object parameter) |
| 1400 | + { |
| 1401 | + if (DynamoSelection.Instance.Selection.Count > 0 && SelectionHasPythonNodes()) |
| 1402 | + { |
| 1403 | + return true; |
| 1404 | + } |
| 1405 | + return false; |
| 1406 | + } |
| 1407 | + private bool SelectionHasPythonNodes() |
| 1408 | + { |
| 1409 | + if (GetSelectedPythonNodes().Any()) |
| 1410 | + { |
| 1411 | + return true; |
| 1412 | + } |
| 1413 | + return false; |
| 1414 | + } |
| 1415 | + /// <summary> |
| 1416 | + /// Updates the engine for the Python nodes, |
| 1417 | + /// if the nodes belong to another workspace (like custom nodes), they will be opened silently. |
| 1418 | + /// </summary> |
| 1419 | + /// <param name="pythonNode"></param> |
| 1420 | + /// <param name="engine"></param> |
| 1421 | + internal void UpdatePythonNodeEngine(PythonNodeBase pythonNode, string engine) |
| 1422 | + { |
| 1423 | + try |
| 1424 | + { |
| 1425 | + var workspaceGUID = Guid.Empty; |
| 1426 | + var cnWorkspace = GetCustomNodeWorkspace(pythonNode); |
| 1427 | + if (cnWorkspace != null) |
| 1428 | + { |
| 1429 | + workspaceGUID = cnWorkspace.Guid; |
| 1430 | + FocusCustomNodeWorkspace(cnWorkspace.CustomNodeId, true); |
| 1431 | + } |
| 1432 | + this.ExecuteCommand( |
| 1433 | + new DynamoModel.UpdateModelValueCommand( |
| 1434 | + workspaceGUID, pythonNode.GUID, nameof(pythonNode.EngineName), engine)); |
| 1435 | + pythonNode.OnNodeModified(); |
| 1436 | + } |
| 1437 | + catch(Exception ex) |
| 1438 | + { |
| 1439 | + Model.Logger.Log("Failed to update Python node engine: " + ex.Message, LogLevel.Console); |
| 1440 | + } |
| 1441 | + |
| 1442 | + } |
| 1443 | + internal void UpdateAllPythonEngine(object param) |
| 1444 | + { |
| 1445 | + var pNodes = GetSelectedPythonNodes(Model.CurrentWorkspace.Nodes); |
| 1446 | + if (pNodes.Count == 0) return; |
| 1447 | + var result = MessageBoxService.Show( |
| 1448 | + Owner, |
| 1449 | + string.Format(Resources.UpdateAllPythonEngineWarning, pNodes.Count, param.ToString()), |
| 1450 | + Resources.UpdateAllPythonEngineWarningTitle, |
| 1451 | + MessageBoxButton.YesNo, |
| 1452 | + MessageBoxImage.Exclamation); |
| 1453 | + if (result == MessageBoxResult.Yes) |
| 1454 | + { |
| 1455 | + pNodes.ForEach(x => UpdatePythonNodeEngine(x, param.ToString())); |
| 1456 | + } |
| 1457 | + } |
| 1458 | + internal bool CanUpdateAllPythonEngine(object param) |
| 1459 | + { |
| 1460 | + return true; |
| 1461 | + } |
| 1462 | + |
| 1463 | + /// <summary> |
| 1464 | + /// Adds the python engine to the menu items and subscribes to their click event for updating the engine. |
| 1465 | + /// </summary> |
| 1466 | + /// <param name="pythonNodeModel">List of python nodes</param> |
| 1467 | + /// <param name="pythonEngineVersionMenu">context menu item to which the engines will be added to</param> |
| 1468 | + /// <param name="updateEngineDelegate">Update event handler, to trigger engine update for the node</param> |
| 1469 | + /// <param name="engineName">Python engine to be added</param> |
| 1470 | + /// <param name="isBinding">Should be set to true, if you require to bind the passed |
| 1471 | + /// NodeModel engine value with the menu item, works only when a single node is passed in the list.</param> |
| 1472 | + internal void AddPythonEngineToMenuItems(List<PythonNodeBase> pythonNodeModel, |
| 1473 | + MenuItem pythonEngineVersionMenu, |
| 1474 | + RoutedEventHandler updateEngineDelegate, |
| 1475 | + string engineName, bool isBinding = false) |
| 1476 | + { |
| 1477 | + //if all nodes in the selection are set to a specific engine, then that engine will be checked in the list. |
| 1478 | + bool hasCommonEngine = pythonNodeModel.All(x => x.EngineName == engineName); |
| 1479 | + var currentItem = pythonEngineVersionMenu.Items.Cast<MenuItem>().FirstOrDefault(x => x.Header as string == engineName); |
| 1480 | + if (currentItem != null) |
| 1481 | + { |
| 1482 | + if (pythonNodeModel.Count == 1) return; |
| 1483 | + currentItem.IsChecked = hasCommonEngine; |
| 1484 | + return; |
| 1485 | + } |
| 1486 | + MenuItem pythonEngineItem = null; |
| 1487 | + //if single node, then checked property is bound to the engine value, as python node context menu is not recreated |
| 1488 | + if (pythonNodeModel.Count == 1 && isBinding) |
| 1489 | + { |
| 1490 | + var pythonNode = pythonNodeModel.FirstOrDefault(); ; |
| 1491 | + pythonEngineItem = new MenuItem { Header = engineName, IsCheckable = false }; |
| 1492 | + pythonEngineItem.SetBinding(MenuItem.IsCheckedProperty, new System.Windows.Data.Binding(nameof(pythonNode.EngineName)) |
| 1493 | + { |
| 1494 | + Source = pythonNode, |
| 1495 | + Converter = new CompareToParameterConverter(), |
| 1496 | + ConverterParameter = engineName |
| 1497 | + }); |
| 1498 | + } |
| 1499 | + else |
| 1500 | + { |
| 1501 | + //when updating multiple nodes checked value is not bound to any specific node, |
| 1502 | + //rather takes into account all the selected nodes |
| 1503 | + pythonEngineItem = new MenuItem { Header = engineName, IsCheckable = true }; |
| 1504 | + pythonEngineItem.IsChecked = hasCommonEngine; |
| 1505 | + } |
| 1506 | + pythonEngineItem.Click += updateEngineDelegate; |
| 1507 | + pythonEngineVersionMenu.Items.Add(pythonEngineItem); |
| 1508 | + } |
| 1509 | + /// <summary> |
| 1510 | + /// Gets the Python nodes from the provided list, including python nodes inside custom nodes as well. |
| 1511 | + /// If no list is provided then the current selection will be considered. |
| 1512 | + /// </summary> |
| 1513 | + /// <returns></returns> |
| 1514 | + internal List<PythonNodeBase> GetSelectedPythonNodes(IEnumerable<NodeModel> nodes = null) |
| 1515 | + { |
| 1516 | + if (nodes == null) |
| 1517 | + { |
| 1518 | + nodes = DynamoSelection.Instance.Selection.OfType<NodeModel>(); |
| 1519 | + } |
| 1520 | + var selectedPythonNodes = nodes.OfType<PythonNodeBase>().ToList(); |
| 1521 | + var customNodes = nodes.Where(x => x.IsCustomFunction).ToList(); |
| 1522 | + if (customNodes.Count > 0) |
| 1523 | + { |
| 1524 | + foreach (var cNode in customNodes) |
| 1525 | + { |
| 1526 | + var customNodeFunction = cNode as Function; |
| 1527 | + var pythonNodesInCN = customNodeFunction?.Definition.FunctionBody.OfType<PythonNodeBase>().ToList(); |
| 1528 | + if (pythonNodesInCN.Count > 0) |
| 1529 | + { |
| 1530 | + selectedPythonNodes.AddRange(pythonNodesInCN); |
| 1531 | + } |
| 1532 | + } |
| 1533 | + } |
| 1534 | + return selectedPythonNodes; |
| 1535 | + } |
| 1536 | + private CustomNodeWorkspaceModel GetCustomNodeWorkspace(NodeModel node) |
| 1537 | + { |
| 1538 | + var wg = model.CustomNodeManager.LoadedWorkspaces.Where(x => x.Nodes.Contains(node)).FirstOrDefault(); |
| 1539 | + return wg ?? null; |
| 1540 | + } |
1397 | 1541 | /// <summary>
|
1398 | 1542 | /// After command framework is implemented, this method should now be only
|
1399 | 1543 | /// called from a menu item (i.e. Ctrl + W). It should not be used as a way
|
@@ -2567,17 +2711,18 @@ internal bool CanShowPackageManager(object parameters)
|
2567 | 2711 | }
|
2568 | 2712 |
|
2569 | 2713 | /// <summary>
|
2570 |
| - /// Change the currently visible workspace to a custom node's workspace |
| 2714 | + /// Change the currently visible workspace to a custom node's workspace, unless the silent flag is set to true. |
2571 | 2715 | /// </summary>
|
2572 | 2716 | /// <param name="symbol">The function definition for the custom node workspace to be viewed</param>
|
2573 |
| - internal void FocusCustomNodeWorkspace(Guid symbol) |
| 2717 | + /// <param name="silent">When true, the focus will not switch to the workspace, but it will be opened silently.</param> |
| 2718 | + internal void FocusCustomNodeWorkspace(Guid symbol, bool silent = false) |
2574 | 2719 | {
|
2575 | 2720 | if (symbol == null)
|
2576 | 2721 | {
|
2577 | 2722 | throw new Exception(Resources.MessageNodeWithNullFunction);
|
2578 | 2723 | }
|
2579 |
| - |
2580 |
| - if (model.OpenCustomNodeWorkspace(symbol)) |
| 2724 | + var res = silent ? model.OpenCustomNodeWorkspaceSilent(symbol) : model.OpenCustomNodeWorkspace(symbol); |
| 2725 | + if (res) |
2581 | 2726 | {
|
2582 | 2727 | //set the zoom and offsets events
|
2583 | 2728 | CurrentSpace.OnCurrentOffsetChanged(this, new PointEventArgs(new Point2D(CurrentSpace.X, CurrentSpace.Y)));
|
|
0 commit comments