Skip to content

Commit

Permalink
Complete Merge command
Browse files Browse the repository at this point in the history
  • Loading branch information
stevenmcohn committed Jul 14, 2020
1 parent d2f329b commit d0c7652
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 50 deletions.
2 changes: 1 addition & 1 deletion AssemblyInfo.Shared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ internal static partial class AssemblyInfo
public const string Company = "River Software";
public const string Copyright = "Copyright \u00a9 2016 Steven M Cohn. All rights reserved.";

public const string Version = "2.5.2";
public const string Version = "2.6";
public const string FileVersion = Version;

public const string Configuration =
Expand Down
40 changes: 38 additions & 2 deletions OneMore/ApplicationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,30 @@ public void NavigateTo (string pageTag)
}



// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Editing...


// https://docs.microsoft.com/en-us/office/client-developer/onenote/application-interface-onenote#deletehierarchy-method

/// <summary>
/// Deletes the given object(s) from the hierarchy; used for merging
/// </summary>
/// <param name="element"></param>
public void DeleteHierarchy(string objectId)
{
try
{
Application.DeleteHierarchy(objectId);
}
catch (Exception exc)
{
logger.WriteLine($"MGR ERROR deleting hierarchy object {objectId}");
}
}


// https://docs.microsoft.com/en-us/office/client-developer/onenote/application-interface-onenote#updatehierarchy-method

/// <summary>
Expand All @@ -251,7 +275,17 @@ public void NavigateTo (string pageTag)
public void UpdateHierarchy (XElement element)
{
string xml = element.ToString(SaveOptions.DisableFormatting);
Application.UpdateHierarchy(xml);

try
{
Application.UpdateHierarchy(xml);
}
catch (Exception exc)
{
logger.WriteLine("MGR ERROR updating hierarchy", exc);
logger.WriteLine(element.ToString());
logger.WriteLine();
}
}


Expand All @@ -269,7 +303,9 @@ public void UpdatePageContent (XElement element)
}
catch (Exception exc)
{
logger.WriteLine("ERROR updating page content", exc);
logger.WriteLine("MGR ERROR updating page content", exc);
logger.WriteLine(element.ToString());
logger.WriteLine();
}
}

Expand Down
142 changes: 95 additions & 47 deletions OneMore/Commands/MergeCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

namespace River.OneMoreAddIn
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
Expand All @@ -14,19 +13,24 @@ namespace River.OneMoreAddIn
internal class MergeCommand : Command
{

private struct Bounds
{
public double Top;
public double Height;
}
private const int OutlineMargin = 30;


private class QuickRef
{
public XElement Element { get; private set; }
public QuickStyleDef Style { get; private set; }
public QuickRef(XElement element) { Element = element; Style = new QuickStyleDef(element); }
public string OriginalIndex { get; private set; }

public QuickRef(XElement element)
{
Element = element;
Style = new QuickStyleDef(element);
OriginalIndex = Style.Index.ToString();
}
}


private XElement page;
private XNamespace ns;
private List<QuickRef> quickies;
Expand Down Expand Up @@ -70,122 +74,166 @@ public void Execute()
}


// get first selected (active) page, reference its quick styles, outline, size

//System.Diagnostics.Debugger.Launch();
// get first selected (active) page and reference its quick styles, outline, size

page = manager.GetPage(active.Attribute("ID").Value);

quickies = page.Elements(ns + "QuickStyleDef")
.Select(p => new QuickRef(p))
.ToList();

var outline = page.Elements(ns + "Outline").LastOrDefault();
var bounds = GetBounds(ns, outline);
var offset = GetPageBottomOffset();

// track running bottom as we add new outlines
var maxOffset = offset;

// find maximum z-offset
var z = page.Elements(ns + "Outline").Elements(ns + "Position")
.Attributes("z").Max(a => int.Parse(a.Value)) + 1;

// merge each of the subsequently selected pages into the active page

foreach (var selected in selections.ToList())
foreach (var selection in selections.ToList())
{
var childPage = manager.GetPage(selected.Attribute("ID").Value);
var childPage = manager.GetPage(selection.Attribute("ID").Value);

var styles = MergeQuickStyles(childPage);

var topOffset = childPage.Elements(ns + "Outline")
.Elements(ns + "Position").Min(p => double.Parse(p.Attribute("y").Value));

foreach (var childOutline in childPage.Elements(ns + "Outline"))
{
var childBounds = GetBounds(ns, childOutline);
// adjust position relative to new parent page outlines
var position = childOutline.Elements(ns + "Position").FirstOrDefault();
position.Attribute("y").Value = (childBounds.Top + bounds.Height + 30).ToString("#0.0");
bounds.Height += childBounds.Height + 30;
var y = double.Parse(position.Attribute("y").Value) - topOffset + offset + OutlineMargin;
position.Attribute("y").Value = y.ToString("#0.0");

// keep track of lowest bottom
var size = childOutline.Elements(ns + "Size").FirstOrDefault();
var bottom = y + double.Parse(size.Attribute("height").Value);
if (bottom > maxOffset)
{
maxOffset = bottom;
}

position.Attribute("z").Value = z.ToString();
z++;

// remove its IDs so the page can apply its own
childOutline.Attributes("objectID").Remove();
childOutline.Descendants().Attributes("objectID").Remove();

ResolveQuickStyles(childPage, childOutline);
AdjustQuickStyles(styles, childOutline);

page.Add(childOutline);
}

// remove selected (child) page from the section
selected.Remove();
if (maxOffset > offset)
{
offset = maxOffset;
}
}

