/// <summary>
        /// Copies all contents from another TextKeyViewModel instance to this one, merging all
        /// data, overwriting conflicts. Subitems are not copied to the new item.
        /// </summary>
        /// <param name="sourceKey"></param>
        public void MergeFrom(TextKeyViewModel sourceKey)
        {
            // Set or append comment
            if (!string.IsNullOrWhiteSpace(sourceKey.Comment))
            {
                if (!string.IsNullOrWhiteSpace(Comment))
                {
                    Comment += Environment.NewLine;
                }
                Comment += sourceKey.Comment;
            }

            // Set or keep full key state
            IsFullKey |= sourceKey.IsFullKey;

            if (sourceKey.IsFullKey)
            {
                foreach (CultureTextViewModel otherctVM in sourceKey.CultureTextVMs)
                {
                    var ctVM = CultureTextVMs.FirstOrDefault(c => c.CultureName == otherctVM.CultureName);
                    if (ctVM == null)
                    {
                        // Culture doesn't exist here, just copy-add it
                        CultureTextVMs.Add(otherctVM.Clone(this));
                    }
                    else
                    {
                        // Merge culture text data
                        ctVM.MergeFrom(otherctVM);
                    }
                }
                UpdateCultureTextSeparators();
            }
        }
        /// <summary>
        /// Creates a new CultureTextViewModel instance with all contents of this instance.
        /// </summary>
        /// <param name="textKeyVM">New TextKeyViewModel instance to connect the clone with.</param>
        /// <returns></returns>
        public CultureTextViewModel Clone(TextKeyViewModel textKeyVM)
        {
            CultureTextViewModel clone = new CultureTextViewModel(CultureName, textKeyVM);

            clone.Text = Text;
            foreach (QuantifiedTextViewModel qtVM in QuantifiedTextVMs)
            {
                clone.QuantifiedTextVMs.Add(qtVM.Clone(clone));
            }
            return(clone);
        }
        /// <summary>
        /// Creates a new TextKeyViewModel instance with all contents of this instance.
        /// </summary>
        /// <returns></returns>
        public TextKeyViewModel Clone()
        {
            TextKeyViewModel clone = new TextKeyViewModel(this.TextKey, this.IsFullKey, this.Parent, this.MainWindowVM);

            clone.Comment     = this.Comment;
            clone.IsNamespace = this.IsNamespace;
            foreach (CultureTextViewModel ctVM in CultureTextVMs)
            {
                clone.CultureTextVMs.Add(ctVM.Clone(clone));
            }
            return(clone);
        }
        /// <summary>
        /// Returns a value indicating whether the text key or any parent is selected.
        /// </summary>
        /// <returns></returns>
        public bool IsSelectedRecursive()
        {
            if (IsSelected)
            {
                return(true);
            }
            TextKeyViewModel parentVM = Parent as TextKeyViewModel;

            if (parentVM != null)
            {
                return(parentVM.IsSelectedRecursive());
            }
            return(false);
        }
        public CultureTextViewModel(string cultureName, TextKeyViewModel textKeyVM)
        {
            this.cultureName = cultureName;

            TextKeyVM = textKeyVM;
            LastOfLanguage = true;   // Change value once to set the brush value

            BackgroundBrush = cultureName == TextKeyVM.MainWindowVM.PrimaryCulture ?
                new SolidColorBrush(Color.FromArgb(20, 0, 192, 0)) :
                new SolidColorBrush(Color.FromArgb(20, 0, 192, 0));

            if (App.Settings.View.NativeCultureNames)
                cultureNativeName = Tx.U(CultureInfo.GetCultureInfo(cultureName).NativeName);
            else
                cultureNativeName = Tx.U(CultureInfo.GetCultureInfo(cultureName).DisplayName);
        }
        /// <summary>
        /// Compares two TextKeyViewModel instances to determine the sort order in the text keys
        /// tree.
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public static int Compare(object a, object b)
        {
            TextKeyViewModel ta = a as TextKeyViewModel;
            TextKeyViewModel tb = b as TextKeyViewModel;

            if (ta == null || tb == null)
            {
                return(0);
            }

            bool aNs   = ta.IsNamespace;
            bool bNs   = tb.IsNamespace;
            bool aTxNs = ta.IsNamespace && ta.TextKey == "Tx";
            bool bTxNs = tb.IsNamespace && tb.TextKey == "Tx";

            // Tx: always comes first
            if (aTxNs ^ bTxNs)
            {
                if (aTxNs)
                {
                    return(-1);
                }
                if (bTxNs)
                {
                    return(1);
                }
            }
            // Namespaces come before normal keys
            if (aNs ^ bNs)
            {
                if (aNs)
                {
                    return(-1);
                }
                if (bNs)
                {
                    return(1);
                }
            }
            // Sort everything else by full key name
            //return string.Compare(ta.TextKey, tb.TextKey, StringComparison.InvariantCultureIgnoreCase);
            return(NaturalSort.NatCompare(ta.TextKey, tb.TextKey));
        }
 /// <summary>
 /// Copies all child keys recursively from another TextKeyViewModel instance to this one,
 /// also merging all text data, overwriting conflicts.
 /// </summary>
 /// <param name="sourceKey"></param>
 public void MergeChildrenRecursive(TextKeyViewModel sourceKey)
 {
     foreach (TextKeyViewModel sourceChild in sourceKey.Children)
     {
         var myChild = Children.FirstOrDefault(c => c.DisplayName == sourceChild.DisplayName) as TextKeyViewModel;
         if (myChild != null)
         {
             myChild.MergeFrom(sourceChild);
         }
         else
         {
             myChild        = sourceChild.Clone();
             myChild.Parent = this;
             myChild.SetKey(this.TextKey + "." + sourceChild.DisplayName, null);
             Children.Add(myChild);
         }
         myChild.MergeChildrenRecursive(sourceChild);
     }
 }
        public CultureTextViewModel(string cultureName, TextKeyViewModel textKeyVM)
        {
            this.cultureName = cultureName;

            TextKeyVM      = textKeyVM;
            LastOfLanguage = true;               // Change value once to set the brush value

            BackgroundBrush = cultureName == TextKeyVM.MainWindowVM.PrimaryCulture ?
                              new SolidColorBrush(Color.FromArgb(20, 0, 192, 0)) :
                              new SolidColorBrush(Color.FromArgb(20, 0, 192, 0));

            if (App.Settings.View.NativeCultureNames)
            {
                cultureNativeName = Tx.U(CultureInfo.GetCultureInfo(cultureName).NativeName);
            }
            else
            {
                cultureNativeName = Tx.U(CultureInfo.GetCultureInfo(cultureName).DisplayName);
            }
        }
        public MainViewModel()
        {
            Instance = this;

            TextKeys = new Dictionary<string, TextKeyViewModel>();
            LoadedCultureNames = new HashSet<string>();
            DeletedCultureNames = new HashSet<string>();
            RootTextKey = new TextKeyViewModel(null, false, null, this);
            ProblemKeys = new ObservableHashSet<TextKeyViewModel>();

            searchDc = DelayedCall.Create(UpdateSearch, 250);
            SearchText = "";   // Change value once to set the clear button visibility
            ClearViewHistory();
            UpdateTitle();

            FontScale = App.Settings.View.FontScale;

            App.Settings.View.OnPropertyChanged(s => s.ShowSuggestions, UpdateSuggestionsLayout);
            App.Settings.View.OnPropertyChanged(s => s.SuggestionsHorizontalLayout, UpdateSuggestionsLayout);
            UpdateSuggestionsLayout();
        }
        private int DuplicateTextKeyRecursive(TextKeyViewModel srcTextKey, TextKeyViewModel destParent)
        {
            string destKeyName = destParent.TextKey + (destParent.IsNamespace ? ":" : ".") + srcTextKey.DisplayName;
            TextKeyViewModel destKey = FindOrCreateTextKey(destKeyName);
            destKey.MergeFrom(srcTextKey);
            int affectedKeys = srcTextKey.IsFullKey ? 1 : 0;

            foreach (TextKeyViewModel child in srcTextKey.Children)
            {
                affectedKeys += DuplicateTextKeyRecursive(child, destKey);
            }
            return affectedKeys;
        }
 private void DeleteTextKey(TextKeyViewModel tk, bool includeChildren = true)
 {
     if (includeChildren)
     {
         foreach (TextKeyViewModel child in tk.Children.ToArray())
         {
             DeleteTextKey(child);
         }
     }
     if (tk.IsFullKey)
     {
         TextKeys.Remove(tk.TextKey);
         ProblemKeys.Remove(tk);
     }
     if (tk.Children.Count == 0)
     {
         tk.Parent.Children.Remove(tk);
     }
     else
     {
         tk.IsFullKey = false;
         tk.CultureTextVMs.Clear();
         tk.Comment = null;
         tk.Validate();
         OnPropertyChanged("SelectionDummy");
     }
 }
 private void ScanAllTexts(TextKeyViewModel tk)
 {
     int maxDistance = (int) Math.Round((float) TranslationText.Text.Length / 2, MidpointRounding.AwayFromZero);
     if (tk.IsFullKey && tk.CultureTextVMs[0].Text == TranslationText.Text)
     {
         suggestions.Add(new SuggestionViewModel(null) { TextKey = tk.TextKey, BaseText = tk.CultureTextVMs[0].Text, ScoreNum = 1000, IsExactMatch = true });
     }
     else if (tk.IsFullKey && !String.IsNullOrEmpty(tk.CultureTextVMs[0].Text))
     {
         // TODO: Maybe we should split both strings in words and compare them separately, only accepting if at least one word has a small distance
         int distance = ComputeEditDistance(TranslationText.Text, tk.CultureTextVMs[0].Text);
         if (distance <= maxDistance)
         {
             float score;
             if (distance == 0)
             {
                 score = 1000;
             }
             else
             {
                 score = 100f / distance;
             }
             suggestions.Add(new SuggestionViewModel(null) { TextKey = tk.TextKey, BaseText = tk.CultureTextVMs[0].Text, ScoreNum = score });
         }
     }
     foreach (TextKeyViewModel child in tk.Children)
     {
         ScanAllTexts(child);
     }
 }
 /// <summary>
 /// Creates a new CultureTextViewModel instance with all contents of this instance.
 /// </summary>
 /// <param name="textKeyVM">New TextKeyViewModel instance to connect the clone with.</param>
 /// <returns></returns>
 public CultureTextViewModel Clone(TextKeyViewModel textKeyVM)
 {
     CultureTextViewModel clone = new CultureTextViewModel(CultureName, textKeyVM);
     clone.Text = Text;
     foreach (QuantifiedTextViewModel qtVM in QuantifiedTextVMs)
     {
         clone.QuantifiedTextVMs.Add(qtVM.Clone(clone));
     }
     return clone;
 }
 /// <summary>
 /// Copies all child keys recursively from another TextKeyViewModel instance to this one,
 /// also merging all text data, overwriting conflicts.
 /// </summary>
 /// <param name="sourceKey"></param>
 public void MergeChildrenRecursive(TextKeyViewModel sourceKey)
 {
     foreach (TextKeyViewModel sourceChild in sourceKey.Children)
     {
         var myChild = Children.FirstOrDefault(c => c.DisplayName == sourceChild.DisplayName) as TextKeyViewModel;
         if (myChild != null)
         {
             myChild.MergeFrom(sourceChild);
         }
         else
         {
             myChild = sourceChild.Clone();
             myChild.Parent = this;
             myChild.SetKey(this.TextKey + "." + sourceChild.DisplayName, null);
             Children.Add(myChild);
         }
         myChild.MergeChildrenRecursive(sourceChild);
     }
 }
        /// <summary>
        /// Copies all contents from another TextKeyViewModel instance to this one, merging all
        /// data, overwriting conflicts. Subitems are not copied to the new item.
        /// </summary>
        /// <param name="sourceKey"></param>
        public void MergeFrom(TextKeyViewModel sourceKey)
        {
            // Set or append comment
            if (!string.IsNullOrWhiteSpace(sourceKey.Comment))
            {
                if (!string.IsNullOrWhiteSpace(Comment))
                {
                    Comment += Environment.NewLine;
                }
                Comment += sourceKey.Comment;
            }

            // Set or keep full key state
            IsFullKey |= sourceKey.IsFullKey;

            if (sourceKey.IsFullKey)
            {
                foreach (CultureTextViewModel otherctVM in sourceKey.CultureTextVMs)
                {
                    var ctVM = CultureTextVMs.FirstOrDefault(c => c.CultureName == otherctVM.CultureName);
                    if (ctVM == null)
                    {
                        // Culture doesn't exist here, just copy-add it
                        CultureTextVMs.Add(otherctVM.Clone(this));
                    }
                    else
                    {
                        // Merge culture text data
                        ctVM.MergeFrom(otherctVM);
                    }
                }
                UpdateCultureTextSeparators();
            }
        }
 private void SelectCultureText(TextKeyViewModel tk, string cultureName)
 {
     if (tk != null &&
         tk.CultureTextVMs != null)
     {
         var ct = tk.CultureTextVMs.FirstOrDefault(vm => vm.CultureName == cultureName);
         if (ct != null)
         {
             ct.ViewCommandManager.InvokeLoaded("FocusText");
         }
     }
 }
 /// <summary>
 /// Creates a new TextKeyViewModel instance with all contents of this instance.
 /// </summary>
 /// <returns></returns>
 public TextKeyViewModel Clone()
 {
     TextKeyViewModel clone = new TextKeyViewModel(this.TextKey, this.IsFullKey, this.Parent, this.MainWindowVM);
     clone.Comment = this.Comment;
     clone.IsNamespace = this.IsNamespace;
     foreach (CultureTextViewModel ctVM in CultureTextVMs)
     {
         clone.CultureTextVMs.Add(ctVM.Clone(clone));
     }
     return clone;
 }
 private void DeleteCulture(TextKeyViewModel root, string cultureName, bool validate)
 {
     foreach (TextKeyViewModel tk in root.Children)
     {
         tk.CultureTextVMs.Filter(ct => ct.CultureName != cultureName);
         tk.UpdateCultureTextSeparators();
         if (tk.Children.Count > 0)
         {
             DeleteCulture(tk, cultureName, validate);
         }
     }
     LoadedCultureNames.Remove(cultureName);
     if (!DeletedCultureNames.Contains(cultureName))
     {
         DeletedCultureNames.Add(cultureName);
     }
     if (validate)
     {
         ValidateTextKeysDelayed();
     }
 }
        /// <summary>
        /// Searches up the tree parents, starting from the specified key, and deletes the
        /// top-most unused text key, i.e. a key that is partial and has no children.
        /// </summary>
        /// <param name="tk"></param>
        private void DeletePartialParentKeys(TextKeyViewModel tk)
        {
            TextKeyViewModel nodeToDelete = null;
            TextKeyViewModel current = tk;

            while (true)
            {
                if (current == null)
                {
                    // No more parents
                    break;
                }
                if (current == RootTextKey)
                {
                    // Don't try to delete the root key
                    break;
                }
                if (current.IsFullKey || CountTextKeys(current) > 0)
                {
                    // The current key is not unused
                    break;
                }
                nodeToDelete = current;
                current = current.Parent as TextKeyViewModel;
            }
            if (nodeToDelete != null)
            {
                DeleteTextKey(nodeToDelete);
            }
        }
 private void SelectTextKey(TextKeyViewModel tk, bool async = false)
 {
     if (tk != null)
     {
         bool wasExpanded = tk.IsExpanded;
         tk.IsExpanded = true;   // Expands all parents
         if (!wasExpanded)
             tk.IsExpanded = false;   // Collapses the item again like it was before
     }
     if (async)
         ViewCommandManager.InvokeLoaded("SelectTextKey", tk);
     else
         ViewCommandManager.Invoke("SelectTextKey", tk);
 }
        private void WriteTextKeysToXml(string cultureName, XmlElement xe, TextKeyViewModel textKeyVM, bool exporting)
        {
            if (textKeyVM.IsFullKey && textKeyVM.TextKey != null &&
                (!exporting || textKeyVM.IsSelectedRecursive()))
            {
                var cultureTextVM = textKeyVM.CultureTextVMs.FirstOrDefault(vm => vm.CultureName == cultureName);
                if (cultureTextVM != null)
                {
                    if (!string.IsNullOrEmpty(cultureTextVM.Text) ||
                        textKeyVM.IsEmpty() && cultureName == PrimaryCulture && !textKeyVM.TextKey.StartsWith("Tx:") ||   // Save empty text keys in the primary culture at least (not for system keys)
                        cultureName == PrimaryCulture && !string.IsNullOrWhiteSpace(textKeyVM.Comment) ||   // Always keep comments in the primary culture
                        cultureTextVM.AcceptMissing || cultureTextVM.AcceptPlaceholders || cultureTextVM.AcceptPunctuation)   // Keep accept flags
                    {
                        var textElement = xe.OwnerDocument.CreateElement("text");
                        xe.AppendChild(textElement);
                        var keyAttr = xe.OwnerDocument.CreateAttribute("key");
                        keyAttr.Value = textKeyVM.TextKey;
                        textElement.Attributes.Append(keyAttr);
                        if (!string.IsNullOrEmpty(cultureTextVM.Text))
                        {
                            textElement.InnerText = cultureTextVM.Text;
                        }

                        if (cultureTextVM.AcceptMissing)
                        {
                            var acceptMissingAttr = xe.OwnerDocument.CreateAttribute("acceptmissing");
                            acceptMissingAttr.Value = "true";
                            textElement.Attributes.Append(acceptMissingAttr);
                        }
                        if (cultureTextVM.AcceptPlaceholders)
                        {
                            var acceptPlaceholdersAttr = xe.OwnerDocument.CreateAttribute("acceptplaceholders");
                            acceptPlaceholdersAttr.Value = "true";
                            textElement.Attributes.Append(acceptPlaceholdersAttr);
                        }
                        if (cultureTextVM.AcceptPunctuation)
                        {
                            var acceptPunctuationAttr = xe.OwnerDocument.CreateAttribute("acceptpunctuation");
                            acceptPunctuationAttr.Value = "true";
                            textElement.Attributes.Append(acceptPunctuationAttr);
                        }

                        // Add the text key comment to the primary culture
                        // (If no primary culture is set, the first-displayed is used to save the comments)
                        if (!string.IsNullOrWhiteSpace(textKeyVM.Comment))
                        {
                            if (PrimaryCulture != null && cultureName == PrimaryCulture ||
                                PrimaryCulture == null && cultureName == textKeyVM.CultureTextVMs[0].CultureName)
                            {
                                var commentAttr = xe.OwnerDocument.CreateAttribute("comment");
                                commentAttr.Value = textKeyVM.Comment;
                                textElement.Attributes.Append(commentAttr);
                            }
                        }
                    }
                    foreach (var quantifiedTextVM in cultureTextVM.QuantifiedTextVMs.OrderBy(qt => qt.Count).ThenBy(qt => qt.Modulo))
                    {
                        var textElement = xe.OwnerDocument.CreateElement("text");
                        xe.AppendChild(textElement);

                        var keyAttr = xe.OwnerDocument.CreateAttribute("key");
                        keyAttr.Value = textKeyVM.TextKey;
                        textElement.Attributes.Append(keyAttr);

                        if (quantifiedTextVM.Count < 0)
                        {
                            throw new Exception("Invalid count value " + quantifiedTextVM.Count + " set for text key " +
                                textKeyVM.TextKey + ", culture " + cultureName);
                        }
                        var countAttr = xe.OwnerDocument.CreateAttribute("count");
                        countAttr.Value = quantifiedTextVM.Count.ToString();
                        textElement.Attributes.Append(countAttr);

                        if (quantifiedTextVM.Modulo != 0 &&
                            (quantifiedTextVM.Modulo < 2 && quantifiedTextVM.Modulo > 1000))
                        {
                            throw new Exception("Invalid modulo value " + quantifiedTextVM.Modulo + " set for text key " +
                                textKeyVM.TextKey + ", culture " + cultureName + ", count " + quantifiedTextVM.Count);
                        }
                        if (quantifiedTextVM.Modulo > 1)
                        {
                            var modAttr = xe.OwnerDocument.CreateAttribute("mod");
                            modAttr.Value = quantifiedTextVM.Modulo.ToString();
                            textElement.Attributes.Append(modAttr);
                        }

                        if (quantifiedTextVM.AcceptMissing)
                        {
                            var acceptMissingAttr = xe.OwnerDocument.CreateAttribute("acceptmissing");
                            acceptMissingAttr.Value = "true";
                            textElement.Attributes.Append(acceptMissingAttr);
                        }
                        if (quantifiedTextVM.AcceptPlaceholders)
                        {
                            var acceptPlaceholdersAttr = xe.OwnerDocument.CreateAttribute("acceptplaceholders");
                            acceptPlaceholdersAttr.Value = "true";
                            textElement.Attributes.Append(acceptPlaceholdersAttr);
                        }
                        if (quantifiedTextVM.AcceptPunctuation)
                        {
                            var acceptPunctuationAttr = xe.OwnerDocument.CreateAttribute("acceptpunctuation");
                            acceptPunctuationAttr.Value = "true";
                            textElement.Attributes.Append(acceptPunctuationAttr);
                        }

                        if (!string.IsNullOrEmpty(quantifiedTextVM.Text))
                        {
                            textElement.InnerText = quantifiedTextVM.Text;
                        }
                    }
                }
            }
            foreach (TextKeyViewModel child in textKeyVM.Children.OrderBy(tk => tk.DisplayName))
            {
                WriteTextKeysToXml(cultureName, xe, child, exporting);
            }
        }
 private void WriteTextKeysToDictionary(string cultureName, Dictionary<string, Dictionary<int, string>> dict, TextKeyViewModel textKeyVM)
 {
     if (textKeyVM.IsFullKey && textKeyVM.TextKey != null)
     {
         var cultureTextVM = textKeyVM.CultureTextVMs.FirstOrDefault(vm => vm.CultureName == cultureName);
         if (cultureTextVM != null)
         {
             if (!string.IsNullOrEmpty(cultureTextVM.Text) && textKeyVM.TextKey.StartsWith("Tx:"))
             {
                 if (!string.IsNullOrEmpty(cultureTextVM.Text))
                 {
                     if (!dict.ContainsKey(textKeyVM.TextKey))
                     {
                         dict[textKeyVM.TextKey] = new Dictionary<int, string>();
                     }
                     dict[textKeyVM.TextKey][-1] = cultureTextVM.Text;
                 }
             }
             foreach (var quantifiedTextVM in cultureTextVM.QuantifiedTextVMs.OrderBy(qt => qt.Count).ThenBy(qt => qt.Modulo))
             {
                 if (quantifiedTextVM.Count < 0)
                 {
                     continue;
                 }
                 if (quantifiedTextVM.Modulo != 0 &&
                     (quantifiedTextVM.Modulo < 2 && quantifiedTextVM.Modulo > 1000))
                 {
                     continue;
                 }
                 if (!string.IsNullOrEmpty(quantifiedTextVM.Text))
                 {
                     int count = quantifiedTextVM.Count;
                     if (quantifiedTextVM.Modulo != 0)
                     {
                         // Encode the modulo value into the quantifier.
                         count = (quantifiedTextVM.Modulo << 16) | count;
                     }
                     if (!dict.ContainsKey(textKeyVM.TextKey))
                     {
                         dict[textKeyVM.TextKey] = new Dictionary<int, string>();
                     }
                     dict[textKeyVM.TextKey][count] = quantifiedTextVM.Text;
                 }
             }
         }
     }
     foreach (TextKeyViewModel child in textKeyVM.Children.OrderBy(tk => tk.DisplayName))
     {
         WriteTextKeysToDictionary(cultureName, dict, child);
     }
 }
        private int UpdateTextKeyVisibility(TextKeyViewModel tk, bool isSearch)
        {
            int count = 0;
            foreach (TextKeyViewModel child in tk.Children)
            {
                bool isVisible =
                    !isSearch ||
                    child.TextKey.ToLower().Contains(searchText.ToLower()) ||
                    child.CultureTextVMs.Any(ct => ct.Text != null && ct.Text.ToLower().Contains(searchText.ToLower()));
                if (ProblemFilterActive)
                {
                    isVisible &= child.HasOwnProblem || child.HasProblem;
                }

                child.IsVisible = isVisible;
                if (isVisible)
                {
                    count++;
                    TreeViewItemViewModel parent = child.Parent;
                    while (parent != null)
                    {
                        parent.IsVisible = true;
                        parent = parent.Parent;
                    }
                }
                if (child.Children.Count > 0)
                {
                    count += UpdateTextKeyVisibility(child, isSearch);
                }
            }
            return count;
        }
        private void SortCulturesInTextKey(TextKeyViewModel root)
        {
            foreach (TextKeyViewModel tk in root.Children)
            {
                var ctList = tk.CultureTextVMs.ToArray();
                tk.CultureTextVMs.Clear();
                foreach (var ct in ctList)
                {
                    tk.CultureTextVMs.InsertSorted(ct, (a, b) => a.CompareTo(b));
                }

                tk.UpdateCultureTextSeparators();
                if (tk.Children.Count > 0)
                {
                    SortCulturesInTextKey(tk);
                }
            }
        }
 private void EnsureCultureInTextKey(TextKeyViewModel tk, string cultureName)
 {
     if (!tk.CultureTextVMs.Any(vm => vm.CultureName == cultureName))
     {
         tk.CultureTextVMs.InsertSorted(new CultureTextViewModel(cultureName, tk), (a, b) => a.CompareTo(b));
     }
 }
        /// <summary>
        /// Finds an existing TextKeyViewModel or creates a new one in the correct place.
        /// </summary>
        /// <param name="textKey">The full text key to find or create.</param>
        /// <param name="updateTextKeys">true to add the new text key to the TextKeys dictionary. (Only if <paramref name="create"/> is set.)</param>
        /// <param name="create">true to create a new full text key if it doesn't exist yet, false to return null or partial TextKeyViewModels instead.</param>
        /// <param name="isNamespace">true to indicate that a single key segment is meant to be a namespace key.</param>
        /// <returns></returns>
        private TextKeyViewModel FindOrCreateTextKey(string textKey, bool updateTextKeys = true, bool create = true, bool isNamespace = false)
        {
            // Tokenize text key to find the tree node
            string partialKey = "";
            TextKeyViewModel tk = RootTextKey;
            if (!textKey.Contains(':') && isNamespace)
            {
                // Fake the separator to use existing code; clean up later
                textKey += ":";
            }
            string[] nsParts = textKey.Split(':');
            string localKey;
            if (nsParts.Length > 1)
            {
                // Namespace set
                partialKey = nsParts[0];
                var subtk = tk.Children.OfType<TextKeyViewModel>()
                    .SingleOrDefault(vm => vm.DisplayName == nsParts[0]);
                if (subtk != null && !subtk.IsNamespace)
                {
                    throw new NonNamespaceExistsException();
                }
                if (subtk == null)
                {
                    // Namespace tree item does not exist yet, create it
                    if (!create) return null;
                    subtk = new TextKeyViewModel(nsParts[0], false, tk, tk.MainWindowVM);
                    subtk.DisplayName = nsParts[0];
                    subtk.IsNamespace = true;
                    tk.Children.InsertSorted(subtk, TextKeyViewModel.Compare);
                }
                tk = subtk;
                // Continue with namespace-free text key
                localKey = nsParts[1];
                partialKey += ":";
            }
            else
            {
                // No namespace set, continue with entire key
                localKey = textKey;
            }

            if (localKey != "")
            {
                string[] keySegments = localKey.Split('.');
                for (int i = 0; i < keySegments.Length; i++)
                {
                    string keySegment = keySegments[i];
                    partialKey += keySegment;

                    // Search for tree item
                    var subtk = tk.Children.OfType<TextKeyViewModel>()
                        .SingleOrDefault(vm => vm.DisplayName == keySegment);
                    if (subtk != null && subtk.IsNamespace)
                    {
                        throw new NamespaceExistsException();
                    }
                    if (subtk == null)
                    {
                        // This level of text key item does not exist yet, create it
                        if (!create) return null;
                        subtk = new TextKeyViewModel(partialKey, i == keySegments.Length - 1, tk, tk.MainWindowVM);
                        subtk.DisplayName = keySegment;
                        tk.Children.InsertSorted(subtk, TextKeyViewModel.Compare);
                    }
                    tk = subtk;
                    partialKey += ".";
                }
            }

            if (create)
            {
                if (updateTextKeys && !TextKeys.ContainsKey(textKey))
                    TextKeys.Add(textKey, tk);
                tk.IsFullKey = true;
            }
            return tk;
        }
 private void AddNewCulture(TextKeyViewModel root, string cultureName, bool validate)
 {
     foreach (TextKeyViewModel tk in root.Children)
     {
         EnsureCultureInTextKey(tk, cultureName);
         tk.UpdateCultureTextSeparators();
         if (tk.Children.Count > 0)
         {
             AddNewCulture(tk, cultureName, validate);
         }
     }
     if (!LoadedCultureNames.Contains(cultureName))
     {
         LoadedCultureNames.Add(cultureName);
     }
     DeletedCultureNames.Remove(cultureName);   // in case it's been deleted before
     if (validate)
     {
         ValidateTextKeysDelayed();
     }
 }
 /// <summary>
 /// Counts all full keys within the specified subtree, including the specified text key.
 /// </summary>
 /// <param name="tk">Text key to start counting at.</param>
 /// <returns></returns>
 private int CountTextKeys(TextKeyViewModel tk)
 {
     int count = tk.IsFullKey ? 1 : 0;
     if (tk.IsFullKey)
         lastCountedTextKey = tk.TextKey;
     foreach (TextKeyViewModel child in tk.Children)
     {
         count += CountTextKeys(child);
     }
     return count;
 }