예제 #1
0
            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);
                //}
            }
예제 #2
0
        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);
        }
예제 #3
0
        public bool Add(out List <ElemCreationResult> results,
                        ElemCreationFlags options,
                        params ElementBuilder[]      builders)
        {
            results = AsyncContext.Run(() => AddInternalAsync(options, builders));

            return(results != null);
        }
예제 #4
0
        // 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);
            }
        }
예제 #5
0
        //
        //

        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);
        }
예제 #7
0
        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);
            }
        }
예제 #8
0
        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();
            }
        }
예제 #9
0
 // 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));
 }
예제 #10
0
        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();
            }
        }