PageRenderTime 48ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/GitUI/CommandsDialogs/BrowseDialog/DashboardControl/Dashboard.cs

https://github.com/qgppl/gitextensions
C# | 497 lines | 395 code | 64 blank | 38 comment | 56 complexity | 7c037b96097142d46938a085fb9e94ea MD5 | raw file
Possible License(s): GPL-3.0
  1. using System;
  2. using System.Collections;
  3. using System.Configuration;
  4. using System.Diagnostics;
  5. using System.Drawing;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Text.RegularExpressions;
  9. using System.Windows.Forms;
  10. using GitCommands;
  11. using GitCommands.Repository;
  12. using GitUI.Properties;
  13. using GitUIPluginInterfaces.RepositoryHosts;
  14. using ResourceManager;
  15. namespace GitUI.CommandsDialogs.BrowseDialog.DashboardControl
  16. {
  17. public partial class Dashboard : GitModuleControl
  18. {
  19. private readonly TranslationString cloneFork = new TranslationString("Clone {0} repository");
  20. private readonly TranslationString cloneRepository = new TranslationString("Clone repository");
  21. private readonly TranslationString cloneSvnRepository = new TranslationString("Clone SVN repository");
  22. private readonly TranslationString createRepository = new TranslationString("Create new repository");
  23. private readonly TranslationString develop = new TranslationString("Develop");
  24. private readonly TranslationString donate = new TranslationString("Donate");
  25. private readonly TranslationString issues = new TranslationString("Issues");
  26. private readonly TranslationString openRepository = new TranslationString("Open repository");
  27. private readonly TranslationString translate = new TranslationString("Translate");
  28. private readonly TranslationString directoryIsNotAValidRepositoryCaption = new TranslationString("Open");
  29. private readonly TranslationString directoryIsNotAValidRepository = new TranslationString("The selected item is not a valid git repository.\n\nDo you want to abort and remove it from the recent repositories list?");
  30. private readonly TranslationString directoryIsNotAValidRepositoryOpenIt = new TranslationString("The selected item is not a valid git repository.\n\nDo you want to open it?");
  31. private readonly TranslationString _showCurrentBranch = new TranslationString("Show current branch");
  32. private bool initialized;
  33. public Dashboard()
  34. {
  35. InitializeComponent();
  36. Translate();
  37. RecentRepositories.DashboardItemClick += dashboardItem_Click;
  38. RecentRepositories.RepositoryRemoved += RecentRepositories_RepositoryRemoved;
  39. RecentRepositories.DisableContextMenu();
  40. RecentRepositories.DashboardCategoryChanged += dashboardCategory_DashboardCategoryChanged;
  41. //Repositories.RepositoryCategories.ListChanged += new ListChangedEventHandler(RepositoryCategories_ListChanged);
  42. Bitmap image = Lemmings.GetPictureBoxImage(DateTime.Now);
  43. if (image != null)
  44. {
  45. pictureBox1.Image = image;
  46. }
  47. // Do this at runtime, because it is difficult to keep consistent at design time.
  48. pictureBox1.BringToFront();
  49. pictureBox1.Location = new Point(this.Width - pictureBox1.Image.Width - 10, this.Height - pictureBox1.Image.Height - 10);
  50. Load += Dashboard_Load;
  51. }
  52. void RecentRepositories_RepositoryRemoved(object sender, DashboardCategory.RepositoryEventArgs e)
  53. {
  54. var repository = e.Repository;
  55. if (repository != null)
  56. Repositories.RepositoryHistory.RemoveRepository(repository);
  57. }
  58. private void Dashboard_Load(object sender, EventArgs e)
  59. {
  60. DonateCategory.Dock = DockStyle.Top;
  61. //Show buttons
  62. CommonActions.DisableContextMenu();
  63. var openItem = new DashboardItem(Resources.IconRepoOpen, openRepository.Text);
  64. openItem.Click += openItem_Click;
  65. CommonActions.AddItem(openItem);
  66. var cloneItem = new DashboardItem(Resources.IconCloneRepoGit, cloneRepository.Text);
  67. cloneItem.Click += cloneItem_Click;
  68. CommonActions.AddItem(cloneItem);
  69. var cloneSvnItem = new DashboardItem(Resources.IconCloneRepoSvn, cloneSvnRepository.Text);
  70. cloneSvnItem.Click += cloneSvnItem_Click;
  71. CommonActions.AddItem(cloneSvnItem);
  72. foreach (IRepositoryHostPlugin el in RepoHosts.GitHosters)
  73. {
  74. IRepositoryHostPlugin gitHoster = el;
  75. var di = new DashboardItem(Resources.IconCloneRepoGithub, string.Format(cloneFork.Text, el.Description));
  76. di.Click += (repoSender, eventArgs) => UICommands.StartCloneForkFromHoster(this, gitHoster, GitModuleChanged);
  77. CommonActions.AddItem(di);
  78. }
  79. var createItem = new DashboardItem(Resources.IconRepoCreate, createRepository.Text);
  80. createItem.Click += createItem_Click;
  81. CommonActions.AddItem(createItem);
  82. DonateCategory.DisableContextMenu();
  83. var GitHubItem = new DashboardItem(Resources.develop.ToBitmap(), develop.Text);
  84. GitHubItem.Click += GitHubItem_Click;
  85. DonateCategory.AddItem(GitHubItem);
  86. var DonateItem = new DashboardItem(Resources.dollar.ToBitmap(), donate.Text);
  87. DonateItem.Click += DonateItem_Click;
  88. DonateCategory.AddItem(DonateItem);
  89. var TranslateItem = new DashboardItem(Resources.EditItem, translate.Text);
  90. TranslateItem.Click += TranslateItem_Click;
  91. DonateCategory.AddItem(TranslateItem);
  92. var IssuesItem = new DashboardItem(Resources.bug, issues.Text);
  93. IssuesItem.Click += IssuesItem_Click;
  94. DonateCategory.AddItem(IssuesItem);
  95. //
  96. // create Show current branch menu item and add to Dashboard menu
  97. //
  98. var showCurrentBranchMenuItem = new ToolStripMenuItem(_showCurrentBranch.Text);
  99. showCurrentBranchMenuItem.Click += showCurrentBranchMenuItem_Click;
  100. showCurrentBranchMenuItem.Checked = GitCommands.AppSettings.DashboardShowCurrentBranch;
  101. var menuStrip = FindControl<MenuStrip>(Parent.Parent.Parent, p => true); // TODO: improve: Parent.Parent.Parent == FormBrowse
  102. var dashboardMenu = (ToolStripMenuItem)menuStrip.Items.Cast<ToolStripItem>().SingleOrDefault(p => p.Name == "dashboardToolStripMenuItem");
  103. dashboardMenu.DropDownItems.Add(showCurrentBranchMenuItem);
  104. }
  105. /// <summary>
  106. /// code duplicated from GerritPlugin.cs
  107. /// </summary>
  108. /// <typeparam name="T"></typeparam>
  109. /// <param name="form"></param>
  110. /// <param name="predicate"></param>
  111. /// <returns></returns>
  112. private T FindControl<T>(Control form, Func<T, bool> predicate)
  113. where T : Control
  114. {
  115. return FindControl(form.Controls, predicate);
  116. }
  117. /// <summary>
  118. /// code duplicated from GerritPlugin.cs
  119. /// </summary>
  120. /// <typeparam name="T"></typeparam>
  121. /// <param name="controls"></param>
  122. /// <param name="predicate"></param>
  123. /// <returns></returns>
  124. private T FindControl<T>(IEnumerable controls, Func<T, bool> predicate)
  125. where T : Control
  126. {
  127. foreach (Control control in controls)
  128. {
  129. var result = control as T;
  130. if (result != null && predicate(result))
  131. return result;
  132. result = FindControl(control.Controls, predicate);
  133. if (result != null)
  134. return result;
  135. }
  136. return null;
  137. }
  138. public void SaveSplitterPositions()
  139. {
  140. try
  141. {
  142. Properties.Settings.Default.Dashboard_MainSplitContainer_SplitterDistance = splitContainer5.SplitterDistance;
  143. Properties.Settings.Default.Dashboard_CommonSplitContainer_SplitterDistance = splitContainer6.SplitterDistance;
  144. Properties.Settings.Default.Save();
  145. }
  146. catch (ConfigurationException)
  147. {
  148. //TODO: howto restore a corrupted config? Properties.Settings.Default.Reset() doesn't work.
  149. }
  150. }
  151. public event EventHandler<GitModuleEventArgs> GitModuleChanged;
  152. public virtual void OnModuleChanged(object sender, GitModuleEventArgs e)
  153. {
  154. var handler = GitModuleChanged;
  155. if (handler != null)
  156. handler(this, e);
  157. }
  158. private void AddDashboardEntry(RepositoryCategory entry)
  159. {
  160. var dashboardCategory = new DashboardCategory(entry.Description, entry);
  161. this.groupLayoutPanel.Controls.Add(dashboardCategory);
  162. dashboardCategory.DashboardItemClick += dashboardItem_Click;
  163. dashboardCategory.DashboardCategoryChanged += dashboardCategory_DashboardCategoryChanged;
  164. }
  165. private void dashboardCategory_DashboardCategoryChanged(object sender, EventArgs e)
  166. {
  167. Refresh();
  168. }
  169. public override void Refresh()
  170. {
  171. initialized = false;
  172. ShowRecentRepositories();
  173. }
  174. public void ShowRecentRepositories()
  175. {
  176. if (!Visible)
  177. {
  178. return;
  179. }
  180. // Make sure the dashboard is only initialized once
  181. if (!initialized)
  182. {
  183. // Remove favorites
  184. var categories = (from DashboardCategory i in this.groupLayoutPanel.Controls
  185. select i).ToList();
  186. this.groupLayoutPanel.Controls.Clear();
  187. foreach (var category in categories)
  188. {
  189. category.DashboardCategoryChanged -= dashboardCategory_DashboardCategoryChanged;
  190. category.DashboardItemClick -= dashboardItem_Click;
  191. category.Clear();
  192. }
  193. // Show favorites
  194. foreach (var category in Repositories.RepositoryCategories)
  195. {
  196. AddDashboardEntry(category);
  197. }
  198. initialized = true;
  199. }
  200. splitContainer6.Panel1MinSize = 1;
  201. splitContainer6.Panel2MinSize = 1;
  202. splitContainer7.Panel1MinSize = 1;
  203. splitContainer7.Panel2MinSize = 1;
  204. RecentRepositories.Clear();
  205. RepositoryCategory filteredRecentRepositoryHistory = new RepositoryCategory();
  206. filteredRecentRepositoryHistory.Description = Repositories.RepositoryHistory.Description;
  207. filteredRecentRepositoryHistory.CategoryType = Repositories.RepositoryHistory.CategoryType;
  208. foreach (Repository repository in Repositories.RepositoryHistory.Repositories)
  209. {
  210. if (!Repositories.RepositoryCategories.Any(c => c.Repositories.Any(r => r.Path != null && r.Path.Equals(repository.Path, StringComparison.CurrentCultureIgnoreCase))))
  211. {
  212. repository.RepositoryType = RepositoryType.History;
  213. filteredRecentRepositoryHistory.Repositories.Add(repository);
  214. }
  215. }
  216. RecentRepositories.RepositoryCategory = filteredRecentRepositoryHistory;
  217. pictureBox1.BringToFront();
  218. SetSplitterPositions();
  219. }
  220. void showCurrentBranchMenuItem_Click(object sender, EventArgs e)
  221. {
  222. bool newValue = !GitCommands.AppSettings.DashboardShowCurrentBranch;
  223. GitCommands.AppSettings.DashboardShowCurrentBranch = newValue;
  224. ((ToolStripMenuItem)sender).Checked = newValue;
  225. Refresh();
  226. }
  227. private void SetSplitterPositions()
  228. {
  229. try
  230. {
  231. SetSplitterDistance(
  232. splitContainer6,
  233. Properties.Settings.Default.Dashboard_CommonSplitContainer_SplitterDistance,
  234. Math.Max(2, (int)(CommonActions.Height * 1.2)));
  235. SetSplitterDistance(
  236. splitContainer7,
  237. 0, // No settings property for this splitter. Will use default always.
  238. Math.Max(2, splitContainer7.Height - (DonateCategory.Height + 25)));
  239. SetSplitterDistance(
  240. splitContainer5,
  241. Properties.Settings.Default.Dashboard_MainSplitContainer_SplitterDistance,
  242. 315);
  243. }
  244. catch (ConfigurationException)
  245. {
  246. //TODO: howto restore a corrupted config? Properties.Settings.Default.Reset() doesn't work.
  247. }
  248. }
  249. private void SetSplitterDistance(SplitContainer splitContainer, int value, int @default)
  250. {
  251. try
  252. {
  253. if (isValidSplit(splitContainer,value))
  254. {
  255. splitContainer.SplitterDistance = value;
  256. }
  257. else if (isValidSplit(splitContainer, @default))
  258. {
  259. splitContainer.SplitterDistance = @default;
  260. }
  261. else
  262. {
  263. // Both the value and default are invalid.
  264. // Don't attempt to change the SplitterDistance
  265. }
  266. }
  267. catch (SystemException)
  268. {
  269. // The attempt to set even the default value has failed.
  270. }
  271. }
  272. /// <summary>
  273. /// Determine whether a given splitter value would be permitted for a given SplitContainer
  274. /// </summary>
  275. /// <param name="splitcontainer">The SplitContainer to check</param>
  276. /// <param name="value">The potential SplitterDistance to try </param>
  277. /// <returns>true if it is expected that setting a SplitterDistance of value would succeed
  278. /// </returns>
  279. bool isValidSplit(SplitContainer splitcontainer, int value)
  280. {
  281. bool valid;
  282. int limit = (splitcontainer.Orientation == Orientation.Horizontal)
  283. ? splitcontainer.Height
  284. : splitcontainer.Width;
  285. valid = (value > splitcontainer.Panel1MinSize) && (value < limit - splitcontainer.Panel2MinSize);
  286. return valid;
  287. }
  288. private void TranslateItem_Click(object sender, EventArgs e)
  289. {
  290. Process.Start(Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "TranslationApp.exe"));
  291. }
  292. private static void GitHubItem_Click(object sender, EventArgs e)
  293. {
  294. Process.Start(@"http://github.com/gitextensions/gitextensions");
  295. }
  296. private static void IssuesItem_Click(object sender, EventArgs e)
  297. {
  298. Process.Start(@"http://github.com/gitextensions/gitextensions/issues");
  299. }
  300. private void dashboardItem_Click(object sender, EventArgs e)
  301. {
  302. var label = sender as DashboardItem;
  303. if (label == null || string.IsNullOrEmpty(label.Path))
  304. return;
  305. //Open urls in browser, but open directories in GitExtensions
  306. if (Regex.IsMatch(label.Path,
  307. @"^(?#Protocol)(?:(?:ht|f)tp(?:s?)\:\/\/|~\/|\/)?(?#Username:Password)(?:\w+:\w+@)?(?#Subdomains)(?:(?:[-\w]+\.)+(?#TopLevel Domains)(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum|travel|[a-z]{2}))(?#Port)(?::[\d]{1,5})?(?#Directories)(?:(?:(?:\/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|\/)+|\?|#)?(?#Query)(?:(?:\?(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)*(?#Anchor)(?:#(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)?$"))
  308. {
  309. Process.Start(label.Path);
  310. }
  311. else
  312. {
  313. OpenPath(label.Path);
  314. }
  315. }
  316. private void OpenPath(string path)
  317. {
  318. GitModule module = new GitModule(path);
  319. if (!module.IsValidGitWorkingDir())
  320. {
  321. DialogResult dialogResult = MessageBox.Show(this, directoryIsNotAValidRepository.Text,
  322. directoryIsNotAValidRepositoryCaption.Text, MessageBoxButtons.YesNoCancel,
  323. MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);
  324. if (dialogResult == DialogResult.Cancel)
  325. {
  326. return;
  327. }
  328. if (dialogResult == DialogResult.Yes)
  329. {
  330. Repositories.RepositoryHistory.RemoveRecentRepository(path);
  331. Refresh();
  332. return;
  333. }
  334. }
  335. Repositories.AddMostRecentRepository(module.WorkingDir);
  336. OnModuleChanged(this, new GitModuleEventArgs(module));
  337. }
  338. private void openItem_Click(object sender, EventArgs e)
  339. {
  340. GitModule module = FormOpenDirectory.OpenModule(this);
  341. if (module != null)
  342. OnModuleChanged(this, new GitModuleEventArgs(module));
  343. }
  344. private void cloneItem_Click(object sender, EventArgs e)
  345. {
  346. UICommands.StartCloneDialog(this, null, false, OnModuleChanged);
  347. }
  348. private void cloneSvnItem_Click(object sender, EventArgs e)
  349. {
  350. UICommands.StartSvnCloneDialog(this, OnModuleChanged);
  351. }
  352. private void createItem_Click(object sender, EventArgs e)
  353. {
  354. UICommands.StartInitializeDialog(this, Module.WorkingDir, OnModuleChanged);
  355. }
  356. private static void DonateItem_Click(object sender, EventArgs e)
  357. {
  358. Process.Start(
  359. @"https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=WAL2SSDV8ND54&lc=US&item_name=GitExtensions&no_note=1&no_shipping=1&currency_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted");
  360. }
  361. private void pictureBox1_Click(object sender, EventArgs e)
  362. {
  363. using (var frm = new FormDashboardEditor()) frm.ShowDialog(this);
  364. Refresh();
  365. }
  366. private void groupLayoutPanel_DragDrop(object sender, DragEventArgs e)
  367. {
  368. var fileNameArray = e.Data.GetData(DataFormats.FileDrop) as string[];
  369. if (fileNameArray != null)
  370. {
  371. if (fileNameArray.Length != 1)
  372. return;
  373. string dir = fileNameArray[0];
  374. if (!string.IsNullOrEmpty(dir) && Directory.Exists(dir))
  375. {
  376. GitModule module = new GitModule(dir);
  377. if (!module.IsValidGitWorkingDir())
  378. {
  379. DialogResult dialogResult = MessageBox.Show(this, directoryIsNotAValidRepositoryOpenIt.Text,
  380. directoryIsNotAValidRepositoryCaption.Text, MessageBoxButtons.YesNo,
  381. MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button2);
  382. if (dialogResult == DialogResult.No)
  383. return;
  384. }
  385. Repositories.AddMostRecentRepository(module.WorkingDir);
  386. OnModuleChanged(this, new GitModuleEventArgs(module));
  387. }
  388. return;
  389. }
  390. var text = e.Data.GetData(DataFormats.UnicodeText) as string;
  391. if (!string.IsNullOrEmpty(text))
  392. {
  393. var lines = text.Split('\n');
  394. if (lines.Length != 1)
  395. return;
  396. string url = lines[0];
  397. if (!string.IsNullOrEmpty(url))
  398. {
  399. UICommands.StartCloneDialog(this, url, false, OnModuleChanged);
  400. }
  401. }
  402. }
  403. private void groupLayoutPanel_DragEnter(object sender, DragEventArgs e)
  404. {
  405. var fileNameArray = e.Data.GetData(DataFormats.FileDrop) as string[];
  406. if (fileNameArray != null)
  407. {
  408. if (fileNameArray.Length != 1)
  409. return;
  410. string dir = fileNameArray[0];
  411. if (!string.IsNullOrEmpty(dir) && Directory.Exists(dir))
  412. {
  413. //Allow drop (copy, not move) folders
  414. e.Effect = DragDropEffects.Copy;
  415. }
  416. return;
  417. }
  418. var text = e.Data.GetData(DataFormats.UnicodeText) as string;
  419. if (!string.IsNullOrEmpty(text))
  420. {
  421. var lines = text.Split('\n');
  422. if (lines.Length != 1)
  423. return;
  424. string url = lines[0];
  425. if (!string.IsNullOrEmpty(url))
  426. {
  427. //Allow drop (copy, not move) folders
  428. e.Effect = DragDropEffects.Copy;
  429. }
  430. }
  431. }
  432. }
  433. }