├── Screenshots ├── task.png ├── export.png ├── issue.png ├── sort1.gif ├── sort2.gif ├── export2.png ├── settings.gif ├── settings.png ├── main-window.png ├── new-project.png └── visible-columns.gif ├── MiniBug ├── Resources │ ├── MiniBug.png │ ├── Minibug.ico │ ├── Info_16x16.png │ ├── Info_64x64.png │ ├── About_32x32.png │ ├── Delete_32x32.png │ ├── EditBug_32x32.png │ ├── NewBug_32x32.png │ ├── NewTask_32x32.png │ ├── Priority_High.png │ ├── CloneBug_32x32.png │ ├── CloneTask_32x32.png │ ├── DeleteBug_32x32.png │ ├── EditTask_32x32.png │ ├── FileError_64x64.png │ ├── Priority_Urgent.png │ ├── Settings_32x32.png │ ├── StatusOK_16x16.png │ ├── DeleteTask_32x32.png │ ├── FolderError_64x64.png │ ├── NewProject_32x32.png │ ├── OpenProject_32x32.png │ ├── Priority_Immediate.png │ ├── ConfigureView_32x32.png │ ├── CriticalError_64x64.png │ ├── MiniBug- about-image.png │ ├── StatusWarning_16x16.png │ └── StatusCriticalError_16x16.png ├── Classes │ ├── ComboBoxItem.cs │ ├── GridColumn.cs │ ├── Extensions.cs │ ├── GridTasksSortSettings.cs │ ├── GridIssuesSortSettings.cs │ ├── ImportExportResult.cs │ ├── Task.cs │ ├── Issue.cs │ ├── TasksDataGridViewRowComparer.cs │ ├── ApplicationData.cs │ ├── IssuesDataGridViewRowComparer.cs │ └── Project.cs ├── packages.config ├── Program.cs ├── AboutForm.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Settings.settings │ └── Settings.Designer.cs ├── App.config ├── FeedbackForm.Designer.cs ├── ProjectForm.cs ├── IssueForm.resx ├── TaskForm.resx ├── FeedbackForm.resx ├── ConfigureViewForm.resx ├── ImportExportFeedbackForm.resx ├── SettingsForm.resx ├── ExportForm.resx ├── ProjectForm.resx ├── AboutForm.Designer.cs ├── MainForm.resx ├── ImportExportFeedbackForm.Designer.cs ├── AboutForm.resx ├── FeedbackForm.cs ├── TaskForm.cs ├── ExportForm.cs ├── IssueForm.cs └── ConfigureViewForm.Designer.cs ├── LICENSE ├── MiniBug.sln ├── .gitattributes ├── README.md └── .gitignore /Screenshots/task.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/Screenshots/task.png -------------------------------------------------------------------------------- /Screenshots/export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/Screenshots/export.png -------------------------------------------------------------------------------- /Screenshots/issue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/Screenshots/issue.png -------------------------------------------------------------------------------- /Screenshots/sort1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/Screenshots/sort1.gif -------------------------------------------------------------------------------- /Screenshots/sort2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/Screenshots/sort2.gif -------------------------------------------------------------------------------- /Screenshots/export2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/Screenshots/export2.png -------------------------------------------------------------------------------- /Screenshots/settings.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/Screenshots/settings.gif -------------------------------------------------------------------------------- /Screenshots/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/Screenshots/settings.png -------------------------------------------------------------------------------- /Screenshots/main-window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/Screenshots/main-window.png -------------------------------------------------------------------------------- /Screenshots/new-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/Screenshots/new-project.png -------------------------------------------------------------------------------- /MiniBug/Resources/MiniBug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/MiniBug.png -------------------------------------------------------------------------------- /MiniBug/Resources/Minibug.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/Minibug.ico -------------------------------------------------------------------------------- /MiniBug/Resources/Info_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/Info_16x16.png -------------------------------------------------------------------------------- /MiniBug/Resources/Info_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/Info_64x64.png -------------------------------------------------------------------------------- /Screenshots/visible-columns.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/Screenshots/visible-columns.gif -------------------------------------------------------------------------------- /MiniBug/Resources/About_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/About_32x32.png -------------------------------------------------------------------------------- /MiniBug/Resources/Delete_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/Delete_32x32.png -------------------------------------------------------------------------------- /MiniBug/Resources/EditBug_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/EditBug_32x32.png -------------------------------------------------------------------------------- /MiniBug/Resources/NewBug_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/NewBug_32x32.png -------------------------------------------------------------------------------- /MiniBug/Resources/NewTask_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/NewTask_32x32.png -------------------------------------------------------------------------------- /MiniBug/Resources/Priority_High.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/Priority_High.png -------------------------------------------------------------------------------- /MiniBug/Resources/CloneBug_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/CloneBug_32x32.png -------------------------------------------------------------------------------- /MiniBug/Resources/CloneTask_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/CloneTask_32x32.png -------------------------------------------------------------------------------- /MiniBug/Resources/DeleteBug_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/DeleteBug_32x32.png -------------------------------------------------------------------------------- /MiniBug/Resources/EditTask_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/EditTask_32x32.png -------------------------------------------------------------------------------- /MiniBug/Resources/FileError_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/FileError_64x64.png -------------------------------------------------------------------------------- /MiniBug/Resources/Priority_Urgent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/Priority_Urgent.png -------------------------------------------------------------------------------- /MiniBug/Resources/Settings_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/Settings_32x32.png -------------------------------------------------------------------------------- /MiniBug/Resources/StatusOK_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/StatusOK_16x16.png -------------------------------------------------------------------------------- /MiniBug/Resources/DeleteTask_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/DeleteTask_32x32.png -------------------------------------------------------------------------------- /MiniBug/Resources/FolderError_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/FolderError_64x64.png -------------------------------------------------------------------------------- /MiniBug/Resources/NewProject_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/NewProject_32x32.png -------------------------------------------------------------------------------- /MiniBug/Resources/OpenProject_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/OpenProject_32x32.png -------------------------------------------------------------------------------- /MiniBug/Resources/Priority_Immediate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/Priority_Immediate.png -------------------------------------------------------------------------------- /MiniBug/Resources/ConfigureView_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/ConfigureView_32x32.png -------------------------------------------------------------------------------- /MiniBug/Resources/CriticalError_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/CriticalError_64x64.png -------------------------------------------------------------------------------- /MiniBug/Resources/MiniBug- about-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/MiniBug- about-image.png -------------------------------------------------------------------------------- /MiniBug/Resources/StatusWarning_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/StatusWarning_16x16.png -------------------------------------------------------------------------------- /MiniBug/Resources/StatusCriticalError_16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joaomartiniano/MiniBug/HEAD/MiniBug/Resources/StatusCriticalError_16x16.png -------------------------------------------------------------------------------- /MiniBug/Classes/ComboBoxItem.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) João Martiniano. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace MiniBug 11 | { 12 | public class ComboBoxItem 13 | { 14 | public int Value { get; set; } 15 | public string Text { get; set; } 16 | 17 | public ComboBoxItem(int value, string text) 18 | { 19 | Value = value; 20 | Text = text; 21 | } 22 | 23 | public override string ToString() 24 | { 25 | return Text; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /MiniBug/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /MiniBug/Program.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) João Martiniano. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using System.Windows.Forms; 9 | 10 | namespace MiniBug 11 | { 12 | static class Program 13 | { 14 | /// 15 | /// A software project that this application will work with. Contains issues and tasks. 16 | /// 17 | public static Project SoftwareProject = null; 18 | 19 | /// 20 | /// The main entry point for the application. 21 | /// 22 | [STAThread] 23 | static void Main() 24 | { 25 | Application.EnableVisualStyles(); 26 | Application.SetCompatibleTextRenderingDefault(false); 27 | Application.Run(new MainForm()); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 João Martiniano 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MiniBug.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.28307.168 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiniBug", "MiniBug\MiniBug.csproj", "{EF183892-A316-466B-B997-2BA7FAD4DB45}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {EF183892-A316-466B-B997-2BA7FAD4DB45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {EF183892-A316-466B-B997-2BA7FAD4DB45}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {EF183892-A316-466B-B997-2BA7FAD4DB45}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {EF183892-A316-466B-B997-2BA7FAD4DB45}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {B34963BE-9B6F-47FF-B4C2-D61746118ABB} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /MiniBug/Classes/GridColumn.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) João Martiniano. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace MiniBug 11 | { 12 | /// 13 | /// A column in the issues or tasks DataGridView. 14 | /// 15 | public class GridColumn 16 | { 17 | /// Unique identification of this column in the issues DataGridView. 18 | public string Name { get; set; } = string.Empty; 19 | 20 | /// Header text of this column in the issues DataGridView. 21 | public string HeaderText { get; set; } = string.Empty; 22 | 23 | /// If true, this column is visible in the issues DataGridView. 24 | public bool Visible { get; set; } = false; 25 | 26 | /// The order of the column in the issues DataGridView. The first item has a value of 0. 27 | public int DisplayIndex { get; set; } = -1; 28 | 29 | /// A description of the column. 30 | public string Description { get; set; } = string.Empty; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /MiniBug/Classes/Extensions.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) João Martiniano. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Reflection; 10 | using System.ComponentModel; 11 | 12 | namespace MiniBug 13 | { 14 | public static class ExtensionMethods 15 | { 16 | /// 17 | /// Returns a human-readable description of a member of a enum. 18 | /// 19 | /// The enum member. 20 | /// A string containing the description. 21 | public static string ToDescription(this Enum e) 22 | { 23 | // This code was adapted from: https://blogs.msdn.microsoft.com/abhinaba/2005/10/21/c-3-0-using-extension-methods-for-enum-tostring/ 24 | 25 | Type type = e.GetType(); 26 | MemberInfo[] memInfo = type.GetMember(e.ToString()); 27 | 28 | if (memInfo != null && memInfo.Length > 0) 29 | { 30 | object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); 31 | 32 | if (attrs != null && attrs.Length > 0) 33 | { 34 | return ((DescriptionAttribute)attrs[0]).Description; 35 | } 36 | } 37 | 38 | return e.ToString(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /MiniBug/AboutForm.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) João Martiniano. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.ComponentModel; 7 | using System.Data; 8 | using System.Drawing; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using System.Windows.Forms; 13 | 14 | namespace MiniBug 15 | { 16 | public partial class AboutForm : Form 17 | { 18 | public AboutForm() 19 | { 20 | InitializeComponent(); 21 | } 22 | 23 | private void AboutForm_Load(object sender, EventArgs e) 24 | { 25 | this.Text = "About MiniBug"; 26 | this.AcceptButton = btOK; 27 | 28 | pictureBox1.Left = (lblApplicationName.Left / 2) - (pictureBox1.Width / 2); 29 | 30 | lblVersion.Text = $"Version {Application.ProductVersion.ToString()}"; 31 | } 32 | 33 | /// 34 | /// Open a web browser when the user clicks on the link label. 35 | /// 36 | private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) 37 | { 38 | System.Diagnostics.Process.Start(linkLabel1.Text); 39 | } 40 | 41 | /// 42 | /// Close this form. 43 | /// 44 | private void btOK_Click(object sender, EventArgs e) 45 | { 46 | this.Close(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /MiniBug/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Resources; 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | 6 | // General Information about an assembly is controlled through the following 7 | // set of attributes. Change these attribute values to modify the information 8 | // associated with an assembly. 9 | [assembly: AssemblyTitle("MiniBug")] 10 | [assembly: AssemblyDescription("Issue Tracker and Todo List")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("João Martiniano")] 13 | [assembly: AssemblyProduct("MiniBug")] 14 | [assembly: AssemblyCopyright("Copyright © 2019")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | 18 | // Setting ComVisible to false makes the types in this assembly not visible 19 | // to COM components. If you need to access a type in this assembly from 20 | // COM, set the ComVisible attribute to true on that type. 21 | [assembly: ComVisible(false)] 22 | 23 | // The following GUID is for the ID of the typelib if this project is exposed to COM 24 | [assembly: Guid("ef183892-a316-466b-b997-2ba7fad4db45")] 25 | 26 | // Version information for an assembly consists of the following four values: 27 | // 28 | // Major Version 29 | // Minor Version 30 | // Build Number 31 | // Revision 32 | // 33 | // You can specify all the values or you can default the Build and Revision Numbers 34 | // by using the '*' as shown below: 35 | // [assembly: AssemblyVersion("1.0.*")] 36 | [assembly: AssemblyVersion("1.0.0.0")] 37 | [assembly: AssemblyFileVersion("1.0.0.0")] 38 | [assembly: NeutralResourcesLanguage("")] 39 | 40 | -------------------------------------------------------------------------------- /MiniBug/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | True 15 | 16 | 17 | 197, 197, 197 18 | 19 | 20 | 172, 212, 253 21 | 22 | 23 | Black 24 | 25 | 26 | Segoe UI, 8.25pt 27 | 28 | 29 | False 30 | 31 | 32 | White 33 | 34 | 35 | White 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /MiniBug/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | True 7 | 8 | 9 | 197, 197, 197 10 | 11 | 12 | 172, 212, 253 13 | 14 | 15 | Black 16 | 17 | 18 | Segoe UI, 8.25pt 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | False 28 | 29 | 30 | White 31 | 32 | 33 | White 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /MiniBug/Classes/GridTasksSortSettings.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) João Martiniano. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | 11 | namespace MiniBug 12 | { 13 | /// 14 | /// Sort settings for the tasks DataGridView. 15 | /// 16 | public class GridTasksSortSettings : IEquatable 17 | { 18 | /// 19 | /// First, sort by this column. 20 | /// 21 | public TaskFieldsUI FirstColumn { get; set; } 22 | 23 | /// 24 | /// Sort order for the first sort column. 25 | /// 26 | public SortOrder FirstColumnSortOrder { get; set; } 27 | 28 | /// 29 | /// Then, sort by this column. 30 | /// 31 | public TaskFieldsUI? SecondColumn { get; set; } 32 | 33 | /// 34 | /// Sort order for the second sort column. 35 | /// 36 | public SortOrder? SecondColumnSortOrder { get; set; } 37 | 38 | public GridTasksSortSettings(TaskFieldsUI firstColumn, SortOrder firstColumnSortOrder, TaskFieldsUI? secondColumn, SortOrder? secondColumnOrder) 39 | { 40 | FirstColumn = firstColumn; 41 | FirstColumnSortOrder = firstColumnSortOrder; 42 | SecondColumn = secondColumn; 43 | SecondColumnSortOrder = secondColumnOrder; 44 | } 45 | 46 | public override bool Equals(object obj) 47 | { 48 | return this.Equals(obj as GridTasksSortSettings); 49 | } 50 | 51 | public bool Equals(GridTasksSortSettings s) 52 | { 53 | // If parameter is null, return false. 54 | if (Object.ReferenceEquals(s, null)) 55 | { 56 | return false; 57 | } 58 | 59 | // Optimization for a common success case. 60 | if (Object.ReferenceEquals(this, s)) 61 | { 62 | return true; 63 | } 64 | 65 | // If run-time types are not exactly the same, return false. 66 | if (this.GetType() != s.GetType()) 67 | { 68 | return false; 69 | } 70 | 71 | // Return true if all fields match. 72 | bool test1 = (FirstColumn == s.FirstColumn) && (FirstColumnSortOrder == s.FirstColumnSortOrder); 73 | bool test2 = (SecondColumn == s.SecondColumn) && (SecondColumnSortOrder == s.SecondColumnSortOrder); 74 | 75 | return test1 && test2; 76 | } 77 | 78 | public override int GetHashCode() 79 | { 80 | unchecked 81 | { 82 | int hash = 17; 83 | hash = hash * 23 + FirstColumn.GetHashCode(); 84 | hash = hash * 23 + FirstColumnSortOrder.GetHashCode(); 85 | hash = hash * 23 + ((SecondColumn == null) ? 0 : SecondColumn.GetHashCode()); 86 | hash = hash * 23 + ((SecondColumnSortOrder == null) ? 0 : SecondColumnSortOrder.GetHashCode()); 87 | return hash; 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /MiniBug/Classes/GridIssuesSortSettings.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) João Martiniano. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | 11 | namespace MiniBug 12 | { 13 | /// 14 | /// Sort settings for the issues DataGridView. 15 | /// 16 | public class GridIssuesSortSettings : IEquatable 17 | { 18 | /// 19 | /// First, sort by this column. 20 | /// 21 | public IssueFieldsUI FirstColumn { get; set; } 22 | 23 | /// 24 | /// Sort order for the first sort column. 25 | /// 26 | public SortOrder FirstColumnSortOrder { get; set; } 27 | 28 | /// 29 | /// Then, sort by this column. 30 | /// 31 | public IssueFieldsUI? SecondColumn { get; set; } 32 | 33 | /// 34 | /// Sort order for the second sort column. 35 | /// 36 | public SortOrder? SecondColumnSortOrder { get; set; } 37 | 38 | public GridIssuesSortSettings(IssueFieldsUI firstColumn, SortOrder firstColumnSortOrder, IssueFieldsUI? secondColumn, SortOrder? secondColumnOrder) 39 | { 40 | FirstColumn = firstColumn; 41 | FirstColumnSortOrder = firstColumnSortOrder; 42 | SecondColumn = secondColumn; 43 | SecondColumnSortOrder = secondColumnOrder; 44 | } 45 | 46 | public override bool Equals(object obj) 47 | { 48 | return this.Equals(obj as GridIssuesSortSettings); 49 | } 50 | 51 | public bool Equals(GridIssuesSortSettings s) 52 | { 53 | // If parameter is null, return false. 54 | if (Object.ReferenceEquals(s, null)) 55 | { 56 | return false; 57 | } 58 | 59 | // Optimization for a common success case. 60 | if (Object.ReferenceEquals(this, s)) 61 | { 62 | return true; 63 | } 64 | 65 | // If run-time types are not exactly the same, return false. 66 | if (this.GetType() != s.GetType()) 67 | { 68 | return false; 69 | } 70 | 71 | // Return true if all fields match. 72 | bool test1 = (FirstColumn == s.FirstColumn) && (FirstColumnSortOrder == s.FirstColumnSortOrder); 73 | bool test2 = (SecondColumn == s.SecondColumn) && (SecondColumnSortOrder == s.SecondColumnSortOrder); 74 | 75 | return test1 && test2; 76 | } 77 | 78 | public override int GetHashCode() 79 | { 80 | unchecked 81 | { 82 | int hash = 17; 83 | hash = hash * 23 + FirstColumn.GetHashCode(); 84 | hash = hash * 23 + FirstColumnSortOrder.GetHashCode(); 85 | hash = hash * 23 + ((SecondColumn == null) ? 0 : SecondColumn.GetHashCode()); 86 | hash = hash * 23 + ((SecondColumnSortOrder == null) ? 0 : SecondColumnSortOrder.GetHashCode()); 87 | return hash; 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MiniBug - Issue Tracker and To-do List 2 | 3 | MiniBug is a barebones, simple issue tracker and to-do list. It is a Windows desktop single-user application. 4 | 5 | MiniBug main window 6 | 7 | MiniBug does not use a database to store data: instead the application stores each project in a .json file. This means that if you need to work on projects, in different computers, you can share a MiniBug project between computers, by putting the .json file in something like Dropbox. 8 | 9 | ## Features 10 | 11 | - Issues: create, edit, delete, clone 12 | - Tasks: create, edit, delete, clone 13 | - Show/hide/sort columns 14 | - Some user defined settings 15 | - Export issues and tasks to CSV format 16 | 17 | ## Sample project 18 | 19 | I've made a small sample project, with bugs and tasks copied from some applications' public bug trackers (Inkscape, Firefox, MariaDB and Kodi). 20 | 21 | Download the file minibug-MiniBug Sample Project.json and open it in MiniBug. 22 | 23 | # Getting Started 24 | 25 | ## Prerequisites 26 | 27 | - Microsoft Windows 7 (maybe it works on older versions but I haven't tested) 28 | - Microsoft .NET Framework 4.6.1 29 | 30 | # How To Use 31 | 32 | First you need to create a new project (File > New Project), define a project name and choose a location to save it: 33 | 34 | New project window 35 | 36 | Next you can start adding issues and tasks: 37 | - issues are bugs/problems 38 | - tasks are items in a to-do list 39 | 40 | ## Issues 41 | 42 | Edit issue 43 | 44 | 45 | ## Tasks 46 | 47 | Edit task 48 | 49 | ## Settings 50 | 51 | The user can modify some settings (File > Settings) in order to customize the look and feel of the application: 52 | 53 | Edit settings 54 | 55 | Settings in action: 56 | 57 | Edit settings 58 | 59 | ## Sorting 60 | 61 | You can sort the grid rows in two ways: 62 | 63 | - by clicking on a column header: 64 | 65 | Sort rows (first method) 66 | 67 | - by using the **Configure Columns** window: 68 | 69 | Sort rows (second method) 70 | 71 | Using the second method you can sort by up to two columns and with different criteria (ascending or descending). 72 | 73 | ## Column visibility 74 | 75 | You can show/hide any column (except the **ID** column, which is always visible), using the **Configure Columns** window: 76 | 77 | Show/hide columns 78 | 79 | ## Exporting 80 | 81 | You can export a project's issues and tasks to CSV (comma separated values) files: 82 | 83 | Export project 84 | 85 | Because issues and tasks have a slightly different structure, they are exported to separate files. If a project only has issues or tasks, only one file will be generated: 86 | 87 | Export project only with issues 88 | 89 | # License 90 | 91 | This project is licensed under the MIT License - see the LICENSE.md file for details. 92 | 93 | # Acknowledgments 94 | 95 | This project uses the following libraries: 96 | 97 | - Json.NET: for reading/writing to .json files 98 | - CsvHelper: for exporting to CSV -------------------------------------------------------------------------------- /MiniBug/Classes/ImportExportResult.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) João Martiniano. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace MiniBug 11 | { 12 | public enum ImportExportOperation { None = 0, Import, Export } 13 | 14 | /// 15 | /// Detail information about a specific import/export operation. 16 | /// 17 | public class ImportExportResultData 18 | { 19 | public string FileName { get; set; } = string.Empty; 20 | 21 | public string Path { get; set; } = string.Empty; 22 | 23 | private FileSystemOperationStatus _result; 24 | 25 | public FileSystemOperationStatus Result 26 | { 27 | get { return _result; } 28 | set { _result = value; ComposeMessage(); } 29 | } 30 | 31 | public string ResultMessage { get; set; } = string.Empty; 32 | 33 | public string ResultDescription { get; set; } = string.Empty; 34 | 35 | public string ResultSolution { get; set; } = string.Empty; 36 | 37 | private void ComposeMessage() 38 | { 39 | switch (Result) 40 | { 41 | case FileSystemOperationStatus.ExportOK: 42 | ResultMessage = "Export successful"; 43 | break; 44 | 45 | case FileSystemOperationStatus.ExportToCsvErrorDirectoryNotFound: 46 | ResultMessage = "Location not found"; 47 | ResultDescription = "The specified location does not exist."; 48 | ResultSolution = "Choose a different location."; 49 | break; 50 | 51 | case FileSystemOperationStatus.ExportToCsvErrorPathTooLong: 52 | ResultMessage = "Path too long"; 53 | ResultDescription = "The path, file name or both are too long."; 54 | ResultSolution = "Choose a different location and/or a shorter filename."; 55 | break; 56 | 57 | case FileSystemOperationStatus.ExportToCsvErrorExporterComponent: 58 | ResultMessage = "Component error"; 59 | ResultDescription = "The component responsible for exporting the project has failed."; 60 | ResultSolution = "Please try again."; 61 | break; 62 | 63 | case FileSystemOperationStatus.ExportToCsvIOError: 64 | ResultMessage = "I/O Error"; 65 | ResultDescription = "There was a general input/output error while attempting to export."; 66 | ResultSolution = "Choose a different drive/device to export the project."; 67 | break; 68 | 69 | default: 70 | ResultMessage = string.Empty; 71 | ResultDescription = string.Empty; 72 | ResultSolution = string.Empty; 73 | break; 74 | } 75 | } 76 | } 77 | 78 | /// 79 | /// Result of an import/export operation. Contains details about the import/export operation. 80 | /// 81 | public class ImportExportResult 82 | { 83 | public ImportExportOperation Operation { get; set; } = ImportExportOperation.None; 84 | 85 | public ImportExportResultData Issues { get; set; } = null; 86 | 87 | public ImportExportResultData Tasks { get; set; } = null; 88 | 89 | public ImportExportResult(ImportExportOperation operation) 90 | { 91 | Operation = operation; 92 | 93 | Issues = new ImportExportResultData(); 94 | Tasks = new ImportExportResultData(); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /MiniBug/Classes/Task.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) João Martiniano. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.ComponentModel; 10 | 11 | namespace MiniBug 12 | { 13 | /// 14 | /// Status of a task. 15 | /// 16 | public enum TaskStatus 17 | { 18 | None = 0, 19 | [DescriptionAttribute("Not started")] 20 | NotStarted, 21 | [DescriptionAttribute("In progress")] 22 | InProgress, 23 | Finished 24 | }; 25 | 26 | /// 27 | /// Priority of a task. 28 | /// 29 | public enum TaskPriority { None = 0, Low, Normal, High, Urgent, Immediate }; 30 | 31 | /// 32 | /// Fields used on the user interface (in a DataGridView and on a form) to represent a task. 33 | /// 34 | public enum TaskFieldsUI 35 | { 36 | ID = 0, 37 | Priority, 38 | Status, 39 | TargetVersion, 40 | [DescriptionAttribute("Date created")] 41 | Summary, 42 | Description, 43 | [DescriptionAttribute("Target version")] 44 | DateCreated, 45 | [DescriptionAttribute("Date modified")] 46 | DateModified 47 | }; 48 | 49 | [Serializable] 50 | public class Task 51 | { 52 | /// 53 | /// Gets the ID of this task. 54 | /// 55 | public int ID { get; set; } 56 | 57 | /// 58 | /// Gets or sets the priority of this task. 59 | /// 60 | public TaskPriority Priority { get; set; } 61 | 62 | /// 63 | /// Gets or sets the status of this task. 64 | /// 65 | public TaskStatus Status { get; set; } 66 | 67 | /// 68 | /// Gets or sets the future version on which this task will be applied/finished. 69 | /// 70 | public string TargetVersion { get; set; } 71 | 72 | /// 73 | /// Gets or sets the summary of this task. 74 | /// 75 | public string Summary { get; set; } 76 | 77 | /// 78 | /// Gets or sets the description of this task. 79 | /// 80 | public string Description { get; set; } 81 | 82 | /// 83 | /// Gets or sets the date/time this task was created. 84 | /// 85 | public DateTime DateCreated { get; set; } 86 | 87 | /// 88 | /// Gets or sets the date/time this task was modified. 89 | /// 90 | public DateTime DateModified { get; set; } 91 | 92 | /// 93 | /// Creates a new task. 94 | /// 95 | public Task() 96 | { 97 | ; 98 | } 99 | 100 | /// 101 | /// Creates a new task. 102 | /// 103 | /// The ID of this task. 104 | public Task(int id) 105 | { 106 | ID = id; 107 | } 108 | 109 | /// 110 | /// Creates a new task. 111 | /// 112 | public Task(TaskStatus status, TaskPriority priority, string summary, string description, string targetVersion) 113 | { 114 | Status = status; 115 | Priority = priority; 116 | Summary = summary; 117 | Description = description; 118 | TargetVersion = targetVersion; 119 | 120 | DateCreated = DateTime.Now; 121 | DateModified = DateTime.Now; 122 | } 123 | 124 | /// 125 | /// Create a clone of an instance of the Task class. 126 | /// 127 | /// The instance of the Task class that will get the cloned instance's data. 128 | public void Clone(ref Task clonedInstance) 129 | { 130 | clonedInstance.Status = this.Status; 131 | clonedInstance.Priority = this.Priority; 132 | clonedInstance.Summary = this.Summary; 133 | clonedInstance.Description = this.Description; 134 | clonedInstance.TargetVersion = this.TargetVersion; 135 | clonedInstance.DateCreated = clonedInstance.DateModified = DateTime.Now; 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /MiniBug/Classes/Issue.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) João Martiniano. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.ComponentModel; 10 | 11 | namespace MiniBug 12 | { 13 | /// 14 | /// Status of an issue. 15 | /// 16 | public enum IssueStatus 17 | { 18 | None = 0, 19 | Unconfirmed, 20 | Confirmed, 21 | [DescriptionAttribute("In progress")] 22 | InProgress, 23 | Resolved, 24 | Closed 25 | }; 26 | 27 | /// 28 | /// Priority of an issue. 29 | /// 30 | public enum IssuePriority { None = 0, Low, Normal, High, Urgent, Immediate }; 31 | 32 | /// 33 | /// Fields used on the user interface (in a DataGridView and on a form) to represent an issue. 34 | /// 35 | public enum IssueFieldsUI 36 | { 37 | ID = 0, 38 | Priority, 39 | Status, 40 | Version, 41 | [DescriptionAttribute("Target version")] 42 | TargetVersion, 43 | Summary, 44 | Description, 45 | [DescriptionAttribute("Date created")] 46 | DateCreated, 47 | [DescriptionAttribute("Date modified")] 48 | DateModified 49 | }; 50 | 51 | [Serializable] 52 | public class Issue 53 | { 54 | /// 55 | /// Gets the ID of this issue. 56 | /// 57 | public int ID { get; set; } = 0; 58 | 59 | /// 60 | /// Gets or sets the priority of this issue. 61 | /// 62 | public IssuePriority Priority { get; set; } = IssuePriority.None; 63 | 64 | /// 65 | /// Gets or sets the status of this issue. 66 | /// 67 | public IssueStatus Status { get; set; } = IssueStatus.None; 68 | 69 | /// 70 | /// Gets or sets the version of the software on which this issue occurs. 71 | /// 72 | public string Version { get; set; } = string.Empty; 73 | 74 | /// 75 | /// Gets or sets the future version on which this issue will be resolved. 76 | /// 77 | public string TargetVersion { get; set; } = string.Empty; 78 | 79 | /// 80 | /// Gets or sets the summary of this issue. 81 | /// 82 | public string Summary { get; set; } = string.Empty; 83 | 84 | /// 85 | /// Gets or sets the description of this issue. 86 | /// 87 | public string Description { get; set; } = string.Empty; 88 | 89 | /// 90 | /// Gets or sets the date/time this issue was created. 91 | /// 92 | public DateTime DateCreated { get; set; } 93 | 94 | /// 95 | /// Gets or sets the date/time this issue was modified. 96 | /// 97 | public DateTime DateModified { get; set; } 98 | 99 | /// 100 | /// Creates a new issue. 101 | /// 102 | public Issue() 103 | { 104 | ; 105 | } 106 | 107 | /// 108 | /// Creates a new issue. 109 | /// 110 | /// The ID of this issue. 111 | public Issue(int id) 112 | { 113 | ID = id; 114 | 115 | DateCreated = DateTime.Now; 116 | DateModified = DateTime.Now; 117 | } 118 | 119 | /// 120 | /// Creates a new issue. 121 | /// 122 | public Issue(IssueStatus status, IssuePriority priority, string summary, string description, string version, string targetVersion) 123 | { 124 | Status = status; 125 | Priority = priority; 126 | Summary = summary; 127 | Description = description; 128 | Version = version; 129 | TargetVersion = targetVersion; 130 | 131 | DateCreated = DateTime.Now; 132 | DateModified = DateTime.Now; 133 | } 134 | 135 | /// 136 | /// Create a clone of an instance of the Issue class. 137 | /// 138 | /// The instance of the Issue class that will get the cloned instance's data. 139 | public void Clone(ref Issue clonedInstance) 140 | { 141 | clonedInstance.Status = this.Status; 142 | clonedInstance.Priority = this.Priority; 143 | clonedInstance.Summary = this.Summary; 144 | clonedInstance.Description = this.Description; 145 | clonedInstance.Version = this.Version; 146 | clonedInstance.TargetVersion = this.TargetVersion; 147 | clonedInstance.DateCreated = clonedInstance.DateModified = DateTime.Now; 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /MiniBug/FeedbackForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MiniBug 2 | { 3 | partial class FeedbackForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.btClose = new System.Windows.Forms.Button(); 32 | this.lblMessageTitle = new System.Windows.Forms.Label(); 33 | this.lblMessage = new System.Windows.Forms.Label(); 34 | this.pictureBox1 = new System.Windows.Forms.PictureBox(); 35 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); 36 | this.SuspendLayout(); 37 | // 38 | // btClose 39 | // 40 | this.btClose.Location = new System.Drawing.Point(621, 228); 41 | this.btClose.Name = "btClose"; 42 | this.btClose.Size = new System.Drawing.Size(75, 23); 43 | this.btClose.TabIndex = 0; 44 | this.btClose.Text = "Close"; 45 | this.btClose.UseVisualStyleBackColor = true; 46 | this.btClose.Click += new System.EventHandler(this.btClose_Click); 47 | // 48 | // lblMessageTitle 49 | // 50 | this.lblMessageTitle.AutoEllipsis = true; 51 | this.lblMessageTitle.Font = new System.Drawing.Font("Segoe UI", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 52 | this.lblMessageTitle.Location = new System.Drawing.Point(91, 23); 53 | this.lblMessageTitle.Name = "lblMessageTitle"; 54 | this.lblMessageTitle.Size = new System.Drawing.Size(605, 32); 55 | this.lblMessageTitle.TabIndex = 1; 56 | this.lblMessageTitle.Text = "MessageTitle"; 57 | // 58 | // lblMessage 59 | // 60 | this.lblMessage.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 61 | this.lblMessage.Location = new System.Drawing.Point(94, 75); 62 | this.lblMessage.Name = "lblMessage"; 63 | this.lblMessage.Size = new System.Drawing.Size(583, 126); 64 | this.lblMessage.TabIndex = 2; 65 | this.lblMessage.Text = "Message"; 66 | // 67 | // pictureBox1 68 | // 69 | this.pictureBox1.Location = new System.Drawing.Point(12, 23); 70 | this.pictureBox1.Name = "pictureBox1"; 71 | this.pictureBox1.Size = new System.Drawing.Size(64, 64); 72 | this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.CenterImage; 73 | this.pictureBox1.TabIndex = 3; 74 | this.pictureBox1.TabStop = false; 75 | // 76 | // FeedbackForm 77 | // 78 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 79 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 80 | this.ClientSize = new System.Drawing.Size(708, 263); 81 | this.Controls.Add(this.pictureBox1); 82 | this.Controls.Add(this.lblMessage); 83 | this.Controls.Add(this.lblMessageTitle); 84 | this.Controls.Add(this.btClose); 85 | this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 86 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 87 | this.MaximizeBox = false; 88 | this.MinimizeBox = false; 89 | this.Name = "FeedbackForm"; 90 | this.ShowIcon = false; 91 | this.ShowInTaskbar = false; 92 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 93 | this.Text = "FeedbackForm"; 94 | this.Load += new System.EventHandler(this.FeedbackForm_Load); 95 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); 96 | this.ResumeLayout(false); 97 | 98 | } 99 | 100 | #endregion 101 | 102 | private System.Windows.Forms.Button btClose; 103 | private System.Windows.Forms.Label lblMessageTitle; 104 | private System.Windows.Forms.Label lblMessage; 105 | private System.Windows.Forms.PictureBox pictureBox1; 106 | } 107 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | project.fragment.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.jfm 193 | *.pfx 194 | *.publishsettings 195 | node_modules/ 196 | orleans.codegen.cs 197 | 198 | # Since there are multiple workflows, uncomment next line to ignore bower_components 199 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 200 | #bower_components/ 201 | 202 | # RIA/Silverlight projects 203 | Generated_Code/ 204 | 205 | # Backup & report files from converting an old project file 206 | # to a newer Visual Studio version. Backup files are not needed, 207 | # because we have git ;-) 208 | _UpgradeReport_Files/ 209 | Backup*/ 210 | UpgradeLog*.XML 211 | UpgradeLog*.htm 212 | 213 | # SQL Server files 214 | *.mdf 215 | *.ldf 216 | 217 | # Business Intelligence projects 218 | *.rdl.data 219 | *.bim.layout 220 | *.bim_*.settings 221 | 222 | # Microsoft Fakes 223 | FakesAssemblies/ 224 | 225 | # GhostDoc plugin setting file 226 | *.GhostDoc.xml 227 | 228 | # Node.js Tools for Visual Studio 229 | .ntvs_analysis.dat 230 | 231 | # Visual Studio 6 build log 232 | *.plg 233 | 234 | # Visual Studio 6 workspace options file 235 | *.opt 236 | 237 | # Visual Studio LightSwitch build output 238 | **/*.HTMLClient/GeneratedArtifacts 239 | **/*.DesktopClient/GeneratedArtifacts 240 | **/*.DesktopClient/ModelManifest.xml 241 | **/*.Server/GeneratedArtifacts 242 | **/*.Server/ModelManifest.xml 243 | _Pvt_Extensions 244 | 245 | # Paket dependency manager 246 | .paket/paket.exe 247 | paket-files/ 248 | 249 | # FAKE - F# Make 250 | .fake/ 251 | 252 | # JetBrains Rider 253 | .idea/ 254 | *.sln.iml 255 | 256 | # CodeRush 257 | .cr/ 258 | 259 | # Python Tools for Visual Studio (PTVS) 260 | __pycache__/ 261 | *.pyc -------------------------------------------------------------------------------- /MiniBug/ProjectForm.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) João Martiniano. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.ComponentModel; 7 | using System.Data; 8 | using System.Drawing; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using System.Windows.Forms; 13 | using System.IO; 14 | 15 | namespace MiniBug 16 | { 17 | public partial class ProjectForm : Form 18 | { 19 | /// 20 | /// Gets the current operation. 21 | /// 22 | public MiniBug.OperationType Operation { get; private set; } = OperationType.None; 23 | 24 | /// 25 | /// Gets the name of the project. 26 | /// 27 | public string ProjectName { get; private set; } = string.Empty; 28 | 29 | /// 30 | /// Gets the name of the project file. 31 | /// 32 | public string ProjectFilename { get; private set; } = string.Empty; 33 | 34 | /// 35 | /// Gets the location of the project file. 36 | /// 37 | public string ProjectLocation { get; private set; } = string.Empty; 38 | 39 | public ProjectForm(OperationType operation, string projectName = "", string projectFilename = "", string projectLocation = "") 40 | { 41 | InitializeComponent(); 42 | 43 | Operation = operation; 44 | 45 | if (Operation == OperationType.Edit) 46 | { 47 | // Edit an existing project 48 | ProjectName = projectName; 49 | ProjectFilename = projectFilename; 50 | ProjectLocation = projectLocation; 51 | } 52 | } 53 | 54 | private void ProjectForm_Load(object sender, EventArgs e) 55 | { 56 | // Suspend the layout logic for the form, while the application is initializing 57 | this.SuspendLayout(); 58 | 59 | this.AcceptButton = btOk; 60 | this.CancelButton = btCancel; 61 | 62 | lblFormTitle.Width = this.ClientRectangle.Width; 63 | 64 | txtName.MaxLength = 255; 65 | 66 | // Make initializations based on the type of operation 67 | if (Operation == OperationType.New) 68 | { 69 | this.Text = "New Project"; 70 | lblFormTitle.Text = "Create a new project"; 71 | 72 | btOk.Enabled = false; 73 | } 74 | else if (Operation == OperationType.Edit) 75 | { 76 | this.Text = "Edit Project"; 77 | lblFormTitle.Text = "Edit the current project"; 78 | 79 | // Populate the controls 80 | txtName.Text = ProjectName; 81 | txtFilename.Text = ProjectFilename; 82 | txtLocation.Text = ProjectLocation; 83 | } 84 | 85 | // Resume the layout logic 86 | this.ResumeLayout(); 87 | 88 | SetAccessibilityInformation(); 89 | } 90 | 91 | /// 92 | /// Add accessibility data to form controls. 93 | /// 94 | private void SetAccessibilityInformation() 95 | { 96 | txtFilename.AccessibleDescription = "The name of the file containing the project"; 97 | txtLocation.AccessibleDescription = "Folder where the project file will be saved"; 98 | btBrowse.AccessibleDescription = "Browse for the folder where the project file will be saved"; 99 | } 100 | 101 | /// 102 | /// Browse the location where the project will be saved. 103 | /// 104 | private void btBrowse_Click(object sender, EventArgs e) 105 | { 106 | if (folderBrowserDialog1.ShowDialog() == DialogResult.OK) 107 | { 108 | txtLocation.Text = folderBrowserDialog1.SelectedPath; 109 | 110 | if (txtName.Text != string.Empty) 111 | { 112 | btOk.Enabled = true; 113 | } 114 | } 115 | } 116 | 117 | /// 118 | /// Close the form. 119 | /// 120 | private void btOk_Click(object sender, EventArgs e) 121 | { 122 | if ((!string.IsNullOrWhiteSpace(txtName.Text)) && (!string.IsNullOrWhiteSpace(txtLocation.Text))) 123 | { 124 | ProjectName = txtName.Text; 125 | ProjectFilename = txtFilename.Text; 126 | ProjectLocation = txtLocation.Text; 127 | 128 | this.DialogResult = DialogResult.OK; 129 | this.Close(); 130 | } 131 | } 132 | 133 | /// 134 | /// Cancel this operation and close the form. 135 | /// 136 | private void btCancel_Click(object sender, EventArgs e) 137 | { 138 | this.DialogResult = DialogResult.Cancel; 139 | this.Close(); 140 | } 141 | 142 | /// 143 | /// Handle the TextChanged event for the project name textbox control. 144 | /// 145 | private void txtName_TextChanged(object sender, EventArgs e) 146 | { 147 | if (!string.IsNullOrWhiteSpace(txtName.Text)) 148 | { 149 | txtFilename.Text = $"minibug-{txtName.Text}.json"; 150 | 151 | if (txtLocation.Text != string.Empty) 152 | { 153 | btOk.Enabled = true; 154 | } 155 | } 156 | else 157 | { 158 | txtFilename.Text = string.Empty; 159 | btOk.Enabled = false; 160 | } 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /MiniBug/IssueForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /MiniBug/TaskForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /MiniBug/FeedbackForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /MiniBug/ConfigureViewForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /MiniBug/ImportExportFeedbackForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /MiniBug/SettingsForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 17, 17 122 | 123 | -------------------------------------------------------------------------------- /MiniBug/ExportForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 17, 17 122 | 123 | -------------------------------------------------------------------------------- /MiniBug/ProjectForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 17, 17 122 | 123 | -------------------------------------------------------------------------------- /MiniBug/AboutForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MiniBug 2 | { 3 | partial class AboutForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AboutForm)); 32 | this.btOK = new System.Windows.Forms.Button(); 33 | this.lblApplicationName = new System.Windows.Forms.Label(); 34 | this.pictureBox1 = new System.Windows.Forms.PictureBox(); 35 | this.lblVersion = new System.Windows.Forms.Label(); 36 | this.label2 = new System.Windows.Forms.Label(); 37 | this.linkLabel1 = new System.Windows.Forms.LinkLabel(); 38 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); 39 | this.SuspendLayout(); 40 | // 41 | // btOK 42 | // 43 | this.btOK.Location = new System.Drawing.Point(315, 146); 44 | this.btOK.Name = "btOK"; 45 | this.btOK.Size = new System.Drawing.Size(75, 23); 46 | this.btOK.TabIndex = 0; 47 | this.btOK.Text = "OK"; 48 | this.btOK.UseVisualStyleBackColor = true; 49 | this.btOK.Click += new System.EventHandler(this.btOK_Click); 50 | // 51 | // lblApplicationName 52 | // 53 | this.lblApplicationName.AutoSize = true; 54 | this.lblApplicationName.Font = new System.Drawing.Font("Microsoft Sans Serif", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 55 | this.lblApplicationName.Location = new System.Drawing.Point(107, 12); 56 | this.lblApplicationName.Name = "lblApplicationName"; 57 | this.lblApplicationName.Size = new System.Drawing.Size(197, 24); 58 | this.lblApplicationName.TabIndex = 1; 59 | this.lblApplicationName.Text = "MiniBug Issue Tracker"; 60 | // 61 | // pictureBox1 62 | // 63 | this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image"))); 64 | this.pictureBox1.Location = new System.Drawing.Point(20, 12); 65 | this.pictureBox1.Name = "pictureBox1"; 66 | this.pictureBox1.Size = new System.Drawing.Size(64, 64); 67 | this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; 68 | this.pictureBox1.TabIndex = 2; 69 | this.pictureBox1.TabStop = false; 70 | // 71 | // lblVersion 72 | // 73 | this.lblVersion.AutoSize = true; 74 | this.lblVersion.Location = new System.Drawing.Point(107, 52); 75 | this.lblVersion.Name = "lblVersion"; 76 | this.lblVersion.Size = new System.Drawing.Size(46, 13); 77 | this.lblVersion.TabIndex = 3; 78 | this.lblVersion.Text = "Version"; 79 | // 80 | // label2 81 | // 82 | this.label2.AutoSize = true; 83 | this.label2.Location = new System.Drawing.Point(107, 81); 84 | this.label2.Name = "label2"; 85 | this.label2.Size = new System.Drawing.Size(192, 13); 86 | this.label2.TabIndex = 4; 87 | this.label2.Text = "Copyright © 2019 - João Martiniano"; 88 | // 89 | // linkLabel1 90 | // 91 | this.linkLabel1.AutoSize = true; 92 | this.linkLabel1.Location = new System.Drawing.Point(107, 110); 93 | this.linkLabel1.Name = "linkLabel1"; 94 | this.linkLabel1.Size = new System.Drawing.Size(265, 13); 95 | this.linkLabel1.TabIndex = 5; 96 | this.linkLabel1.TabStop = true; 97 | this.linkLabel1.Text = "https://www.github.com/joaomartiniano/MiniBug"; 98 | this.linkLabel1.LinkClicked += new System.Windows.Forms.LinkLabelLinkClickedEventHandler(this.linkLabel1_LinkClicked); 99 | // 100 | // AboutForm 101 | // 102 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 103 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 104 | this.ClientSize = new System.Drawing.Size(402, 181); 105 | this.Controls.Add(this.linkLabel1); 106 | this.Controls.Add(this.label2); 107 | this.Controls.Add(this.lblVersion); 108 | this.Controls.Add(this.pictureBox1); 109 | this.Controls.Add(this.lblApplicationName); 110 | this.Controls.Add(this.btOK); 111 | this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 112 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 113 | this.MaximizeBox = false; 114 | this.MinimizeBox = false; 115 | this.Name = "AboutForm"; 116 | this.ShowInTaskbar = false; 117 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 118 | this.Text = "AboutForm"; 119 | this.Load += new System.EventHandler(this.AboutForm_Load); 120 | ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); 121 | this.ResumeLayout(false); 122 | this.PerformLayout(); 123 | 124 | } 125 | 126 | #endregion 127 | 128 | private System.Windows.Forms.Button btOK; 129 | private System.Windows.Forms.Label lblApplicationName; 130 | private System.Windows.Forms.PictureBox pictureBox1; 131 | private System.Windows.Forms.Label lblVersion; 132 | private System.Windows.Forms.Label label2; 133 | private System.Windows.Forms.LinkLabel linkLabel1; 134 | } 135 | } -------------------------------------------------------------------------------- /MiniBug/MainForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 17, 17 122 | 123 | 124 | 122, 17 125 | 126 | 127 | 237, 17 128 | 129 | -------------------------------------------------------------------------------- /MiniBug/ImportExportFeedbackForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MiniBug 2 | { 3 | partial class ImportExportFeedbackForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.lblFormTitle = new System.Windows.Forms.Label(); 32 | this.btClose = new System.Windows.Forms.Button(); 33 | this.IconDescription = new System.Windows.Forms.PictureBox(); 34 | this.lblDescription = new System.Windows.Forms.Label(); 35 | this.label1 = new System.Windows.Forms.Label(); 36 | this.FlowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel(); 37 | ((System.ComponentModel.ISupportInitialize)(this.IconDescription)).BeginInit(); 38 | this.SuspendLayout(); 39 | // 40 | // lblFormTitle 41 | // 42 | this.lblFormTitle.BackColor = System.Drawing.Color.White; 43 | this.lblFormTitle.Font = new System.Drawing.Font("Segoe UI", 18F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 44 | this.lblFormTitle.Location = new System.Drawing.Point(0, 0); 45 | this.lblFormTitle.Name = "lblFormTitle"; 46 | this.lblFormTitle.Padding = new System.Windows.Forms.Padding(10, 0, 0, 0); 47 | this.lblFormTitle.Size = new System.Drawing.Size(609, 63); 48 | this.lblFormTitle.TabIndex = 2; 49 | this.lblFormTitle.Text = "Export Project to CSV"; 50 | this.lblFormTitle.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; 51 | // 52 | // btClose 53 | // 54 | this.btClose.Location = new System.Drawing.Point(517, 342); 55 | this.btClose.Name = "btClose"; 56 | this.btClose.Size = new System.Drawing.Size(75, 23); 57 | this.btClose.TabIndex = 3; 58 | this.btClose.Text = "Close"; 59 | this.btClose.UseVisualStyleBackColor = true; 60 | this.btClose.Click += new System.EventHandler(this.btClose_Click); 61 | // 62 | // IconDescription 63 | // 64 | this.IconDescription.Location = new System.Drawing.Point(17, 85); 65 | this.IconDescription.Name = "IconDescription"; 66 | this.IconDescription.Size = new System.Drawing.Size(16, 16); 67 | this.IconDescription.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize; 68 | this.IconDescription.TabIndex = 13; 69 | this.IconDescription.TabStop = false; 70 | // 71 | // lblDescription 72 | // 73 | this.lblDescription.AutoSize = true; 74 | this.lblDescription.Location = new System.Drawing.Point(35, 87); 75 | this.lblDescription.Name = "lblDescription"; 76 | this.lblDescription.Size = new System.Drawing.Size(66, 13); 77 | this.lblDescription.TabIndex = 14; 78 | this.lblDescription.Text = "Description"; 79 | // 80 | // label1 81 | // 82 | this.label1.AutoSize = true; 83 | this.label1.Location = new System.Drawing.Point(14, 118); 84 | this.label1.Name = "label1"; 85 | this.label1.Size = new System.Drawing.Size(45, 13); 86 | this.label1.TabIndex = 15; 87 | this.label1.Text = "&Details:"; 88 | // 89 | // FlowLayoutPanel1 90 | // 91 | this.FlowLayoutPanel1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; 92 | this.FlowLayoutPanel1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 93 | this.FlowLayoutPanel1.Location = new System.Drawing.Point(17, 136); 94 | this.FlowLayoutPanel1.Name = "FlowLayoutPanel1"; 95 | this.FlowLayoutPanel1.Padding = new System.Windows.Forms.Padding(0, 2, 0, 2); 96 | this.FlowLayoutPanel1.Size = new System.Drawing.Size(575, 188); 97 | this.FlowLayoutPanel1.TabIndex = 16; 98 | // 99 | // ImportExportFeedbackForm 100 | // 101 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 102 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 103 | this.ClientSize = new System.Drawing.Size(609, 377); 104 | this.Controls.Add(this.FlowLayoutPanel1); 105 | this.Controls.Add(this.label1); 106 | this.Controls.Add(this.lblDescription); 107 | this.Controls.Add(this.IconDescription); 108 | this.Controls.Add(this.btClose); 109 | this.Controls.Add(this.lblFormTitle); 110 | this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 111 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 112 | this.MaximizeBox = false; 113 | this.MinimizeBox = false; 114 | this.Name = "ImportExportFeedbackForm"; 115 | this.ShowIcon = false; 116 | this.ShowInTaskbar = false; 117 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 118 | this.Text = "ImportExportFeedbackForm"; 119 | this.Load += new System.EventHandler(this.ImportExportFeedbackForm_Load); 120 | ((System.ComponentModel.ISupportInitialize)(this.IconDescription)).EndInit(); 121 | this.ResumeLayout(false); 122 | this.PerformLayout(); 123 | 124 | } 125 | 126 | #endregion 127 | 128 | private System.Windows.Forms.Label lblFormTitle; 129 | private System.Windows.Forms.Button btClose; 130 | private System.Windows.Forms.PictureBox IconDescription; 131 | private System.Windows.Forms.Label lblDescription; 132 | private System.Windows.Forms.Label label1; 133 | private System.Windows.Forms.FlowLayoutPanel FlowLayoutPanel1; 134 | } 135 | } -------------------------------------------------------------------------------- /MiniBug/Classes/TasksDataGridViewRowComparer.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) João Martiniano. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | 11 | namespace MiniBug 12 | { 13 | public class TasksDataGridViewRowComparer : System.Collections.IComparer 14 | { 15 | private static int sortOrderModifierColumn1 = 1; 16 | private static int sortOrderModifierColumn2 = 1; 17 | 18 | public TasksDataGridViewRowComparer(SortOrder sortOrder) 19 | { 20 | if (ApplicationSettings.GridTasksSort.FirstColumnSortOrder == SortOrder.Descending) 21 | { 22 | sortOrderModifierColumn1 = -1; 23 | } 24 | else if (ApplicationSettings.GridTasksSort.FirstColumnSortOrder == SortOrder.Ascending) 25 | { 26 | sortOrderModifierColumn1 = 1; 27 | } 28 | 29 | if (ApplicationSettings.GridTasksSort.SecondColumnSortOrder != null) 30 | { 31 | if (ApplicationSettings.GridTasksSort.SecondColumnSortOrder == SortOrder.Descending) 32 | { 33 | sortOrderModifierColumn2 = -1; 34 | } 35 | else if (ApplicationSettings.GridTasksSort.SecondColumnSortOrder == SortOrder.Ascending) 36 | { 37 | sortOrderModifierColumn2 = 1; 38 | } 39 | } 40 | } 41 | 42 | private int CompareValues(TaskFieldsUI fieldType, object field1, object field2) 43 | { 44 | switch (fieldType) 45 | { 46 | case TaskFieldsUI.ID: 47 | case TaskFieldsUI.Status: 48 | case TaskFieldsUI.Priority: 49 | int value1 = Convert.ToInt32(field1); 50 | int value2 = Convert.ToInt32(field2); 51 | 52 | if (value1 < value2) 53 | { 54 | return -1; 55 | } 56 | else if (value1 == value2) 57 | { 58 | return 0; 59 | } 60 | else 61 | { 62 | return 1; 63 | } 64 | 65 | case TaskFieldsUI.TargetVersion: 66 | case TaskFieldsUI.Summary: 67 | return string.Compare(field1.ToString(), field2.ToString()); 68 | 69 | case TaskFieldsUI.DateCreated: 70 | case TaskFieldsUI.DateModified: 71 | DateTime dtValue1, dtValue2; 72 | 73 | if ((DateTime.TryParse(field1.ToString(), out dtValue1)) && (DateTime.TryParse(field2.ToString(), out dtValue2))) 74 | { 75 | if (dtValue1 < dtValue2) 76 | { 77 | return -1; 78 | } 79 | else if (dtValue1 == dtValue2) 80 | { 81 | return 0; 82 | } 83 | else 84 | { 85 | return 1; 86 | } 87 | } 88 | 89 | return 0; 90 | 91 | default: 92 | return 0; 93 | } 94 | } 95 | 96 | private object GetValueForField(TaskFieldsUI column, int id) 97 | { 98 | switch (column) 99 | { 100 | case TaskFieldsUI.ID: 101 | return id; 102 | case TaskFieldsUI.Priority: 103 | return Program.SoftwareProject.Tasks[id].Priority; 104 | case TaskFieldsUI.Status: 105 | return Program.SoftwareProject.Tasks[id].Status; 106 | case TaskFieldsUI.TargetVersion: 107 | return Program.SoftwareProject.Tasks[id].TargetVersion; 108 | case TaskFieldsUI.Summary: 109 | return Program.SoftwareProject.Tasks[id].Summary; 110 | case TaskFieldsUI.DateCreated: 111 | return Program.SoftwareProject.Tasks[id].DateCreated; 112 | case TaskFieldsUI.DateModified: 113 | return Program.SoftwareProject.Tasks[id].DateModified; 114 | default: 115 | return null; 116 | } 117 | } 118 | 119 | public int Compare(object x, object y) 120 | { 121 | DataGridViewRow DataGridViewRow1 = (DataGridViewRow)x; 122 | DataGridViewRow DataGridViewRow2 = (DataGridViewRow)y; 123 | 124 | int CompareResult = 0; 125 | object field1, field2; 126 | 127 | int id1 = Convert.ToInt32(DataGridViewRow1.Cells[0].Value); 128 | int id2 = Convert.ToInt32(DataGridViewRow2.Cells[0].Value); 129 | 130 | // Get the values for the first and second fields, in the first column 131 | field1 = GetValueForField(ApplicationSettings.GridTasksSort.FirstColumn, id1); 132 | field2 = GetValueForField(ApplicationSettings.GridTasksSort.FirstColumn, id2); 133 | 134 | CompareResult = CompareValues(ApplicationSettings.GridTasksSort.FirstColumn, field1, field2); 135 | 136 | // Regarding sort column 1, one of the rows is either lower or greater than the other 137 | if ((CompareResult == -1) || (CompareResult == 1)) 138 | { 139 | return CompareResult * sortOrderModifierColumn1; 140 | } 141 | else 142 | { 143 | // Both rows are equal (regarding sort column 1) 144 | 145 | // If only sorting by one column, then both rows are equal 146 | if (ApplicationSettings.GridTasksSort.SecondColumn == null) 147 | { 148 | // Resort to ID to give some final order 149 | return ((id1 < id2) ? -1 : 1); 150 | } 151 | 152 | // Get the values for the first and second fields, in the second column 153 | field1 = GetValueForField(ApplicationSettings.GridTasksSort.SecondColumn.Value, id1); 154 | field2 = GetValueForField(ApplicationSettings.GridTasksSort.SecondColumn.Value, id2); 155 | 156 | CompareResult = CompareValues(ApplicationSettings.GridTasksSort.SecondColumn.Value, field1, field2); 157 | 158 | if ((CompareResult == -1) || (CompareResult == 1)) 159 | { 160 | return CompareResult * sortOrderModifierColumn2; 161 | } 162 | else 163 | { 164 | // Both rows are equal: resort to ID to give some final order 165 | return ((id1 < id2) ? -1 : 1); 166 | } 167 | } 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /MiniBug/Classes/ApplicationData.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) João Martiniano. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Collections; 10 | using System.IO; 11 | using Newtonsoft.Json; 12 | using CsvHelper; 13 | 14 | namespace MiniBug 15 | { 16 | public enum OperationType { None = 0, New, Edit, Delete }; 17 | 18 | /// 19 | /// Identify which grid to reference of performe some operation. 20 | /// 21 | public enum GridType { None = 0, All, Issues, Tasks } 22 | 23 | /// 24 | /// Result of a filesystem operation. 25 | /// 26 | public enum FileSystemOperationStatus { 27 | None = 0, 28 | 29 | /// The operation was successfull. 30 | OK = 1, 31 | 32 | /// Trying to load a project with an unsupported file format. 33 | ProjectLoadErrorUnsupportedFormat, 34 | 35 | /// Trying to load a project file from a non-existing directory. 36 | ProjectLoadErrorDirectoryNotFound, 37 | 38 | /// File not found while trying to load a project. 39 | ProjectLoadErrorFileNotFound, 40 | 41 | /// Project file path and/or file name too long. 42 | ProjectLoadErrorPathTooLong, 43 | 44 | /// Error deserializing the project file. 45 | ProjectLoadErrorDeserialization, 46 | 47 | /// General I/O error while trying to load the project file. 48 | ProjectLoadErrorIO, 49 | 50 | /// Trying to save a project file to a non-existing directory. 51 | ProjectSaveErrorDirectoryNotFound, 52 | 53 | /// Project file's path and/or file name too long. 54 | ProjectSaveErrorPathTooLong, 55 | 56 | /// Error serializing the project file. 57 | ProjectSaveErrorSerialization, 58 | 59 | /// General I/O error while trying to save the project file. 60 | ProjectSaveIOError, 61 | 62 | /// Project successfully exported. 63 | ExportOK, 64 | 65 | /// Export projet to CSV: trying to export to a non-existing directory. 66 | ExportToCsvErrorDirectoryNotFound, 67 | 68 | /// Export projet to CSV: file path and/or file name too long. 69 | ExportToCsvErrorPathTooLong, 70 | 71 | /// Export projet to CSV: error reported by the component responsible for performing the export. 72 | ExportToCsvErrorExporterComponent, 73 | 74 | /// Export projet to CSV: general I/O error. 75 | ExportToCsvIOError 76 | } 77 | 78 | public static class ApplicationData 79 | { 80 | /// 81 | /// Saves a project data to a file. The file is overwritten. 82 | /// 83 | /// An instance of the Project class. 84 | public static FileSystemOperationStatus SaveProject(in Project softwareProject) 85 | { 86 | string output = string.Empty; 87 | string filename = string.Empty; 88 | 89 | output = JsonConvert.SerializeObject(softwareProject); 90 | filename = System.IO.Path.Combine(softwareProject.Location, softwareProject.Filename); 91 | 92 | try 93 | { 94 | System.IO.File.WriteAllText(filename, output); 95 | } 96 | catch (System.IO.DirectoryNotFoundException) // The directory does not exist 97 | { 98 | return FileSystemOperationStatus.ProjectSaveErrorDirectoryNotFound; 99 | } 100 | catch (System.IO.PathTooLongException) // The path is too long 101 | { 102 | return FileSystemOperationStatus.ProjectSaveErrorPathTooLong; 103 | } 104 | catch (JsonException) // Error serializing the project 105 | { 106 | return FileSystemOperationStatus.ProjectSaveErrorSerialization; 107 | } 108 | catch // General input/output error 109 | { 110 | return FileSystemOperationStatus.ProjectSaveIOError; 111 | } 112 | 113 | return FileSystemOperationStatus.OK; 114 | } 115 | 116 | /// 117 | /// Load project data from a file. 118 | /// 119 | /// Name and location of the project file. 120 | /// An instance of the Project class. 121 | public static FileSystemOperationStatus LoadProject(string filename, out Project softwareProject) 122 | { 123 | String input = string.Empty; 124 | 125 | try 126 | { 127 | // Open the file 128 | System.IO.StreamReader r = new System.IO.StreamReader(filename); 129 | 130 | // Read the file contents 131 | input = r.ReadToEnd(); 132 | 133 | r.Close(); 134 | 135 | softwareProject = JsonConvert.DeserializeObject(input); 136 | 137 | // Check the version of the project file: if not supported, abort the operation 138 | if (softwareProject.Version != ApplicationSettings.ProjectFileFormatVersion) 139 | { 140 | softwareProject = null; 141 | return FileSystemOperationStatus.ProjectLoadErrorUnsupportedFormat; 142 | } 143 | 144 | // Insert the path and filename into the project 145 | softwareProject.Location = Path.GetDirectoryName(filename); 146 | softwareProject.Filename = Path.GetFileName(filename); 147 | } 148 | catch (System.IO.DirectoryNotFoundException) // The directory does not exist 149 | { 150 | softwareProject = null; 151 | return FileSystemOperationStatus.ProjectLoadErrorDirectoryNotFound; 152 | } 153 | catch (System.IO.FileNotFoundException) // The file does not exist 154 | { 155 | softwareProject = null; 156 | return FileSystemOperationStatus.ProjectLoadErrorFileNotFound; 157 | } 158 | catch (System.IO.PathTooLongException) // The path is too long 159 | { 160 | softwareProject = null; 161 | return FileSystemOperationStatus.ProjectLoadErrorPathTooLong; 162 | } 163 | catch (JsonException) // Error deserializing the file 164 | { 165 | softwareProject = null; 166 | return FileSystemOperationStatus.ProjectLoadErrorDeserialization; 167 | } 168 | catch // General input/output error 169 | { 170 | softwareProject = null; 171 | return FileSystemOperationStatus.ProjectLoadErrorIO; 172 | } 173 | 174 | return FileSystemOperationStatus.OK; 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /MiniBug/Classes/IssuesDataGridViewRowComparer.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) João Martiniano. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | 11 | namespace MiniBug 12 | { 13 | public class IssuesDataGridViewRowComparer : System.Collections.IComparer 14 | { 15 | private static int sortOrderModifierColumn1 = 1; 16 | private static int sortOrderModifierColumn2 = 1; 17 | 18 | public IssuesDataGridViewRowComparer(SortOrder sortOrder) 19 | { 20 | if (ApplicationSettings.GridIssuesSort.FirstColumnSortOrder == SortOrder.Descending) 21 | { 22 | sortOrderModifierColumn1 = -1; 23 | } 24 | else if (ApplicationSettings.GridIssuesSort.FirstColumnSortOrder == SortOrder.Ascending) 25 | { 26 | sortOrderModifierColumn1 = 1; 27 | } 28 | 29 | if (ApplicationSettings.GridIssuesSort.SecondColumnSortOrder != null) 30 | { 31 | if (ApplicationSettings.GridIssuesSort.SecondColumnSortOrder == SortOrder.Descending) 32 | { 33 | sortOrderModifierColumn2 = -1; 34 | } 35 | else if (ApplicationSettings.GridIssuesSort.SecondColumnSortOrder == SortOrder.Ascending) 36 | { 37 | sortOrderModifierColumn2 = 1; 38 | } 39 | } 40 | } 41 | 42 | private int CompareValues(IssueFieldsUI fieldType, object field1, object field2) 43 | { 44 | switch (fieldType) 45 | { 46 | case IssueFieldsUI.ID: 47 | case IssueFieldsUI.Status: 48 | case IssueFieldsUI.Priority: 49 | int value1 = Convert.ToInt32(field1); 50 | int value2 = Convert.ToInt32(field2); 51 | 52 | if (value1 < value2) 53 | { 54 | return -1; 55 | } 56 | else if (value1 == value2) 57 | { 58 | return 0; 59 | } 60 | else 61 | { 62 | return 1; 63 | } 64 | 65 | case IssueFieldsUI.Version: 66 | case IssueFieldsUI.TargetVersion: 67 | case IssueFieldsUI.Summary: 68 | return string.Compare(field1.ToString(), field2.ToString()); 69 | 70 | case IssueFieldsUI.DateCreated: 71 | case IssueFieldsUI.DateModified: 72 | DateTime dtValue1, dtValue2; 73 | 74 | if ((DateTime.TryParse(field1.ToString(), out dtValue1)) && (DateTime.TryParse(field2.ToString(), out dtValue2))) 75 | { 76 | if (dtValue1 < dtValue2) 77 | { 78 | return -1; 79 | } 80 | else if (dtValue1 == dtValue2) 81 | { 82 | return 0; 83 | } 84 | else 85 | { 86 | return 1; 87 | } 88 | } 89 | 90 | return 0; 91 | 92 | default: 93 | return 0; 94 | } 95 | } 96 | 97 | private object GetValueForField(IssueFieldsUI column, int id) 98 | { 99 | switch (column) 100 | { 101 | case IssueFieldsUI.ID: 102 | return id; 103 | case IssueFieldsUI.Priority: 104 | return Program.SoftwareProject.Issues[id].Priority; 105 | case IssueFieldsUI.Status: 106 | return Program.SoftwareProject.Issues[id].Status; 107 | case IssueFieldsUI.Version: 108 | return Program.SoftwareProject.Issues[id].Version; 109 | case IssueFieldsUI.TargetVersion: 110 | return Program.SoftwareProject.Issues[id].TargetVersion; 111 | case IssueFieldsUI.Summary: 112 | return Program.SoftwareProject.Issues[id].Summary; 113 | case IssueFieldsUI.DateCreated: 114 | return Program.SoftwareProject.Issues[id].DateCreated; 115 | case IssueFieldsUI.DateModified: 116 | return Program.SoftwareProject.Issues[id].DateModified; 117 | default: 118 | return null; 119 | } 120 | } 121 | 122 | public int Compare(object x, object y) 123 | { 124 | DataGridViewRow DataGridViewRow1 = (DataGridViewRow)x; 125 | DataGridViewRow DataGridViewRow2 = (DataGridViewRow)y; 126 | 127 | int CompareResult = 0; 128 | object field1, field2; 129 | 130 | int id1 = Convert.ToInt32(DataGridViewRow1.Cells[0].Value); 131 | int id2 = Convert.ToInt32(DataGridViewRow2.Cells[0].Value); 132 | 133 | // Get the values for the first and second fields, in the first column 134 | field1 = GetValueForField(ApplicationSettings.GridIssuesSort.FirstColumn, id1); 135 | field2 = GetValueForField(ApplicationSettings.GridIssuesSort.FirstColumn, id2); 136 | 137 | CompareResult = CompareValues(ApplicationSettings.GridIssuesSort.FirstColumn, field1, field2); 138 | 139 | // Regarding sort column 1, one of the rows is either lower or greater than the other 140 | if ((CompareResult == -1) || (CompareResult == 1)) 141 | { 142 | return CompareResult * sortOrderModifierColumn1; 143 | } 144 | else 145 | { 146 | // Both rows are equal (regarding sort column 1) 147 | 148 | // If only sorting by one column, then both rows are equal 149 | if (ApplicationSettings.GridIssuesSort.SecondColumn == null) 150 | { 151 | // Resort to ID to give some final order 152 | return ((id1 < id2) ? -1 : 1); 153 | } 154 | 155 | // Get the values for the first and second fields, in the second column 156 | field1 = GetValueForField(ApplicationSettings.GridIssuesSort.SecondColumn.Value, id1); 157 | field2 = GetValueForField(ApplicationSettings.GridIssuesSort.SecondColumn.Value, id2); 158 | 159 | CompareResult = CompareValues(ApplicationSettings.GridIssuesSort.SecondColumn.Value, field1, field2); 160 | 161 | if ((CompareResult == -1) || (CompareResult == 1)) 162 | { 163 | return CompareResult * sortOrderModifierColumn2; 164 | } 165 | else 166 | { 167 | // Both rows are equal: resort to ID to give some final order 168 | return ((id1 < id2) ? -1 : 1); 169 | } 170 | } 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /MiniBug/AboutForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | 123 | iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABHNCSVQICAgIfAhkiAAAAAFzUkdCAK7O 124 | HOkAAAAEZ0FNQQAAsY8L/GEFAAAACXBIWXMAADsKAAA7CgEzSyaRAAAAGXRFWHRTb2Z0d2FyZQB3d3cu 125 | aW5rc2NhcGUub3Jnm+48GgAAAn1JREFUeF7tmzFuE2EYRF1Q0XMCTkBBQWHJ67WEKHyBdIiOlgPARbgD 126 | J6DmDvRIdEjQRCgOf6QprCeR7M6s/3zFPmna983YWsvNblZWVlYeYhzHVy1f9vv9n5bbR8rP1uGlKvWj 127 | Hd2249co81i5+wLeqFof2sGvZwUq5G/LW9W7PO3Yr7PjVXJq+aSKlwWHb3vD++dpj+fn3W73RFUvA4/2 128 | hveZux/n4/H4VHWXhwd7w/v/ybftdvtMlZeFx3rD+/fke8tz1V4OHFGtfvD+fWmPw4/D4fBC1ZeBR3rD 129 | +xPyexiG16qfwwO94f2JuW4fwpUmZFDeG96fkWX+K0CqWv3g/bnRDB8Ke8P7c6MZPhT2hvfnRjN8KOwN 130 | 78+NZvhQmEIfk0KfZvhQmEIfk0KfZvhQmEIfk0KfZvhQmEIfk0KfZvhQmEIfk0KfZvhQmEIfk0KfZvhQ 131 | mEIfk0KfZvhQmEIfk0KfZvhQmEIfk0KfZvhQmEIfk0KfZvhQmEIfk0KfZvhQmEIfk0KfZvhQmEIfk0Kf 132 | ZvhQmEIfk0KfZvhQmEIfk0KfZvhQmEIfk0KfZvhQWB321QwfCqvDvprhQ2F12FczfCisDvtqhg+FKfQx 133 | KfRphg+FKfQxKfRphg+FKfQxKfRphg+FKfQxKfRphg+FKfQxKfRphg+FKfQxKfRphg+FKfQxKfRphg+F 134 | KfQxKfRphg+F1WFfzfChsDrsqxk+FFaHfTXDh8LqsK9m+FBYHfbVDB8Kq8O+muFDYXXYVzN8KKwO+2qG 135 | T5NUfGFiaj5qhk+TVHtlZkpO4zh+0ISMYi9NTclpGIb3qr8M7UOo8NrclNy0nu9Ue2VlZeUBNpt/1Pqv 136 | vTzMmPUAAAAASUVORK5CYII= 137 | 138 | 139 | -------------------------------------------------------------------------------- /MiniBug/FeedbackForm.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) João Martiniano. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.ComponentModel; 7 | using System.Data; 8 | using System.Drawing; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using System.Windows.Forms; 13 | 14 | namespace MiniBug 15 | { 16 | public partial class FeedbackForm : Form 17 | { 18 | /// 19 | /// Gets or sets the caption of the form. 20 | /// 21 | public string FormCaption { get; set; } = string.Empty; 22 | 23 | /// 24 | /// Gets or sets the feedback message title. 25 | /// 26 | public string MessageTitle { get; set; } = string.Empty; 27 | 28 | /// 29 | /// Gets or sets the feedback message. 30 | /// 31 | public string Message { get; set; } = string.Empty; 32 | 33 | /// 34 | /// Gets or sets the image displayed in the form. 35 | /// 36 | public Image FormImage { get; set; } = null; 37 | 38 | public FeedbackForm() 39 | { 40 | InitializeComponent(); 41 | } 42 | 43 | private void FeedbackForm_Load(object sender, EventArgs e) 44 | { 45 | this.AcceptButton = btClose; 46 | } 47 | 48 | public void SetUserInterface() 49 | { 50 | // Suspend the layout logic for the form, while the application is initializing 51 | this.SuspendLayout(); 52 | 53 | this.Text = FormCaption; 54 | lblMessageTitle.Text = MessageTitle; 55 | lblMessageTitle.AutoEllipsis = true; 56 | lblMessage.Text = Message; 57 | 58 | pictureBox1.Image = FormImage; 59 | pictureBox1.Left = (lblMessageTitle.Left / 2) - (pictureBox1.Width / 2); 60 | 61 | // Resume the layout logic 62 | this.ResumeLayout(); 63 | } 64 | 65 | public void ComposeMessage(FileSystemOperationStatus MessageType) 66 | { 67 | switch (MessageType) 68 | { 69 | // Errors while opening a project 70 | 71 | case FileSystemOperationStatus.ProjectLoadErrorUnsupportedFormat: 72 | FormCaption = "Project Read Error"; 73 | MessageTitle = "Error Reading Project File: Unsupported Project Format"; 74 | Message = "This version of MiniBug does not support this project file format: it appears it was created using another version of the program."; 75 | Message += "\n\nSolution: use another version of this program to open the project file."; 76 | FormImage = MiniBug.Properties.Resources.FileError_64x64; 77 | break; 78 | 79 | case FileSystemOperationStatus.ProjectLoadErrorDirectoryNotFound: 80 | FormCaption = "Project Read Error"; 81 | MessageTitle = "Error Reading Project File: Location Not Found"; 82 | Message = "The specified location does not exist."; 83 | FormImage = MiniBug.Properties.Resources.FolderError_64x64; 84 | break; 85 | 86 | case FileSystemOperationStatus.ProjectLoadErrorFileNotFound: 87 | FormCaption = "Project Read Error"; 88 | MessageTitle = "Error Reading Project File: File Not Found"; 89 | Message = "The specified file was not found."; 90 | Message += "\n\nPlease choose a different project file."; 91 | FormImage = MiniBug.Properties.Resources.FileError_64x64; 92 | break; 93 | 94 | case FileSystemOperationStatus.ProjectLoadErrorPathTooLong: 95 | FormCaption = "Project Read Error"; 96 | MessageTitle = "Error Reading Project File: Path Too Long"; 97 | Message = "The project path, filename or both are too long."; 98 | Message += "\n\nSolution: choose a project file with a shorter path and/or shorter filename."; 99 | FormImage = MiniBug.Properties.Resources.FileError_64x64; 100 | break; 101 | 102 | case FileSystemOperationStatus.ProjectLoadErrorDeserialization: 103 | FormCaption = "Project Read Error"; 104 | MessageTitle = "Error Reading Project File: Unsupported Project Format"; 105 | Message = "The project file appears to be damaged or in an incorrect format."; 106 | FormImage = MiniBug.Properties.Resources.FileError_64x64; 107 | break; 108 | 109 | case FileSystemOperationStatus.ProjectLoadErrorIO: 110 | FormCaption = "Project Read Error"; 111 | MessageTitle = "Error Reading Project File: I/O Error"; 112 | Message = "There was a general input/output error while reading this project."; 113 | Message += "\n\nCheck the status of the drive/device where the project file is stored."; 114 | FormImage = MiniBug.Properties.Resources.CriticalError_64x64; 115 | break; 116 | 117 | // Errors while saving a project 118 | 119 | case FileSystemOperationStatus.ProjectSaveErrorDirectoryNotFound: 120 | FormCaption = "Project Save Error"; 121 | MessageTitle = "Error Saving Project File: Location Not Found"; 122 | Message = "The specified location does not exist."; 123 | Message += "\n\nSolution: choose a different location for the project file."; 124 | FormImage = MiniBug.Properties.Resources.FolderError_64x64; 125 | break; 126 | 127 | case FileSystemOperationStatus.ProjectSaveErrorPathTooLong: 128 | FormCaption = "Project Save Error"; 129 | MessageTitle = "Error Saving Project File: Path Too Long"; 130 | Message = "The project path, file name or both are too long."; 131 | Message += "\n\nSolution: choose a different location and/or a shorter filename."; 132 | FormImage = MiniBug.Properties.Resources.FileError_64x64; 133 | break; 134 | 135 | case FileSystemOperationStatus.ProjectSaveErrorSerialization: 136 | FormCaption = "Project Save Error"; 137 | MessageTitle = "Error Saving Project File: Unable to Save"; 138 | Message = "The project data appears to be damaged or in an incorrect format."; 139 | FormImage = MiniBug.Properties.Resources.FileError_64x64; 140 | break; 141 | 142 | case FileSystemOperationStatus.ProjectSaveIOError: 143 | FormCaption = "Project Save Error"; 144 | MessageTitle = "Error Saving Project File: I/O Error"; 145 | Message = "There was a general input/output error while saving this project."; 146 | Message += "\n\nSolution: choose a different drive/device to save the project file."; 147 | FormImage = MiniBug.Properties.Resources.CriticalError_64x64; 148 | break; 149 | } 150 | 151 | // Update the user interface 152 | SetUserInterface(); 153 | } 154 | 155 | /// 156 | /// Close the form. 157 | /// 158 | private void btClose_Click(object sender, EventArgs e) 159 | { 160 | this.Close(); 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /MiniBug/TaskForm.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) João Martiniano. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.ComponentModel; 7 | using System.Data; 8 | using System.Drawing; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using System.Windows.Forms; 13 | 14 | namespace MiniBug 15 | { 16 | public partial class TaskForm : Form 17 | { 18 | /// 19 | /// The current operation. 20 | /// 21 | public MiniBug.OperationType Operation { get; private set; } = OperationType.None; 22 | 23 | /// 24 | /// The current task (being created or edited). 25 | /// 26 | public MiniBug.Task CurrentTask { get; private set; } = null; 27 | 28 | /// 29 | /// List of status options. 30 | /// 31 | private List StatusOptionsList = new List(); 32 | 33 | /// 34 | /// List of priority options. 35 | /// 36 | private List PriorityList = new List(); 37 | 38 | public TaskForm(OperationType operation, MiniBug.Task task = null) 39 | { 40 | InitializeComponent(); 41 | 42 | Operation = operation; 43 | 44 | if (Operation == OperationType.New) 45 | { 46 | // Create a new instance of the Task class 47 | CurrentTask = new Task(); 48 | } 49 | else if ((Operation == OperationType.Edit) && (task != null)) 50 | { 51 | // Edit an existing task 52 | CurrentTask = task; 53 | } 54 | 55 | // Populate the status list 56 | foreach (TaskStatus stat in Enum.GetValues(typeof(TaskStatus))) 57 | { 58 | if (stat != TaskStatus.None) 59 | { 60 | StatusOptionsList.Add(new ComboBoxItem(Convert.ToInt32(stat), stat.ToDescription())); 61 | } 62 | } 63 | 64 | // Populate the priority list 65 | foreach (TaskPriority p in Enum.GetValues(typeof(TaskPriority))) 66 | { 67 | if (p != TaskPriority.None) 68 | { 69 | PriorityList.Add(new ComboBoxItem(Convert.ToInt32(p), p.ToString())); 70 | } 71 | } 72 | } 73 | 74 | private void TaskForm_Load(object sender, EventArgs e) 75 | { 76 | // Suspend the layout logic for the form, while the application is initializing 77 | this.SuspendLayout(); 78 | 79 | this.Icon = MiniBug.Properties.Resources.Minibug; 80 | this.AcceptButton = btOk; 81 | this.CancelButton = btCancel; 82 | this.MinimumSize = new Size(690, 351); 83 | 84 | txtDescription.AcceptsReturn = true; 85 | txtDescription.ScrollBars = ScrollBars.Vertical; 86 | 87 | // Initialize and populate the Status combobox 88 | cboStatus.AutoCompleteMode = AutoCompleteMode.None; 89 | cboStatus.DropDownStyle = ComboBoxStyle.DropDownList; 90 | cboStatus.DataSource = StatusOptionsList; 91 | cboStatus.ValueMember = "Value"; 92 | cboStatus.DisplayMember = "Text"; 93 | 94 | // Initialize and populate the Priority combobox 95 | cboPriority.AutoCompleteMode = AutoCompleteMode.None; 96 | cboPriority.DropDownStyle = ComboBoxStyle.DropDownList; 97 | cboPriority.DataSource = PriorityList; 98 | cboPriority.ValueMember = "Value"; 99 | cboPriority.DisplayMember = "Text"; 100 | 101 | // Make initializations based on the type of operation 102 | if (Operation == OperationType.New) 103 | { 104 | this.Text = "Add New Task"; 105 | lblDateCreatedTitle.Visible = false; 106 | lblDateCreated.Visible = false; 107 | lblDateModifiedTitle.Visible = false; 108 | lblDateModified.Visible = false; 109 | 110 | cboStatus.SelectedIndex = 0; 111 | cboPriority.SelectedIndex = 0; 112 | 113 | lblID.Text = Program.SoftwareProject.TaskIdCounter.ToString(); 114 | } 115 | else if (Operation == OperationType.Edit) 116 | { 117 | this.Text = "Edit Task"; 118 | 119 | // Populate the controls 120 | lblDateCreated.Text = CurrentTask.DateCreated.ToString(); 121 | lblDateModified.Text = CurrentTask.DateModified.ToString(); 122 | txtSummary.Text = CurrentTask.Summary; 123 | txtTargetVersion.Text = CurrentTask.TargetVersion; 124 | txtDescription.Text = CurrentTask.Description; 125 | 126 | cboStatus.SelectedValue = Convert.ToInt32(CurrentTask.Status); 127 | cboPriority.SelectedValue = Convert.ToInt32(CurrentTask.Priority); 128 | 129 | lblID.Text = CurrentTask.ID.ToString(); 130 | } 131 | 132 | txtDescription.Font = ApplicationSettings.FormDescriptionFieldFont; 133 | 134 | // Resume the layout logic 135 | this.ResumeLayout(); 136 | 137 | SetAccessibilityInformation(); 138 | } 139 | 140 | /// 141 | /// Add accessibility data to form controls. 142 | /// 143 | private void SetAccessibilityInformation() 144 | { 145 | lblID.AccessibleName = "Task ID"; 146 | lblID.AccessibleDescription = "Unique numerical code assigned to the task"; 147 | lblDateCreated.AccessibleName = "Date Created"; 148 | lblDateCreated.AccessibleDescription = "Date/time when the task was created"; 149 | lblDateModified.AccessibleName = "Date Modified"; 150 | lblDateModified.AccessibleDescription = "Date/time when the task was last modified"; 151 | txtSummary.AccessibleDescription = "Brief summary of the task"; 152 | cboStatus.AccessibleDescription = "Current status of the task"; 153 | cboPriority.AccessibleDescription = "Priority of a task"; 154 | txtTargetVersion.AccessibleDescription = "Version where the task must be resolved"; 155 | txtDescription.AccessibleDescription = "Extended description of the task"; 156 | } 157 | 158 | /// 159 | /// Close the form. 160 | /// 161 | private void btOk_Click(object sender, EventArgs e) 162 | { 163 | if (txtSummary.Text != string.Empty) 164 | { 165 | CurrentTask.Summary = txtSummary.Text; 166 | CurrentTask.Status = ((TaskStatus)((MiniBug.ComboBoxItem)cboStatus.SelectedItem).Value); 167 | CurrentTask.Priority = ((TaskPriority)((MiniBug.ComboBoxItem)cboPriority.SelectedItem).Value); 168 | CurrentTask.TargetVersion = txtTargetVersion.Text; 169 | CurrentTask.Description = txtDescription.Text; 170 | 171 | if (Operation == OperationType.New) 172 | { 173 | CurrentTask.DateCreated = DateTime.Now; 174 | } 175 | 176 | CurrentTask.DateModified = DateTime.Now; 177 | 178 | this.DialogResult = DialogResult.OK; 179 | this.Close(); 180 | } 181 | } 182 | 183 | /// 184 | /// Cancel this operation and close the form. 185 | /// 186 | private void btCancel_Click(object sender, EventArgs e) 187 | { 188 | this.DialogResult = DialogResult.Cancel; 189 | this.Close(); 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /MiniBug/ExportForm.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) João Martiniano. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.ComponentModel; 7 | using System.Data; 8 | using System.Drawing; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using System.Windows.Forms; 13 | 14 | namespace MiniBug 15 | { 16 | public partial class ExportForm : Form 17 | { 18 | /// 19 | /// True if the project has issues. 20 | /// 21 | private bool HasIssues = false; 22 | 23 | /// 24 | /// True if the project has tasks. 25 | /// 26 | private bool HasTasks = false; 27 | 28 | /// 29 | /// Location where the files will be exported. 30 | /// 31 | private string FilesLocation = string.Empty; 32 | 33 | /// 34 | /// File name of the issues .CSV file. 35 | /// 36 | public string IssuesFilename { get; private set; } = string.Empty; 37 | 38 | /// 39 | /// File name of the tasks .CSV file. 40 | /// 41 | public string TasksFilename { get; private set; } = string.Empty; 42 | 43 | public ExportForm() 44 | { 45 | InitializeComponent(); 46 | 47 | HasIssues = ((Program.SoftwareProject.Issues != null) && (Program.SoftwareProject.Issues.Count > 0)) ? true : false; 48 | HasTasks = ((Program.SoftwareProject.Tasks != null) && (Program.SoftwareProject.Tasks.Count > 0)) ? true : false; 49 | 50 | IssuesFilename = $"{Program.SoftwareProject.Name} - Issues"; 51 | TasksFilename = $"{Program.SoftwareProject.Name} - Tasks"; 52 | FilesLocation = Program.SoftwareProject.Location; 53 | } 54 | 55 | private void ExportForm_Load(object sender, EventArgs e) 56 | { 57 | // Suspend the layout logic for the form, while the application is initializing 58 | this.SuspendLayout(); 59 | 60 | this.AcceptButton = btExport; 61 | this.CancelButton = btCancel; 62 | 63 | lblFormTitle.Width = this.ClientRectangle.Width; 64 | 65 | txtIssuesFilename.MaxLength = 251; 66 | txtTasksFilename.MaxLength = 251; 67 | txtLocation.Text = FilesLocation; 68 | 69 | if (HasIssues && HasTasks) 70 | { 71 | lblInfo.Text = "This project will be exported to two CSV files: a file containing the issues and a file containing the tasks."; 72 | txtIssuesFilename.Text = IssuesFilename; 73 | txtTasksFilename.Text = TasksFilename; 74 | } 75 | else if (HasIssues && !HasTasks) 76 | { 77 | lblInfo.Text = "This project will be exported to a .CSV file containing the issues."; 78 | 79 | // Hide controls 80 | lblTasks.Visible = false; 81 | txtTasksFilename.Visible = false; 82 | lblCsvTasks.Visible = false; 83 | 84 | // Push controls up 85 | lblLocation.Top = lblTasks.Top; 86 | txtLocation.Top = txtTasksFilename.Top; 87 | btBrowse.Top = txtLocation.Top; 88 | 89 | txtIssuesFilename.Text = IssuesFilename; 90 | } 91 | else if (!HasIssues && HasTasks) 92 | { 93 | lblInfo.Text = "This project will be exported to a .CSV file containing the tasks."; 94 | 95 | // Hide controls 96 | lblIssues.Visible = false; 97 | txtIssuesFilename.Visible = false; 98 | lblCsvIssues.Visible = false; 99 | 100 | // Push controls up 101 | lblLocation.Top = lblTasks.Top; 102 | txtLocation.Top = txtTasksFilename.Top; 103 | btBrowse.Top = txtLocation.Top; 104 | 105 | lblTasks.Top = lblIssues.Top; 106 | txtTasksFilename.Top = txtIssuesFilename.Top; 107 | lblCsvTasks.Top = lblCsvIssues.Top; 108 | 109 | txtTasksFilename.Text = TasksFilename; 110 | } 111 | 112 | SetControlsState(); 113 | 114 | // Resume the layout logic 115 | this.ResumeLayout(); 116 | 117 | SetAccessibilityInformation(); 118 | } 119 | 120 | /// 121 | /// Add accessibility data to form controls. 122 | /// 123 | private void SetAccessibilityInformation() 124 | { 125 | txtIssuesFilename.AccessibleDescription = "The name of the file containing the project Issues"; 126 | txtTasksFilename.AccessibleDescription = "The name of the file containing the project Tasks"; 127 | txtLocation.AccessibleDescription = "Folder where the project file will be exported"; 128 | btBrowse.AccessibleDescription = "Browse for the folder where the project will be exported"; 129 | btExport.AccessibleDescription = "Start the export operation"; 130 | } 131 | 132 | /// 133 | /// Set the state of controls based on certain conditions. 134 | /// 135 | private void SetControlsState() 136 | { 137 | if (HasIssues && HasTasks) 138 | { 139 | btExport.Enabled = (!string.IsNullOrWhiteSpace(txtIssuesFilename.Text) && !string.IsNullOrWhiteSpace(txtTasksFilename.Text) && !string.IsNullOrWhiteSpace(txtLocation.Text)) ? true : false; 140 | } 141 | else if (HasIssues && !HasTasks) 142 | { 143 | btExport.Enabled = (!string.IsNullOrWhiteSpace(txtIssuesFilename.Text) && !string.IsNullOrWhiteSpace(txtLocation.Text)) ? true : false; 144 | } 145 | else if (!HasIssues && HasTasks) 146 | { 147 | btExport.Enabled = (!string.IsNullOrWhiteSpace(txtTasksFilename.Text) && !string.IsNullOrWhiteSpace(txtLocation.Text)) ? true : false; 148 | } 149 | 150 | } 151 | 152 | /// 153 | /// Check if this textbox is empty. 154 | /// 155 | private void txtIssuesFilename_TextChanged(object sender, EventArgs e) 156 | { 157 | SetControlsState(); 158 | } 159 | 160 | /// 161 | /// Check if this textbox is empty. 162 | /// 163 | private void txtTasksFilename_TextChanged(object sender, EventArgs e) 164 | { 165 | SetControlsState(); 166 | } 167 | 168 | /// 169 | /// Browse the location where the project will be exported. 170 | /// 171 | private void btBrowse_Click(object sender, EventArgs e) 172 | { 173 | if (folderBrowserDialog1.ShowDialog() == DialogResult.OK) 174 | { 175 | txtLocation.Text = folderBrowserDialog1.SelectedPath; 176 | } 177 | 178 | SetControlsState(); 179 | } 180 | 181 | /// 182 | /// Export the project. 183 | /// 184 | private void btExport_Click(object sender, EventArgs e) 185 | { 186 | IssuesFilename = System.IO.Path.Combine(txtLocation.Text, txtIssuesFilename.Text + ".csv"); 187 | TasksFilename = System.IO.Path.Combine(txtLocation.Text, txtTasksFilename.Text + ".csv"); 188 | 189 | this.DialogResult = DialogResult.OK; 190 | this.Close(); 191 | } 192 | 193 | /// 194 | /// Cancel this operation and close the form. 195 | /// 196 | private void btCancel_Click(object sender, EventArgs e) 197 | { 198 | this.DialogResult = DialogResult.Cancel; 199 | this.Close(); 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /MiniBug/IssueForm.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) João Martiniano. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.ComponentModel; 7 | using System.Data; 8 | using System.Drawing; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using System.Windows.Forms; 13 | 14 | namespace MiniBug 15 | { 16 | public partial class IssueForm : Form 17 | { 18 | /// 19 | /// The current operation. 20 | /// 21 | public MiniBug.OperationType Operation { get; private set; } = OperationType.None; 22 | 23 | /// 24 | /// The current issue (being created or edited). 25 | /// 26 | public MiniBug.Issue CurrentIssue { get; private set; } = null; 27 | 28 | /// 29 | /// List of status options. 30 | /// 31 | private List StatusOptionsList = new List(); 32 | 33 | /// 34 | /// List of priority options. 35 | /// 36 | private List PriorityList = new List(); 37 | 38 | public IssueForm(OperationType operation, MiniBug.Issue issue = null) 39 | { 40 | InitializeComponent(); 41 | 42 | Operation = operation; 43 | 44 | if (Operation == OperationType.New) 45 | { 46 | // Create a new instance of the Issue class 47 | CurrentIssue = new Issue(); 48 | } 49 | else if ((Operation == OperationType.Edit) && (issue != null)) 50 | { 51 | // Edit an existing issue 52 | CurrentIssue = issue; 53 | } 54 | 55 | // Populate the status list 56 | foreach (IssueStatus stat in Enum.GetValues(typeof(IssueStatus))) 57 | { 58 | if (stat != IssueStatus.None) 59 | { 60 | StatusOptionsList.Add(new ComboBoxItem(Convert.ToInt32(stat), stat.ToDescription())); 61 | } 62 | } 63 | 64 | // Populate the priority list 65 | foreach (IssuePriority p in Enum.GetValues(typeof(IssuePriority))) 66 | { 67 | if (p != IssuePriority.None) 68 | { 69 | PriorityList.Add(new ComboBoxItem(Convert.ToInt32(p), p.ToString())); 70 | } 71 | } 72 | } 73 | 74 | private void IssueForm_Load(object sender, EventArgs e) 75 | { 76 | // Suspend the layout logic for the form, while the application is initializing 77 | this.SuspendLayout(); 78 | 79 | this.Icon = MiniBug.Properties.Resources.Minibug; 80 | this.AcceptButton = btOk; 81 | this.CancelButton = btCancel; 82 | this.MinimumSize = new Size(685, 351); 83 | 84 | txtDescription.AcceptsReturn = true; 85 | txtDescription.ScrollBars = ScrollBars.Vertical; 86 | 87 | // Initialize and populate the Status combobox 88 | cboStatus.AutoCompleteMode = AutoCompleteMode.None; 89 | cboStatus.DropDownStyle = ComboBoxStyle.DropDownList; 90 | cboStatus.DataSource = StatusOptionsList; 91 | cboStatus.ValueMember = "Value"; 92 | cboStatus.DisplayMember = "Text"; 93 | 94 | // Initialize and populate the Priority combobox 95 | cboPriority.AutoCompleteMode = AutoCompleteMode.None; 96 | cboPriority.DropDownStyle = ComboBoxStyle.DropDownList; 97 | cboPriority.DataSource = PriorityList; 98 | cboPriority.ValueMember = "Value"; 99 | cboPriority.DisplayMember = "Text"; 100 | 101 | // Make initializations based on the type of operation 102 | if (Operation == OperationType.New) 103 | { 104 | this.Text = "Add New Issue"; 105 | lblDateCreatedTitle.Visible = false; 106 | lblDateCreated.Visible = false; 107 | lblDateModifiedTitle.Visible = false; 108 | lblDateModified.Visible = false; 109 | 110 | cboStatus.SelectedIndex = 0; 111 | cboPriority.SelectedIndex = 0; 112 | 113 | lblID.Text = Program.SoftwareProject.IssueIdCounter.ToString(); 114 | } 115 | else if (Operation == OperationType.Edit) 116 | { 117 | this.Text = "Edit Issue"; 118 | 119 | // Populate the controls 120 | lblDateCreated.Text = CurrentIssue.DateCreated.ToString(); 121 | lblDateModified.Text = CurrentIssue.DateModified.ToString(); 122 | txtSummary.Text = CurrentIssue.Summary; 123 | txtVersion.Text = CurrentIssue.Version; 124 | txtTargetVersion.Text = CurrentIssue.TargetVersion; 125 | txtDescription.Text = CurrentIssue.Description; 126 | 127 | cboStatus.SelectedValue = Convert.ToInt32(CurrentIssue.Status); 128 | cboPriority.SelectedValue = Convert.ToInt32(CurrentIssue.Priority); 129 | 130 | lblID.Text = CurrentIssue.ID.ToString(); 131 | } 132 | 133 | txtDescription.Font = ApplicationSettings.FormDescriptionFieldFont; 134 | 135 | // Resume the layout logic 136 | this.ResumeLayout(); 137 | 138 | SetAccessibilityInformation(); 139 | } 140 | 141 | /// 142 | /// Add accessibility data to form controls. 143 | /// 144 | private void SetAccessibilityInformation() 145 | { 146 | lblID.AccessibleName = "Issue ID"; 147 | lblID.AccessibleDescription = "Unique numerical code assigned to the issue"; 148 | lblDateCreated.AccessibleName = "Date Created"; 149 | lblDateCreated.AccessibleDescription = "Date/time when the issue was created"; 150 | lblDateModified.AccessibleName = "Date Modified"; 151 | lblDateModified.AccessibleDescription = "Date/time when the issue was last modified"; 152 | txtSummary.AccessibleDescription = "Brief summary of the issue"; 153 | cboStatus.AccessibleDescription = "Current status of the issue"; 154 | cboPriority.AccessibleDescription = "Priority of an issue"; 155 | txtVersion.AccessibleDescription = "Version where the issue was detected"; 156 | txtTargetVersion.AccessibleDescription = "Version where the issue must be resolved"; 157 | txtDescription.AccessibleDescription = "Extended description of the issue"; 158 | } 159 | 160 | /// 161 | /// Close the form. 162 | /// 163 | private void btOk_Click(object sender, EventArgs e) 164 | { 165 | if (txtSummary.Text != string.Empty) 166 | { 167 | CurrentIssue.Summary = txtSummary.Text; 168 | CurrentIssue.Status = ((IssueStatus)((MiniBug.ComboBoxItem)cboStatus.SelectedItem).Value); 169 | CurrentIssue.Priority = ((IssuePriority)((MiniBug.ComboBoxItem)cboPriority.SelectedItem).Value); 170 | CurrentIssue.Version = txtVersion.Text; 171 | CurrentIssue.TargetVersion = txtTargetVersion.Text; 172 | CurrentIssue.Description = txtDescription.Text; 173 | 174 | if (Operation == OperationType.New) 175 | { 176 | CurrentIssue.DateCreated = DateTime.Now; 177 | } 178 | 179 | CurrentIssue.DateModified = DateTime.Now; 180 | 181 | this.DialogResult = DialogResult.OK; 182 | this.Close(); 183 | } 184 | } 185 | 186 | /// 187 | /// Cancel this operation and close the form. 188 | /// 189 | private void btCancel_Click(object sender, EventArgs e) 190 | { 191 | this.DialogResult = DialogResult.Cancel; 192 | this.Close(); 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /MiniBug/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace MiniBug.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | 26 | [global::System.Configuration.UserScopedSettingAttribute()] 27 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 28 | [global::System.Configuration.DefaultSettingValueAttribute("True")] 29 | public bool GridShowBorders { 30 | get { 31 | return ((bool)(this["GridShowBorders"])); 32 | } 33 | set { 34 | this["GridShowBorders"] = value; 35 | } 36 | } 37 | 38 | [global::System.Configuration.UserScopedSettingAttribute()] 39 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 40 | [global::System.Configuration.DefaultSettingValueAttribute("197, 197, 197")] 41 | public global::System.Drawing.Color GridBorderColor { 42 | get { 43 | return ((global::System.Drawing.Color)(this["GridBorderColor"])); 44 | } 45 | set { 46 | this["GridBorderColor"] = value; 47 | } 48 | } 49 | 50 | [global::System.Configuration.UserScopedSettingAttribute()] 51 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 52 | [global::System.Configuration.DefaultSettingValueAttribute("172, 212, 253")] 53 | public global::System.Drawing.Color GridSelectionBackColor { 54 | get { 55 | return ((global::System.Drawing.Color)(this["GridSelectionBackColor"])); 56 | } 57 | set { 58 | this["GridSelectionBackColor"] = value; 59 | } 60 | } 61 | 62 | [global::System.Configuration.UserScopedSettingAttribute()] 63 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 64 | [global::System.Configuration.DefaultSettingValueAttribute("Black")] 65 | public global::System.Drawing.Color GridSelectionForeColor { 66 | get { 67 | return ((global::System.Drawing.Color)(this["GridSelectionForeColor"])); 68 | } 69 | set { 70 | this["GridSelectionForeColor"] = value; 71 | } 72 | } 73 | 74 | [global::System.Configuration.UserScopedSettingAttribute()] 75 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 76 | [global::System.Configuration.DefaultSettingValueAttribute("Segoe UI, 8.25pt")] 77 | public global::System.Drawing.Font GridFont { 78 | get { 79 | return ((global::System.Drawing.Font)(this["GridFont"])); 80 | } 81 | set { 82 | this["GridFont"] = value; 83 | } 84 | } 85 | 86 | [global::System.Configuration.UserScopedSettingAttribute()] 87 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 88 | public global::System.Collections.Specialized.StringCollection RecentProjectsNames { 89 | get { 90 | return ((global::System.Collections.Specialized.StringCollection)(this["RecentProjectsNames"])); 91 | } 92 | set { 93 | this["RecentProjectsNames"] = value; 94 | } 95 | } 96 | 97 | [global::System.Configuration.UserScopedSettingAttribute()] 98 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 99 | public global::System.Collections.Specialized.StringCollection RecentProjectsPaths { 100 | get { 101 | return ((global::System.Collections.Specialized.StringCollection)(this["RecentProjectsPaths"])); 102 | } 103 | set { 104 | this["RecentProjectsPaths"] = value; 105 | } 106 | } 107 | 108 | [global::System.Configuration.UserScopedSettingAttribute()] 109 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 110 | [global::System.Configuration.DefaultSettingValueAttribute("False")] 111 | public bool GridAlternatingRowColor { 112 | get { 113 | return ((bool)(this["GridAlternatingRowColor"])); 114 | } 115 | set { 116 | this["GridAlternatingRowColor"] = value; 117 | } 118 | } 119 | 120 | [global::System.Configuration.UserScopedSettingAttribute()] 121 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 122 | [global::System.Configuration.DefaultSettingValueAttribute("White")] 123 | public global::System.Drawing.Color GridRowBackColor { 124 | get { 125 | return ((global::System.Drawing.Color)(this["GridRowBackColor"])); 126 | } 127 | set { 128 | this["GridRowBackColor"] = value; 129 | } 130 | } 131 | 132 | [global::System.Configuration.UserScopedSettingAttribute()] 133 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 134 | [global::System.Configuration.DefaultSettingValueAttribute("White")] 135 | public global::System.Drawing.Color GridAlternateRowBackColor { 136 | get { 137 | return ((global::System.Drawing.Color)(this["GridAlternateRowBackColor"])); 138 | } 139 | set { 140 | this["GridAlternateRowBackColor"] = value; 141 | } 142 | } 143 | 144 | [global::System.Configuration.UserScopedSettingAttribute()] 145 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 146 | public int[] GridIssuesSort { 147 | get { 148 | return ((int[])(this["GridIssuesSort"])); 149 | } 150 | set { 151 | this["GridIssuesSort"] = value; 152 | } 153 | } 154 | 155 | [global::System.Configuration.UserScopedSettingAttribute()] 156 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 157 | public global::System.Collections.Specialized.StringCollection GridIssuesColumnsSettings { 158 | get { 159 | return ((global::System.Collections.Specialized.StringCollection)(this["GridIssuesColumnsSettings"])); 160 | } 161 | set { 162 | this["GridIssuesColumnsSettings"] = value; 163 | } 164 | } 165 | 166 | [global::System.Configuration.UserScopedSettingAttribute()] 167 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 168 | public int[] GridTasksSort { 169 | get { 170 | return ((int[])(this["GridTasksSort"])); 171 | } 172 | set { 173 | this["GridTasksSort"] = value; 174 | } 175 | } 176 | 177 | [global::System.Configuration.UserScopedSettingAttribute()] 178 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 179 | public global::System.Collections.Specialized.StringCollection GridTasksColumnsSettings { 180 | get { 181 | return ((global::System.Collections.Specialized.StringCollection)(this["GridTasksColumnsSettings"])); 182 | } 183 | set { 184 | this["GridTasksColumnsSettings"] = value; 185 | } 186 | } 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /MiniBug/Classes/Project.cs: -------------------------------------------------------------------------------- 1 | // Copyright(c) João Martiniano. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.IO; 10 | using Newtonsoft.Json; 11 | using CsvHelper; 12 | 13 | namespace MiniBug 14 | { 15 | /// 16 | /// Stores the issues and tasks of a software project. 17 | /// 18 | [Serializable] 19 | public class Project 20 | { 21 | /// 22 | /// Gets the file format version of the project file. 23 | /// 24 | [JsonProperty] 25 | public string Version { get; private set; } = string.Empty; 26 | 27 | /// 28 | /// Gets the current value of issue ID counter: the next issue created will have this value. This property is incremented automatically. 29 | /// 30 | [JsonProperty] 31 | public int IssueIdCounter { get; private set; } = 0; 32 | 33 | /// 34 | /// Gets the current value of task ID counter: the next task created will have this value. This property is incremented automatically. 35 | /// 36 | [JsonProperty] 37 | public int TaskIdCounter { get; private set; } = 0; 38 | 39 | /// 40 | /// Gets or sets the project name. 41 | /// 42 | public string Name { get; set; } = string.Empty; 43 | 44 | /// 45 | /// Gets or sets the name of the project file. 46 | /// 47 | [JsonIgnore] 48 | public string Filename { get; set; } = string.Empty; 49 | 50 | /// 51 | /// Gets or sets the location of the project file. 52 | /// 53 | [JsonIgnore] 54 | public string Location { get; set; } = string.Empty; 55 | 56 | /// 57 | /// Gets or sets the project issues. 58 | /// 59 | public Dictionary Issues { get; set; } = new Dictionary(); 60 | 61 | /// 62 | /// Gets or sets the project tasks. 63 | /// 64 | public Dictionary Tasks { get; set; } = new Dictionary(); 65 | 66 | /// 67 | /// Information about the result of the last import operation that was executed. 68 | /// 69 | public ImportExportResult ImportResult { get; set; } = null; 70 | 71 | /// 72 | /// Information about the result of the last export operation that was executed. 73 | /// 74 | public ImportExportResult ExportResult { get; set; } = null; 75 | 76 | /// 77 | /// Creates a new project. 78 | /// 79 | public Project() 80 | { 81 | Version = ApplicationSettings.ProjectFileFormatVersion; 82 | IssueIdCounter = 1; 83 | TaskIdCounter = 1; 84 | } 85 | 86 | /// 87 | /// Creates a new project. 88 | /// 89 | /// The project name. 90 | public Project(string name) 91 | { 92 | Version = ApplicationSettings.ProjectFileFormatVersion; 93 | Name = name; 94 | IssueIdCounter = 1; 95 | TaskIdCounter = 1; 96 | } 97 | 98 | /// 99 | /// Add a new issue. 100 | /// 101 | /// An instance of the Issue class to add. 102 | /// The id of the added issue. 103 | public int AddIssue(Issue newIssue) 104 | { 105 | newIssue.ID = IssueIdCounter; 106 | Issues.Add(IssueIdCounter, newIssue); 107 | IssueIdCounter++; 108 | 109 | return newIssue.ID; 110 | } 111 | 112 | /// 113 | /// Add a new task. 114 | /// 115 | /// An instance of the Task class to add. 116 | /// The id of the added task. 117 | public int AddTask(Task newTask) 118 | { 119 | newTask.ID = TaskIdCounter; 120 | Tasks.Add(TaskIdCounter, newTask); 121 | TaskIdCounter++; 122 | 123 | return newTask.ID; 124 | } 125 | 126 | /// 127 | /// Export the issues and tasks of a project to a file. 128 | /// 129 | public void Export(string fileNameIssues, string fileNameTasks) 130 | { 131 | ExportResult = new ImportExportResult(ImportExportOperation.Export); 132 | 133 | // Export the issues 134 | if ((Issues != null) && (Issues.Count > 0)) 135 | { 136 | ExportResult.Issues.Result = ExportIssues(fileNameIssues); 137 | ExportResult.Issues.FileName = System.IO.Path.GetFileName(fileNameIssues); 138 | ExportResult.Issues.Path = System.IO.Path.GetDirectoryName(fileNameIssues); 139 | } 140 | 141 | // Export the tasks 142 | if ((Tasks != null) && (Tasks.Count > 0)) 143 | { 144 | ExportResult.Tasks.Result = ExportTasks(fileNameTasks); 145 | ExportResult.Tasks.FileName = System.IO.Path.GetFileName(fileNameTasks); 146 | ExportResult.Tasks.Path = System.IO.Path.GetDirectoryName(fileNameTasks); 147 | } 148 | } 149 | 150 | /// 151 | /// Export the project issues. 152 | /// 153 | /// 154 | /// 155 | private FileSystemOperationStatus ExportIssues(string fileName) 156 | { 157 | try 158 | { 159 | using (var writer = new StreamWriter(fileName, false, System.Text.Encoding.UTF8)) 160 | { 161 | using (var csv = new CsvWriter(writer)) 162 | { 163 | csv.WriteRecords(Issues.Values); 164 | } 165 | } 166 | } 167 | catch (System.IO.DirectoryNotFoundException) // The directory does not exist 168 | { 169 | return FileSystemOperationStatus.ExportToCsvErrorDirectoryNotFound; 170 | } 171 | catch (System.IO.PathTooLongException) // The path is too long 172 | { 173 | return FileSystemOperationStatus.ExportToCsvErrorPathTooLong; 174 | } 175 | catch (CsvHelperException) // Error reported by CsvHelper 176 | { 177 | return FileSystemOperationStatus.ExportToCsvErrorExporterComponent; 178 | } 179 | catch // General input/output error 180 | { 181 | return FileSystemOperationStatus.ExportToCsvIOError; 182 | } 183 | 184 | return FileSystemOperationStatus.ExportOK; 185 | } 186 | 187 | /// 188 | /// Export the project tasks. 189 | /// 190 | /// 191 | /// 192 | /// 193 | private FileSystemOperationStatus ExportTasks(string fileName) 194 | { 195 | try 196 | { 197 | using (var writer = new StreamWriter(fileName, false, System.Text.Encoding.UTF8)) 198 | { 199 | using (var csv = new CsvWriter(writer)) 200 | { 201 | csv.WriteRecords(Tasks.Values); 202 | } 203 | } 204 | } 205 | catch (System.IO.DirectoryNotFoundException) // The directory does not exist 206 | { 207 | return FileSystemOperationStatus.ExportToCsvErrorDirectoryNotFound; 208 | } 209 | catch (System.IO.PathTooLongException) // The path is too long 210 | { 211 | return FileSystemOperationStatus.ExportToCsvErrorPathTooLong; 212 | } 213 | catch (CsvHelperException) // Error reported by CsvHelper 214 | { 215 | return FileSystemOperationStatus.ExportToCsvErrorExporterComponent; 216 | } 217 | catch // General input/output error 218 | { 219 | return FileSystemOperationStatus.ExportToCsvIOError; 220 | } 221 | 222 | return FileSystemOperationStatus.ExportOK; 223 | } 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /MiniBug/ConfigureViewForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MiniBug 2 | { 3 | partial class ConfigureViewForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.btOK = new System.Windows.Forms.Button(); 32 | this.btCancel = new System.Windows.Forms.Button(); 33 | this.TabControl = new System.Windows.Forms.TabControl(); 34 | this.tabPage1 = new System.Windows.Forms.TabPage(); 35 | this.GridIssues = new System.Windows.Forms.DataGridView(); 36 | this.tabPage2 = new System.Windows.Forms.TabPage(); 37 | this.GridTasks = new System.Windows.Forms.DataGridView(); 38 | this.label1 = new System.Windows.Forms.Label(); 39 | this.TabControl.SuspendLayout(); 40 | this.tabPage1.SuspendLayout(); 41 | ((System.ComponentModel.ISupportInitialize)(this.GridIssues)).BeginInit(); 42 | this.tabPage2.SuspendLayout(); 43 | ((System.ComponentModel.ISupportInitialize)(this.GridTasks)).BeginInit(); 44 | this.SuspendLayout(); 45 | // 46 | // btOK 47 | // 48 | this.btOK.Location = new System.Drawing.Point(592, 286); 49 | this.btOK.Name = "btOK"; 50 | this.btOK.Size = new System.Drawing.Size(75, 23); 51 | this.btOK.TabIndex = 2; 52 | this.btOK.Text = "OK"; 53 | this.btOK.UseVisualStyleBackColor = true; 54 | this.btOK.Click += new System.EventHandler(this.btOK_Click); 55 | // 56 | // btCancel 57 | // 58 | this.btCancel.Location = new System.Drawing.Point(673, 286); 59 | this.btCancel.Name = "btCancel"; 60 | this.btCancel.Size = new System.Drawing.Size(75, 23); 61 | this.btCancel.TabIndex = 3; 62 | this.btCancel.Text = "Cancel"; 63 | this.btCancel.UseVisualStyleBackColor = true; 64 | this.btCancel.Click += new System.EventHandler(this.btCancel_Click); 65 | // 66 | // TabControl 67 | // 68 | this.TabControl.Controls.Add(this.tabPage1); 69 | this.TabControl.Controls.Add(this.tabPage2); 70 | this.TabControl.Location = new System.Drawing.Point(12, 44); 71 | this.TabControl.Name = "TabControl"; 72 | this.TabControl.SelectedIndex = 0; 73 | this.TabControl.Size = new System.Drawing.Size(736, 236); 74 | this.TabControl.TabIndex = 1; 75 | // 76 | // tabPage1 77 | // 78 | this.tabPage1.Controls.Add(this.GridIssues); 79 | this.tabPage1.Location = new System.Drawing.Point(4, 22); 80 | this.tabPage1.Name = "tabPage1"; 81 | this.tabPage1.Padding = new System.Windows.Forms.Padding(3); 82 | this.tabPage1.Size = new System.Drawing.Size(728, 210); 83 | this.tabPage1.TabIndex = 0; 84 | this.tabPage1.Text = "Issues"; 85 | this.tabPage1.UseVisualStyleBackColor = true; 86 | // 87 | // GridIssues 88 | // 89 | this.GridIssues.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; 90 | this.GridIssues.Dock = System.Windows.Forms.DockStyle.Fill; 91 | this.GridIssues.Location = new System.Drawing.Point(3, 3); 92 | this.GridIssues.Name = "GridIssues"; 93 | this.GridIssues.Size = new System.Drawing.Size(722, 204); 94 | this.GridIssues.TabIndex = 0; 95 | this.GridIssues.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.GridIssues_CellContentClick); 96 | this.GridIssues.CellPainting += new System.Windows.Forms.DataGridViewCellPaintingEventHandler(this.GridIssues_CellPainting); 97 | // 98 | // tabPage2 99 | // 100 | this.tabPage2.Controls.Add(this.GridTasks); 101 | this.tabPage2.Location = new System.Drawing.Point(4, 22); 102 | this.tabPage2.Name = "tabPage2"; 103 | this.tabPage2.Padding = new System.Windows.Forms.Padding(3); 104 | this.tabPage2.Size = new System.Drawing.Size(728, 210); 105 | this.tabPage2.TabIndex = 1; 106 | this.tabPage2.Text = "Tasks"; 107 | this.tabPage2.UseVisualStyleBackColor = true; 108 | // 109 | // GridTasks 110 | // 111 | this.GridTasks.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; 112 | this.GridTasks.Dock = System.Windows.Forms.DockStyle.Fill; 113 | this.GridTasks.Location = new System.Drawing.Point(3, 3); 114 | this.GridTasks.Name = "GridTasks"; 115 | this.GridTasks.Size = new System.Drawing.Size(722, 204); 116 | this.GridTasks.TabIndex = 0; 117 | this.GridTasks.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.GridTasks_CellContentClick); 118 | this.GridTasks.CellPainting += new System.Windows.Forms.DataGridViewCellPaintingEventHandler(this.GridTasks_CellPainting); 119 | // 120 | // label1 121 | // 122 | this.label1.AutoSize = true; 123 | this.label1.Location = new System.Drawing.Point(13, 17); 124 | this.label1.Name = "label1"; 125 | this.label1.Size = new System.Drawing.Size(510, 13); 126 | this.label1.TabIndex = 0; 127 | this.label1.Text = "Select which columns to show and which columns to use for sorting in the Issues a" + 128 | "nd Tasks grids:"; 129 | // 130 | // ConfigureViewForm 131 | // 132 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 133 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 134 | this.ClientSize = new System.Drawing.Size(760, 321); 135 | this.Controls.Add(this.label1); 136 | this.Controls.Add(this.TabControl); 137 | this.Controls.Add(this.btCancel); 138 | this.Controls.Add(this.btOK); 139 | this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 140 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 141 | this.MaximizeBox = false; 142 | this.MinimizeBox = false; 143 | this.Name = "ConfigureViewForm"; 144 | this.ShowIcon = false; 145 | this.ShowInTaskbar = false; 146 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 147 | this.Text = "Configure Columns"; 148 | this.Load += new System.EventHandler(this.ConfigureViewForm_Load); 149 | this.TabControl.ResumeLayout(false); 150 | this.tabPage1.ResumeLayout(false); 151 | ((System.ComponentModel.ISupportInitialize)(this.GridIssues)).EndInit(); 152 | this.tabPage2.ResumeLayout(false); 153 | ((System.ComponentModel.ISupportInitialize)(this.GridTasks)).EndInit(); 154 | this.ResumeLayout(false); 155 | this.PerformLayout(); 156 | 157 | } 158 | 159 | #endregion 160 | private System.Windows.Forms.Button btOK; 161 | private System.Windows.Forms.Button btCancel; 162 | private System.Windows.Forms.TabControl TabControl; 163 | private System.Windows.Forms.TabPage tabPage1; 164 | private System.Windows.Forms.TabPage tabPage2; 165 | private System.Windows.Forms.DataGridView GridIssues; 166 | private System.Windows.Forms.Label label1; 167 | private System.Windows.Forms.DataGridView GridTasks; 168 | } 169 | } --------------------------------------------------------------------------------