"feat:Enable-hover-highlight-and-refactor"
This commit is contained in:
@@ -13,9 +13,9 @@ namespace AGVMapEditor.Forms
|
||||
/// </summary>
|
||||
public partial class ImageEditorForm : Form
|
||||
{
|
||||
private MapNode _targetNode;
|
||||
private MapImage _targetNode;
|
||||
|
||||
public ImageEditorForm(MapNode imageNode = null)
|
||||
public ImageEditorForm(MapImage imageNode = null)
|
||||
{
|
||||
InitializeComponent();
|
||||
_targetNode = imageNode;
|
||||
@@ -25,7 +25,7 @@ namespace AGVMapEditor.Forms
|
||||
{
|
||||
LoadImageFromNode(imageNode);
|
||||
}
|
||||
|
||||
|
||||
this.KeyPreview = true;
|
||||
this.KeyDown += (s1, e1) => {
|
||||
if (e1.KeyCode == Keys.Escape) this.Close();
|
||||
@@ -38,7 +38,7 @@ namespace AGVMapEditor.Forms
|
||||
imageCanvas.BrushSize = trackBrush.Value;
|
||||
imageCanvas.BrushModeEnabled = chkBrushMode.Checked;
|
||||
imageCanvas.BackColor = Color.FromArgb(32,32,32);
|
||||
|
||||
|
||||
// 이벤트 연결
|
||||
chkBrushMode.CheckedChanged += (s, e) => imageCanvas.BrushModeEnabled = chkBrushMode.Checked;
|
||||
}
|
||||
@@ -48,7 +48,7 @@ namespace AGVMapEditor.Forms
|
||||
imageCanvas.BrushSize = trackBrush.Value;
|
||||
}
|
||||
|
||||
private void LoadImageFromNode(MapNode node)
|
||||
private void LoadImageFromNode(MapImage node)
|
||||
{
|
||||
if (node.LoadedImage != null)
|
||||
{
|
||||
@@ -66,7 +66,7 @@ namespace AGVMapEditor.Forms
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void LoadImageFromFile(string filePath)
|
||||
{
|
||||
try
|
||||
@@ -160,7 +160,7 @@ namespace AGVMapEditor.Forms
|
||||
return;
|
||||
}
|
||||
|
||||
if (_targetNode != null && _targetNode.Type == NodeType.Image)
|
||||
if (_targetNode != null)
|
||||
{
|
||||
// 표시 크기로 리사이즈된 이미지 가져오기
|
||||
var finalImage = imageCanvas.GetResizedImage();
|
||||
|
||||
@@ -7,6 +7,8 @@ using System.Windows.Forms;
|
||||
using AGVMapEditor.Models;
|
||||
using AGVNavigationCore.Controls;
|
||||
using AGVNavigationCore.Models;
|
||||
using MapImage = AGVNavigationCore.Models.MapImage;
|
||||
using MapLabel = AGVNavigationCore.Models.MapLabel;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace AGVMapEditor.Forms
|
||||
@@ -18,11 +20,11 @@ namespace AGVMapEditor.Forms
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private List<MapNode> _mapNodes;
|
||||
// private List<MapNode> this._mapCanvas.Nodes;
|
||||
private UnifiedAGVCanvas _mapCanvas;
|
||||
|
||||
// 현재 선택된 노드
|
||||
private MapNode _selectedNode;
|
||||
private NodeBase _selectedNode;
|
||||
|
||||
// 파일 경로
|
||||
private string _currentMapFile = string.Empty;
|
||||
@@ -93,20 +95,18 @@ namespace AGVMapEditor.Forms
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region Initialization
|
||||
|
||||
private void InitializeData()
|
||||
{
|
||||
_mapNodes = new List<MapNode>();
|
||||
// this._mapCanvas.Nodes = new List<MapNode>();
|
||||
}
|
||||
|
||||
private void InitializeMapCanvas()
|
||||
{
|
||||
_mapCanvas = new UnifiedAGVCanvas();
|
||||
_mapCanvas.Dock = DockStyle.Fill;
|
||||
_mapCanvas.Nodes = _mapNodes;
|
||||
// RfidMappings 제거 - MapNode에 통합됨
|
||||
|
||||
// 이벤트 연결
|
||||
_mapCanvas.NodeAdded += OnNodeAdded;
|
||||
@@ -115,12 +115,14 @@ namespace AGVMapEditor.Forms
|
||||
_mapCanvas.NodeMoved += OnNodeMoved;
|
||||
_mapCanvas.NodeDeleted += OnNodeDeleted;
|
||||
_mapCanvas.ConnectionDeleted += OnConnectionDeleted;
|
||||
_mapCanvas.ImageNodeDoubleClicked += OnImageNodeDoubleClicked;
|
||||
_mapCanvas.ImageDoubleClicked += OnImageDoubleClicked;
|
||||
_mapCanvas.MapChanged += OnMapChanged;
|
||||
|
||||
// 스플리터 패널에 맵 캔버스 추가
|
||||
panel1.Controls.Add(_mapCanvas);
|
||||
|
||||
// ...
|
||||
|
||||
// 툴바 버튼 이벤트 연결
|
||||
WireToolbarButtonEvents();
|
||||
}
|
||||
@@ -160,7 +162,7 @@ namespace AGVMapEditor.Forms
|
||||
|
||||
private void MainForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
|
||||
RefreshNodeList();
|
||||
// 속성 변경 시 이벤트 연결
|
||||
_propertyGrid.PropertyValueChanged += PropertyGrid_PropertyValueChanged;
|
||||
@@ -174,7 +176,7 @@ namespace AGVMapEditor.Forms
|
||||
}
|
||||
}
|
||||
|
||||
private void OnNodeAdded(object sender, MapNode node)
|
||||
private void OnNodeAdded(object sender, NodeBase node)
|
||||
{
|
||||
_hasChanges = true;
|
||||
UpdateTitle();
|
||||
@@ -199,7 +201,7 @@ namespace AGVMapEditor.Forms
|
||||
// }
|
||||
//}
|
||||
|
||||
private void OnNodesSelected(object sender, List<MapNode> nodes)
|
||||
private void OnNodesSelected(object sender, List<NodeBase> nodes)
|
||||
{
|
||||
// 다중 선택 시 처리
|
||||
if (nodes == null || nodes.Count == 0)
|
||||
@@ -227,14 +229,14 @@ namespace AGVMapEditor.Forms
|
||||
}
|
||||
}
|
||||
|
||||
private void OnNodeMoved(object sender, MapNode node)
|
||||
private void OnNodeMoved(object sender, NodeBase node)
|
||||
{
|
||||
_hasChanges = true;
|
||||
UpdateTitle();
|
||||
RefreshNodeList();
|
||||
}
|
||||
|
||||
private void OnNodeDeleted(object sender, MapNode node)
|
||||
private void OnNodeDeleted(object sender, NodeBase node)
|
||||
{
|
||||
_hasChanges = true;
|
||||
UpdateTitle();
|
||||
@@ -258,20 +260,17 @@ namespace AGVMapEditor.Forms
|
||||
UpdateNodeProperties(); // 연결 정보 업데이트
|
||||
}
|
||||
|
||||
private void OnImageNodeDoubleClicked(object sender, MapNode node)
|
||||
private void OnImageDoubleClicked(object sender, MapImage image)
|
||||
{
|
||||
// 이미지 노드 더블클릭 시 이미지 편집창 표시
|
||||
if (node != null && node.Type == NodeType.Image)
|
||||
using (var editor = new ImageEditorForm(image))
|
||||
{
|
||||
using (var editor = new ImageEditorForm(node))
|
||||
if (editor.ShowDialog(this) == DialogResult.OK)
|
||||
{
|
||||
if (editor.ShowDialog(this) == DialogResult.OK)
|
||||
{
|
||||
_hasChanges = true;
|
||||
UpdateTitle();
|
||||
_mapCanvas.Invalidate(); // 캔버스 다시 그리기
|
||||
UpdateNodeProperties(); // 속성 업데이트
|
||||
}
|
||||
_hasChanges = true;
|
||||
UpdateTitle();
|
||||
_mapCanvas.Invalidate(); // 캔버스 다시 그리기
|
||||
UpdateNodeProperties(); // 속성 업데이트
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -401,12 +400,11 @@ namespace AGVMapEditor.Forms
|
||||
private void AddNewNode()
|
||||
{
|
||||
var nodeId = GenerateNodeId();
|
||||
var nodeName = $"노드{_mapNodes.Count + 1}";
|
||||
var position = new Point(100 + _mapNodes.Count * 50, 100 + _mapNodes.Count * 50);
|
||||
var position = new Point(100 + this._mapCanvas.Nodes.Count * 50, 100 + this._mapCanvas.Nodes.Count * 50);
|
||||
|
||||
var node = new MapNode(nodeId, nodeName, position, NodeType.Normal);
|
||||
var node = new MapNode(nodeId, position, StationType.Normal);
|
||||
|
||||
_mapNodes.Add(node);
|
||||
this._mapCanvas.Nodes.Add(node);
|
||||
_hasChanges = true;
|
||||
|
||||
RefreshNodeList();
|
||||
@@ -422,13 +420,14 @@ namespace AGVMapEditor.Forms
|
||||
return;
|
||||
}
|
||||
|
||||
var result = MessageBox.Show($"노드 '{_selectedNode.Name}'를 삭제하시겠습니까?\n연결된 RFID 매핑도 함께 삭제됩니다.",
|
||||
var rfidDisplay = (_selectedNode as MapNode)?.RfidId ?? "";
|
||||
var result = MessageBox.Show($"노드 {rfidDisplay}[{_selectedNode.Id}] 를 삭제하시겠습니까?\n연결된 RFID 매핑도 함께 삭제됩니다.",
|
||||
"삭제 확인", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
|
||||
|
||||
if (result == DialogResult.Yes)
|
||||
{
|
||||
// 노드 제거
|
||||
_mapNodes.Remove(_selectedNode);
|
||||
_mapCanvas.RemoveItem(_selectedNode);
|
||||
_selectedNode = null;
|
||||
_hasChanges = true;
|
||||
|
||||
@@ -441,15 +440,15 @@ namespace AGVMapEditor.Forms
|
||||
|
||||
private void AddConnectionToSelectedNode()
|
||||
{
|
||||
if (_selectedNode == null)
|
||||
if (!(_selectedNode is MapNode selectedMapNode))
|
||||
{
|
||||
MessageBox.Show("연결을 추가할 노드를 선택하세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
MessageBox.Show("연결을 추가할 노드(MapNode)를 선택하세요.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
// 다른 노드들 중에서 선택
|
||||
var availableNodes = _mapNodes.Where(n => n.NodeId != _selectedNode.NodeId &&
|
||||
!_selectedNode.ConnectedNodes.Contains(n.NodeId)).ToList();
|
||||
var availableNodes = this._mapCanvas.Nodes.Where(n => n.Id != selectedMapNode.Id &&
|
||||
!selectedMapNode.ConnectedNodes.Contains(n.Id)).ToList();
|
||||
|
||||
if (availableNodes.Count == 0)
|
||||
{
|
||||
@@ -458,13 +457,13 @@ namespace AGVMapEditor.Forms
|
||||
}
|
||||
|
||||
// 간단한 선택 다이얼로그 (실제로는 별도 폼을 만들어야 함)
|
||||
var nodeNames = availableNodes.Select(n => $"{n.NodeId}: {n.Name}").ToArray();
|
||||
var nodeNames = availableNodes.Select(n => $"{n.Id}: {n.RfidId}").ToArray();
|
||||
var input = Microsoft.VisualBasic.Interaction.InputBox("연결할 노드를 선택하세요:", "노드 연결", nodeNames[0]);
|
||||
|
||||
var targetNode = availableNodes.FirstOrDefault(n => input.StartsWith(n.NodeId));
|
||||
var targetNode = availableNodes.FirstOrDefault(n => input.StartsWith(n.Id));
|
||||
if (targetNode != null)
|
||||
{
|
||||
_selectedNode.AddConnection(targetNode.NodeId);
|
||||
selectedMapNode.AddConnection(targetNode.Id);
|
||||
_hasChanges = true;
|
||||
RefreshMapCanvas();
|
||||
UpdateNodeProperties();
|
||||
@@ -474,25 +473,25 @@ namespace AGVMapEditor.Forms
|
||||
|
||||
private void RemoveConnectionFromSelectedNode()
|
||||
{
|
||||
if (_selectedNode == null || _selectedNode.ConnectedNodes.Count == 0)
|
||||
if (!(_selectedNode is MapNode selectedMapNode) || selectedMapNode.ConnectedNodes.Count == 0)
|
||||
{
|
||||
MessageBox.Show("연결을 제거할 노드를 선택하거나 연결된 노드가 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
// 연결된 노드들 중에서 선택
|
||||
var connectedNodeNames = _selectedNode.ConnectedNodes.Select(connectedNodeId =>
|
||||
var connectedNodeNames = selectedMapNode.ConnectedNodes.Select(connectedNodeId =>
|
||||
{
|
||||
var node = _mapNodes.FirstOrDefault(n => n.NodeId == connectedNodeId);
|
||||
return node != null ? $"{node.NodeId}: {node.Name}" : connectedNodeId;
|
||||
var node = this._mapCanvas.Nodes.FirstOrDefault(n => n.Id == connectedNodeId);
|
||||
return node != null ? $"{node.Id}: {node.RfidId}" : connectedNodeId;
|
||||
}).ToArray();
|
||||
|
||||
var input = Microsoft.VisualBasic.Interaction.InputBox("제거할 연결을 선택하세요:", "연결 제거", connectedNodeNames[0]);
|
||||
|
||||
var targetNodeId = input.Split(':')[0];
|
||||
if (_selectedNode.ConnectedNodes.Contains(targetNodeId))
|
||||
if (selectedMapNode.ConnectedNodes.Contains(targetNodeId))
|
||||
{
|
||||
_selectedNode.RemoveConnection(targetNodeId);
|
||||
selectedMapNode.RemoveConnection(targetNodeId);
|
||||
_hasChanges = true;
|
||||
RefreshMapCanvas();
|
||||
UpdateNodeProperties();
|
||||
@@ -509,7 +508,7 @@ namespace AGVMapEditor.Forms
|
||||
{
|
||||
nodeId = $"N{counter:D3}";
|
||||
counter++;
|
||||
} while (_mapNodes.Any(n => n.NodeId == nodeId));
|
||||
} while (this._mapCanvas.Nodes.Any(n => n.Id == nodeId));
|
||||
|
||||
return nodeId;
|
||||
}
|
||||
@@ -520,7 +519,7 @@ namespace AGVMapEditor.Forms
|
||||
|
||||
private void NewMap()
|
||||
{
|
||||
_mapNodes.Clear();
|
||||
this._mapCanvas.Nodes.Clear();
|
||||
_selectedNode = null;
|
||||
_currentMapFile = string.Empty;
|
||||
_hasChanges = false;
|
||||
@@ -533,7 +532,7 @@ namespace AGVMapEditor.Forms
|
||||
{
|
||||
if (CheckSaveChanges())
|
||||
{
|
||||
_mapNodes.Clear();
|
||||
this._mapCanvas.Nodes.Clear();
|
||||
_selectedNode = null;
|
||||
_currentMapFile = string.Empty;
|
||||
_hasChanges = false;
|
||||
@@ -547,7 +546,7 @@ namespace AGVMapEditor.Forms
|
||||
{
|
||||
var openFileDialog = new OpenFileDialog
|
||||
{
|
||||
Filter = "AGV Map Files (*.agvmap)|*.agvmap|All Files (*.*)|*.*",
|
||||
Filter = "AGV Map Files (*.agvmap;*.json)|*.agvmap;*.json|All Files (*.*)|*.*",
|
||||
DefaultExt = "agvmap",
|
||||
};
|
||||
|
||||
@@ -624,10 +623,14 @@ namespace AGVMapEditor.Forms
|
||||
|
||||
if (result.Success)
|
||||
{
|
||||
_mapNodes = result.Nodes;
|
||||
this._mapCanvas.Nodes = result.Nodes;
|
||||
|
||||
// 맵 캔버스에 데이터 설정
|
||||
_mapCanvas.Nodes = _mapNodes;
|
||||
_mapCanvas.Nodes = this._mapCanvas.Nodes;
|
||||
_mapCanvas.Labels = result.Labels; // 추가
|
||||
_mapCanvas.Images = result.Images; // 추가
|
||||
_mapCanvas.Marks = result.Marks;
|
||||
_mapCanvas.Magnets = result.Magnets;
|
||||
// RfidMappings 제거됨 - MapNode에 통합
|
||||
|
||||
// 🔥 맵 설정 적용 (배경색, 그리드 표시)
|
||||
@@ -693,7 +696,7 @@ namespace AGVMapEditor.Forms
|
||||
ShowGrid = _mapCanvas.ShowGrid
|
||||
};
|
||||
|
||||
if (MapLoader.SaveMapToFile(filePath, _mapNodes, settings))
|
||||
if (MapLoader.SaveMapToFile(filePath, this._mapCanvas.Nodes, _mapCanvas.Labels, _mapCanvas.Images, _mapCanvas.Marks, _mapCanvas.Magnets, settings))
|
||||
{
|
||||
// 현재 파일 경로 업데이트
|
||||
_currentMapFile = filePath;
|
||||
@@ -718,7 +721,7 @@ namespace AGVMapEditor.Forms
|
||||
private void UpdateRfidMappings()
|
||||
{
|
||||
// RFID 자동 할당 제거 - 사용자가 직접 입력한 값 유지
|
||||
// MapLoader.AssignAutoRfidIds(_mapNodes);
|
||||
// MapLoader.AssignAutoRfidIds(this._mapCanvas.Nodes);
|
||||
}
|
||||
|
||||
private bool CheckSaveChanges()
|
||||
@@ -780,14 +783,14 @@ namespace AGVMapEditor.Forms
|
||||
private void RefreshNodeList()
|
||||
{
|
||||
listBoxNodes.DataSource = null;
|
||||
listBoxNodes.DataSource = _mapNodes;
|
||||
listBoxNodes.DataSource = this._mapCanvas.Nodes;
|
||||
listBoxNodes.DisplayMember = "DisplayText";
|
||||
listBoxNodes.ValueMember = "NodeId";
|
||||
listBoxNodes.ValueMember = "Id";
|
||||
|
||||
// 노드 목록 클릭 이벤트 연결
|
||||
listBoxNodes.SelectedIndexChanged -= ListBoxNodes_SelectedIndexChanged;
|
||||
listBoxNodes.SelectedIndexChanged += ListBoxNodes_SelectedIndexChanged;
|
||||
|
||||
|
||||
// 노드 타입별 색상 적용
|
||||
listBoxNodes.DrawMode = DrawMode.OwnerDrawFixed;
|
||||
listBoxNodes.DrawItem -= ListBoxNodes_DrawItem;
|
||||
@@ -812,14 +815,14 @@ namespace AGVMapEditor.Forms
|
||||
{
|
||||
e.DrawBackground();
|
||||
|
||||
if (e.Index >= 0 && e.Index < _mapNodes.Count)
|
||||
if (e.Index >= 0 && e.Index < this._mapCanvas.Items.Count)
|
||||
{
|
||||
var node = _mapNodes[e.Index];
|
||||
|
||||
var node = this._mapCanvas.Items[e.Index];
|
||||
|
||||
// 노드 타입에 따른 색상 설정
|
||||
Color foreColor = Color.Black;
|
||||
Color backColor = e.BackColor;
|
||||
|
||||
|
||||
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
|
||||
{
|
||||
backColor = SystemColors.Highlight;
|
||||
@@ -827,22 +830,30 @@ namespace AGVMapEditor.Forms
|
||||
}
|
||||
else
|
||||
{
|
||||
backColor = Color.White;
|
||||
switch (node.Type)
|
||||
{
|
||||
case NodeType.Normal:
|
||||
foreColor = Color.Black;
|
||||
backColor = Color.White;
|
||||
|
||||
var item = node as MapNode;
|
||||
if (item.StationType == StationType.Normal)
|
||||
foreColor = Color.DimGray;
|
||||
else if (item.StationType == StationType.Charger)
|
||||
foreColor = Color.Red;
|
||||
else
|
||||
foreColor = Color.DarkGreen;
|
||||
break;
|
||||
case NodeType.Loader:
|
||||
case NodeType.UnLoader:
|
||||
case NodeType.Clearner:
|
||||
case NodeType.Buffer:
|
||||
foreColor = Color.DarkGreen;
|
||||
backColor = Color.LightGreen;
|
||||
|
||||
case NodeType.Label:
|
||||
case NodeType.Mark:
|
||||
case NodeType.Image:
|
||||
foreColor = Color.DarkBlue;
|
||||
break;
|
||||
case NodeType.Charging:
|
||||
case NodeType.Magnet:
|
||||
foreColor = Color.DarkMagenta;
|
||||
break;
|
||||
default:
|
||||
foreColor = Color.DarkRed;
|
||||
backColor = Color.LightPink;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -854,17 +865,21 @@ namespace AGVMapEditor.Forms
|
||||
}
|
||||
|
||||
// 텍스트 그리기 (노드ID - 노드명 - RFID 순서)
|
||||
var displayText = node.NodeId;
|
||||
|
||||
if (!string.IsNullOrEmpty(node.Name))
|
||||
string displayText;
|
||||
if (node.Type == NodeType.Normal)
|
||||
{
|
||||
displayText += $" - {node.Name}";
|
||||
var item = node as MapNode;
|
||||
if (item.HasRfid())
|
||||
displayText = $"[{node.Id}-{item.RfidId}] {item.StationType}";
|
||||
else
|
||||
displayText = $"[{node.Id}] {item.StationType}";
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(node.RfidId))
|
||||
else if(node.Type == NodeType.Label)
|
||||
{
|
||||
displayText += $" - [{node.RfidId}]";
|
||||
var item = node as MapLabel;
|
||||
displayText = $"{item.Type.ToString().ToUpper()} - {item.Text}";
|
||||
}
|
||||
else displayText = $"{node.Type.ToString().ToUpper()}";
|
||||
|
||||
using (var brush = new SolidBrush(foreColor))
|
||||
{
|
||||
@@ -881,31 +896,31 @@ namespace AGVMapEditor.Forms
|
||||
var processedPairs = new HashSet<string>();
|
||||
|
||||
// 모든 노드의 연결 정보를 수집 (중복 방지)
|
||||
foreach (var fromNode in _mapNodes)
|
||||
foreach (var fromNode in this._mapCanvas.Nodes)
|
||||
{
|
||||
foreach (var toNodeId in fromNode.ConnectedNodes)
|
||||
{
|
||||
var toNode = _mapNodes.FirstOrDefault(n => n.NodeId == toNodeId);
|
||||
var toNode = this._mapCanvas.Nodes.FirstOrDefault(n => n.Id == toNodeId);
|
||||
if (toNode != null)
|
||||
{
|
||||
// 중복 체크 (단일 연결만 표시)
|
||||
string pairKey1 = $"{fromNode.NodeId}-{toNode.NodeId}";
|
||||
string pairKey2 = $"{toNode.NodeId}-{fromNode.NodeId}";
|
||||
string pairKey1 = $"{fromNode.Id}-{toNode.Id}";
|
||||
string pairKey2 = $"{toNode.Id}-{fromNode.Id}";
|
||||
|
||||
if (!processedPairs.Contains(pairKey1) && !processedPairs.Contains(pairKey2))
|
||||
{
|
||||
// 사전 순으로 정렬하여 일관성 있게 표시
|
||||
var (firstNode, secondNode) = string.Compare(fromNode.NodeId, toNode.NodeId) < 0
|
||||
var (firstNode, secondNode) = string.Compare(fromNode.Id, toNode.Id) < 0
|
||||
? (fromNode, toNode)
|
||||
: (toNode, fromNode);
|
||||
|
||||
connections.Add(new NodeConnectionInfo
|
||||
{
|
||||
FromNodeId = firstNode.NodeId,
|
||||
FromNodeName = firstNode.Name,
|
||||
FromNodeId = firstNode.Id,
|
||||
FromNodeName = "",
|
||||
FromRfidId = firstNode.RfidId,
|
||||
ToNodeId = secondNode.NodeId,
|
||||
ToNodeName = secondNode.Name,
|
||||
ToNodeId = secondNode.Id,
|
||||
ToNodeName = "",
|
||||
ToRfidId = secondNode.RfidId,
|
||||
ConnectionType = "양방향" // 모든 연결이 양방향
|
||||
});
|
||||
@@ -940,7 +955,7 @@ namespace AGVMapEditor.Forms
|
||||
_mapCanvas?.HighlightConnection(connectionInfo.FromNodeId, connectionInfo.ToNodeId);
|
||||
|
||||
// 연결된 노드들을 맵에서 하이라이트 표시 (선택적)
|
||||
var fromNode = _mapNodes.FirstOrDefault(n => n.NodeId == connectionInfo.FromNodeId);
|
||||
var fromNode = this._mapCanvas.Nodes.FirstOrDefault(n => n.Id == connectionInfo.FromNodeId);
|
||||
if (fromNode != null)
|
||||
{
|
||||
_selectedNode = fromNode;
|
||||
@@ -1002,7 +1017,7 @@ namespace AGVMapEditor.Forms
|
||||
private void UpdateImageEditButton()
|
||||
{
|
||||
// ToolStripButton으로 변경됨
|
||||
btnEditImage.Enabled = (_selectedNode != null && _selectedNode.Type == NodeType.Image);
|
||||
btnEditImage.Enabled = (_mapCanvas.SelectedImage != null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1019,9 +1034,10 @@ namespace AGVMapEditor.Forms
|
||||
/// </summary>
|
||||
private void BtnToolbarEditImage_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (_selectedNode != null && _selectedNode.Type == NodeType.Image)
|
||||
var selectedImage = _mapCanvas.SelectedImage;
|
||||
if (selectedImage != null)
|
||||
{
|
||||
using (var editor = new ImageEditorForm(_selectedNode))
|
||||
using (var editor = new ImageEditorForm(selectedImage))
|
||||
{
|
||||
if (editor.ShowDialog(this) == DialogResult.OK)
|
||||
{
|
||||
@@ -1059,7 +1075,7 @@ namespace AGVMapEditor.Forms
|
||||
if (listBoxNodes != null)
|
||||
{
|
||||
listBoxNodes.DataSource = null;
|
||||
listBoxNodes.DataSource = _mapNodes;
|
||||
listBoxNodes.DataSource = this._mapCanvas.Items;
|
||||
listBoxNodes.DisplayMember = "DisplayText";
|
||||
}
|
||||
}
|
||||
@@ -1121,13 +1137,13 @@ namespace AGVMapEditor.Forms
|
||||
|
||||
// 🔥 다중 선택 여부 확인 및 선택된 노드들 저장
|
||||
bool isMultiSelect = _propertyGrid.SelectedObjects is MapNode[]; // _propertyGrid.SelectedObject is MapNode[];
|
||||
|
||||
|
||||
//var a = _propertyGrid.SelectedObject;
|
||||
List<MapNode> selectedNodes = null;
|
||||
List<NodeBase> selectedNodes = null;
|
||||
if (isMultiSelect)
|
||||
{
|
||||
// 캔버스에서 현재 선택된 노드들 가져오기
|
||||
selectedNodes = new List<MapNode>(_mapCanvas.SelectedNodes);
|
||||
selectedNodes = new List<NodeBase>(_mapCanvas.SelectedNodes);
|
||||
System.Diagnostics.Debug.WriteLine($"[PropertyGrid] 다중 선택 노드 수: {selectedNodes.Count}");
|
||||
}
|
||||
|
||||
@@ -1144,7 +1160,7 @@ namespace AGVMapEditor.Forms
|
||||
// 🔥 다중 선택인 경우 MultiNodePropertyWrapper를 다시 생성하여 바인딩
|
||||
if (isMultiSelect && selectedNodes != null && selectedNodes.Count > 0)
|
||||
{
|
||||
// System.Diagnostics.Debug.WriteLine($"[PropertyGrid] MultiNodePropertyWrapper 재생성: {selectedNodes.Count}개");
|
||||
// System.Diagnostics.Debug.WriteLine($"[PropertyGrid] MultiNodePropertyWrapper 재생성: {selectedNodes.Count}개");
|
||||
//var multiWrapper = new MultiNodePropertyWrapper(selectedNodes);
|
||||
_propertyGrid.SelectedObjects = selectedNodes.ToArray();// multiWrapper;
|
||||
}
|
||||
@@ -1153,9 +1169,9 @@ namespace AGVMapEditor.Forms
|
||||
_propertyGrid.Refresh();
|
||||
|
||||
// 🔥 단일 선택인 경우에만 노드를 다시 선택 (다중 선택은 캔버스에서 유지)
|
||||
if (!isMultiSelect && currentSelectedNode != null)
|
||||
if (!isMultiSelect && currentSelectedNode is MapNode mapNode)
|
||||
{
|
||||
var nodeIndex = _mapNodes.IndexOf(currentSelectedNode);
|
||||
var nodeIndex = this._mapCanvas.Nodes.IndexOf(mapNode);
|
||||
if (nodeIndex >= 0)
|
||||
{
|
||||
listBoxNodes.SelectedIndex = nodeIndex;
|
||||
@@ -1170,7 +1186,7 @@ namespace AGVMapEditor.Forms
|
||||
/// <returns>중복되면 true, 아니면 false</returns>
|
||||
private bool CheckRfidDuplicate(string rfidValue)
|
||||
{
|
||||
if (string.IsNullOrEmpty(rfidValue) || _mapNodes == null)
|
||||
if (string.IsNullOrEmpty(rfidValue) || this._mapCanvas.Nodes == null)
|
||||
return false;
|
||||
|
||||
// 현재 편집 중인 노드 제외하고 중복 검사
|
||||
@@ -1192,10 +1208,10 @@ namespace AGVMapEditor.Forms
|
||||
//}
|
||||
|
||||
int duplicateCount = 0;
|
||||
foreach (var node in _mapNodes)
|
||||
foreach (var node in this._mapCanvas.Nodes)
|
||||
{
|
||||
// 현재 편집 중인 노드는 제외
|
||||
if (node.NodeId == currentNodeId)
|
||||
if (node.Id == currentNodeId)
|
||||
continue;
|
||||
|
||||
// 같은 RFID 값을 가진 노드가 있는지 확인
|
||||
@@ -1234,23 +1250,23 @@ namespace AGVMapEditor.Forms
|
||||
if (result == DialogResult.Yes)
|
||||
{
|
||||
// 단일 연결 삭제
|
||||
var fromNode = _mapNodes.FirstOrDefault(n => n.NodeId == connectionInfo.FromNodeId);
|
||||
var toNode = _mapNodes.FirstOrDefault(n => n.NodeId == connectionInfo.ToNodeId);
|
||||
var fromNode = this._mapCanvas.Nodes.FirstOrDefault(n => n.Id == connectionInfo.FromNodeId);
|
||||
var toNode = this._mapCanvas.Nodes.FirstOrDefault(n => n.Id == connectionInfo.ToNodeId);
|
||||
|
||||
if (fromNode != null && toNode != null)
|
||||
{
|
||||
// 양방향 연결 삭제 (양쪽 방향 모두 제거)
|
||||
bool removed = false;
|
||||
|
||||
if (fromNode.ConnectedNodes.Contains(toNode.NodeId))
|
||||
if (fromNode.ConnectedNodes.Contains(toNode.Id))
|
||||
{
|
||||
fromNode.RemoveConnection(toNode.NodeId);
|
||||
fromNode.RemoveConnection(toNode.Id);
|
||||
removed = true;
|
||||
}
|
||||
|
||||
if (toNode.ConnectedNodes.Contains(fromNode.NodeId))
|
||||
if (toNode.ConnectedNodes.Contains(fromNode.Id))
|
||||
{
|
||||
toNode.RemoveConnection(fromNode.NodeId);
|
||||
toNode.RemoveConnection(fromNode.Id);
|
||||
removed = true;
|
||||
}
|
||||
|
||||
@@ -1277,108 +1293,18 @@ namespace AGVMapEditor.Forms
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region Multi-Node Selection
|
||||
|
||||
private void ShowMultiNodeContextMenu(List<MapNode> nodes)
|
||||
{
|
||||
// 다중 선택 시 간단한 다이얼로그 표시
|
||||
var result = MessageBox.Show(
|
||||
$"{nodes.Count}개의 노드가 선택되었습니다.\n\n" +
|
||||
"일괄 속성 변경을 하시겠습니까?\n\n" +
|
||||
"예: 글자색 변경\n" +
|
||||
"아니오: 배경색 변경\n" +
|
||||
"취소: 닫기",
|
||||
"다중 노드 속성 변경",
|
||||
MessageBoxButtons.YesNoCancel,
|
||||
MessageBoxIcon.Question);
|
||||
|
||||
if (result == DialogResult.Yes)
|
||||
{
|
||||
BatchChangeForeColor();
|
||||
}
|
||||
else if (result == DialogResult.No)
|
||||
{
|
||||
BatchChangeBackColor();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 선택된 노드들의 글자색 일괄 변경
|
||||
/// </summary>
|
||||
public void BatchChangeForeColor()
|
||||
{
|
||||
var selectedNodes = _mapCanvas.SelectedNodes;
|
||||
if (selectedNodes == null || selectedNodes.Count == 0)
|
||||
{
|
||||
MessageBox.Show("선택된 노드가 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
using (var colorDialog = new ColorDialog())
|
||||
{
|
||||
colorDialog.Color = selectedNodes[0].ForeColor;
|
||||
if (colorDialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
foreach (var node in selectedNodes)
|
||||
{
|
||||
node.ForeColor = colorDialog.Color;
|
||||
node.ModifiedDate = DateTime.Now;
|
||||
}
|
||||
|
||||
_hasChanges = true;
|
||||
UpdateTitle();
|
||||
RefreshMapCanvas();
|
||||
MessageBox.Show($"{selectedNodes.Count}개 노드의 글자색이 변경되었습니다.", "완료", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 선택된 노드들의 배경색 일괄 변경
|
||||
/// </summary>
|
||||
public void BatchChangeBackColor()
|
||||
{
|
||||
var selectedNodes = _mapCanvas.SelectedNodes;
|
||||
if (selectedNodes == null || selectedNodes.Count == 0)
|
||||
{
|
||||
MessageBox.Show("선택된 노드가 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
using (var colorDialog = new ColorDialog())
|
||||
{
|
||||
colorDialog.Color = selectedNodes[0].DisplayColor;
|
||||
if (colorDialog.ShowDialog() == DialogResult.OK)
|
||||
{
|
||||
foreach (var node in selectedNodes)
|
||||
{
|
||||
node.DisplayColor = colorDialog.Color;
|
||||
node.ModifiedDate = DateTime.Now;
|
||||
}
|
||||
|
||||
_hasChanges = true;
|
||||
UpdateTitle();
|
||||
RefreshMapCanvas();
|
||||
MessageBox.Show($"{selectedNodes.Count}개 노드의 배경색이 변경되었습니다.", "완료", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void allTurnLeftRightCrossOnToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
//모든노드의 trun left/right/ cross 속성을 true로 변경합니다
|
||||
if (_mapNodes == null || _mapNodes.Count == 0)
|
||||
if (this._mapCanvas.Nodes == null || this._mapCanvas.Nodes.Count == 0)
|
||||
{
|
||||
MessageBox.Show("맵에 노드가 없습니다.", "알림", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
var result = MessageBox.Show(
|
||||
$"모든 노드({_mapNodes.Count}개)의 회전/교차 속성을 활성화하시겠습니까?\n\n" +
|
||||
$"모든 노드({this._mapCanvas.Nodes.Count}개)의 회전/교차 속성을 활성화하시겠습니까?\n\n" +
|
||||
"• CanTurnLeft = true\n" +
|
||||
"• CanTurnRight = true\n" +
|
||||
"• DisableCross = false",
|
||||
@@ -1388,20 +1314,20 @@ namespace AGVMapEditor.Forms
|
||||
|
||||
if (result == DialogResult.Yes)
|
||||
{
|
||||
foreach (var node in _mapNodes)
|
||||
foreach (var node in this._mapCanvas.Nodes)
|
||||
{
|
||||
node.CanTurnLeft = true;
|
||||
node.CanTurnRight = true;
|
||||
node.DisableCross =false;
|
||||
node.DisableCross = false;
|
||||
node.ModifiedDate = DateTime.Now;
|
||||
}
|
||||
|
||||
_hasChanges = true;
|
||||
UpdateTitle();
|
||||
RefreshMapCanvas();
|
||||
|
||||
|
||||
MessageBox.Show(
|
||||
$"{_mapNodes.Count}개 노드의 회전/교차 속성이 모두 활성화되었습니다.",
|
||||
$"{this._mapCanvas.Nodes.Count}개 노드의 회전/교차 속성이 모두 활성화되었습니다.",
|
||||
"완료",
|
||||
MessageBoxButtons.OK,
|
||||
MessageBoxIcon.Information);
|
||||
|
||||
Reference in New Issue
Block a user