// update page and section hierarchy

manager.UpdatePageContent(page);
manager.UpdateHierarchy(section);

foreach (var selection in selections)
{
manager.DeleteHierarchy(selection.Attribute("ID").Value);
}
}
}


private Bounds GetBounds(XNamespace ns, XElement outline)
private double GetPageBottomOffset()
{
var size = outline.Elements(ns + "Size").FirstOrDefault();
if (size != null)
// find bottom of current page; bottom of lowest Outline
double offset = 0.0;
foreach (var outline in page.Elements(ns + "Outline"))
{
var position = outline.Elements(ns + "Position").FirstOrDefault();
if (position != null)
{
return new Bounds
var size = outline.Elements(ns + "Size").FirstOrDefault();
if (size != null)
{
Top = (int)Math.Ceiling(double.Parse(position.Attribute("y").Value)),
Height = (int)Math.Ceiling(double.Parse(size.Attribute("height").Value))
};
var bottom = double.Parse(position.Attribute("y").Value)
+ double.Parse(size.Attribute("height").Value);

if (bottom > offset)
{
offset = bottom;
}
}
}
}

return new Bounds { Top = 0, Height = 0 };
return offset;
}


private void ResolveQuickStyles(XElement childPage, XElement childOutline)
private List<QuickRef> MergeQuickStyles(XElement childPage)
{
// resolve quick styles
var childQuickies = childPage.Elements(ns + "QuickStyleDef")
var styleRefs = childPage.Elements(ns + "QuickStyleDef")
.Where(p => p.Attribute("name")?.Value != "PageTitle")
.Select(p => new QuickRef(p))
.ToList();

foreach (var childQuickie in childQuickies)
{
var staleIndex = childQuickie.Style.Index.ToString();
// next available index; O(n) is OK here; there should only be a few
var index = quickies.Max(q => q.Style.Index) + 1;

var quickie = quickies.Find(q => q.Style.Equals(childQuickie.Style));
foreach (var styleRef in styleRefs)
{
var quickie = quickies.Find(q => q.Style.Equals(styleRef.Style));
var same = false;
if (quickie != null)
{
// found match but is it the same index?
same = quickie.Style.Index == childQuickie.Style.Index;
same = quickie.Style.Index == styleRef.Style.Index;
}
else
{
// no match so add it and set index to maxIndex+1
// O(n) is OK here; there should only be a few
childQuickie.Style.Index = quickies.Max(q => q.Style.Index) + 1;
childQuickie.Element.Attribute("index").Value = childQuickie.Style.Index.ToString();
quickies.Add(childQuickie);
styleRef.Style.Index = index++;
styleRef.Element.Attribute("index").Value = styleRef.Style.Index.ToString();
quickies.Add(styleRef);

var last = page.Elements(ns + "QuickStyleDef").LastOrDefault();
if (last != null)
{
last.AddAfterSelf(childQuickie.Element);
last.AddAfterSelf(styleRef.Element);
}
else
{
page.AddFirst(childQuickie.Element);
page.AddFirst(styleRef.Element);
}
}
}

if (!same)
return styleRefs;
}


private void AdjustQuickStyles(List<QuickRef> styles, XElement childOutline)
{
// need to reverse sort the styles so the logic doesn't continually overwrite
// subsequent index references

foreach (var style in styles.OrderByDescending(s => s.Style.Index))
{
if (style.OriginalIndex != style.Style.Index.ToString())
{
// apply new index to child outline elements

var quickStyleIndex = childQuickie.Style.Index.ToString();

var elements = childOutline.Descendants()
.Where(e => e.Attribute("quickStyleIndex")?.Value == staleIndex);
.Where(e => e.Attribute("quickStyleIndex")?.Value == style.OriginalIndex);

foreach (var element in elements)
{
element.Attribute("quickStyleIndex").Value = quickStyleIndex;
element.Attribute("quickStyleIndex").Value = style.Style.Index.ToString();
}
}
}
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ A OneNote add-in with powerful yet simple and effective features including:
* Manage menu of [**Favorites**](#favorites) for one-click link to your most referenced pages
* [Paste Rich Text](#exCodeBox) (preserve colors when **pasting code** from Visual Studio)<sup>1</sup>
* [**Search and replace**](#other) text on the current page
* Merge pages, preserving formatting and position of outlines
* Add or remove [**footnotes**](#footnotes) (endnotes)

*Want more from OneMore? OneMore has more...*
Expand Down

0 comments on commit d0c7652

Please sign in to comment.