You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1593 lines
51 KiB
C#

1 month ago
using DevExpress.XtraScheduler.Outlook.Native;
using GeoSigmaDrawLib;
using Microsoft.VisualBasic.Devices;
using System;
1 month ago
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using UCDraw;
namespace SymbolLibManager
{
/// <summary>
/// 符号管理界面
/// 这个类有两个作用:一是符号及符号组的增删改查,一是供图层/图元修饰时选择符号
/// 基础概念:
/// 最左侧一栏我们称为符号组,对应一个符号文件
/// 中间那栏我们称为符号
/// 右边那栏是符号详情和编辑
/// </summary>
public partial class FrmMarkMain : Form
{
public string LibPath { get; set; }
private const string CurrentFile = "当前文件";
private readonly List<ListViewItem> arrItemsCache = new List<ListViewItem>();
private ImageList lstSymbolImage;
public bool IsDirty { get; set; } = false;
public const string ClipboardFormatSymbol = "ClipboardFormatSymbol";
private string SelectedFile = "";
private bool OnSearchStatus = false;
private GeoSigma.UCDraw.MainView mainView;
private readonly DelayedRefreshHelper refreshHelper;
private const int delay = 100;
public GeoSigmaXY DrawerGeo { get; set; }
/// <summary>
/// 是否显示为符号选择状态
/// </summary>
public bool ShowInSelectMode { get; set; } = false;
public string SelectedSymbolName { get; set; } = null;
public FrmMarkMain(string dataPath) : this(null, dataPath)
{
}
public FrmMarkMain(GeoSigmaXY geo, string dataPath)
{
InitializeComponent();
1 month ago
tsbtnRemoveSymbol.Visible = false; // 初始默认隐藏
1 month ago
DrawerGeo = geo;
InitSymbolGroup(dataPath);
InitSymbols();
SelectFirstGroup();
refreshHelper = new DelayedRefreshHelper(delay, ShowSelectedSymbolGroup);
}
private void SelectFirstGroup()
{
if (trvSymbol.Nodes.Count > 0)
{
TreeNode firstChild = trvSymbol.Nodes[0];
if (firstChild != null && firstChild.Nodes.Count > 0)
{
TreeNode firstGrandchild = firstChild.Nodes[0];
trvSymbol.SelectedNode = firstGrandchild;
}
}
}
private void InitSymbolGroup(string dataPath)
{
if (string.IsNullOrEmpty(dataPath))
{
// 符号表默认目录
1 month ago
//var symbolFilePath = Directory.GetParent(Application.StartupPath);
var symbolFilePath = Path.GetFullPath(Path.Combine(Application.StartupPath, ".."));
LibPath = Path.Combine(symbolFilePath, "Symbol");
1 month ago
}
else
{
LibPath = dataPath;
}
string symbolData = "";
GeoSigmaXY.InitLibrary(LibPath, ref symbolData);
InitSymbolTree(symbolData);
}
private void InitSymbols()
{
lstSymbolImage = new ImageList();
lstSymbolImage.ColorDepth = ColorDepth.Depth24Bit;
lstSymbolImage.ImageSize = new Size(50, 50);
lsvSymbol.LargeImageList = lstSymbolImage;
lsvSymbol.View = View.LargeIcon;
lsvSymbol.HideSelection = false;
lsvSymbol.VirtualMode = true;
lsvSymbol.MultiSelect = false;
lsvSymbol.LabelEdit = true;
lsvSymbol.ContextMenuStrip = ctmSymbols;
1 month ago
// 允许使用鼠标框选
lsvSymbol.MultiSelect = true; // 必须设为 true
// 确保父容器允许捕获鼠标移动
lsvSymbol.FullRowSelect = true;
1 month ago
}
private void ApplicationIdle(object sender, EventArgs e)
{
try
{
1 month ago
// 获取选中项的数量
int selectedCount = lsvSymbol.SelectedIndices.Count;
bool bHasSelected = selectedCount > 0;
bool bSingleSelected = selectedCount == 1; // 是否只选中了一个
1 month ago
bool bHasClip = false;
var clipData = Clipboard.GetData(ClipboardFormatSymbol);
if (clipData is List<string> lstPxy)
{
if (lstPxy.Count > 0)
{
bHasClip = true;
}
}
1 month ago
// 粘贴、添加逻辑
1 month ago
tsbtnSymbolPaste.Enabled = (bHasClip && OnSearchStatus == false);
ctsbtnPasteSymbol.Enabled = tsbtnSymbolPaste.Enabled;
tsbtnListAdd.Enabled = !OnSearchStatus;
if (string.IsNullOrEmpty(SelectedFile))
{
tsbtnListAdd.Enabled = false;
}
1 month ago
// 复制逻辑:支持多选
1 month ago
tsbtnSymbolCopy.Enabled = bHasSelected;
ctsbtnCopySymbol.Enabled = tsbtnSymbolCopy.Enabled;
1 month ago
// 删除逻辑:支持多选
tsbtnListRemove.Enabled = bHasSelected;
1 month ago
ctsbtnDeleteSymbol.Enabled = tsbtnListRemove.Enabled;
1 month ago
// 编辑名称逻辑:通常只在选中单个符号时允许编辑
tsbtnEdit.Enabled = bSingleSelected;
ctsbtnEditName.Enabled = bSingleSelected;
// 符号库(左侧树)逻辑
1 month ago
var root = trvSymbol.Nodes[0];
bool bGroupChecked = false;
foreach (TreeNode node in root.Nodes)
{
if (node.Checked)
{
bGroupChecked = true;
break;
}
}
tsbtnDeleteFile.Enabled = bGroupChecked;
tsbtnExportFile.Enabled = bGroupChecked;
}
catch
{
1 month ago
// 静默处理异常以防 Idle 事件崩溃影响 UI 响应
1 month ago
}
}
/// <summary>
/// 初始化最左侧的符号组
/// </summary>
/// <param name="nodeData">以分号分割的字符串,表示符号组列表</param>
private void InitSymbolTree(string nodeData)
{
var root = trvSymbol.Nodes.Add("符号库");
root.ImageIndex = 1;
root.SelectedImageIndex = 1;
root.StateImageIndex = 1;
root.Nodes.AddRange(nodeData.Split(';')
.Where(itemData => !string.IsNullOrEmpty(itemData))
.Select(itemData => new TreeNode
{
Text = Path.GetFileNameWithoutExtension(itemData),
ImageIndex = 0,
Tag = itemData
})
.ToArray());
// 如果当前有选中文件,符号组中添加当前文件
if (DrawerGeo != null)
{
TreeNode currentNode = new TreeNode
{
Text = CurrentFile,
Tag = CurrentFile,
};
root.Nodes.Insert(0, currentNode);
}
root.Expand();
}
private void lsvSymbol_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
{
if (arrItemsCache == null || arrItemsCache.Count == 0)
{
e.Item = new ListViewItem();
}
e.Item = arrItemsCache[e.ItemIndex];
}
protected override void OnClosed(EventArgs e)
{
mainView.CloseFaultEditor();
base.OnClosed(e);
}
private void FrmMarkMain_Load(object sender, EventArgs e)
{
mainView = new GeoSigma.UCDraw.MainView();
//设置窗体最大化
mainView.Dock = DockStyle.Fill;
//指定当前子窗体显示的容器
mainView.Parent = spMain.Panel2;
mainView.BringToFront();
mainView.Show();
mainView.PropertyVisible = true;
mainView.LayerVisible = false;
mainView.StatisticVisble = false;
mainView.ToolstripView.Visible = false;
mainView.BeforeSaveFileEvent += MainView_SaveFile;
mainView.AutoReleaseView = false;
DrawerEnable(false);
SetUIMode();
Application.Idle += ApplicationIdle;
}
private void SetUIMode()
{
if (ShowInSelectMode)
{
spMain.Panel2Collapsed = true;
tspTree.Visible = false;
tspItems.Visible = false;
tsbtnOK.Visible = true;
tsbtnCancel.Visible = true;
}
}
private ListViewItem CreateSymbolItem(string symbolName, int imageIndex, SymbolItemData itemData)
{
ListViewItem item = new ListViewItem(symbolName);
item.Name = symbolName;
item.ImageIndex = imageIndex;
item.Tag = itemData;
return item;
}
/// <summary>
/// 获取分组的符号列表
/// </summary>
/// <param name="group">分组名称</param>
/// <returns></returns>
private List<string> GetGroupSymbolNames(string group)
{
if (group == null)
{
return new List<string>();
}
SelectedFile = group;
if (string.IsNullOrEmpty(SelectedFile))
{
return new List<string>();
}
return ListSymbols(SelectedFile);
}
/// <summary>
/// 添加符号
/// </summary>
/// <param name="symbol"></param>
private void AddSymbol(SymbolItemData symbol)
{
CreateBitMap(symbol.Group, symbol.Name, out Bitmap bmp, out IntPtr pXy);
SymbolItemData itemData = new SymbolItemData();
itemData.Group = symbol.Group;
itemData.Name = symbol.Name;
itemData.PtrXY = pXy;
ListViewItem item = CreateSymbolItem(symbol.Name, arrItemsCache.Count, itemData);
lstSymbolImage.Images.Add(bmp);
arrItemsCache.Add(item);
lsvSymbol.VirtualListSize = arrItemsCache.Count;
}
/// <summary>
/// 查找符号
/// </summary>
/// <returns></returns>
private List<SymbolItemData> FindSymbol(string text)
{
List<SymbolItemData> result = new List<SymbolItemData>();
foreach (string group in ListAllGroups())
{
List<string> symbols = ListSymbols(group);
foreach (string symbol in symbols)
{
if (!symbol.Contains(text))
{
continue;
}
var item = new SymbolItemData()
{
Name = symbol,
Group = group,
};
result.Add(item);
}
}
return result;
}
/// <summary>
/// 清除当前的符号
/// </summary>
private void ClearSymbols()
{
foreach (Image image in lstSymbolImage.Images)
{
image.Dispose();
}
arrItemsCache.Clear();
lstSymbolImage.Images.Clear();
lsvSymbol.VirtualListSize = arrItemsCache.Count;
}
/// <summary>
/// 查出所有符号组
/// </summary>
/// <returns>返回符号组列表</returns>
private List<string> ListAllGroups()
{
TreeNode rootNode = trvSymbol.Nodes[0];
return rootNode.Nodes.Cast<TreeNode>()
.Select(node => node.Tag.ToString())
.ToList();
}
private void RemoveGroup(string group)
{
GeoSigmaXY.LibraryRemoveGroup(group);
try
{
File.Delete(group);
}
catch
{
}
}
private List<string> ListSymbols(string group)
{
// 获取符号名称列表的逻辑
string strNames = string.Empty;
if (group.Equals(CurrentFile))
{
// 当前文件时
DrawerGeo?.GetDrawerSymbolNames(ref strNames);
}
else
{
GeoSigmaXY.GetLibrarySymbolNames(group, ref strNames);
}
List<string> symbols = strNames.Split(';')
.Where(name => !string.IsNullOrEmpty(name))
.OrderBy(name => name)
.ToList();
return symbols;
}
/// <summary>
/// 构建同一组的符号
/// </summary>
/// <param name="group">符号组</param>
/// <param name="symbols"></param>
private void BuildSymbols(string group, List<string> symbols)
{
foreach (string symbol in symbols)
{
SymbolItemData item = new SymbolItemData()
{
Name = symbol,
Group = group,
};
AddSymbol(item);
}
}
private void BuildSymbols(List<SymbolItemData> symbols)
{
foreach (SymbolItemData item in symbols)
{
AddSymbol(item);
}
}
/// <summary>
/// 显示左侧选中的符号组
/// </summary>
private void ShowSelectedSymbolGroup()
{
TreeNode node = trvSymbol.SelectedNode;
OnSearchStatus = false;
string group = node.Tag?.ToString();
if (group == null)
{
return;
}
ClearSymbols();
List<string> symbolNames = GetGroupSymbolNames(group);
BuildSymbols(group, symbolNames);
if (lsvSymbol.Items.Count > 0)
{
lsvSymbol.Items[0].Selected = true;
}
else
{
// 如果该组没有任何符号,要清理最右侧的显示,不然会显示上次打开的内容
ClearymbolDetials();
}
string strGroupName = Path.GetFileNameWithoutExtension(group);
statusLableGroup.Text = $"所属: {strGroupName}";
}
private void CreateBitMap(string group, string symbolName, out Bitmap bmp, out IntPtr pXy)
{
bmp = new Bitmap(50, 50);
Graphics gBmp = Graphics.FromImage(bmp);
IntPtr pBmpDC = gBmp.GetHdc();
pXy = IntPtr.Zero;
if (group.Equals(CurrentFile))
{
DrawerGeo?.DrawerDrawSymbol(symbolName, pBmpDC, 50, 50, ref pXy);
}
else
{
GeoSigmaXY.LibaryDrawSymbol(group, symbolName, pBmpDC, 50, 50, ref pXy);
}
gBmp.ReleaseHdc();
}
private void CreateBitMap(string symbolName, out Bitmap bmp, out IntPtr pXy)
{
bmp = new Bitmap(50, 50);
Graphics gBmp = Graphics.FromImage(bmp);
IntPtr pBmpDC = gBmp.GetHdc();
pXy = IntPtr.Zero;
if (SelectedFile.Equals(CurrentFile))
{
DrawerGeo?.DrawerDrawSymbol(symbolName, pBmpDC, 50, 50, ref pXy);
}
else
{
GeoSigmaXY.LibaryDrawSymbol(SelectedFile, symbolName, pBmpDC, 50, 50, ref pXy);
}
gBmp.ReleaseHdc();
}
private void trvSymbol_AfterSelect(object sender, TreeViewEventArgs e)
{
1 month ago
// 获取当前选中的节点对应的文件名/组名
SelectedFile = e.Node.Text;
// 只有当选中“当前文件”时,才显示按钮
tsbtnRemoveSymbol.Visible = (SelectedFile == CurrentFile);
// 刷新右侧列表显示
1 month ago
ShowSelectedSymbolGroup();
}
/// <summary>
/// 关闭前
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void FrmMarkMain_FormClosing(object sender, FormClosingEventArgs e)
{
if (!SelectedFile.Equals(CurrentFile) && mainView.IsDocumentModified())
{
IsDirty = true;
}
if (IsDirty)
{
DialogResult result = MessageBox.Show(
this,
"是否保存修改?",
"关闭文档",
MessageBoxButtons.YesNoCancel);
switch (result)
{
case DialogResult.Yes:
// 保存文件
MainView_SaveFile(new CancelEventArgs());
break;
case DialogResult.Cancel:
e.Cancel = true;
return;
case DialogResult.No:
break;
}
}
}
private void FrmMarkMain_FormClosed(object sender, FormClosedEventArgs e)
{
Application.Idle -= ApplicationIdle;
DrawerGeo?.ResetSymbol();
ClearSymbols();
}
private void MainView_SaveFile(CancelEventArgs e)
{
if (!SelectedFile.Equals(CurrentFile) && mainView.IsDocumentModified())
{
IsDirty = true;
}
if (!IsDirty)
{
e.Cancel = true;
return;
}
bool bSaved = GeoSigmaXY.LibarySaveAll();
if (bSaved)
{
mainView.SetDocumentModified(!bSaved);
// 重新激活左侧当前符号组,让它刷新缩略图
//TreeNode node = trvSymbol.SelectedNode;
//trvSymbol.SelectedNode = null;
//trvSymbol.SelectedNode = node;
updateCurrentSymbolIcon();
}
IsDirty = !bSaved;
if (e != null)
{
e.Cancel = true;
}
}
/// <summary>
/// 查找符号事件
/// </summary>
/// <param name="sender">查找按钮</param>
/// <param name="e">事件参数</param>
private void tsbtnFindSymbol_Click(object sender, EventArgs e)
{
string strSearchName = txtSearchName.Text.Trim();
if (string.IsNullOrEmpty(strSearchName))
{
return;
}
// 设置查询状态标识
OnSearchStatus = true;
ClearSymbols();
List<SymbolItemData> symbols = FindSymbol(strSearchName);
BuildSymbols(symbols);
}
private void txtSearchName_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Enter)
{
e.Handled = true;
SendKeys.Send("{TAB}");
tsbtnFindSymbol.PerformClick();
}
}
/// <summary>
/// 确定
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsbtnOK_Click(object sender, EventArgs e)
{
if (lsvSymbol.SelectedIndices.Count <= 0)
{
MessageBox.Show("没有选中任何符号!");
return;
}
ListViewItem itemLast = lsvSymbol.Items[lsvSymbol.SelectedIndices[0]];
SymbolItemData itemDataLast = itemLast.Tag as SymbolItemData;
string strName = itemDataLast.Name;
// 检查名称重复
ListViewItem lviFind = arrItemsCache.Find(p =>
{
return p.Text.Equals(strName,
StringComparison.CurrentCultureIgnoreCase);
});
if (lviFind != null)
{
IntPtr pxyNew = IntPtr.Zero;
DrawerGeo.DrawerCopySymbol(itemDataLast.PtrXY, strName, ref pxyNew);
}
SelectedSymbolName = strName;
DialogResult = DialogResult.OK;
Close();
}
/// <summary>
/// 取消
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsbtnCancel_Click(object sender, EventArgs e)
{
if (IsDirty)
{
DialogResult result = MessageBox.Show(this,
"符号已修改,是否放弃修改并退出符号管理界面?",
"退出",
MessageBoxButtons.YesNo);
if (result == DialogResult.No)
{
return;
}
}
DialogResult = DialogResult.Cancel;
Close();
}
/// <summary>
/// 修改符号名称
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsbtnEdit_Click(object sender, EventArgs e)
{
ListView.SelectedIndexCollection selItems = lsvSymbol.SelectedIndices;
if (selItems.Count > 0)
{
lsvSymbol.Items[selItems[0]].BeginEdit();
}
}
/// <summary>
/// 修改符号名称
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void lsvSymbol_AfterLabelEdit(object sender, LabelEditEventArgs e)
{
if (e.Label == null)
{
return;
}
string newName = e.Label.Trim();
if (string.IsNullOrEmpty(newName))
{
MessageBox.Show("此名称不能为空!");
e.CancelEdit = true;
return;
}
ListViewItem lvi = lsvSymbol.Items[e.Item];
ListViewItem lviFind = null;
if (arrItemsCache != null)
{
lviFind = arrItemsCache.FirstOrDefault(item =>
item.Text.Equals(newName, StringComparison.CurrentCultureIgnoreCase));
}
else
{
var ie = lsvSymbol.Items.GetEnumerator();
while (ie.MoveNext())
{
ListViewItem lviCur = (ListViewItem)ie.Current;
if (lviCur.Text.Equals(newName, StringComparison.CurrentCultureIgnoreCase))
{
lviFind = lviCur;
break;
}
}
}
if (lviFind != null)
{
MessageBox.Show("此名称已经存在!");
e.CancelEdit = true;
return;
}
lvi.Text = newName;
lvi.Name = newName;
IntPtr ptr = ((SymbolItemData)(lvi.Tag)).PtrXY;
if (!SelectedFile.Equals(CurrentFile))
{
IsDirty = GeoSigmaXY.SetDrawXyName(ptr, ((SymbolItemData)(lvi.Tag)).Group, newName);
((SymbolItemData)(lvi.Tag)).Name = newName;
IsDirty = true;
}
else
{
DrawerGeo.RenameSymble(((SymbolItemData)(lvi.Tag)).Name, newName);
((SymbolItemData)(lvi.Tag)).Name = newName;
}
}
/// <summary>
/// 显示符号预览图大图,最右侧那块儿
/// </summary>
/// <param name="itemData">标识数据</param>
private void ShowSymbolDetials(SymbolItemData itemData)
{
if (!SelectedFile.Equals(CurrentFile) && mainView.IsDocumentModified())
{
IsDirty = true;
}
mainView.Visible = true;
mainView.OpenXy(itemData.PtrXY);
mainView.SetBackColor(192, 192, 192);
mainView.SetSymbolView(true);
RectangleF rect = new RectangleF(-1, -1, 2, 2);
mainView.InitlizePaint(rect);
DrawerEnable(true);
}
/// <summary>
/// 清空符号预览图大图
/// </summary>
private void ClearymbolDetials()
{
if (mainView != null)
{
mainView.Visible = false;
}
}
/// <summary>
/// 选择符号
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void lsvSymbol_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
{
if (e.IsSelected == false || e.Item.Tag == null)
{
return;
}
SymbolItemData itemData = e.Item.Tag as SymbolItemData;
// 非选择模式
if (!ShowInSelectMode)
{
ShowSymbolDetials(itemData);
}
statusLableGroup.Text = $"所属: {Path.GetFileNameWithoutExtension(itemData.Group)}";
}
/// <summary>
/// updates the current symbol icon.
/// </summary>
private void updateCurrentSymbolIcon()
{
if (lsvSymbol.SelectedIndices != null && lsvSymbol.SelectedIndices.Count > 0)
{
ListView.SelectedIndexCollection indices = lsvSymbol.SelectedIndices;
int nIndex = indices[0];
ListViewItem item = arrItemsCache[nIndex];// lsvSymbol.Items[nIndex];
//lstSymbolImage.Images.Add(bmp);
//arrItemsCache.Add(item);
SymbolItemData itemData = item.Tag as SymbolItemData;
Bitmap bmp = lstSymbolImage.Images[nIndex] as Bitmap;
Graphics gBmp = Graphics.FromImage(bmp);
gBmp.Clear(Color.White);
IntPtr pBmpDC = gBmp.GetHdc();
IntPtr pXy = itemData.PtrXY;
GeoSigmaXY.XyDrawSymbol(pXy, pBmpDC, 50, 50);
gBmp.ReleaseHdc();
lstSymbolImage.Images[nIndex] = bmp;
lsvSymbol.Invalidate();
}
}
private void lsvSymbol_ItemCheck(object sender, ItemCheckEventArgs e)
{
CheckState status = e.NewValue;
}
private void lsvSymbol_ItemChecked(object sender, ItemCheckedEventArgs e)
{
bool bChecked = e.Item.Checked;
}
private string NewSymbolName()
{
for (int newSymbolIdx = 1; newSymbolIdx < int.MaxValue; newSymbolIdx++)
{
string strSymbolName = "新建符号" + newSymbolIdx;
if (!arrItemsCache.Any(p => p.Text.Equals(strSymbolName, StringComparison.CurrentCultureIgnoreCase)))
{
return strSymbolName;
}
}
return string.Empty;
}
/// <summary>
/// 添加符号
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsbtnListAdd_Click(object sender, EventArgs e)
{
string strSymbolName = NewSymbolName();
IntPtr pXy = IntPtr.Zero;
Bitmap bmp = new Bitmap(50, 50);
Graphics gBmp = Graphics.FromImage(bmp);
gBmp.Clear(Color.White);
IntPtr pBmpDC = gBmp.GetHdc();
// 当前文件时
if (SelectedFile.Equals(CurrentFile))
{
DrawerGeo.CreateDrawerSymbol(strSymbolName, ref pXy);
DrawerGeo.DrawerDrawSymbol(strSymbolName, pBmpDC, 50, 50, ref pXy);
}
else
{
GeoSigmaXY.CreateLibrarySymbol(SelectedFile, strSymbolName, ref pXy);
GeoSigmaXY.LibaryDrawSymbol(SelectedFile, strSymbolName, pBmpDC, 50, 50, ref pXy);
}
gBmp.ReleaseHdc();
SymbolItemData itemData = new SymbolItemData()
{
Group = SelectedFile,
Name = strSymbolName,
PtrXY = pXy,
};
ListViewItem item = new ListViewItem(strSymbolName);
item.Name = strSymbolName;
item.ImageIndex = lstSymbolImage.Images.Count;
item.Tag = itemData;
arrItemsCache.Add(item);
lstSymbolImage.Images.Add(bmp);
lsvSymbol.VirtualListSize = arrItemsCache.Count;
SelectSymbol(arrItemsCache.Count - 1);
IsDirty = true;
}
private void SelectSymbol(int nIndex)
{
lsvSymbol.Focus();
if (nIndex >= lsvSymbol.Items.Count)
{
return;
}
lsvSymbol.Items[nIndex].Selected = true;
if (lsvSymbol.SelectedIndices != null && lsvSymbol.SelectedIndices.Count > 0)
{
ListView.SelectedIndexCollection indices = lsvSymbol.SelectedIndices;
lsvSymbol.Items[indices[0]].Selected = false;
}
lsvSymbol.Items[nIndex].Selected = true;
lsvSymbol.EnsureVisible(nIndex);
}
/// <summary>
/// 移除符号
/// </summary>
/// <param name="index">符号下标</param>
private void RemoveSymbol(int index)
{
string strName = lsvSymbol.Items[index].Text;
SymbolItemData itemData = lsvSymbol.Items[index].Tag as SymbolItemData;
IntPtr pXy = itemData.PtrXY;
ListViewItem cachedItem = arrItemsCache.FirstOrDefault(item =>
{
SymbolItemData itemDataFind = (SymbolItemData)item.Tag;
return itemDataFind.PtrXY == pXy;
});
if (cachedItem != null)
{
arrItemsCache.Remove(cachedItem);
}
lsvSymbol.VirtualListSize = arrItemsCache.Count;
if (pXy == IntPtr.Zero)
{
return;
}
if (SelectedFile.Equals(CurrentFile))
{
// 当前文件时
DrawerGeo.RemoveDrawerSymbol(pXy);
}
else
{
IsDirty = GeoSigmaXY.RemoveLibrarySymbol(SelectedFile, pXy);
}
}
/// <summary>
/// 选中下一个符号
/// </summary>
/// <param name="index">被删除的 index 下标</param>
private void SelectNextSymbol(int index)
{
if (index == arrItemsCache.Count)
{
index = arrItemsCache.Count - 1;
}
if (index >= 0 && index < arrItemsCache.Count)
{
lsvSymbol.Items[index].Selected = true;
}
else
{
// 清空画布
DrawerEnable(false);
mainView.ClearGeo();
}
}
/// <summary>
/// 删除符号
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsbtnListRemove_Click(object sender, EventArgs e)
{
1 month ago
// 获取所有选中项的索引
ListView.SelectedIndexCollection selIndices = lsvSymbol.SelectedIndices;
if (arrItemsCache == null || selIndices.Count == 0)
{
return;
}
string msg = selIndices.Count > 1 ? $"确定要删除选中的 {selIndices.Count} 个符号吗?" : "确定要删除选中符号吗?";
if (MessageBox.Show(msg, "删除确认", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
1 month ago
{
return;
}
1 month ago
// 必须【从后往前】遍历索引进行删除,否则前面的项被移除后,后面的索引会改变
List<int> sortedIndices = selIndices.Cast<int>().OrderByDescending(i => i).ToList();
foreach (int index in sortedIndices)
{
// 调用你现有的移除逻辑
RemoveSymbol(index);
}
// 刷新选择状态
if (arrItemsCache.Count > 0)
{
// 尝试选中删除点附近的项
int nextIndex = Math.Min(sortedIndices.Last(), arrItemsCache.Count - 1);
SelectNextSymbol(nextIndex);
}
else
{
DrawerEnable(false);
mainView.ClearGeo();
}
1 month ago
1 month ago
IsDirty = true;
1 month ago
}
1 month ago
/// <summary>
/// 删除当前未使用的符号
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsbtnRemoveSymbol_Click(object sender, EventArgs e)
{
// 只有选中“当前文件”才执行
if (SelectedFile != CurrentFile || DrawerGeo == null) return;
string strNames = string.Empty;
DrawerGeo.GetDrawerCurrentSymbolNames(ref strNames);
HashSet<string> validNames = new HashSet<string>(
strNames.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries),
StringComparer.OrdinalIgnoreCase
);
//找出所有需要删除的索引
List<int> deleteIndices = new List<int>();
for (int i = 0; i < arrItemsCache.Count; i++)
{
if (!validNames.Contains(arrItemsCache[i].Text))
{
deleteIndices.Add(i);
}
}
if (deleteIndices.Count == 0) return;
string msg = $"检测到 {deleteIndices.Count} 个未使用的符号,确定要清理吗?";
if (MessageBox.Show(msg, "清理确认", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
{
return;
}
// 从后往前遍历索引,调用你现有的 RemoveSymbol 进行同步删除
// 必须倒序,否则删掉前面的,后面的索引就乱了
for (int i = deleteIndices.Count - 1; i >= 0; i--)
{
int targetIndex = deleteIndices[i];
RemoveSymbol(targetIndex);
}
IsDirty = true;
if (arrItemsCache.Count > 0)
{
// 清除旧的选择,尝试选中第一项
lsvSymbol.SelectedIndices.Clear();
SelectSymbol(0);
}
else
{
DrawerEnable(false);
mainView.ClearGeo();
}
}
1 month ago
private void DrawerEnable(bool enable)
{
mainView.ToolDefault();
mainView.ToolstripElements.Enabled = enable;
mainView.ToolstripView.Enabled = enable;
//mainView.ToolAntiAlias(false);
}
/// <summary>
/// 符号拷贝
/// </summary>
/// <param name="sender">拷贝按钮</param>
/// <param name="e">事件参数</param>
private void tsbtnSymbolCopy_Click(object sender, EventArgs e)
{
List<string> lstXyData = new List<string>();
1 month ago
// 遍历所有选中的索引
foreach (int index in lsvSymbol.SelectedIndices)
1 month ago
{
1 month ago
// 虚拟模式下,直接操作数据源 arrItemsCache 性能更好
var item = arrItemsCache[index];
1 month ago
SymbolItemData itemData = (SymbolItemData)item.Tag;
1 month ago
if (itemData?.PtrXY != IntPtr.Zero)
{
string strXyData = GeoSigmaXY.WriteXy2DFD(itemData.PtrXY);
lstXyData.Add(strXyData);
}
1 month ago
}
1 month ago
1 month ago
if (lstXyData.Count > 0)
{
Clipboard.Clear();
1 month ago
// 存入 List<string>
1 month ago
Clipboard.SetData(ClipboardFormatSymbol, lstXyData);
}
}
/// <summary>
/// 粘贴事件
/// </summary>
/// <param name="sender">粘贴按钮</param>
/// <param name="e">事件参数</param>
private void tsbtnSymbolPaste_Click(object sender, EventArgs e)
{
List<string> lstXyData = (List<string>)Clipboard.GetData(ClipboardFormatSymbol);
if (lstXyData == null)
{
return;
}
foreach (string xyData in lstXyData)
{
IntPtr pXy = GeoSigmaXY.CreateXyFromDFD(xyData);
string strName = GeoSigmaXY.GetDrawXyName(pXy);
// 检查名称重复
int nNameAppend = 0;
string strNameSource = strName;
while (true)
{
if (nNameAppend == 0)
{
strName = strNameSource;
}
else
{
strName = $"{strNameSource}{nNameAppend}";
}
ListViewItem lviFind = arrItemsCache.Find(p =>
{
return p.Text.Equals(strName,
StringComparison.CurrentCultureIgnoreCase);
});
if (lviFind != null)
{
nNameAppend++;
continue;
}
break;
}
Bitmap bmp = new Bitmap(50, 50);
Graphics gBmp = Graphics.FromImage(bmp);
IntPtr pBmpDC = gBmp.GetHdc();
IntPtr pxyNew = IntPtr.Zero;
// 当前文件时
if (SelectedFile.Equals(CurrentFile))
{
DrawerGeo.DrawerCopySymbol(pXy, strName, ref pxyNew);
DrawerGeo.DrawerDrawSymbol(strName, pBmpDC, 50, 50, ref pxyNew);
}
else
{
GeoSigmaXY.LibraryCopySymbol(SelectedFile, pXy, strName, ref pxyNew);
GeoSigmaXY.LibaryDrawSymbol(SelectedFile, strName, pBmpDC, 50, 50, ref pxyNew);
IsDirty = true;
}
gBmp.ReleaseHdc();
lstSymbolImage.Images.Add(bmp);
SymbolItemData itemData = new SymbolItemData();
itemData.Name = strName;
itemData.PtrXY = pxyNew;
itemData.Group = SelectedFile;
ListViewItem item = new ListViewItem(strName);
item.Name = strName;
item.ImageIndex = lstSymbolImage.Images.Count - 1;
item.Tag = itemData;
arrItemsCache.Add(item);
}
lsvSymbol.VirtualListSize = arrItemsCache.Count;
SelectSymbol(arrItemsCache.Count - 1);
}
private void ctmSymbols_Opening(object sender, CancelEventArgs e)
{
//string strMsg = sender.ToString();
}
private string NewGroupName()
{
List<string> fileNames = Directory.EnumerateFiles(LibPath)
.Select(path => Path.GetFileName(path))
.ToList();
for (int newIndex = 1; newIndex < int.MaxValue; newIndex++)
{
string newFileName = $"自定义符号{newIndex}.kev";
if (!fileNames.Any(s => s.Equals(newFileName, StringComparison.OrdinalIgnoreCase)))
{
return newFileName;
}
}
return string.Empty;
}
/// <summary>
/// 新建分组
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsbtnNewGroup_Click(object sender, EventArgs e)
{
string newFilename = NewGroupName();
string strNewFile = Path.Combine(LibPath, newFilename);
File.Create(strNewFile).Close();
AddNewGroup(strNewFile);
}
/// <summary>
/// 添加新符号库
/// </summary>
/// <param name="strNewFile">符号文件</param>
private void AddNewGroup(string strNewFile)
{
GeoSigmaXY.LibraryAddNewGroup(strNewFile);
TreeNode tnGroup = new TreeNode();
tnGroup.Text = Path.GetFileNameWithoutExtension(strNewFile);
tnGroup.ImageIndex = 0;
tnGroup.Tag = strNewFile;
var root = trvSymbol.Nodes[0];
root.Nodes.Add(tnGroup);
trvSymbol.SelectedNode = tnGroup;
}
/// <summary>
/// 删除分组
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tsbtnDeleteFile_Click(object sender, EventArgs e)
{
var root = trvSymbol.Nodes[0];
List<TreeNode> lstCheckedNodes = root.Nodes.Cast<TreeNode>().Where(node => node.Checked).ToList();
if (lstCheckedNodes.Count == 0)
{
return;
}
if (MessageBox.Show("是否删除选中符号库?") != DialogResult.OK)
{
return;
}
trvSymbol.BeginUpdate();
TreeNode tnNext = lstCheckedNodes[lstCheckedNodes.Count - 1].NextNode;
for (int i = 0; i < lstCheckedNodes.Count; i++)
{
string strFileName = lstCheckedNodes[i].Tag as string;
RemoveGroup(strFileName);
lstCheckedNodes[i].Remove();
}
trvSymbol.EndUpdate();
ClearSymbols();
if (tnNext == null && root.Nodes.Count > 0)
{
tnNext = root.Nodes[root.Nodes.Count - 1];
}
if (tnNext != null)
{
trvSymbol.SelectedNode = tnNext;
}
}
/// <summary>
/// 从文件导入符号
/// </summary>
/// <param name="sender">事件按钮</param>
/// <param name="e">事件参数</param>
private void tsbtnImportFile_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.RestoreDirectory = true;
ofd.Filter = "*.kev|*.kev|*.pcg|*.pcg|*.*|*.*";
if (ofd.ShowDialog() != DialogResult.OK)
{
return;
}
if (!Directory.Exists(LibPath))
{
Directory.CreateDirectory(LibPath);
}
string strFile = ofd.FileName;
string strFileNew = string.Empty;
// string strDir = Path.GetDirectoryName(strFile);
string strOldName = Path.GetFileNameWithoutExtension(strFile);
string[] straOldFiles = Directory.GetFiles(LibPath);
string strFind = straOldFiles.FirstOrDefault(it =>
{
string strName = Path.GetFileNameWithoutExtension(it);
return strName.Equals(strOldName);
});
if (strFind != null)
{
strOldName += "1";
strFileNew = Path.Combine(LibPath, strOldName + Path.GetExtension(strFile));
}
else
{
strFileNew = Path.Combine(LibPath, Path.GetFileName(strFile));
}
Cursor.Current = Cursors.WaitCursor;
trvSymbol.BeginUpdate();
FileInfo fi = new FileInfo(strFile);
fi.CopyTo(strFileNew);
AddNewGroup(strFileNew);
Cursor.Current = Cursors.Default;
trvSymbol.EndUpdate();
}
/// <summary>
/// 符号导出到文件
/// </summary>
/// <param name="sender">符号导出事件</param>
/// <param name="e">事件参数</param>
private void tsbtnExportFile_Click(object sender, EventArgs e)
{
FolderBrowserDialog fldDlg = new FolderBrowserDialog();
fldDlg.Description = @"将选中节点的符号导出到指定文件夹中。
";
if (fldDlg.ShowDialog() != DialogResult.OK)
{
return;
}
string strDir = fldDlg.SelectedPath;
if (!Directory.Exists(strDir))
{
Directory.CreateDirectory(strDir);
}
// 保存编辑中的图形
MainView_SaveFile(null);
var root = trvSymbol.Nodes[0];
List<TreeNode> lstCheckedNodes = new List<TreeNode>();
foreach (TreeNode node in root.Nodes)
{
if (!node.Checked)
{
continue;
}
string strFileSource = node.Tag as string;
string strNameDes = Path.GetFileName(strFileSource);
string strFileDes = Path.Combine(strDir, strNameDes);
bool bOverwrite = false;
if (File.Exists(strFileDes))
{
DialogResult result = MessageBox.Show(this, $@"存在同名的文件“{strNameDes}”,覆盖还是跳过?
",
"导出",
MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);
if (result == DialogResult.Cancel)
{
break;
}
else if (result == DialogResult.Yes)
{
bOverwrite = true;
}
else
{
bOverwrite = false;
continue;
}
}
FileInfo fi = new FileInfo(strFileSource);
try
{
fi.CopyTo(strFileDes, bOverwrite);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
MessageBox.Show("导出成功!");
}
/// <summary>
/// 删除选中文件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tmiDeleteFile_Click(object sender, EventArgs e)
{
if (trvSymbol.SelectedNode == null)
{
return;
}
DialogResult result = MessageBox.Show($"要删除选中“{trvSymbol.SelectedNode.Text}”吗?\r\n删除后不可恢复"
, "删除", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
if (result != DialogResult.Yes)
{
return;
}
string strFileName = trvSymbol.SelectedNode.Tag as string;
GeoSigmaXY.LibraryRemoveGroup(strFileName);
trvSymbol.SelectedNode.Remove();
try
{
File.Delete(strFileName);
}
catch
{
}
}
/// <summary>
/// 重命名选中文件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tmiRename_Click(object sender, EventArgs e)
{
trvSymbol.SelectedNode?.BeginEdit();
}
// 重命名符号组
private void trvSymbol_AfterLabelEdit(object sender, NodeLabelEditEventArgs e)
{
if (string.IsNullOrEmpty(e.Label))
{
return;
}
string strFileName = trvSymbol.SelectedNode.Tag as string;
string strNewFile = e.Label + Path.GetExtension(strFileName);
string strNewFileFull = Path.Combine(Path.GetDirectoryName(strFileName), strNewFile);
if (GeoSigmaXY.LibraryRenameGroup(strFileName, strNewFileFull) != true)
{
e.CancelEdit = true; // 取消节点的编辑操作
MessageBox.Show("重命名失败!");
return;
}
Computer MyComputer = new Computer();
MyComputer.FileSystem.RenameFile(strFileName, strNewFile);
trvSymbol.SelectedNode.Tag = strNewFileFull;
}
/// <summary>
/// 鼠标点击,右键菜单
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void trvSymbol_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
Point point = new Point(e.X, e.Y);
TreeNode treeNode = trvSymbol.GetNodeAt(point);
if (treeNode == trvSymbol.Nodes[0])
{
return;
}
trvSymbol.SelectedNode = treeNode;
if (e.Button == MouseButtons.Right)
{
if (treeNode.Text == CurrentFile)
{
EnableRenameDeleteItems(false);
}
else
{
EnableRenameDeleteItems(true);
}
//显示上下文菜单
ctmTree.Show(trvSymbol.PointToScreen(point));
}
}
private void EnableRenameDeleteItems(bool enabled)
{
ctmTree.Items.OfType<ToolStripMenuItem>()
.Where(x => x.Text == "重命名" || x.Text == "删除")
.ToList()
.ForEach(x => x.Enabled = enabled);
}
private void tmiMerge_Click(object sender, EventArgs e)
{
if (trvSymbol.SelectedNode == null || string.IsNullOrWhiteSpace(SelectedFile))
{
return;
}
// 选择文件
var dialog = new OpenFileDialog
{
Filter = "*.kev;*.dfd;*.pcg|*kev;*.dfd;*.pcg"
};
if (dialog.ShowDialog(this) != DialogResult.OK)
{
return;
}
// 合并符号
MergeSymbol(dialog.FileName);
// 刷新
ShowSelectedSymbolGroup();
}
private void MergeSymbol(string mergeFile)
{
if (SelectedFile.Equals(CurrentFile))
{
MergeSymbol(DrawerGeo.GetDrawerXy(), mergeFile);
}
else
{
MergeSymbol(SelectedFile, mergeFile);
}
}
private void MergeSymbol(string sourceFile, string mergeFile)
{
var drawerData = new DrawerData();
if (drawerData.OpenFile(SelectedFile))
{
drawerData.MergeSymbol(mergeFile);
drawerData.SaveAs(sourceFile);
drawerData.Dispose();
// FIXME: 符号库里面有缓存,这里需要重新加载一下,
// 也许正确的方法应该是符号库去追踪文件变化,然后自动重新加载
GeoSigmaLib.LibraryReloadGroup(sourceFile);
}
}
private void MergeSymbol(IntPtr sourceXy, string mergeFile)
{
var drawerData = new DrawerData();
drawerData.OpenXy(sourceXy);
drawerData.MergeSymbol(mergeFile);
drawerData.Dispose();
}
}
public class SymbolItemData
{
public string Group { get; set; }
public string Name { get; set; }
public IntPtr PtrXY { get; set; }
}
}