diff --git a/LICENSE.MD b/LICENSE.MD new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE.MD @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/NoteWidget.sln b/NoteWidget.sln new file mode 100644 index 0000000..8bc5c3b --- /dev/null +++ b/NoteWidget.sln @@ -0,0 +1,51 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31919.166 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NoteWidgetTests", "NoteWidgetTests\NoteWidgetTests.csproj", "{FB85C7C6-5DB7-4EFB-9BC4-AFFF33C1E3EA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NoteWidgetAddIn", "NoteWidgetAddIn\NoteWidgetAddIn.csproj", "{280104F5-13BA-4193-BFF5-5A5C48261640}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{732EB35D-6A00-4E3C-B1AF-2A7A0ABF10D2}" + ProjectSection(SolutionItems) = preProject + cheatsheet_snapshot.png = cheatsheet_snapshot.png + LICENSE.MD = LICENSE.MD + powershell-copyfile-runas.bat = powershell-copyfile-runas.bat + preview_snapshot.png = preview_snapshot.png + README.MD = README.MD + EndProjectSection +EndProject +Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "NoteWidgetAddIn.Setup", "NoteWidgetAddInSetup\NoteWidgetAddInSetup.vdproj", "{1D540207-035C-40B7-BD5B-48FF023152C2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Publish|Any CPU = Publish|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {FB85C7C6-5DB7-4EFB-9BC4-AFFF33C1E3EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FB85C7C6-5DB7-4EFB-9BC4-AFFF33C1E3EA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FB85C7C6-5DB7-4EFB-9BC4-AFFF33C1E3EA}.Publish|Any CPU.ActiveCfg = Publish|Any CPU + {FB85C7C6-5DB7-4EFB-9BC4-AFFF33C1E3EA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FB85C7C6-5DB7-4EFB-9BC4-AFFF33C1E3EA}.Release|Any CPU.Build.0 = Release|Any CPU + {280104F5-13BA-4193-BFF5-5A5C48261640}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {280104F5-13BA-4193-BFF5-5A5C48261640}.Debug|Any CPU.Build.0 = Debug|Any CPU + {280104F5-13BA-4193-BFF5-5A5C48261640}.Publish|Any CPU.ActiveCfg = Publish|Any CPU + {280104F5-13BA-4193-BFF5-5A5C48261640}.Publish|Any CPU.Build.0 = Publish|Any CPU + {280104F5-13BA-4193-BFF5-5A5C48261640}.Release|Any CPU.ActiveCfg = Release|Any CPU + {280104F5-13BA-4193-BFF5-5A5C48261640}.Release|Any CPU.Build.0 = Release|Any CPU + {1D540207-035C-40B7-BD5B-48FF023152C2}.Debug|Any CPU.ActiveCfg = Debug + {1D540207-035C-40B7-BD5B-48FF023152C2}.Debug|Any CPU.Build.0 = Debug + {1D540207-035C-40B7-BD5B-48FF023152C2}.Publish|Any CPU.ActiveCfg = Publish + {1D540207-035C-40B7-BD5B-48FF023152C2}.Release|Any CPU.ActiveCfg = Release + {1D540207-035C-40B7-BD5B-48FF023152C2}.Release|Any CPU.Build.0 = Release + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {6124B423-3309-464C-A107-BD5DD68ED3A3} + EndGlobalSection +EndGlobal diff --git a/NoteWidgetAddIn/.editorconfig b/NoteWidgetAddIn/.editorconfig new file mode 100644 index 0000000..370b77b --- /dev/null +++ b/NoteWidgetAddIn/.editorconfig @@ -0,0 +1,134 @@ +# To learn more about .editorconfig see https://aka.ms/editorconfigdocs +############################### +# Core EditorConfig Options # +############################### +root = true +# All files +[*] +indent_style = space + +# XML project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +indent_size = 2 + +# XML config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_size = 2 + +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 +insert_final_newline = true +charset = utf-8-bom +file_header_template = Copyright (c) Efrey Kong. All Rights Reserved.\nLicensed under the Apache License, Version 2.0. +############################### +# .NET Coding Conventions # +############################### +[*.{cs,vb}] +# Organize usings +dotnet_sort_system_directives_first = true +# this. preferences +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_readonly_field = true:suggestion +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +############################### +# Naming Conventions # +############################### +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const +############################### +# C# Coding Conventions # +############################### +[*.cs] +# var preferences +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:silent +# Expression-bodied members +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +# Pattern matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion +# Expression-level preferences +csharp_prefer_braces = true:silent +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +############################### +# C# Formatting Rules # +############################### +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left +# Space preferences +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +# Wrapping preferences +csharp_preserve_single_line_statements = true +csharp_preserve_single_line_blocks = true +############################### +# VB Coding Conventions # +############################### +[*.vb] +# Modifier preferences +visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion diff --git a/NoteWidgetAddIn/AddIn.Ribbon.cs b/NoteWidgetAddIn/AddIn.Ribbon.cs new file mode 100644 index 0000000..e745489 --- /dev/null +++ b/NoteWidgetAddIn/AddIn.Ribbon.cs @@ -0,0 +1,81 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices.ComTypes; +using System.Threading.Tasks; +using Microsoft.Office.Core; +using NoteWidgetAddIn.RibbonCommand; + +#pragma warning disable CS3001 // Argument type is not CLS-compliant +namespace NoteWidgetAddIn +{ + public partial class AddIn : IRibbonExtensibility + { + + #region implements IRibbonExtensibility + /// + /// Gets the defined ribbon tab xml string content + /// This method is triggered after OnAddInsUpdate + /// + /// + /// The xml string content which customized ribbon menu items + public string GetCustomUI(string RibbonID) + { + return Properties.Resources.ribbon; + } + #endregion + + #region Custom Ribbon methods + public IStream GetImage(string imageName) + { + var ms = new MemoryStream(); + BindingFlags flags = BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + var b = typeof(Properties.Resources).GetProperty(Path.GetFileNameWithoutExtension(imageName), flags).GetValue(null, null) as Bitmap; + b.Save(ms, ImageFormat.Png); + return new CCOMStreamWrapper(ms); + } + + #endregion + + #region Ribbon Action Commands + /// + /// Export to file + /// + /// + /// + public async Task ExportFileCmd(IRibbonControl control) => await _commandFactory.Run(control.Tag); + + /// + /// Export to hierarchical files + /// + /// + /// + public async Task ExportPathCmd(IRibbonControl control) => await _commandFactory.Run(control.Tag, control.Context); + + /// + /// View current page's markdown content as Html in a new window. + /// + /// + /// + public async Task PreviewMarkdownCmd(IRibbonControl control) => await _commandFactory.Run(); + /// + /// Show markdown cheat sheet window + /// + /// + /// + public async Task MarkdownCheatsheetCmd(IRibbonControl control) => await _commandFactory.Run(); + /// + /// Show advanced settings window + /// + /// + /// + public async Task WidgetAdvancedSettingsCmd(IRibbonControl control) => await _commandFactory.Run(); + + #endregion + } +} diff --git a/NoteWidgetAddIn/AddIn.cs b/NoteWidgetAddIn/AddIn.cs new file mode 100644 index 0000000..2abcb53 --- /dev/null +++ b/NoteWidgetAddIn/AddIn.cs @@ -0,0 +1,75 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using Extensibility; +using NLog; +using NoteWidgetAddIn.RibbonCommand; + +#pragma warning disable CS3008 // Type is not CLS-compliant +#pragma warning disable CS3003 // Type is not CLS-compliant +#pragma warning disable CS3001 // Type is not CLS-compliant +namespace NoteWidgetAddIn +{ + [ComVisible(true)] + [Guid("EEE896F2-39B1-4D71-8A54-3EFDFB48BB06"), ProgId("NoteWidget.AddIn")] + public partial class AddIn : IDTExtensibility2 + { + private static readonly ILogger _logger = LogManager.GetCurrentClassLogger(); + private CommandFactory _commandFactory; + static AddIn() + { + var nlogConfigPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "NLog.config"); + LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration(nlogConfigPath); + } + + public void OnConnection(object Application, ext_ConnectMode ConnectMode, object AddInInst, ref Array custom) + { + //OneNote window is not ready to use. + _logger.Debug("OnConnection"); + WpfAddInApplication.Current = new WpfAddInApplication(); + } + + public void OnDisconnection(ext_DisconnectMode RemoveMode, ref Array custom) + { + GC.Collect(); + //GC.WaitForPendingFinalizers(); + Environment.Exit(0); + } + + public void OnAddInsUpdate(ref Array custom) + { + } + + public void OnStartupComplete(ref Array custom) + { + //Leave factory instainced here, since from now on OneNote Window is ready to access. + try + { + NoteApplicationContext context = new NoteApplicationContext(); + _commandFactory = new CommandFactory(context); + } + catch (Exception ex) + { + _logger.Error(ex); + } + } + + public void OnBeginShutdown(ref Array custom) + { + _logger.Debug("OnBeginShutdown"); + try + { + _commandFactory = null; + WpfAddInApplication.Current.Shutdown(); + } + catch(Exception ex) + { + _logger.Error(ex); + } + } + } +} diff --git a/NoteWidgetAddIn/CCOMStreamWrapper.cs b/NoteWidgetAddIn/CCOMStreamWrapper.cs new file mode 100644 index 0000000..6d8fdf4 --- /dev/null +++ b/NoteWidgetAddIn/CCOMStreamWrapper.cs @@ -0,0 +1,110 @@ +/* + * Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. + */ + +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; + +namespace NoteWidgetAddIn +{ + class CCOMStreamWrapper : IStream + { + public CCOMStreamWrapper(System.IO.Stream streamWrap) + { + m_stream = streamWrap; + } + + public void Clone(out IStream ppstm) + { + ppstm = new CCOMStreamWrapper(m_stream); + } + + public void Commit(int grfCommitFlags) + { + m_stream.Flush(); + } + + public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten) + { + } + + public void LockRegion(long libOffset, long cb, int dwLockType) + { + throw new System.NotImplementedException(); + } + + public void Read(byte[] pv, int cb, IntPtr pcbRead) + { + Marshal.WriteInt64(pcbRead, m_stream.Read(pv, 0, cb)); + } + + public void Revert() + { + throw new System.NotImplementedException(); + } + + public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition) + { + long posMoveTo = 0; + Marshal.WriteInt64(plibNewPosition, m_stream.Position); + switch (dwOrigin) + { + case 0: + { + /* STREAM_SEEK_SET */ + posMoveTo = dlibMove; + } + break; + case 1: + { + /* STREAM_SEEK_CUR */ + posMoveTo = m_stream.Position + dlibMove; + + } + break; + case 2: + { + /* STREAM_SEEK_END */ + posMoveTo = m_stream.Length + dlibMove; + } + break; + default: + return; + } + if (posMoveTo >= 0 && posMoveTo < m_stream.Length) + { + m_stream.Position = posMoveTo; + Marshal.WriteInt64(plibNewPosition, m_stream.Position); + } + } + + public void SetSize(long libNewSize) + { + m_stream.SetLength(libNewSize); + } + + public void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg, int grfStatFlag) + { + pstatstg = new System.Runtime.InteropServices.ComTypes.STATSTG(); + pstatstg.cbSize = m_stream.Length; + if ((grfStatFlag & 0x0001/* STATFLAG_NONAME */) != 0) + return; + pstatstg.pwcsName = m_stream.ToString(); + } + + public void UnlockRegion(long libOffset, long cb, int dwLockType) + { + throw new System.NotImplementedException(); + } + + public void Write(byte[] pv, int cb, IntPtr pcbWritten) + { + Marshal.WriteInt64(pcbWritten, 0); + m_stream.Write(pv, 0, cb); + Marshal.WriteInt64(pcbWritten, cb); + } + + private System.IO.Stream m_stream; + } +} diff --git a/NoteWidgetAddIn/Export/AbstractExportor.cs b/NoteWidgetAddIn/Export/AbstractExportor.cs new file mode 100644 index 0000000..28cb62d --- /dev/null +++ b/NoteWidgetAddIn/Export/AbstractExportor.cs @@ -0,0 +1,115 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using System.Text; +using NoteWidgetAddIn.Model; + +namespace NoteWidgetAddIn.Export +{ + internal abstract class AbstractExportor : IExportor + { + private NoteApplicationContext _context; + public AbstractExportor(ExportFormat fileFormat) + { + FileFormat = fileFormat; + } + public ExportFormat FileFormat { get; private set; } + protected NoteApplication NoteApp { get; private set; } + + public string FileExtension + { + get + { + return ExportHelper.GetExportFormatFileExtension(FileFormat); + } + } + public void SetContext(NoteApplicationContext context) + { + _context = context; + NoteApp = _context.CreateApplication(); + } + + public abstract void ExportNodeToSingleFile(string nodeID, string filePath); + + protected abstract void CreatePageFile(string pageID, string file); + + public virtual string ExportNodeToHierarchicalFiles(string nodeID, string exportPath, bool createsHierarchicalFolder = true) + { + ExceptionAssertion.ThrowArgumentNullExceptionIfNull(nodeID, nameof(nodeID)); + + if (createsHierarchicalFolder) + { + var rootNode = NoteApp.GetNoteNodeHierarchy(nodeID); + if (rootNode != null) + { + return ExportFileHierarchyRecursively(rootNode, exportPath); + } + return null; + } + else + { + string rootPath; + var rootNode = NoteApp.GetNoteNodeHierarchy(nodeID); + if (rootNode != null) + { + var folderName = $"OneNote_{rootNode.Name}_{DateTime.Now.ToString("yyyyMMddHHmmss")}"; + rootPath = PathHelper.MakeUniqueFolderName(Path.Combine(exportPath, folderName)); + Directory.CreateDirectory(rootPath); + foreach (var node in rootNode.Descendants(n => n.NodeType == NodeType.Page)) + { + var filePath = Path.Combine(rootPath, GetFullPathNodeName(node, '_') + FileExtension); + CreatePageFile(node.ID, filePath); + } + return rootPath; + } + return null; + } + } + + private string ExportFileHierarchyRecursively(NoteNode parentNode, string hierarchyFolderPath) + { + if (parentNode.NodeType == NodeType.Page) + { + var file = Path.Combine(hierarchyFolderPath, parentNode.Name + FileExtension); + CreatePageFile(parentNode.ID, file); + } + string currentFolderPath; + if (parentNode.NodeType != NodeType.Page || (parentNode.NodeType == NodeType.Page && parentNode.Children.Count > 0)) + { + currentFolderPath = PathHelper.MakeUniqueFolderName(Path.Combine(hierarchyFolderPath, parentNode.Name)); + Directory.CreateDirectory(currentFolderPath); + } + else + { + currentFolderPath = hierarchyFolderPath; + } + foreach (var childNode in parentNode.Children) + { + ExportFileHierarchyRecursively(childNode, currentFolderPath); + } + + return currentFolderPath; + } + + protected string GetFullPathNodeName(NoteNode node, char seperatorChar) + { + var fileNameBuilder = new StringBuilder(); + NoteNode tmp = node; + while (tmp != null) + { + if (fileNameBuilder.Length == 0) + { + fileNameBuilder.Append(tmp.Name); + } + else + { + fileNameBuilder.Insert(0, tmp.Name + seperatorChar.ToString()); + } + tmp = tmp.Parent; + } + return fileNameBuilder.ToString(); + } + } +} diff --git a/NoteWidgetAddIn/Export/CustomFileExportor.cs b/NoteWidgetAddIn/Export/CustomFileExportor.cs new file mode 100644 index 0000000..d968d43 --- /dev/null +++ b/NoteWidgetAddIn/Export/CustomFileExportor.cs @@ -0,0 +1,47 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.IO; +using NoteWidgetAddIn.Model; + +namespace NoteWidgetAddIn.Export +{ + internal abstract class CustomFileExportor : AbstractExportor + { + protected CustomFileExportor(ExportFormat fileFormat) : base(fileFormat) { } + public override void ExportNodeToSingleFile(string nodeID, string filePath) + { + ExceptionAssertion.ThrowArgumentNullExceptionIfNull(nodeID, nameof(nodeID)); + ExceptionAssertion.ThrowArgumentNullExceptionIfNull(filePath, nameof(filePath)); + + var rootNode = NoteApp.GetNoteNodeHierarchy(nodeID); + if (rootNode != null) + { + using (var writer = BeginCreateFile(filePath)) + { + foreach (var node in rootNode.Descendants(n => n.NodeType == NodeType.Page)) + { + WriteFileContent(writer, NoteApp.GetNotePage(node.ID), GetFullPathNodeName(node, '\\')); + } + EndCreateFile(writer); + } + } + } + + protected override void CreatePageFile(string pageID, string file) + { + var page = NoteApp.GetNotePage(pageID); + using (var writer = BeginCreateFile(file)) + { + WriteFileContent(writer, page); + EndCreateFile(writer); + } + } + + protected abstract StreamWriter BeginCreateFile(string file); + protected abstract void WriteFileContent(StreamWriter writer, NotePage page, string title = null); + protected abstract void EndCreateFile(StreamWriter writer); + } +} diff --git a/NoteWidgetAddIn/Export/ExportFactory.cs b/NoteWidgetAddIn/Export/ExportFactory.cs new file mode 100644 index 0000000..f816300 --- /dev/null +++ b/NoteWidgetAddIn/Export/ExportFactory.cs @@ -0,0 +1,42 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; +using System.Text; +using System.Xml.Linq; +using Microsoft.Office.Interop.OneNote; +using NoteWidgetAddIn.Export; +using NoteWidgetAddIn.Model; + +namespace NoteWidgetAddIn +{ + public class ExportFactory + { + /// + /// Creates an instance from IExportor + /// + /// + /// + /// + public static IExportor CreateExportor(NoteApplicationContext context,ExportFormat format) + { + IExportor exportor; + if (format == ExportFormat.Markdown) + { + exportor = new MarkdownExportor(format); + } + else if (format == ExportFormat.Html) + { + exportor = new HtmlExportor(format); + } + else + { + exportor = new OneNoteHostedExporter(format); + } + exportor.SetContext(context); + + return exportor; + } + } +} diff --git a/NoteWidgetAddIn/Export/ExportFormat.cs b/NoteWidgetAddIn/Export/ExportFormat.cs new file mode 100644 index 0000000..d7e7860 --- /dev/null +++ b/NoteWidgetAddIn/Export/ExportFormat.cs @@ -0,0 +1,30 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System.ComponentModel; +using Microsoft.Office.Interop.OneNote; +using NoteWidgetAddIn.Model; + +#pragma warning disable CS3016 +namespace NoteWidgetAddIn +{ + public enum ExportFormat + { + [Description("PDF(*.pdf)")] + [RestrictedNodeType(NodeType.Notebook, NodeType.Section, NodeType.Page)] + PDF = PublishFormat.pfPDF, + [Description("XPS Document(*.xps)")] + [RestrictedNodeType(NodeType.Notebook, NodeType.Section, NodeType.Page)] + XPS = PublishFormat.pfXPS, + [Description("Single File Web Page(*.mht)")] + [RestrictedNodeType(NodeType.Section)] + MHTML = PublishFormat.pfMHTML, + [Description("Microsoft Word XML Document(*.docx)")] + [RestrictedNodeType(NodeType.Section)] + Word = PublishFormat.pfWord, + [Description("Markdown Document(*.md)")] + Markdown = 100, + [Description("Html Document from markdown(*.html)")] + Html = 101 + } +} diff --git a/NoteWidgetAddIn/Export/ExportHelper.cs b/NoteWidgetAddIn/Export/ExportHelper.cs new file mode 100644 index 0000000..7eee796 --- /dev/null +++ b/NoteWidgetAddIn/Export/ExportHelper.cs @@ -0,0 +1,50 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using NoteWidgetAddIn.Model; + +namespace NoteWidgetAddIn +{ + public class ExportHelper + { + private static IDictionary dictAvailableFormats = new Dictionary(); + public static ExportFormat[] GetAvailableExportFormats(NodeType nodeType) + { + if (!dictAvailableFormats.ContainsKey(nodeType)) + { + var result = new List(); + foreach (var e in (ExportFormat[])Enum.GetValues(typeof(ExportFormat))) + { + var restricted = e.GetRestrictedNodeTypes(); + if (restricted.Length == 0 || (e.GetRestrictedNodeTypes().Contains(nodeType))) + { + result.Add(e); + } + } + dictAvailableFormats.Add(nodeType, result.ToArray()); + } + return dictAvailableFormats[nodeType]; + } + + public static string GetExportFormatExtPattern(ExportFormat format) + { + var desc = format.GetDescription(); + var match = Regex.Match(desc, @"\(([^)]*)\)"); + if (match.Success) + { + return match.Groups[1].Value; + } + throw new InvalidOperationException($"No extension described in {format.GetType()}.{format}"); + } + + public static string GetExportFormatFileExtension(ExportFormat format) + { + return Path.GetExtension(GetExportFormatExtPattern(format)); + } + } +} diff --git a/NoteWidgetAddIn/Export/HtmlExportor.cs b/NoteWidgetAddIn/Export/HtmlExportor.cs new file mode 100644 index 0000000..c99c7c7 --- /dev/null +++ b/NoteWidgetAddIn/Export/HtmlExportor.cs @@ -0,0 +1,50 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; +using NoteWidgetAddIn.Markdown; +using NoteWidgetAddIn.Model; + +namespace NoteWidgetAddIn.Export +{ + internal class HtmlExportor : CustomFileExportor + { + public HtmlExportor(ExportFormat fileFormat) : base(fileFormat) + { + + } + protected override StreamWriter BeginCreateFile(string file) + { + var writer = File.CreateText(file); + var title = Path.GetFileNameWithoutExtension(file); + var header = HtmlTemplate.OnlineResourceTemplate.ToHead(title); + writer.WriteLine(header); + writer.Flush(); + return writer; + } + protected override void WriteFileContent(StreamWriter writer, NotePage page, string title = null) + { + if (!string.IsNullOrEmpty(title)) + { + writer.WriteLine("
"); + writer.WriteLine($"

{title}

"); + writer.WriteLine("
"); + } + writer.WriteLine(MarkdownHelper.MarkdownToHtml(page.ContentInnerText)); + writer.WriteLine("

"); + writer.Flush(); + } + + protected override void EndCreateFile(StreamWriter writer) + { + writer.WriteLine(HtmlTemplate.OnlineResourceTemplate.ToFoot()); + writer.Flush(); + } + } +} diff --git a/NoteWidgetAddIn/Export/IExportor.cs b/NoteWidgetAddIn/Export/IExportor.cs new file mode 100644 index 0000000..c9ec93c --- /dev/null +++ b/NoteWidgetAddIn/Export/IExportor.cs @@ -0,0 +1,29 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +namespace NoteWidgetAddIn +{ + public interface IExportor + { + void SetContext(NoteApplicationContext context); + /// + /// Gets export file format + /// + ExportFormat FileFormat { get; } + /// + /// Export all OneNote pages under specified node to a single file. + /// + /// A Notebook, Section Group, Section or Page ID + /// File name with absolute path + /// the file name of created single file. + void ExportNodeToSingleFile(string nodeID, string filePath); + /// + /// Export OneNote pages under specified node, Each page a file. + /// + /// A Notebook, Section Group, Section or Page ID + /// A absolute path which export file(s) will be created to + /// Creates folder for each Notebook/Section Group/Section + /// the path contains exported files + string ExportNodeToHierarchicalFiles(string nodeID, string exportPath, bool createsHierarchicalFolder = true); + } +} diff --git a/NoteWidgetAddIn/Export/MarkdownExportor.cs b/NoteWidgetAddIn/Export/MarkdownExportor.cs new file mode 100644 index 0000000..97a8fba --- /dev/null +++ b/NoteWidgetAddIn/Export/MarkdownExportor.cs @@ -0,0 +1,41 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; +using NoteWidgetAddIn.Model; + +namespace NoteWidgetAddIn.Export +{ + internal class MarkdownExportor : CustomFileExportor + { + public MarkdownExportor(ExportFormat fileFormat) : base(fileFormat) {} + + protected override StreamWriter BeginCreateFile(string file) + { + return File.CreateText(file); + } + protected override void WriteFileContent(StreamWriter writer, NotePage page, string title = null) + { + if (!string.IsNullOrEmpty(title)) + { + writer.WriteLine(new String('-', 50) + " "); + writer.WriteLine(title + " "); + writer.WriteLine(new String('-', 50) + " "); + } + writer.WriteLine(page.ContentInnerText); + writer.WriteLine("\r\n"); + writer.Flush(); + } + + protected override void EndCreateFile(StreamWriter writer) + { + //Nothing to do + } + } +} diff --git a/NoteWidgetAddIn/Export/OneNoteHostedExportor.cs b/NoteWidgetAddIn/Export/OneNoteHostedExportor.cs new file mode 100644 index 0000000..a6c3250 --- /dev/null +++ b/NoteWidgetAddIn/Export/OneNoteHostedExportor.cs @@ -0,0 +1,30 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Office.Interop.OneNote; + +namespace NoteWidgetAddIn.Export +{ + internal class OneNoteHostedExporter : AbstractExportor + { + public OneNoteHostedExporter(ExportFormat fileFormat) : base(fileFormat) + { + } + + public override void ExportNodeToSingleFile(string nodeID, string filePath) + { + ExceptionAssertion.ThrowArgumentNullExceptionIfNull(nodeID, nameof(nodeID)); + CreatePageFile(nodeID, filePath); + } + + protected override void CreatePageFile(string nodeID, string file) + { + NoteApp.Publish(nodeID, (PublishFormat)FileFormat, file); + } + } +} diff --git a/NoteWidgetAddIn/Markdown/Extension/ColorSchemeExtension.cs b/NoteWidgetAddIn/Markdown/Extension/ColorSchemeExtension.cs new file mode 100644 index 0000000..f609b56 --- /dev/null +++ b/NoteWidgetAddIn/Markdown/Extension/ColorSchemeExtension.cs @@ -0,0 +1,59 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NoteWidgetAddIn.Markdown.Extension +{ + internal class ColorSchemeExtension : ITemplateExtension + { + internal class ColorSchemeRender : ITemplateRender + { + public ColorScheme ColorScheme { get; set; } + public TemplateResourceType ResourceType { get; set; } + public void Render(HtmlTemplate template) + { + string githubCssFileName; + string schemeStyleVar; + if (ColorScheme == ColorScheme.Light) + { + githubCssFileName = "github-markdown-light.css"; + schemeStyleVar = "body{--color-previewwindow-default: #ffffff;}.notewidget-footer{--color-fg-default:#24292f;--color-canvas-default:#ffffff}"; + } + else if (ColorScheme == ColorScheme.Dark) + { + githubCssFileName = "github-markdown-dark.css"; + schemeStyleVar = "body{--color-previewwindow-default: #202020;}.notewidget-footer{--color-fg-default:#c9d1d9;--color-canvas-default:#202020;}::-webkit-scrollbar { width: 10px; height: 10px;}::-webkit-scrollbar-button { background-color: #666; }::-webkit-scrollbar-track { background-color: #202020;}::-webkit-scrollbar-track-piece { background-color: #000;}::-webkit-scrollbar-thumb { height: 50px; background-color: #666; border-radius: 3px;}::-webkit-scrollbar-corner { background-color: #202020;}::-webkit-resizer { background-color: #666;}"; + } + else //Use System Settings + { + githubCssFileName = "github-markdown.min.css"; + schemeStyleVar = "@media (prefers-color-scheme: dark) {body{--color-previewwindow-default: #202020;}.notewidget-footer{--color-fg-default:#c9d1d9;--color-canvas-default:#0d1117;}::-webkit-scrollbar { width: 10px; height: 10px;}::-webkit-scrollbar-button { background-color: #666; }::-webkit-scrollbar-track { background-color: #202020;}::-webkit-scrollbar-track-piece { background-color: #000;}::-webkit-scrollbar-thumb { height: 50px; background-color: #666; border-radius: 3px;}::-webkit-scrollbar-corner { background-color: #202020;}::-webkit-resizer { background-color: #666;}}@media(prefers-color-scheme: light){body{--color-previewwindow-default: #ffffff;}.notewidget-footer{--color-fg-default:#24292f;--color-canvas-default:#ffffff}}"; + } + string schemeStyle = ""; + + string gitHubCssUrl; + if (ResourceType == TemplateResourceType.Local) + { + gitHubCssUrl = $"http://notewidget-vitual-host/resources/css/{githubCssFileName}"; + } + else + { + gitHubCssUrl = $"https://cdn.jsdelivr.net/npm/github-markdown-css/{githubCssFileName}"; + } + template.Stylesheets.Add($""); + template.Stylesheets.Add(schemeStyle); + } + } + public void Setup(HtmlTemplateBuilder builder, TemplateResourceType resourceType) + { + var scheme = Enum.TryParse(Properties.Settings.Default.Markdown_ColorScheme ?? String.Empty, out var result) ? result : ColorScheme.System; + var render = new ColorSchemeRender { ColorScheme = scheme, ResourceType = resourceType }; + builder.Renders.Add(render); + } + } +} diff --git a/NoteWidgetAddIn/Markdown/Extension/DiagramExtension.cs b/NoteWidgetAddIn/Markdown/Extension/DiagramExtension.cs new file mode 100644 index 0000000..48ac290 --- /dev/null +++ b/NoteWidgetAddIn/Markdown/Extension/DiagramExtension.cs @@ -0,0 +1,37 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NoteWidgetAddIn.Markdown.Extension +{ + internal class DiagramExtension : ITemplateExtension + { + internal class DiagramRender : ITemplateRender + { + public TemplateResourceType ResourceType { get; set; } + public void Render(HtmlTemplate template) + { + string mermaidJsUrl; + if (ResourceType == TemplateResourceType.Local) + { + mermaidJsUrl = "http://notewidget-vitual-host/resources/js/mermaid.min.js"; + } + else + { + mermaidJsUrl = "https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"; + } + template.PostScripts.Add($""); + template.PostScripts.Add(""); + } + } + public void Setup(HtmlTemplateBuilder builder, TemplateResourceType resourceType) + { + builder.Renders.Add(new DiagramRender { ResourceType = resourceType }); + } + } +} diff --git a/NoteWidgetAddIn/Markdown/Extension/HighlightExtension.cs b/NoteWidgetAddIn/Markdown/Extension/HighlightExtension.cs new file mode 100644 index 0000000..b9da744 --- /dev/null +++ b/NoteWidgetAddIn/Markdown/Extension/HighlightExtension.cs @@ -0,0 +1,49 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NoteWidgetAddIn.Markdown.Extension +{ + internal class HighlightExtension : ITemplateExtension + { + internal class HighlightTemplateRender : ITemplateRender + { + public HighlightTheme HighlightTheme { get; set; } + public TemplateResourceType ResourceType { get; set; } + public void Render(HtmlTemplate template) + { + string highlightCssFileName; + if (HighlightTheme == HighlightTheme.Default) + { + highlightCssFileName = "prism.css"; + } + else + { + highlightCssFileName = $"prism-{HighlightTheme.ToString().ToLower()}.min.css"; + } + if (ResourceType == TemplateResourceType.Local) + { + template.Stylesheets.Add($""); + template.PostScripts.Add(""); + } + else + { + template.Stylesheets.Add($""); + template.PostScripts.Add(""); + template.PostScripts.Add(""); + } + } + } + public void Setup(HtmlTemplateBuilder builder, TemplateResourceType resourceType) + { + var theme = Enum.TryParse(Properties.Settings.Default.Markdown_HighlightTheme ?? String.Empty, out var result) ? result : HighlightTheme.Default; + var render = new HighlightTemplateRender { HighlightTheme = theme, ResourceType = resourceType }; + builder.Renders.Add(render); + } + } +} diff --git a/NoteWidgetAddIn/Markdown/HtmlTemplate.cs b/NoteWidgetAddIn/Markdown/HtmlTemplate.cs new file mode 100644 index 0000000..8dee759 --- /dev/null +++ b/NoteWidgetAddIn/Markdown/HtmlTemplate.cs @@ -0,0 +1,88 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Text; +using System.Web; +using Markdig; + +namespace NoteWidgetAddIn.Markdown +{ + public class HtmlTemplate + { + static HtmlTemplate() + { + BuildDefaultTemplates(); + } + #region consts + private const string HTML_HEAD = @" + + + + + {{HeadContent}} + {{Title}} + + +
"; + private const string HTML_FOOT = @" +
+
+

{{footer}}

+
+ {{FootContent}} + + +"; + #endregion + public static HtmlTemplate LocalResourceTemplate { get; private set; } + public static HtmlTemplate OnlineResourceTemplate { get; private set; } + public static void BuildDefaultTemplates() + { + var templateBuilder = HtmlTemplateBuilder.CreateDefaultHtmlTemplateBuilder(TemplateResourceType.Local); + LocalResourceTemplate = templateBuilder.Build(); + templateBuilder = HtmlTemplateBuilder.CreateDefaultHtmlTemplateBuilder(TemplateResourceType.Online); + OnlineResourceTemplate = templateBuilder.Build(); + } + + public IList Stylesheets { get; } + public IList Scripts { get; } + public IList PostScripts { get; } + public HtmlTemplate() + { + Stylesheets = new List(); + Scripts = new List(); + PostScripts = new List(); + } + public string ToHead(string title) + { + StringBuilder stringBuilder = new StringBuilder(); + foreach(var style in Stylesheets) + { + stringBuilder.AppendLine($"\t\t{style}"); + } + + foreach(var script in Scripts) + { + stringBuilder.AppendLine($"\t\t{script}"); + } + + return HTML_HEAD.Replace("{{HeadContent}}", stringBuilder.ToString()).Replace("{{Title}}", HttpUtility.HtmlDecode(title ?? string.Empty)); + } + public string ToFoot(string footer = null) + { + StringBuilder stringBuilder = new StringBuilder(); + foreach (var script in PostScripts) + { + stringBuilder.AppendLine($"\t\t{script}"); + } + return HTML_FOOT.Replace("{{footer}}", footer ?? string.Empty).Replace("{{FootContent}}", stringBuilder.ToString()); + } + + public string ToHtml(string title, string htmlBodyContent, string footer = null) + { + return ToHead(title) + htmlBodyContent + ToFoot(footer); + } + } +} diff --git a/NoteWidgetAddIn/Markdown/HtmlTemplateBuilder.cs b/NoteWidgetAddIn/Markdown/HtmlTemplateBuilder.cs new file mode 100644 index 0000000..2f0c520 --- /dev/null +++ b/NoteWidgetAddIn/Markdown/HtmlTemplateBuilder.cs @@ -0,0 +1,68 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NoteWidgetAddIn.Markdown.Extension; + +namespace NoteWidgetAddIn.Markdown +{ + public class HtmlTemplateBuilder + { + private HtmlTemplateBuilder(TemplateResourceType resourceType) + { + _extensions = new List(); + Renders = new List(); + ResourceType = resourceType; + } + public TemplateResourceType ResourceType { get; } + private List _extensions; + public IEnumerable Extensions + { + get + { + return _extensions; + } + } + public HtmlTemplateBuilder AddExtensionIfNotExists() where T: ITemplateExtension, new() + { + if (!Contains()) + { + _extensions.Add(new T()); + } + return this; + } + + public bool Contains() + { + return _extensions.Any(e => e is T); + } + + public static HtmlTemplateBuilder CreateDefaultHtmlTemplateBuilder(TemplateResourceType resourceType) + { + return new HtmlTemplateBuilder(resourceType) + .AddExtensionIfNotExists() + .AddExtensionIfNotExists() + .AddExtensionIfNotExists(); + } + + public List Renders { get; } + + public HtmlTemplate Build() + { + foreach(var extension in Extensions) + { + extension.Setup(this, ResourceType); + } + var template = new HtmlTemplate(); + foreach(var render in Renders) + { + render.Render(template); + } + return template; + } + } +} diff --git a/NoteWidgetAddIn/Markdown/ITemplateExtension.cs b/NoteWidgetAddIn/Markdown/ITemplateExtension.cs new file mode 100644 index 0000000..61f78c4 --- /dev/null +++ b/NoteWidgetAddIn/Markdown/ITemplateExtension.cs @@ -0,0 +1,48 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NoteWidgetAddIn.Markdown +{ + public enum TemplateResourceType + { + Local, + Online + } + public enum ColorScheme + { + /// + /// Follow system theme. Default theme. + /// + [Description("Use system setting")] + System = 0, + [Description("Light")] + Light = 1, + [Description("Dark")] + Dark = 2 + } + /// + /// Source code hight theme + /// + public enum HighlightTheme + { + Default, + Coy, + Dark, + Funky, + Okaidia, + Solarizedlight, + Tomorrow, + Twilight + } + public interface ITemplateExtension + { + void Setup(HtmlTemplateBuilder builder, TemplateResourceType resourceType); + } +} diff --git a/NoteWidgetAddIn/Markdown/ITemplateRender.cs b/NoteWidgetAddIn/Markdown/ITemplateRender.cs new file mode 100644 index 0000000..d8c95d3 --- /dev/null +++ b/NoteWidgetAddIn/Markdown/ITemplateRender.cs @@ -0,0 +1,16 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NoteWidgetAddIn.Markdown +{ + public interface ITemplateRender + { + void Render(HtmlTemplate template); + } +} diff --git a/NoteWidgetAddIn/Model/NodeType.cs b/NoteWidgetAddIn/Model/NodeType.cs new file mode 100644 index 0000000..71eaca4 --- /dev/null +++ b/NoteWidgetAddIn/Model/NodeType.cs @@ -0,0 +1,19 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NoteWidgetAddIn.Model +{ + public enum NodeType + { + Notebook, + SectionGroup, + Section, + Page + } +} diff --git a/NoteWidgetAddIn/Model/NoteNode.cs b/NoteWidgetAddIn/Model/NoteNode.cs new file mode 100644 index 0000000..cf032ba --- /dev/null +++ b/NoteWidgetAddIn/Model/NoteNode.cs @@ -0,0 +1,180 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace NoteWidgetAddIn.Model +{ + public class NoteNode + { + public NoteNode(NoteNode parent, NodeType nodeType) + { + Parent = parent; + NodeType = nodeType; + + Children = new List(); + } + + public NoteNode Parent { get; private set; } + public NodeType NodeType { get; private set; } + public bool Colored + { + get + { + bool result = false; + switch (NodeType) + { + case NodeType.Notebook: + case NodeType.Section: + result = true; + break; + } + + return result; + } + } + + public string ID { get; set; } + public string Name { get; set; } + public string Nickname { get; set; } + public string CreatedTime { get; set; } + public string LastModifiedTime { get; set; } + public bool IsCurrentlyViewed { get; set; } + public string Color { get; set; } + public string Path { get; set; } + public int PageLevel { get; set; } + + public ICollection Children { get; protected set; } + + /// + /// + /// + /// + /// + public static ICollection HierarchicalNodesFrom(string xmlContent) + { + ExceptionAssertion.ThrowArgumentNullExceptionIfNull(xmlContent, nameof(xmlContent)); + XDocument xdoc = XDocument.Parse(xmlContent); + ICollection result = new List(); + + if (xdoc != null) + { + var localName = xdoc.Root.Name.LocalName == "Notebooks" ? "Notebook" : xdoc.Root.Name.LocalName; + var nodeType = (NodeType)Enum.Parse(typeof(NodeType), localName); + var ns = xdoc.Root.Name.Namespace; + foreach (var xe in xdoc.Descendants(ns + nodeType.ToString())) + { + var node = CreateNoteNode(nodeType, null, xe); + result.Add(node); + + if (nodeType != NodeType.Page) + { + LoadChildren(node, xdoc); + } + } + } + + return result; + } + private static void LoadChildren(NoteNode parent, XDocument xdoc) + { + var desendants = xdoc.Descendants() + .Where(e => e.Parent?.Attribute("ID")?.Value == parent.ID + && !(e.Attribute("isRecycleBin")?.Value == "true" || e.Attribute("isInRecycleBin")?.Value == "true")); + foreach (var xe in desendants) + { + var nodeType = (NodeType)Enum.Parse(typeof(NodeType), xe.Name.LocalName); + + var node = CreateNoteNode(nodeType, parent, xe); + if (node.NodeType != NodeType.Page) + { + parent.Children.Add(node); + LoadChildren(node, xdoc); + } + else + { + if (node.PageLevel == 1) + { + parent.Children.Add(node); + } + else + { + NoteNode parentPage = parent.Children.LastOrDefault(); + NoteNode last = parentPage; + while (true) + { + if (node.PageLevel - parentPage.PageLevel == 1 || last == null) + { + parentPage.Children.Add(node); + break; + } + else if (last.Children.Count == 0) + { + last.Children.Add(node); + break; + } + + if (parentPage.ID != last.ID) + { + parentPage = last; + } + + last = parentPage.Children.LastOrDefault(); + } + } + } + } + } + private static NoteNode CreateNoteNode(NodeType nodeType, NoteNode parent, XElement xelement) + { + var node = new NoteNode(parent, nodeType); + node.ID = xelement.Attribute("ID")?.Value; + node.Name = xelement.Attribute("name")?.Value; + node.LastModifiedTime = xelement.Attribute("lastModifiedTime")?.Value; + node.IsCurrentlyViewed = xelement.Attribute("isCurrentlyViewed")?.Value == "true"; + + if (nodeType == NodeType.Notebook) + { + node.Color = xelement.Attribute("color")?.Value; + node.Path = xelement.Attribute("path")?.Value; + node.Nickname = xelement.Attribute("nickname")?.Value; + } + else if (nodeType == NodeType.SectionGroup) + { + node.Path = xelement.Attribute("path")?.Value; + } + else if (nodeType == NodeType.Section) + { + node.Color = xelement.Attribute("color")?.Value; + node.Path = xelement.Attribute("path")?.Value; + } + else if (nodeType == NodeType.Page) + { + node.CreatedTime = xelement.Attribute("dateTime")?.Value; + node.PageLevel = int.Parse(xelement.Attribute("pageLevel")?.Value ?? "1"); + } + + return node; + } + + #region EmptySequence + private static IEnumerable s_emptySequence; + public static IEnumerable EmptySequence + { + get + { + if (s_emptySequence == null) + { + s_emptySequence = new NoteNode[0]; + } + return s_emptySequence; + } + } + #endregion + } +} diff --git a/NoteWidgetAddIn/Model/NotePage.cs b/NoteWidgetAddIn/Model/NotePage.cs new file mode 100644 index 0000000..f8be5cf --- /dev/null +++ b/NoteWidgetAddIn/Model/NotePage.cs @@ -0,0 +1,238 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web; +using System.Xml.Linq; + +namespace NoteWidgetAddIn.Model +{ + public class NotePage + { + #region Element class + public class NoteTextRange + { + public NoteTextRange(XElement xe) + { + ExceptionAssertion.ThrowArgumentNullExceptionIfNull(xe, nameof(xe)); + if (xe.Name.LocalName != "T") + { + throw new InvalidCastException($"Invalid element: {xe.Name}. "); + } + Root = xe; + } + public XElement Root { get; } + /// + /// Raw value of element. + /// May contains formated styles. E.g.function to create embedded image string as base64]]> + /// + public string Value + { + get + { + return Root.Value; + } + } + /// + /// Inner Text of TextRange () element. + /// Formated styles is removed. + /// + public string InnerText + { + get + { + var htmlContent = Root.Value; + if (string.IsNullOrEmpty(htmlContent)) + { + return htmlContent; + } + var htmlDoc = new HtmlAgilityPack.HtmlDocument(); + htmlDoc.LoadHtml(htmlContent); + return HttpUtility.HtmlDecode(htmlDoc.DocumentNode.InnerText); + } + } + } + public class NoteImage + { + public NoteImage(XElement xe) + { + ExceptionAssertion.ThrowArgumentNullExceptionIfNull(xe, nameof(xe)); + if (xe.Name.LocalName != "Image") + { + throw new InvalidCastException($"Invalid element: {xe.Name}. "); + } + Root = xe; + } + public XElement Root { get; } + /// + /// Image format. Png, Jpg etc. + /// + public string Format + { + get + { + return Root.Attribute("format")?.Value; + } + } + public Size Size + { + get + { + var e = Root.Element(Root.Name.Namespace + "Size"); + if (e != null) + { + return new Size(int.Parse(e.Attribute("width")?.Value ?? "0"), int.Parse(e.Attribute("height")?.Value ?? "0")); + } + return new Size(0, 0); + } + } + public string Data + { + get + { + return Root.Element(Root.Name.Namespace + "Data")?.Value?.Replace("\r", "").Replace("\n", ""); + } + } + public string ToBase64Image() + { + return $"data:image/{Format};base64,{Data}"; + } + public void Save(string imageFilePath) + { + System.IO.File.WriteAllBytes(imageFilePath, Convert.FromBase64String(Data)); + } + } + #endregion + + public const string MarkdownFlag = "IsMarkdownPage"; + public NotePage(XElement xelement) + { + ExceptionAssertion.ThrowArgumentNullExceptionIfNull(xelement, nameof(xelement)); + if (xelement.Name.LocalName != "Page") + { + throw new InvalidOperationException($"Invalid argument: {xelement.Name}"); + } + Root = xelement; + PageID = Root.Attribute("ID").Value; + Namespace = Root.Name.Namespace; + } + public string PageID { get;} + public XElement Root { get;} + /// + /// Root Namespace + /// + public XNamespace Namespace { get; } + public NoteTextRange Title + { + get + { + var oe = Root.Descendants(Namespace + "Title").FirstOrDefault()?.Descendants(Namespace + "OE").FirstOrDefault(); + XElement tr = new XElement(Namespace + "T", new XCData(oe?.Value)); + return new NoteTextRange(tr); + } + set + { + var toe = Root.Descendants(Namespace + "Title").FirstOrDefault().Descendants(Namespace + "OE").FirstOrDefault(); + toe?.ReplaceNodes(new XElement(Namespace + "T", new XCData(value.Value))); + } + } + /// + /// + /// + public string ContentInnerText + { + get + { + StringBuilder contentBuilder = new StringBuilder(); + AppendElementsInnerText(contentBuilder, Root); + return contentBuilder.ToString().TrimEnd('\r', '\n'); + } + } + private void AppendElementsInnerText(StringBuilder appender, XElement container) + { + foreach(var item in container.Elements()) + { + if (item.Name.LocalName == "Outline") + { + foreach (var oe in item.Elements(Namespace + "OEChildren").Elements(Namespace + "OE")) + { + AppendElementsInnerText(appender, oe); + } + } + else if (item.Name.LocalName == "Image") + { + var ni = new NoteImage(item); + //TODO: Large image (more than 2MB) should be saved to file, not embedded in html content. + appender.AppendLine($"![]({ni.ToBase64Image()})"); + } + else if (item.Name.LocalName == "T") + { + var tr = new NoteTextRange(item); + appender.AppendLine(tr.InnerText); + } + else if (item.Name.LocalName == "Table" && item.Attribute("hasHeaderRow")?.Value == "false") + { + foreach(var oe in item.Descendants(Namespace + "OE")) + { + AppendElementsInnerText(appender, oe); + } + } + else if (item.Name.LocalName == "OEChildren") + { + foreach (var oe in item.Elements(Namespace + "OE")) + { + if (oe.Elements(Namespace + "T").Count() > 0) + { + var tr = new NoteTextRange(new XElement(Namespace + "T", new XCData(oe.Value))); + appender.AppendLine(tr.InnerText); + } + else + { + AppendElementsInnerText(appender, oe); + } + } + } + + } + } + public void AddCustomTagToTitle(string tagName, string symbol) + { + var index = 10000.ToString(); + Root.AddFirst( + new XElement(Namespace + "TagDef", + new XAttribute("index", index), + new XAttribute("type", "0"), + new XAttribute("symbol", symbol), + new XAttribute("name", tagName) + )); + var tag = new XElement(Namespace + "Tag", + new XAttribute("index", index), + new XAttribute("completed", "true"), + new XAttribute("disabled", "false") + ); + Root.Element(Namespace + "Title").Element(Namespace + "OE").AddFirst(tag); + } + public void AddCustomMeta(string name, string value) + { + if (!Root.Elements(Namespace + "Meta").Any(e => e.Attribute("name")?.Value == MarkdownFlag)) + { + Root.AddFirst(new XElement(Namespace + "Meta", + new XAttribute("name", name), + new XAttribute("content", value))); + } + } + public void SetMarkdownFlag() + { + AddCustomMeta(MarkdownFlag, "true"); + } + public override string ToString() + { + return $"NotePage PageID:{PageID}"; + } + } +} diff --git a/NoteWidgetAddIn/Model/NotePageInfo.cs b/NoteWidgetAddIn/Model/NotePageInfo.cs new file mode 100644 index 0000000..981f52e --- /dev/null +++ b/NoteWidgetAddIn/Model/NotePageInfo.cs @@ -0,0 +1,57 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using Microsoft.Office.Interop.OneNote; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NoteWidgetAddIn.Model +{ + public enum NotePageInfo + { + /// + /// Returns only basic page content, without selection markup, file types for binary data objects and binary data objects. + /// This is the standard value to pass. + /// Value = 0 + /// + Basic = PageInfo.piBasic, + /// + /// Returns page content with no selection markup, but with all binary data. + /// Value = 1 + /// + BinaryData = PageInfo.piBinaryData, + /// + /// Returns page content with selection markup, but no binary data. + /// Value = 2 + /// + Selection = PageInfo.piSelection, + /// + /// Returns page content with selection markup and all binary data. + /// Value = 3 + /// + BinaryDataSelection = PageInfo.piBinaryDataSelection, + /// + /// Returns page content with file type info for binary data objects. + /// Value = 4 + /// + FileType = PageInfo.piFileType, + /// + /// Returns page content with file type info for binary data objects and binary data objects + /// Value = 5 + /// + BinaryDataFileType = PageInfo.piBinaryDataFileType, + /// + /// Returns page content with selection markup and file type info for binary data. + /// Value = 6 + /// + SelectionFileType = PageInfo.piSelectionFileType, + /// + /// Returns all page content. + /// Value = 7 + /// + All = PageInfo.piAll, + } +} diff --git a/NoteWidgetAddIn/NLog.config b/NoteWidgetAddIn/NLog.config new file mode 100644 index 0000000..59b5838 --- /dev/null +++ b/NoteWidgetAddIn/NLog.config @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/NoteWidgetAddIn/NoteApplication.cs b/NoteWidgetAddIn/NoteApplication.cs new file mode 100644 index 0000000..5b7c915 --- /dev/null +++ b/NoteWidgetAddIn/NoteApplication.cs @@ -0,0 +1,144 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; +using Microsoft.Office.Interop.OneNote; +using NLog; +using NoteWidgetAddIn.Model; + +#pragma warning disable CS3001 // Type is not CLS-compliant +namespace NoteWidgetAddIn +{ + public sealed class NoteApplication : IDisposable + { + #region Helpers + private class SimpleWin32Window : System.Windows.Forms.IWin32Window + { + public SimpleWin32Window(long windowHandle) + { + Handle = new IntPtr(windowHandle); + } + public IntPtr Handle { get; } + } + + public static System.Windows.Forms.IWin32Window CreateWin32Window(long handle) + { + return new SimpleWin32Window(handle); + } + #endregion + + private readonly NoteApplicationContext _context; + private IApplication _application; + internal NoteApplication(NoteApplicationContext context) + { + _context = context; + _application = context.ResisteredApplicationType == null? new Application() : + (IApplication)Activator.CreateInstance(_context.ResisteredApplicationType); + } + + public string CurrentNotebookID => _application?.Windows.CurrentWindow?.CurrentNotebookId; + public string CurrentSectionGroupID => _application?.Windows.CurrentWindow?.CurrentSectionGroupId; + public string CurrentSectionID => _application?.Windows.CurrentWindow?.CurrentSectionId; + public string CurrentPageID => _application?.Windows.CurrentWindow?.CurrentPageId; + public System.Windows.Forms.IWin32Window CreateCurrentWin32Window() => CreateWin32Window((long)(_application?.Windows.CurrentWindow?.WindowHandle ?? 0)); + + #region NoteNode + public ICollection GetAllNotebookHierarchy() + { + var xmlOut = GetHierarchy(null, HierarchyScope.hsPages); + + if (!string.IsNullOrEmpty(xmlOut)) + { + return NoteNode.HierarchicalNodesFrom(xmlOut); + } + + return new List(); + } + public NoteNode GetNoteNodeHierarchy(string startNodeId) + { + if (string.IsNullOrEmpty(startNodeId)) + throw new ArgumentNullException("startNodeId"); + + _application.GetHierarchy(startNodeId, HierarchyScope.hsPages, out var xmlOut); + + if (!string.IsNullOrEmpty(xmlOut)) + { + return NoteNode.HierarchicalNodesFrom(xmlOut).FirstOrDefault(); + } + return null; + } + #endregion + + #region Hierarchy + /// + /// Returns xml hierarchy for specified startNodeID. Returns all notebooks hierarchy if startNodeID is null. + /// + /// An ID of a Notebook/Section Group/Section/Page. + /// + /// + public string GetHierarchy(string startNodeID, HierarchyScope scope) + { + _application.GetHierarchy(startNodeID, scope, out var xmlOut); + return xmlOut; + } + #endregion + + #region Page Content + public void NewMarkdownPage() + { + var sectionID = CurrentSectionID; + if (sectionID == null) + { + throw new InvalidOperationException("No Section is currently viewed."); + } + _application.CreateNewPage(sectionID, out var newPageID); + var page = GetNotePage(newPageID); + page.SetMarkdownFlag(); + UpdatePage(page); + } + public NotePage GetCurrentNotePage(NotePageInfo pageInfo = NotePageInfo.All) + { + return GetNotePage(CurrentPageID, pageInfo); + } + public NotePage GetNotePage(string pageID, NotePageInfo pageInfo = NotePageInfo.All) + { + ExceptionAssertion.ThrowArgumentNullExceptionIfNull(pageID, nameof(pageID)); + _application.GetPageContent(pageID, out var xmlOut, (PageInfo)pageInfo, XMLSchema.xs2013); + if (!string.IsNullOrEmpty(xmlOut)) + { + return new NotePage(XElement.Parse(xmlOut)); + } + return null; + } + + public void DeletePageContent(string pageID, string objectID) + { + _application.DeletePageContent(pageID, objectID); + } + + public void UpdatePage(NotePage page) + { + _application.UpdatePageContent(page.Root.ToString(SaveOptions.DisableFormatting), DateTime.MinValue, XMLSchema.xs2013, true); + } + #endregion + + #region Export + public void Publish(string nodeID, PublishFormat format, string filePath) + { + ExceptionAssertion.ThrowArgumentNullExceptionIfNull(nodeID, nameof(nodeID)); + ExceptionAssertion.ThrowArgumentNullExceptionIfNull(filePath, nameof(filePath)); + _application.Publish(nodeID, filePath, format); + } + #endregion + + #region IDisposable + public void Dispose() + { + _application = null; + } + #endregion + } +} diff --git a/NoteWidgetAddIn/NoteApplicationContext.cs b/NoteWidgetAddIn/NoteApplicationContext.cs new file mode 100644 index 0000000..1ce96e0 --- /dev/null +++ b/NoteWidgetAddIn/NoteApplicationContext.cs @@ -0,0 +1,39 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using Microsoft.Office.Interop.OneNote; + +namespace NoteWidgetAddIn +{ + public class NoteApplicationContext + { + public NoteApplicationContext() + { + ResisteredApplicationType = null; + } + /// + /// Construct context with specified type. Usually for testing purpose + /// + /// A type inherit from IApplication + /// + public NoteApplicationContext(Type type) + { + ExceptionAssertion.ThrowArgumentNullExceptionIfNull(type, nameof(type)); + if (!typeof(IApplication).IsAssignableFrom(type)) + { + throw new InvalidOperationException($"{nameof(type)} '{type.FullName}' must be derived from Microsoft.Office.Interop.OneNote.IApplication"); + } + if (type.IsInterface || type.IsAbstract) + { + throw new InvalidOperationException($"{nameof(type)} cannot be interface or abstract."); + } + ResisteredApplicationType = type; + } + public Type ResisteredApplicationType { get;} + public NoteApplication CreateApplication() + { + return new NoteApplication(this); + } + } +} diff --git a/NoteWidgetAddIn/NoteWidgetAddIn.csproj b/NoteWidgetAddIn/NoteWidgetAddIn.csproj new file mode 100644 index 0000000..b47c43e --- /dev/null +++ b/NoteWidgetAddIn/NoteWidgetAddIn.csproj @@ -0,0 +1,280 @@ + + + + + Debug + AnyCPU + {280104F5-13BA-4193-BFF5-5A5C48261640} + Library + Properties + NoteWidgetAddIn + NoteWidgetAddIn + v4.7.2 + 512 + true + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + bin\Publish\ + DEBUG;TRACE + full + AnyCPU + 7.3 + prompt + + + true + bin\Win32\Publish\ + AnyCPU + 7.3 + + + + True + + + ..\packages\HtmlAgilityPack.1.11.40\lib\Net45\HtmlAgilityPack.dll + + + ..\packages\Markdig.0.26.0\lib\net452\Markdig.dll + + + ..\packages\Microsoft.Web.WebView2.1.0.1054.31\lib\net45\Microsoft.Web.WebView2.Core.dll + + + ..\packages\Microsoft.Web.WebView2.1.0.1054.31\lib\net45\Microsoft.Web.WebView2.WinForms.dll + + + ..\packages\Microsoft.Web.WebView2.1.0.1054.31\lib\net45\Microsoft.Web.WebView2.Wpf.dll + + + ..\packages\NLog.4.7.13\lib\net45\NLog.dll + + + + + + ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll + + + + + + + ..\packages\System.Memory.4.5.4\lib\net461\System.Memory.dll + + + + ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll + + + ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.3\lib\net461\System.Runtime.CompilerServices.Unsafe.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Form + + + AdvancedSettingsDialog.cs + + + + Form + + + ExportToPathDialog.cs + + + + + + + + + + + True + True + Resources.resx + + + True + True + Settings.settings + + + + + + + + + WebBrowserWindow.xaml + + + + + + ResXFileCodeGenerator + Designer + Resources.Designer.cs + + + AdvancedSettingsDialog.cs + + + ExportToPathDialog.cs + + + + + + + + + {2DF8D04C-5BFA-101B-BDE5-00AA0044DE52} + 2 + 8 + 0 + primary + False + True + + + {0EA692EE-BB50-4E3C-AEF0-356D91732725} + 1 + 1 + 0 + tlbimp + False + True + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + Designer + XamlIntelliSenseFileGenerator + + + Designer + MSBuild:Compile + + + + + + + + + + + + + + + + + + if $(ConfigurationName) == Publish ( +taskkill /fi "pid gt 0" /im ONENOTE.exe +) + + + if $(ConfigurationName) == Publish ( +call C:\Users\efrey\source\Github\OneNoteWidget\powershell-copyfile-runas.bat "$(TargetPath)" "C:\Program Files (x86)\EKStudio\NoteWidget\$(TargetFileName)" +) + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/NoteWidgetAddIn/Properties/AssemblyInfo.cs b/NoteWidgetAddIn/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0faeb06 --- /dev/null +++ b/NoteWidgetAddIn/Properties/AssemblyInfo.cs @@ -0,0 +1,42 @@ +// Copyright (c) Efrey Kong. All Rights Reserved. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NoteWidgetAddIn")] +[assembly: AssemblyDescription("OneNote Widget AddIn")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("EKStudio")] +[assembly: AssemblyProduct("NoteWidgetAddIn")] +[assembly: AssemblyCopyright("Copyright \u00a9 EKStudio. 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(true)] +[assembly: CLSCompliant(true)] +[assembly: InternalsVisibleTo("NoteWidgetTests")] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("01307420-95EF-4ACA-A55A-A49BFACD2EEE")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/NoteWidgetAddIn/Properties/Resources.Designer.cs b/NoteWidgetAddIn/Properties/Resources.Designer.cs new file mode 100644 index 0000000..d424a78 --- /dev/null +++ b/NoteWidgetAddIn/Properties/Resources.Designer.cs @@ -0,0 +1,154 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace NoteWidgetAddIn.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NoteWidgetAddIn.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon documents { + get { + object obj = ResourceManager.GetObject("documents", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap markdown { + get { + object obj = ResourceManager.GetObject("markdown", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon markdown_icon { + get { + object obj = ResourceManager.GetObject("markdown_icon", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap markdownflag { + get { + object obj = ResourceManager.GetObject("markdownflag", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap openfolder { + get { + object obj = ResourceManager.GetObject("openfolder", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized string similar to <?xml version="1.0" encoding="utf-8" ?> + ///<customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" loadImage="GetImage"> + /// <ribbon> + /// <tabs> + /// <tab idMso="TabHome"> + /// <group id="groupNoteWidgetMarkdown" label="Markdown"> + /// <button id="viewMarkdownAsHtmlButton" size="large" + /// label="Preview" + /// screentip="Preview markdown content as Html document" + /// onAction="PreviewMarkdownCmd" + /// image="markdown.png" + /// /> + /// <butto [rest of string was truncated]";. + /// + internal static string ribbon { + get { + return ResourceManager.GetString("ribbon", resourceCulture); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). + /// + internal static System.Drawing.Icon settingicon { + get { + object obj = ResourceManager.GetObject("settingicon", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap settings { + get { + object obj = ResourceManager.GetObject("settings", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/NoteWidgetAddIn/Properties/Resources.resx b/NoteWidgetAddIn/Properties/Resources.resx new file mode 100644 index 0000000..02d2008 --- /dev/null +++ b/NoteWidgetAddIn/Properties/Resources.resx @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + documents.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + markdown.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + markdownflag.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + markdown_icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + openfolder.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ribbon.xml;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 + + + setting.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + settings.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/NoteWidgetAddIn/Properties/Settings.Designer.cs b/NoteWidgetAddIn/Properties/Settings.Designer.cs new file mode 100644 index 0000000..cd9a9a5 --- /dev/null +++ b/NoteWidgetAddIn/Properties/Settings.Designer.cs @@ -0,0 +1,110 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace NoteWidgetAddIn.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("System")] + public string Markdown_ColorScheme { + get { + return ((string)(this["Markdown_ColorScheme"])); + } + set { + this["Markdown_ColorScheme"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Default")] + public string Markdown_HighlightTheme { + get { + return ((string)(this["Markdown_HighlightTheme"])); + } + set { + this["Markdown_HighlightTheme"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1200")] + public double Markdown_Preview_Width { + get { + return ((double)(this["Markdown_Preview_Width"])); + } + set { + this["Markdown_Preview_Width"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1000")] + public double Markdown_Preview_Height { + get { + return ((double)(this["Markdown_Preview_Height"])); + } + set { + this["Markdown_Preview_Height"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool Markdown_Preview_Singleton { + get { + return ((bool)(this["Markdown_Preview_Singleton"])); + } + set { + this["Markdown_Preview_Singleton"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1500")] + public double Markdown_CheatSheet_Width { + get { + return ((double)(this["Markdown_CheatSheet_Width"])); + } + set { + this["Markdown_CheatSheet_Width"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1000")] + public double Markdown_CheatSheet_Height { + get { + return ((double)(this["Markdown_CheatSheet_Height"])); + } + set { + this["Markdown_CheatSheet_Height"] = value; + } + } + } +} diff --git a/NoteWidgetAddIn/Properties/Settings.settings b/NoteWidgetAddIn/Properties/Settings.settings new file mode 100644 index 0000000..b4984a1 --- /dev/null +++ b/NoteWidgetAddIn/Properties/Settings.settings @@ -0,0 +1,27 @@ + + + + + + System + + + Default + + + 1200 + + + 1000 + + + True + + + 1500 + + + 1000 + + + \ No newline at end of file diff --git a/NoteWidgetAddIn/Properties/documents.ico b/NoteWidgetAddIn/Properties/documents.ico new file mode 100644 index 0000000..1f15c3d Binary files /dev/null and b/NoteWidgetAddIn/Properties/documents.ico differ diff --git a/NoteWidgetAddIn/Properties/markdown.png b/NoteWidgetAddIn/Properties/markdown.png new file mode 100644 index 0000000..949d973 Binary files /dev/null and b/NoteWidgetAddIn/Properties/markdown.png differ diff --git a/NoteWidgetAddIn/Properties/markdown_icon.ico b/NoteWidgetAddIn/Properties/markdown_icon.ico new file mode 100644 index 0000000..8fe43ea Binary files /dev/null and b/NoteWidgetAddIn/Properties/markdown_icon.ico differ diff --git a/NoteWidgetAddIn/Properties/markdownflag.png b/NoteWidgetAddIn/Properties/markdownflag.png new file mode 100644 index 0000000..d928418 Binary files /dev/null and b/NoteWidgetAddIn/Properties/markdownflag.png differ diff --git a/NoteWidgetAddIn/Properties/openfolder.png b/NoteWidgetAddIn/Properties/openfolder.png new file mode 100644 index 0000000..be98289 Binary files /dev/null and b/NoteWidgetAddIn/Properties/openfolder.png differ diff --git a/NoteWidgetAddIn/Properties/ribbon.xml b/NoteWidgetAddIn/Properties/ribbon.xml new file mode 100644 index 0000000..2f4b4e9 --- /dev/null +++ b/NoteWidgetAddIn/Properties/ribbon.xml @@ -0,0 +1,102 @@ + + + + + + +