/// <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; }
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; }