using System; using System.Collections.Generic; using System.IO; using System.Text; using System.Drawing; using System.Windows.Forms; namespace SlideCombine { public partial class Form1 : Form { public Form1() { InitializeComponent(); // 设置窗体图标 - 使用嵌入资源 try { var stream = GetType().Assembly.GetManifestResourceStream("SlideCombine.app.ico"); if (stream != null) { this.Icon = new Icon(stream); } } catch { // 如果图标加载失败,使用默认图标 // this.Icon = SystemIcons.Application; } } private void btnBrowseSource_Click(object sender, EventArgs e) { string selectedPath = ShowModernFolderBrowser("请选择包含PDF文件夹的路径(含FreePic2Pdf_bkmk.txt文件)", txtSourcePath.Text); if (!string.IsNullOrEmpty(selectedPath)) { txtSourcePath.Text = selectedPath; LogInfo($"已选择PDF路径: {selectedPath}"); } } private void btnBrowseText_Click(object sender, EventArgs e) { string selectedPath = ShowModernFolderBrowser("请选择包含元数据TXT文件的路径", txtTextPath.Text); if (!string.IsNullOrEmpty(selectedPath)) { txtTextPath.Text = selectedPath; LogInfo($"已选择TXT源路径: {selectedPath}"); } } private void btnBrowseOutput_Click(object sender, EventArgs e) { string selectedPath = ShowModernFolderBrowser("请选择合并后TXT文件的输出路径", txtOutputPath.Text); if (!string.IsNullOrEmpty(selectedPath)) { txtOutputPath.Text = selectedPath; LogInfo($"已选择最终输出路径: {selectedPath}"); } } private void btnClear_Click(object sender, EventArgs e) { txtSourcePath.Clear(); txtTextPath.Clear(); txtOutputPath.Clear(); txtLog.Clear(); progressBar.Value = 0; LogInfo("界面已清空"); } private void btnExit_Click(object sender, EventArgs e) { LogInfo("程序即将退出"); Application.Exit(); } private void btnMerge_Click(object sender, EventArgs e) { try { // 验证输入 if (string.IsNullOrWhiteSpace(txtSourcePath.Text) || string.IsNullOrWhiteSpace(txtTextPath.Text) || string.IsNullOrWhiteSpace(txtOutputPath.Text)) { MessageBox.Show("请选择所有三个路径:PDF路径、TXT源路径和输出路径", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } if (!Directory.Exists(txtSourcePath.Text)) { MessageBox.Show("指定的PDF文件夹路径不存在", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } if (!Directory.Exists(txtTextPath.Text)) { MessageBox.Show("指定的TXT源文件路径不存在", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } // 禁用按钮,防止重复点击 btnMerge.Enabled = false; btnClear.Enabled = false; // 重置进度条和日志 progressBar.Value = 0; txtLog.Clear(); Log("开始处理PDF书签文件..."); // 处理文件 var results = FileMerger.ProcessAllFolders(txtSourcePath.Text, txtTextPath.Text, txtOutputPath.Text); // 显示进度 progressBar.Value = 50; Log($"找到 {results.Count} 个文件组需要处理"); // 保存结果 FileMerger.SaveResults(results, txtOutputPath.Text); progressBar.Value = 100; // 统计成功和失败的数量 int successCount = 0; int failCount = 0; var sb = new StringBuilder(); foreach (var result in results) { if (result.Success) { successCount++; Log($"✓ 成功处理: {result.BaseFileName} (合并了 {result.SourceFiles.Count} 个文件)"); } else { failCount++; Log($"✗ 处理失败: {result.ErrorMessage}"); } } Log($"处理完成! 成功: {successCount}, 失败: {failCount}"); if (successCount > 0) { MessageBox.Show($"书签合并完成!\n成功处理 {successCount} 个文件\n输出路径: {txtOutputPath.Text}", "处理完成", MessageBoxButtons.OK, MessageBoxIcon.Information); } else { MessageBox.Show("没有成功处理任何文件,请检查输入路径和文件格式。", "处理失败", MessageBoxButtons.OK, MessageBoxIcon.Warning); } } catch (Exception ex) { Log($"错误: {ex.Message}"); MessageBox.Show($"处理过程中发生错误:\n{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error); } finally { // 重新启用按钮 btnMerge.Enabled = true; btnClear.Enabled = true; } } private void Log(string msg) { string timestamp = DateTime.Now.ToString("HH:mm:ss"); txtLog.AppendText($"[{timestamp}] {msg}\r\n"); txtLog.ScrollToCaret(); Application.DoEvents(); } private void LogSuccess(string msg) { string timestamp = DateTime.Now.ToString("HH:mm:ss"); txtLog.SelectionStart = txtLog.TextLength; txtLog.SelectionColor = Color.Green; txtLog.AppendText($"[{timestamp}] ✅ {msg}\r\n"); txtLog.SelectionColor = txtLog.ForeColor; txtLog.ScrollToCaret(); Application.DoEvents(); } private void LogError(string msg) { string timestamp = DateTime.Now.ToString("HH:mm:ss"); txtLog.SelectionStart = txtLog.TextLength; txtLog.SelectionColor = Color.Red; txtLog.AppendText($"[{timestamp}] ❌ {msg}\r\n"); txtLog.SelectionColor = txtLog.ForeColor; txtLog.ScrollToCaret(); Application.DoEvents(); } private void LogInfo(string msg) { string timestamp = DateTime.Now.ToString("HH:mm:ss"); txtLog.SelectionStart = txtLog.TextLength; txtLog.SelectionColor = Color.Blue; txtLog.AppendText($"[{timestamp}] ℹ️ {msg}\r\n"); txtLog.SelectionColor = txtLog.ForeColor; txtLog.ScrollToCaret(); Application.DoEvents(); } private string ShowModernFolderBrowser(string title, string initialPath) { using (var form = new Form()) { form.Text = title; form.Size = new Size(800, 600); form.StartPosition = FormStartPosition.CenterParent; form.FormBorderStyle = FormBorderStyle.FixedDialog; form.MaximizeBox = false; form.MinimizeBox = false; form.BackColor = Color.White; // 顶部工具栏 var toolbarPanel = new Panel(); toolbarPanel.Location = new Point(0, 0); toolbarPanel.Size = new Size(800, 60); toolbarPanel.BackColor = Color.FromArgb(248, 248, 248); toolbarPanel.BorderStyle = BorderStyle.FixedSingle; form.Controls.Add(toolbarPanel); // 路径标签 var pathLabel = new Label(); pathLabel.Text = "路径:"; pathLabel.Location = new Point(15, 20); pathLabel.Size = new Size(40, 20); pathLabel.Font = new Font("Microsoft YaHei", 9F); pathLabel.ForeColor = Color.FromArgb(51, 51, 51); toolbarPanel.Controls.Add(pathLabel); // 路径文本框 var pathTextBox = new TextBox(); pathTextBox.Location = new Point(60, 18); pathTextBox.Size = new Size(650, 24); pathTextBox.Font = new Font("Microsoft YaHei", 10F); pathTextBox.Text = initialPath ?? string.Empty; pathTextBox.BorderStyle = BorderStyle.FixedSingle; toolbarPanel.Controls.Add(pathTextBox); // 路径浏览按钮 var browseButton = new Button(); browseButton.Text = "..."; browseButton.Location = new Point(720, 17); browseButton.Size = new Size(30, 24); browseButton.Font = new Font("Microsoft YaHei", 9F); browseButton.BackColor = Color.FromArgb(66, 139, 202); browseButton.ForeColor = Color.White; browseButton.FlatStyle = FlatStyle.Flat; browseButton.FlatAppearance.BorderSize = 0; toolbarPanel.Controls.Add(browseButton); // 左侧文件夹树 var folderTree = new TreeView(); folderTree.Location = new Point(15, 70); folderTree.Size = new Size(250, 450); folderTree.Font = new Font("Microsoft YaHei", 9F); folderTree.BorderStyle = BorderStyle.FixedSingle; folderTree.ShowLines = true; folderTree.ShowPlusMinus = true; folderTree.ShowRootLines = true; folderTree.FullRowSelect = true; folderTree.Scrollable = true; form.Controls.Add(folderTree); // 右侧文件列表 var fileList = new ListView(); fileList.Location = new Point(275, 70); fileList.Size = new Size(510, 450); fileList.View = View.Details; fileList.FullRowSelect = true; fileList.GridLines = true; fileList.Font = new Font("Microsoft YaHei", 9F); fileList.BorderStyle = BorderStyle.FixedSingle; fileList.Columns.Add("名称", 200); fileList.Columns.Add("修改日期", 150); fileList.Columns.Add("类型", 80); fileList.Columns.Add("大小", 80); form.Controls.Add(fileList); // 底部状态栏 var statusPanel = new Panel(); statusPanel.Location = new Point(0, 530); statusPanel.Size = new Size(800, 30); statusPanel.BackColor = Color.FromArgb(240, 240, 240); statusPanel.BorderStyle = BorderStyle.FixedSingle; form.Controls.Add(statusPanel); var statusLabel = new Label(); statusLabel.Text = "就绪"; statusLabel.Location = new Point(10, 5); statusLabel.Size = new Size(780, 20); statusLabel.Font = new Font("Microsoft YaHei", 9F); statusLabel.ForeColor = Color.FromArgb(102, 102, 102); statusPanel.Controls.Add(statusLabel); // 底部按钮 var okButton = new Button(); okButton.Text = "确定"; okButton.Location = new Point(620, 20); okButton.Size = new Size(80, 35); okButton.Font = new Font("Microsoft YaHei", 10F, FontStyle.Bold); okButton.BackColor = Color.FromArgb(92, 184, 92); okButton.ForeColor = Color.White; okButton.FlatStyle = FlatStyle.Flat; okButton.FlatAppearance.BorderSize = 0; form.AcceptButton = okButton; form.Controls.Add(okButton); var cancelButton = new Button(); cancelButton.Text = "取消"; cancelButton.Location = new Point(710, 20); cancelButton.Size = new Size(80, 35); cancelButton.Font = new Font("Microsoft YaHei", 10F); cancelButton.BackColor = Color.FromArgb(217, 83, 79); cancelButton.ForeColor = Color.White; cancelButton.FlatStyle = FlatStyle.Flat; cancelButton.FlatAppearance.BorderSize = 0; form.CancelButton = cancelButton; form.Controls.Add(cancelButton); // 当前路径 string currentPath = string.IsNullOrEmpty(initialPath) ? Environment.GetFolderPath(Environment.SpecialFolder.Desktop) : initialPath; // 加载文件夹树 void LoadFolderTree() { folderTree.Nodes.Clear(); folderTree.BeginUpdate(); try { // 获取所有逻辑驱动器 foreach (var drive in DriveInfo.GetDrives()) { var driveNode = new TreeNode(); driveNode.Text = $"{drive.Name} ({drive.VolumeLabel})"; driveNode.Tag = drive.Name; if (drive.IsReady) { driveNode.Nodes.Add(new TreeNode()); // 占位节点 } driveNode.ImageIndex = 0; driveNode.SelectedImageIndex = 0; folderTree.Nodes.Add(driveNode); } } catch (Exception ex) { statusLabel.Text = $"加载驱动器时出错: {ex.Message}"; } folderTree.EndUpdate(); } // 展开节点时加载子文件夹 void LoadSubFolders(TreeNode parentNode) { if (parentNode.Nodes.Count == 1 && parentNode.Nodes[0].Text == "") { parentNode.Nodes.Clear(); try { var path = parentNode.Tag as string; if (Directory.Exists(path)) { foreach (var dir in Directory.GetDirectories(path)) { var dirInfo = new DirectoryInfo(dir); var node = new TreeNode(dirInfo.Name); node.Tag = dirInfo.FullName; try { // 检查是否有子文件夹 if (Directory.GetDirectories(dir).Length > 0) { node.Nodes.Add(new TreeNode()); // 占位节点 } } catch { // 无权限访问子文件夹 } parentNode.Nodes.Add(node); } } } catch (Exception ex) { statusLabel.Text = $"加载子文件夹时出错: {ex.Message}"; } } } // 加载文件列表 void LoadFileList(string path) { fileList.Items.Clear(); fileList.BeginUpdate(); try { if (Directory.Exists(path)) { // 添加返回上级目录 var parentPath = Path.GetDirectoryName(path); if (!string.IsNullOrEmpty(parentPath)) { var parentItem = new ListViewItem(new[] { "..", "", "文件夹", "" }); parentItem.Tag = parentPath; parentItem.ForeColor = Color.FromArgb(66, 139, 202); fileList.Items.Add(parentItem); } // 添加文件夹 foreach (var dir in Directory.GetDirectories(path)) { var dirInfo = new DirectoryInfo(dir); var item = new ListViewItem(new[] { dirInfo.Name, dirInfo.LastWriteTime.ToString("yyyy-MM-dd HH:mm"), "文件夹", "" }); item.Tag = dirInfo.FullName; item.ForeColor = Color.FromArgb(51, 51, 51); fileList.Items.Add(item); } // 添加文件 foreach (var file in Directory.GetFiles(path)) { var fileInfo = new FileInfo(file); var item = new ListViewItem(new[] { fileInfo.Name, fileInfo.LastWriteTime.ToString("yyyy-MM-dd HH:mm"), GetFileType(fileInfo.Extension), FormatFileSize(fileInfo.Length) }); item.Tag = fileInfo.FullName; item.ForeColor = Color.FromArgb(102, 102, 102); fileList.Items.Add(item); } statusLabel.Text = $"{Directory.GetDirectories(path).Length} 个文件夹, {Directory.GetFiles(path).Length} 个文件"; } } catch (Exception ex) { statusLabel.Text = $"加载文件列表时出错: {ex.Message}"; } fileList.EndUpdate(); } string GetFileType(string extension) { extension = extension.ToLower(); switch (extension) { case ".txt": return "文本文件"; case ".pdf": return "PDF文档"; case ".doc": case ".docx": return "Word文档"; case ".xls": case ".xlsx": return "Excel表格"; default: return $"{extension}文件"; } } string FormatFileSize(long bytes) { string[] sizes = { "B", "KB", "MB", "GB" }; double len = bytes; int order = 0; while (len >= 1024 && order < sizes.Length - 1) { order++; len = len / 1024; } return $"{len:0.##} {sizes[order]}"; } // 事件处理 folderTree.BeforeExpand += (s, e) => { LoadSubFolders(e.Node); }; folderTree.AfterSelect += (s, e) => { if (e.Node.Tag is string path && Directory.Exists(path)) { currentPath = path; pathTextBox.Text = currentPath; LoadFileList(currentPath); } }; fileList.DoubleClick += (s, e) => { if (fileList.SelectedItems.Count > 0) { var selectedItem = fileList.SelectedItems[0]; if (selectedItem.Tag is string selectedPath && Directory.Exists(selectedPath)) { currentPath = selectedPath; pathTextBox.Text = currentPath; LoadFileList(currentPath); // 更新树选择 var nodes = folderTree.Nodes.Find(GetNodeText(selectedPath), true); if (nodes.Length > 0) { folderTree.SelectedNode = nodes[0]; } } } }; browseButton.Click += (s, e) => { using (var dialog = new FolderBrowserDialog()) { dialog.Description = title; dialog.SelectedPath = currentPath; if (dialog.ShowDialog() == DialogResult.OK) { currentPath = dialog.SelectedPath; pathTextBox.Text = currentPath; LoadFileList(currentPath); // 更新树选择 var nodes = folderTree.Nodes.Find(GetNodeText(currentPath), true); if (nodes.Length > 0) { folderTree.SelectedNode = nodes[0]; } } } }; pathTextBox.KeyDown += (s, e) => { if (e.KeyCode == Keys.Enter) { if (Directory.Exists(pathTextBox.Text)) { currentPath = pathTextBox.Text; LoadFileList(currentPath); // 更新树选择 var nodes = folderTree.Nodes.Find(GetNodeText(currentPath), true); if (nodes.Length > 0) { folderTree.SelectedNode = nodes[0]; } } } }; string GetNodeText(string fullPath) { return new DirectoryInfo(fullPath).Name; } okButton.Click += (s, e) => { if (Directory.Exists(pathTextBox.Text)) { form.DialogResult = DialogResult.OK; form.Close(); } else { MessageBox.Show("请输入有效的文件夹路径", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning); } }; cancelButton.Click += (s, e) => { form.DialogResult = DialogResult.Cancel; form.Close(); }; // 初始化 LoadFolderTree(); LoadFileList(currentPath); // 显示对话框 if (form.ShowDialog() == DialogResult.OK) { return pathTextBox.Text; } } return null; } } }