public SubfolderBranchFinder( IElement parent, ElemCreationFlags options) { _parent = parent; _maxChildren = Core.SM.UI.ElementWdw.LimitChildrenCount; _subFolders = parent.Children .Choose(TryMatchSubfolder) .ToList(); _it = options.HasFlag(ElemCreationFlags.ReuseSubFolders) ? 1 : Math.Max(1, _subFolders.Count); //while (newElemCount > 0) //{ // SubfolderData data; // if (i < _subFolders.Count) // { // var (child, sfNo) = _subFolders[i]; // data = new SubfolderData(child.Id, sfNo, _maxChildren - child.ChildrenCount); // } // else // { // data = new SubfolderData(null, ++folderCount, _maxChildren); // } // newElemCount -= data.SlotsLeft; // _data.Add(data); //} }
private int FindDestinationBranch(IElement parent, ElemCreationFlags options) { if (options.HasFlag(ElemCreationFlags.CreateSubfolders) == false) { return(CanAddElement(parent, options) ? parent.Id : -1); } var regExSubfolders = new Regex($"\\[([0-9]+)\\] {Regex.Escape(parent.Title)}"); var subFolders = parent.Children .Select(child => (child, regExSubfolders.Match(child.Title))) .Where(p => p.Item2.Success) .ToList(); if (subFolders.Any() == false) { return(CreateAutoSubfolders(parent, 1)); } var subFolder = subFolders.MaxBy(p => int.Parse(p.Item2.Groups[1].Value)) .First(); if (subFolder.child == null || CanAddElement(subFolder.child, ElemCreationFlags.None) == false) { return(CreateAutoSubfolders( parent, subFolder.child == null ? 1 : int.Parse(subFolder.Item2.Groups[1].Value) + 1 )); } return(subFolder.child.Id); }
public bool Add(out List <ElemCreationResult> results, ElemCreationFlags options, params ElementBuilder[] builders) { results = AsyncContext.Run(() => AddInternalAsync(options, builders)); return(results != null); }
// TODO: Implement after finishing the new hook engine private Task <bool> AddBulkInternalAsync( IEnumerable <ElementBuilder> builder, ElemCreationFlags options, int parentId) { var successFlag = ElemCreationResultCodes.Success; try { // // Create or use auto subfolder, if requested parentId = FindDestinationBranch(this[parentId], options); if (parentId <= 0 || this[parentId] == null) { return(ElemCreationResultCodes.ErrorTooManyChildren); } // // Has a concept been specified for the new element ? if (builder.ConceptId != null) { if (Core.SM.UI.ElementWdw.SetCurrentConcept(builder.ConceptId) == false) { successFlag |= ElemCreationResultCodes.WarningConceptNotSet; } } // // Make sure concept & root are valid if (AdjustRoot(parentId)) { successFlag |= ElemCreationResultCodes.WarningConceptNotSet; } // // Set hook *after* adjusting concept Core.SM.UI.ElementWdw.CurrentHookId = parentId; // // Create return(AddElement(builder, out elemId) ? successFlag : ElemCreationResultCodes.ErrorUnknown); } catch (Exception ex) { LogTo.Error(ex, "Exception caught while adding new element"); return(ElemCreationResultCodes.ErrorUnknown); } }
// // private bool CanAddElement(IElement parent, ElemCreationFlags options) { if (options.HasFlag(ElemCreationFlags.ForceCreate)) { return(true); } return(parent.ChildrenCount < Core.SMA.CollectionConfig.ChildrenPerBranch); }
// // private bool CanAddElement(IElement parent, ElemCreationFlags options) { if (options.HasFlag(ElemCreationFlags.ForceCreate)) { return(true); } return(parent.ChildrenCount < Core.SM.UI.ElementWdw.LimitChildrenCount); }
private ElemCreationResultCode AddElement(ElementBuilder builder, ElemCreationFlags options, int originalHookId, out int elemId) { elemId = -1; try { // // Has a parent been specified for the new element ? var parentId = builder.Parent?.Id ?? originalHookId; // // Create or use auto subfolder, if requested parentId = FindDestinationBranch(this[parentId], options); if (parentId <= 0) { return(ElemCreationResultCode.TooManyChildrenError); } Core.SM.UI.ElementWdw.CurrentHookId = parentId; // // Has a concept been specified for the new element ? //if (builder.Concept != null) //Core.SM.UI.ElementWdw.SetCurrentConcept(builder.Concept.Id); return(AddElement(builder, out elemId) ? ElemCreationResultCode.Success : ElemCreationResultCode.UnknownError); } catch (Exception ex) { LogTo.Error(ex, "Exception caught while adding new element"); return(ElemCreationResultCode.UnknownError); } }
private async Task <List <ElemCreationResult> > AddInternalAsync( ElemCreationFlags options, ElementBuilder[] builders) { if (builders == null || builders.Length == 0) { return(null); } var inSMUpdateLockMode = false; var inSMAUpdateLockMode = false; var singleMode = builders.Length == 1; var results = new List <ElemCreationResult>( builders.Select( b => new ElemCreationResult(ElemCreationResultCodes.ErrorUnknown, b)) ); try { int restoreElementId = Core.SM.UI.ElementWdw.CurrentElementId; int restoreHookId = Core.SM.UI.ElementWdw.CurrentHookId; // // Enter critical section _addMutex.WaitOne(); // // Suspend element changed monitoring inSMAUpdateLockMode = Core.SM.UI.ElementWdw.EnterSMAUpdateLock(); // // Focus // toDispose.Add(new FocusSnapshot(true)); // TODO: Only if inserting 1 element // // Regroup by parent id to optimize remote calls var resultsByParent = results.GroupBy(k => k.Builder.ParentId); // // Freeze element window if we want to insert the element without displaying it immediately if (singleMode == false || builders[0].ShouldDisplay == false) { inSMUpdateLockMode = Core.SM.UI.ElementWdw.EnterSMUpdateLock(); // TODO: Pass in EnterUpdateLock } foreach (var rpGroup in resultsByParent) { using var h = new HookSnapshot(); var parent = rpGroup.Key.HasValue ? this[rpGroup.Key.Value] : this[restoreHookId]; var branchFinder = options.HasFlag(ElemCreationFlags.CreateSubfolders) ? (IDestinationBranchFinder) new SubfolderBranchFinder(parent, options) : (IDestinationBranchFinder) new ConstantBranchFinder(parent); await AddInternalAsync(rpGroup.ToList(), branchFinder).ConfigureAwait(false); } // // Display original element, and unfreeze window -- or simply resume element changed monitoring if (inSMUpdateLockMode) { inSMUpdateLockMode = Core.SM.UI.ElementWdw.QuitSMUpdateLock() == false; Core.SM.UI.ElementWdw.GoToElement(restoreElementId); } inSMAUpdateLockMode = Core.SM.UI.ElementWdw.QuitSMAUpdateLock(inSMUpdateLockMode); return(results); } catch (Exception ex) { LogTo.Error(ex, "An exception was thrown while creating a new element in SM."); return(results); } finally { // // Unlock SM if necessary if (inSMUpdateLockMode) { try { Core.SM.UI.ElementWdw.QuitSMUpdateLock(); } catch (Exception ex) { LogTo.Warning(ex, "Failed to exit SM Update Lock."); MessageBox.Show($@"Failed to exit SuperMemo UI update lock. You might have to restart SuperMemo. Exception: {ex}", "Critical error"); } } // // Unlock element changed monitoring if necessary if (inSMAUpdateLockMode) { Core.SM.UI.ElementWdw.QuitSMAUpdateLock(); } // // Exit Critical section _addMutex.ReleaseMutex(); } }
// This is a fake async until the new hook engine system is implemented. // TODO: New hook engine system https://github.com/supermemo/SuperMemoAssistant/tree/improved-hook-engine public RemoteTask <List <ElemCreationResult> > AddAsync( ElemCreationFlags options, params ElementBuilder[] builders) { return(AddInternalAsync(options, builders)); }
public bool Add(out List <ElemCreationResult> results, ElemCreationFlags options, params ElementBuilder[] builders) { if (builders == null || builders.Length == 0) { results = new List <ElemCreationResult>(); return(false); } var inSMUpdateLockMode = false; var inSMAUpdateLockMode = false; var singleMode = builders.Length == 1; var toDispose = new List <IDisposable>(); results = new List <ElemCreationResult>( builders.Select( b => new ElemCreationResult( ElemCreationResultCode.UnknownError, b)) ); try { bool success = true; int restoreElementId = Core.SM.UI.ElementWdw.CurrentElementId; int restoreHookId = Core.SM.UI.ElementWdw.CurrentHookId; // // Enter critical section _addMutex.WaitOne(); // // Suspend element changed monitoring inSMAUpdateLockMode = Core.SM.UI.ElementWdw.EnterSMAUpdateLock(); // // Save states toDispose.Add(new HookSnapshot()); //toDispose.Add(new ConceptSnapshot()); // // Focus // toDispose.Add(new FocusSnapshot(true)); // TODO: Only if inserting 1 element // // Freeze element window if we want to insert the element without displaying it immediatly if (singleMode == false || builders[0].ShouldDisplay == false) { inSMUpdateLockMode = Core.SM.UI.ElementWdw.EnterSMUpdateLock(); // TODO: Pass in EnterUpdateLock } foreach (var result in results) { result.Result = AddElement(result.Builder, options, restoreHookId, out int elemId); result.ElementId = elemId; success = success && result.Result == ElemCreationResultCode.Success; } // // Display original element, and unfreeze window -- or simply resume element changed monitoring if (inSMUpdateLockMode) { inSMUpdateLockMode = Core.SM.UI.ElementWdw.QuitSMUpdateLock() == false; Core.SM.UI.ElementWdw.GoToElement(restoreElementId); inSMAUpdateLockMode = Core.SM.UI.ElementWdw.QuitSMAUpdateLock(true); } else { inSMAUpdateLockMode = Core.SM.UI.ElementWdw.QuitSMAUpdateLock(); } return(true); } catch (Exception ex) { LogTo.Error(ex, "An exception was thrown while creating a new element in SM."); return(false); } finally { // // Unlock SM if necessary if (inSMUpdateLockMode) { try { Core.SM.UI.ElementWdw.QuitSMUpdateLock(); } catch (Exception ex) { LogTo.Warning(ex, "Failed to exit SM Update Lock."); MessageBox.Show($@"Failed to exit SuperMemo UI update lock. You might have to restart SuperMemo. Exception: {ex}", "Critical error"); } } // // Restore initial context toDispose.ForEach(d => { try { d.Dispose(); } catch (Exception ex) { LogTo.Warning(ex, "Failed to restore context after creating a new SM element."); MessageBox.Show($@"Failed to restore initial context after creating a new SM element. Your hook and/or current concept might have been changed. Exception: {ex}", "Warning"); } }); // // Unlock element changed monitoring if necessary if (inSMAUpdateLockMode) { Core.SM.UI.ElementWdw.QuitSMAUpdateLock(); } // // Exit Critical section _addMutex.ReleaseMutex(); } }