/// <summary> /// Tag a singe OneNote page /// </summary> /// <remarks> /// A tagged page is not saved immediately. The caller can hold on to a previously /// returned page and pass it into this method again. This avoids saving a page /// multiple times, if there are subsequent tagging jobs for the same page. If the /// ID of the page passed into this method does not match the ID of this job, the /// passed in page is saved. /// </remarks> /// <param name="onenote">OneNote application proxy object</param> /// <param name="page"> an unsaved OneNote page which has been tagged previously</param> /// <returns>Unsaved, tagged OneNote page.</returns> internal OneNotePageProxy Execute(OneNoteProxy onenote, OneNotePageProxy page) { if (page == null) { page = new OneNotePageProxy(onenote, _pageid); if (page.IsDeleted) { return(null); } } else if (!_pageid.Equals(page.PageID)) { // cannot continue with the given page page.Update(); page = new OneNotePageProxy(onenote, _pageid); } HashSet <string> pagetags = new HashSet <string>(page.PageTags); int countBefore = pagetags.Count; switch (_op) { case TagOperation.SUBTRACT: pagetags.ExceptWith(_tags); break; case TagOperation.UNITE: pagetags.UnionWith(_tags); break; case TagOperation.REPLACE: pagetags.Clear(); pagetags.UnionWith(_tags); break; } if ((pagetags.Count != countBefore) || _op == TagOperation.REPLACE) { string[] sortedTags = pagetags.ToArray(); Array.Sort <string>(sortedTags, (x, y) => string.Compare(x, y, true)); page.PageTags = sortedTags; } return(page); }
/// <summary> /// Run the background tagger. /// </summary> /// <returns></returns> public Task Run() { TaskFactory tf = new TaskFactory(TaskCreationOptions.LongRunning, TaskContinuationOptions.None); CancellationToken cancel = _cancel.Token; return(tf.StartNew(() => { TraceLogger.Log(TraceCategory.Info(), "Background tagging service started"); try { OneNotePageProxy lastPage = null; while (!_jobs.IsCompleted) { TaggingJob j = _jobs.Take(); cancel.ThrowIfCancellationRequested(); try { lastPage = j.Execute(_onenote, lastPage); if (lastPage != null && _jobs.Count == 0) { // no more pending pages - must update the last one and stop carrying forward lastPage.Update(); lastPage = null; } } catch (Exception e) { lastPage = null; TraceLogger.ShowGenericErrorBox("page tagging failed", e); } } } catch (InvalidOperationException) { TraceLogger.Log(TraceCategory.Warning(), "Background tagging job queue depleted"); TraceLogger.Flush(); } catch (OperationCanceledException) { TraceLogger.Log(TraceCategory.Warning(), "Background tagging canceled"); TraceLogger.Flush(); } }, cancel)); }
/// <summary> /// Remove or rename tag from suggestions when user control is tapped. /// </summary> /// <param name="sender">user control emitting this event</param> /// <param name="e"> event details</param> private void Tag_Action(object sender, RoutedEventArgs e) { var rt = sender as RemovableTag; var rt_mdl = rt.DataContext as RemovableTagModel; string[] toRemove = new string[] { rt_mdl.Key }; if ("DeleteTag".Equals(rt.Tag)) { _model.SuggestedTags.RemoveAll(toRemove); // schedule all pages with this tag for tag removal if (rt_mdl.Tag != null) { foreach (var tp in rt_mdl.Tag.Pages) { _model.OneNoteApp.TaggingService.Add(new TaggingJob(tp.ID, toRemove, TagOperation.SUBTRACT)); } suggestedTags.Notification = rt_mdl.Tag.Pages.Count == 0 ? Properties.Resources.TagEditor_Popup_NothingTagged : string.Format(Properties.Resources.TagEditor_Popup_TaggingInProgress, rt_mdl.Tag.Pages.Count); TraceLogger.Log(TraceCategory.Info(), "{0} page(s) enqueued for background tagging; Operation SUBTRACT {1}", rt_mdl.Tag.Pages.Count, toRemove[0]); } else { suggestedTags.Notification = Properties.Resources.TagEditor_Popup_NothingTagged; } } else if ("RenameTag".Equals(rt.Tag)) { _model.SuggestedTags.RemoveAll(toRemove); string[] newTagNames = (from tn in OneNotePageProxy.ParseTags(rt_mdl.LocalName) select TagFormatter.Format(tn)).ToArray(); // create new tag models unless they already exist List <RemovableTagModel> newTagModels = new List <RemovableTagModel>(); foreach (var newName in newTagNames) { RemovableTagModel tagmodel; if (!_model.SuggestedTags.TryGetValue(newName, out tagmodel)) { tagmodel = new RemovableTagModel() { Tag = new TagPageSet(newName) }; newTagModels.Add(tagmodel); } else if (tagmodel.Tag == null && rt_mdl.Tag != null) { tagmodel.Tag = new TagPageSet(newName); } if (rt_mdl.Tag != null) { // copy the pages into the new tag and update the tag count foreach (var pg in rt_mdl.Tag.Pages) { tagmodel.Tag.Pages.Add(pg); } tagmodel.UseCount = tagmodel.Tag.Pages.Count; } } _model.SuggestedTags.AddAll(newTagModels); if (rt_mdl.Tag != null) { // remove the old tag and add new tag to the pages foreach (var tp in rt_mdl.Tag.Pages) { _model.OneNoteApp.TaggingService.Add(new TaggingJob(tp.ID, toRemove, TagOperation.SUBTRACT)); _model.OneNoteApp.TaggingService.Add(new TaggingJob(tp.ID, newTagNames, TagOperation.UNITE)); } suggestedTags.Notification = rt_mdl.Tag.Pages.Count == 0 ? Properties.Resources.TagEditor_Popup_NothingTagged : string.Format(Properties.Resources.TagEditor_Popup_TaggingInProgress, rt_mdl.Tag.Pages.Count); TraceLogger.Log(TraceCategory.Info(), "{0} page(s) enqueued for background tagging; Operation UNITE {1} SUBTRACT {2}", rt_mdl.Tag.Pages.Count, string.Join(",", newTagNames), toRemove[0]); } else { suggestedTags.Notification = Properties.Resources.TagEditor_Popup_NothingTagged; } } TraceLogger.Flush(); _model.SaveChanges(); }