/// <summary>
        /// Creates a TreeNode tree along genres populated by the texts that claim them.
        /// The list of textsWithNoGenre is also populated.
        /// Recursively descend the genre tree and duplicate parts that have corresponding texts.
        /// </summary>
        /// <param name="parent">The parent to attach the genres to. If null, nothing is done.</param>
        /// <param name="genreList">The owning sequence of genres - its a tree.</param>
        /// <param name="allTexts">The flat list of all texts in the project.</param>
        private void LoadTextsFromGenres(TreeNode parent, IFdoOwningSequence <ICmPossibility> genreList, IEnumerable <IText> allTexts)
        {
            if (parent == null)
            {
                return;
            }
            var sortedGenreList = new List <ICmPossibility>();

            foreach (var gen in genreList)
            {
                sortedGenreList.Add(gen);
            }
            var sorter = new CmPossibilitySorter();

            sortedGenreList.Sort(sorter);
            foreach (var gen in sortedGenreList)
            {
                // This tree node is added to genreTreeNodes if there are texts or children
                var genItem = new TreeNode(gen.ChooserNameTS.Text);

                // LT-12179: Create a List for collecting selected tree nodes which we will later sort
                // before actually adding them to the tree:
                var sortedNodes    = new List <TreeNode>();
                var foundFirstText = false;
                // Create a collator ready for sorting:
                var collator = new ManagedLgIcuCollator();

                foreach (IText tex in allTexts)
                {                   // This tex may not have a genre or it may claim to be in more than one
                    if (Enumerable.Contains(tex.GenresRC, gen))
                    {
                        var texItem = new TreeNode(tex.ChooserNameTS.Text);
                        texItem.Tag  = tex.ContentsOA;
                        texItem.Name = "Text";

                        // LT-12179: Add the new TreeNode to the (not-yet-)sorted list:
                        sortedNodes.Add(texItem);

                        // LT-12179: If this is the first tex we've added, establish the collator's details
                        // according to the writing system at the start of the tex:
                        if (!foundFirstText)
                        {
                            foundFirstText = true;
                            var ws1      = tex.ChooserNameTS.get_WritingSystemAt(0);
                            var wsEngine = gen.Cache.WritingSystemFactory.get_EngineOrNull(ws1);
                            collator.Open(wsEngine.Id);
                        }
                    }
                }

                // LT-12179:
                if (foundFirstText)
                {
                    // Order the TreeNodes alphabetically:
                    sortedNodes.Sort((x, y) => collator.Compare(x.Text, y.Text, LgCollatingOptions.fcoIgnoreCase));
                    // Add the TreeNodes to the tree:
                    genItem.Nodes.AddRange(sortedNodes.ToArray());
                }

                if (gen.SubPossibilitiesOS.Count > 0)
                {                   // Descend to the child genres regardless if there were texts assigned to this genre
                    LoadTextsFromGenres(genItem, gen.SubPossibilitiesOS, allTexts);
                }

                //Add the node even if there are no texts that point to this genre.
                genItem.Tag  = gen;                 // ICmPossibility
                genItem.Name = "Genre";
                parent.Nodes.Add(genItem);
            }
        }
 public ReversalSubEntryIcuComparer(FdoCache cache, string ws)
 {
     m_collator = new ManagedLgIcuCollator();
     m_ws       = cache.WritingSystemFactory.GetWsFromStr(ws);
     m_collator.Open(ws);
 }
        /// ------------------------------------------------------------------------------------
        /// <summary>
        /// Load texts by Genre into the texts tree view.
        /// </summary>
        /// <param name="cache">The cache.</param>
        /// <returns>A control tree of the Texts in the project</returns>
        /// ------------------------------------------------------------------------------------
        public TreeNode LoadTextsByGenreAndWithoutGenre(FdoCache cache)
        {
            if (cache.LanguageProject.GenreListOA == null)
            {
                return(null);
            }
            var genreList = cache.LanguageProject.GenreListOA.PossibilitiesOS;

            Debug.Assert(genreList != null);
            var allTexts = cache.ServiceLocator.GetInstance <ITextRepository>().AllInstances();

            if (allTexts == null)
            {
                return(null);
            }

            // Title node for all texts, Biblical and otherwise
            var textsNode = new TreeNode("All Texts in Genres and not in Genres");

            textsNode.Name = "Texts";

            // For each genre, find the texts that claim it
            LoadTextsFromGenres(textsNode, genreList, allTexts);

            var textsWithNoGenre = new List <TreeNode>();            // and get the ones with no genre
            // LT-12179: Create a List for collecting selected tree nodes which we will later sort
            // before actually adding them to the tree:
            var foundFirstText = false;
            // Create a collator ready for sorting:
            var collator = new ManagedLgIcuCollator();

            foreach (var tex in allTexts)
            {
                if (tex.GenresRC.Count == 0)
                {
                    var texItem = new TreeNode(tex.ChooserNameTS.Text);
                    texItem.Tag  = tex.ContentsOA;
                    texItem.Name = "Text";
                    textsWithNoGenre.Add(texItem);

                    // LT-12179: If this is the first tex we've added, establish the collator's details
                    // according to the writing system at the start of the tex:
                    if (!foundFirstText)
                    {
                        foundFirstText = true;
                        var ws1      = tex.ChooserNameTS.get_WritingSystemAt(0);
                        var wsEngine = cache.WritingSystemFactory.get_EngineOrNull(ws1);
                        collator.Open(wsEngine.Id);
                    }
                }
            }

            if (textsWithNoGenre.Count > 0)
            {
                // LT-12179: Order the TreeNodes alphabetically:
                textsWithNoGenre.Sort((x, y) => collator.Compare(x.Text, y.Text, LgCollatingOptions.fcoIgnoreCase));

                // Make a TreeNode for the texts with no known genre
                var woGenreTreeNode = new TreeNode("No Genre", textsWithNoGenre.ToArray());
                woGenreTreeNode.Name = "TextsWoGenre";
                textsNode.Nodes.Add(woGenreTreeNode);
            }
            return(textsNode);
        }