1 /*
2   KeePass Password Safe - The Open-Source Password Manager
3   Copyright (C) 2003-2021 Dominik Reichl <dominik.reichl@t-online.de>
4 
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19 
20 using System;
21 using System.Collections.Generic;
22 using System.ComponentModel;
23 using System.Diagnostics;
24 using System.Drawing;
25 using System.IO;
26 using System.Text;
27 using System.Windows.Forms;
28 
29 using KeePass.App;
30 using KeePass.App.Configuration;
31 using KeePass.Resources;
32 using KeePass.UI;
33 using KeePass.Util;
34 using KeePass.Util.XmlSerialization;
35 
36 using KeePassLib;
37 using KeePassLib.Translation;
38 using KeePassLib.Utility;
39 
40 namespace KeePass.Forms
41 {
42 	public partial class LanguageForm : Form, IGwmWindow
43 	{
44 		private ImageList m_ilIcons = null;
45 
46 		public bool CanCloseWithoutDataLoss { get { return true; } }
47 
LanguageForm()48 		public LanguageForm()
49 		{
50 			InitializeComponent();
51 			GlobalWindowManager.InitializeForm(this);
52 		}
53 
InitEx()54 		public bool InitEx()
55 		{
56 			try
57 			{
58 				string strDir = UrlUtil.GetFileDirectory(WinUtil.GetExecutable(),
59 					false, true);
60 				List<string> l = UrlUtil.GetFilePaths(strDir, "*." +
61 					KPTranslation.FileExtension, SearchOption.TopDirectoryOnly);
62 				if(l.Count != 0)
63 				{
64 					string str = KPRes.LngInAppDir + MessageService.NewParagraph;
65 
66 					const int cMaxFL = 6;
67 					for(int i = 0; i < Math.Min(l.Count, cMaxFL); ++i)
68 					{
69 						if(i == (cMaxFL - 1)) str += "...";
70 						else str += l[i];
71 						str += MessageService.NewLine;
72 					}
73 					str += MessageService.NewLine;
74 
75 					str += KPRes.LngInAppDirNote + MessageService.NewParagraph;
76 					str += KPRes.LngInAppDirQ;
77 
78 					if(MessageService.AskYesNo(str, PwDefs.ShortProductName, true,
79 						MessageBoxIcon.Warning))
80 					{
81 						WinUtil.OpenUrlDirectly(strDir);
82 						return false;
83 					}
84 				}
85 			}
86 			catch(Exception) { Debug.Assert(false); }
87 
88 			return true;
89 		}
90 
OnFormLoad(object sender, EventArgs e)91 		private void OnFormLoad(object sender, EventArgs e)
92 		{
93 			GlobalWindowManager.AddWindow(this, this);
94 
95 			BannerFactory.CreateBannerEx(this, m_bannerImage,
96 				Properties.Resources.B48x48_Keyboard_Layout,
97 				KPRes.SelectLanguage, KPRes.SelectLanguageDesc);
98 			this.Icon = AppIcons.Default;
99 			this.Text = KPRes.SelectLanguage;
100 
101 			UIUtil.SetExplorerTheme(m_lvLanguages, true);
102 
103 			List<Image> lImg = new List<Image>();
104 			lImg.Add(Properties.Resources.B16x16_Browser);
105 
106 			m_ilIcons = UIUtil.BuildImageListUnscaled(lImg,
107 				DpiUtil.ScaleIntX(16), DpiUtil.ScaleIntY(16));
108 			m_lvLanguages.SmallImageList = m_ilIcons;
109 
110 			m_lvLanguages.Columns.Add(KPRes.InstalledLanguages);
111 			m_lvLanguages.Columns.Add(KPRes.Version);
112 			m_lvLanguages.Columns.Add(KPRes.Author);
113 			m_lvLanguages.Columns.Add(KPRes.Contact);
114 			m_lvLanguages.Columns.Add(KPRes.File);
115 
116 			KPTranslation trlEng = new KPTranslation();
117 			trlEng.Properties.NameEnglish = "English";
118 			trlEng.Properties.NameNative = "English";
119 			trlEng.Properties.ApplicationVersion = PwDefs.VersionString;
120 			trlEng.Properties.AuthorName = AppDefs.DefaultTrlAuthor;
121 			trlEng.Properties.AuthorContact = AppDefs.DefaultTrlContact;
122 
123 			string strDirA = AceApplication.GetLanguagesDir(AceDir.App, false);
124 			string strDirASep = UrlUtil.EnsureTerminatingSeparator(strDirA, false);
125 			string strDirU = AceApplication.GetLanguagesDir(AceDir.User, false);
126 
127 			List<KeyValuePair<string, KPTranslation>> lTrls =
128 				new List<KeyValuePair<string, KPTranslation>>();
129 			lTrls.Add(new KeyValuePair<string, KPTranslation>(string.Empty, trlEng));
130 			AddTranslations(strDirA, lTrls);
131 			if(WinUtil.IsAppX) AddTranslations(strDirU, lTrls);
132 			lTrls.Sort(LanguageForm.CompareTrlItems);
133 
134 			foreach(KeyValuePair<string, KPTranslation> kvp in lTrls)
135 			{
136 				KPTranslationProperties p = kvp.Value.Properties;
137 				string strName = p.NameEnglish + " (" + p.NameNative + ")";
138 				string strVer = PwDefs.GetTranslationDisplayVersion(p.ApplicationVersion);
139 				bool bBuiltIn = ((kvp.Key.Length == 0) || (WinUtil.IsAppX &&
140 					kvp.Key.StartsWith(strDirASep, StrUtil.CaseIgnoreCmp)));
141 
142 				ListViewItem lvi = m_lvLanguages.Items.Add(strName, 0);
143 				lvi.SubItems.Add(strVer);
144 				lvi.SubItems.Add(p.AuthorName);
145 				lvi.SubItems.Add(p.AuthorContact);
146 				lvi.SubItems.Add(bBuiltIn ? KPRes.BuiltInU : kvp.Key);
147 				lvi.Tag = kvp.Key;
148 
149 				// try
150 				// {
151 				//	string nl = MessageService.NewLine;
152 				//	lvi.ToolTipText = strName + " " + strVer + nl + p.AuthorName +
153 				//		nl + p.AuthorContact + nl + nl + kvp.Key;
154 				// }
155 				// catch(Exception) { Debug.Assert(false); } // Too long?
156 
157 				// if(kvp.Key.Equals(Program.Config.Application.GetLanguageFilePath(),
158 				//	StrUtil.CaseIgnoreCmp))
159 				//	UIUtil.SetFocusedItem(m_lvLanguages, lvi, true);
160 			}
161 
162 			UIUtil.ResizeColumns(m_lvLanguages, new int[] { 5, 2, 5, 5, 3 }, true);
163 		}
164 
AddTranslations(string strDir, List<KeyValuePair<string, KPTranslation>> lTrls)165 		private static void AddTranslations(string strDir,
166 			List<KeyValuePair<string, KPTranslation>> lTrls)
167 		{
168 			try
169 			{
170 				List<string> lFiles = UrlUtil.GetFilePaths(strDir, "*." +
171 					KPTranslation.FileExtension, SearchOption.TopDirectoryOnly);
172 				foreach(string strFilePath in lFiles)
173 				{
174 					try
175 					{
176 						XmlSerializerEx xs = new XmlSerializerEx(typeof(KPTranslation));
177 						KPTranslation t = KPTranslation.Load(strFilePath, xs);
178 
179 						if(t != null)
180 							lTrls.Add(new KeyValuePair<string, KPTranslation>(
181 								strFilePath, t));
182 						else { Debug.Assert(false); }
183 					}
184 					catch(Exception ex) { MessageService.ShowWarning(ex); }
185 				}
186 
187 				Debug.Assert(KPTranslation.FileExtension != KPTranslation.FileExtension1x);
188 
189 				lFiles = UrlUtil.GetFilePaths(strDir, "*." +
190 					KPTranslation.FileExtension1x, SearchOption.TopDirectoryOnly);
191 				foreach(string strFilePath in lFiles)
192 				{
193 					KPTranslation t = new KPTranslation();
194 					t.Properties.NameEnglish = UrlUtil.StripExtension(
195 						UrlUtil.GetFileName(strFilePath));
196 					t.Properties.NameNative = KPRes.Incompatible;
197 					t.Properties.ApplicationVersion = "1.x";
198 					t.Properties.AuthorName = "?";
199 					t.Properties.AuthorContact = "?";
200 
201 					lTrls.Add(new KeyValuePair<string, KPTranslation>(
202 						strFilePath, t));
203 				}
204 			}
205 			catch(Exception) { } // Directory might not exist or cause access violation
206 		}
207 
CompareTrlItems(KeyValuePair<string, KPTranslation> a, KeyValuePair<string, KPTranslation> b)208 		private static int CompareTrlItems(KeyValuePair<string, KPTranslation> a,
209 			KeyValuePair<string, KPTranslation> b)
210 		{
211 			KPTranslationProperties pA = a.Value.Properties;
212 			KPTranslationProperties pB = b.Value.Properties;
213 
214 			int c = StrUtil.CompareNaturally(pA.NameEnglish, pB.NameEnglish);
215 			if(c != 0) return c;
216 
217 			c = StrUtil.CompareNaturally(pA.NameNative, pB.NameNative);
218 			if(c != 0) return c;
219 
220 			c = StrUtil.CompareNaturally(pA.ApplicationVersion, pB.ApplicationVersion);
221 			if(c != 0) return ((c < 0) ? 1 : -1); // Descending
222 
223 			return string.Compare(a.Key, b.Key, StrUtil.CaseIgnoreCmp);
224 		}
225 
OnBtnClose(object sender, EventArgs e)226 		private void OnBtnClose(object sender, EventArgs e)
227 		{
228 		}
229 
OnLanguagesItemActivate(object sender, EventArgs e)230 		private void OnLanguagesItemActivate(object sender, EventArgs e)
231 		{
232 			ListView.SelectedListViewItemCollection lvic = m_lvLanguages.SelectedItems;
233 			if((lvic == null) || (lvic.Count != 1)) return;
234 
235 			string strSel = ((lvic[0].Tag as string) ?? string.Empty);
236 
237 			if(strSel.EndsWith("." + KPTranslation.FileExtension1x, StrUtil.CaseIgnoreCmp))
238 			{
239 				string strMsg = strSel + MessageService.NewParagraph + KPRes.Lng1xSel +
240 					MessageService.NewParagraph + KPRes.Lng2xWeb + MessageService.NewLine;
241 				string strUrl = PwDefs.TranslationsUrl;
242 				string strVtd = strMsg + VistaTaskDialog.CreateLink(strUrl, strUrl);
243 
244 				VistaTaskDialog vtd = new VistaTaskDialog();
245 				vtd.AddButton((int)DialogResult.Cancel, KPRes.Ok, null);
246 				vtd.Content = strVtd;
247 				vtd.DefaultButtonID = (int)DialogResult.Cancel;
248 				vtd.EnableHyperlinks = true;
249 				vtd.SetIcon(VtdIcon.Warning);
250 				vtd.WindowTitle = PwDefs.ShortProductName;
251 
252 				if(!vtd.ShowDialog())
253 					MessageService.ShowWarning(strMsg + strUrl);
254 				return;
255 			}
256 
257 			// The following creates confusion when the configured language
258 			// is different from the loaded language (which can occur when
259 			// the language file has been deleted/moved)
260 			// if(strSel.Equals(Program.Config.Application.GetLanguageFilePath(),
261 			//	StrUtil.CaseIgnoreCmp))
262 			//	return; // Is active already, do not close the dialog
263 
264 			Program.Config.Application.SetLanguageFilePath(strSel);
265 			this.DialogResult = DialogResult.OK;
266 		}
267 
OnFormClosed(object sender, FormClosedEventArgs e)268 		private void OnFormClosed(object sender, FormClosedEventArgs e)
269 		{
270 			if(m_ilIcons != null)
271 			{
272 				m_lvLanguages.SmallImageList = null;
273 				m_ilIcons.Dispose();
274 				m_ilIcons = null;
275 			}
276 			else { Debug.Assert(false); }
277 
278 			GlobalWindowManager.RemoveWindow(this);
279 		}
280 
OnBtnGetMore(object sender, EventArgs e)281 		private void OnBtnGetMore(object sender, EventArgs e)
282 		{
283 			WinUtil.OpenUrl(PwDefs.TranslationsUrl, null);
284 			this.DialogResult = DialogResult.Cancel;
285 		}
286 
OnBtnOpenFolder(object sender, EventArgs e)287 		private void OnBtnOpenFolder(object sender, EventArgs e)
288 		{
289 			try
290 			{
291 				AceDir d = (WinUtil.IsAppX ? AceDir.User : AceDir.App);
292 
293 				// try
294 				// {
295 				//	string strU = AceApplication.GetLanguagesDir(AceDir.User, false);
296 				//	List<string> l = UrlUtil.GetFilePaths(strU, "*." +
297 				//		KPTranslation.FileExtension, SearchOption.TopDirectoryOnly);
298 				//	if(l.Count > 0) d = AceDir.User;
299 				// }
300 				// catch(Exception) { }
301 
302 				string str = AceApplication.GetLanguagesDir(d, false);
303 				if(!Directory.Exists(str)) Directory.CreateDirectory(str);
304 
305 				WinUtil.OpenUrlDirectly(str);
306 				this.DialogResult = DialogResult.Cancel;
307 			}
308 			catch(Exception ex) { MessageService.ShowWarning(ex); }
309 		}
310 	}
311 }
312