From cf7680a3eef5a1946ec01093664678c2de104927 Mon Sep 17 00:00:00 2001 From: Sergej Andrejev Date: Fri, 12 Jun 2009 19:57:02 +0000 Subject: [PATCH] InputBindingCategory class, nested categories git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/shortcuts@4285 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- AddIns/ICSharpCode.SharpDevelop.addin | 16 ++ .../ShortcutsManagement.suo | Bin 93696 -> 93696 bytes .../Resources/StringResources.resx | 8 + .../Src/Data/ShortcutsFinder.cs | 3 +- .../Src/Dialogs/Resources.xaml | 1 + .../ShortcutsManagementOptionsPanel.xaml.cs | 185 +++++++++--------- .../ShortcutsManagement/Window1.xaml.cs | 71 ++++++- .../Project/Src/Commands/MenuItemBuilders.cs | 4 +- .../CommandsService/CommandsRegistry.cs | 79 +++++++- .../CommandsService/CommandsService.cs | 4 +- .../CommandsService/InputBindingCategory.cs | 47 +++++ .../CommandsService/InputBindingInfo.cs | 6 +- .../ICSharpCode.Core.Presentation.csproj | 1 + .../Menu/MenuCommand.cs | 2 +- 14 files changed, 314 insertions(+), 113 deletions(-) create mode 100644 src/Main/ICSharpCode.Core.Presentation/CommandsService/InputBindingCategory.cs diff --git a/AddIns/ICSharpCode.SharpDevelop.addin b/AddIns/ICSharpCode.SharpDevelop.addin index 0e67f79cd9..dd7eec13d8 100644 --- a/AddIns/ICSharpCode.SharpDevelop.addin +++ b/AddIns/ICSharpCode.SharpDevelop.addin @@ -51,6 +51,22 @@ + + + + + + + + + + + + + + + + diff --git a/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement.suo b/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement.suo index 484e365e01153a894da9f0fb7e989e3d9dcbb65f..ab151362c48646ad8ac0e7fb23effa3526732cda 100644 GIT binary patch delta 2730 zcma)84Nz3q6~5>3c3EH{3kzfgL|Ab#fUqFYHDS!kPeeu85Hw&=egYVc62+H5j4f-0 z)Ff(Ma_ox#=nEqE%AL3r9oyB`^(Iivq1husT4mr*=CLr6;XcuMsC!6kLUSx z1@yBs5#H^v( zI>JW647u@v=fzJ;;Kc)ve2O2QNeg0Z8AeuIR)-gncgrFh7!bBz!o*|;4sNW$x}SGr z#xLW7Oce1s_a&ZY?U%48s{l_vl!qVKCVI*vFuvD>+io9Irxrp28f|8Na9Stb`SQ&E z8B|yZ(P-N8I%d{JVZ>{nsT0Xy$M-X$;P<{zBF0S=N(fH>;&1qFW*PP`PYHN{n>@+Z z1?tnm(B>n#(bfytowOUzp0wc6tX8!@4eGFN_Yh=q$eji3Pu&?X=l3m*@rT2ArsaZ3 z&wN5$r|}pwvkvvjLP!yD!#IueD5wLKu!+OUVp)0@a8KbPY%bg)9goFiIT6CtvKZK^ zejE)S_7t~ENr3x`e*kCkR`J?!7E2Coggcs*R2oE*1_*D-S8^^9YJCEfp*z~fYgS*E zjUo|bZ9k>VWh@~=uO+=@_^lvB{GzBiClLN{-)f(?HeY7PUTBD{L=^? zQSlY@Y*O^1OGP^F+w>`p$z=Ml%NRkFnERce9=<8Z_x;2=FjZxc%P3j+^*W1gv~myz z&MQC(adKlVy3Vv>YjXh~-Bzg^t(HJX{a6M<(>Zj1(V|PFuP;f-kFa4!ok*qveriu6 zsH0i17Q0_E;mmj}->p0*>0a)9&ZWnzPYt;G$;L>S&wjB3=6At0;sw$*?LxN4>H7r& zn+jHa7T+i9Z1VkY zd3iCAmlm9ON{{P%(?|Qp48COWzEU2}(#UFwW?ZhkIxgddHb-!X@JCB?Y(o>D8XS+3 zzh2pSi@)W$3&LSBwWVmR(0jJrfApnlS>>I%*x&g~XV7U{xc6MS9L|!75;;A;5?uM@ zw_$!i;uxN%QJJ7f^NA5!1z7Kd%eaDw0u9NP$=KA$K4L`p^Y0z_rq_(a-@FA1W_zE& zi?^~c+it_!0t09^@vnS~g;Rqjyz$RE_=kUc=Q_+h_1hFl)03mU5%B2R=L zBr9Q2NG;iOvM$ffqxoy4#np~!$22X*Dw^nPgieAur=O+w5yBzDbA;y!BJU3qh6zUr zszZV*hv=;>;`?=K6*tvs!Wn|#M(ABgc#|ORKhFgTo{=O-5_gKK=->$EoNmUtbq1W- zV8XE#?V7d1z4uPyL`59B(Sa=o_1M^P0{iprX#b&oz_}UJ%^@&^6V`jNrp6L3h8E@K zgO2+*tCwS;0jz4l-B3J~2=Y)U7>80o9yoqnO0TDtZ=zLyg4X|OmPuD*6Kf}q`BJ*U zr%9t<89dNxzUNN6NTNsClVsOQHZ5dR_}$*Y{@!A1-)|B=wMCt<(gjpHm~3EdMA?(L6g~(3@fjz0oXg?7+-HYUgZKj? z)?ZxG9>oroV7N;|g?&vQGM!LVaMHR0Pk5p{YaLKyQSA?c6R-U#3d=5;Fmy`14Ib+{ zNDdZfg-5A?BY+oAXX2vNXYtm3EpvMC!v#;$?YBn8Yp$iZthXa@)8QX_xA#@S1;Yb0 z*nCW?w+iOAb*YwCs^Ku694zuFKARmHQqLOcp}Q2&va;Vv3- MJ`ao3j$LrzU+SEGm;e9( delta 2282 zcmah~4Q$ib5r6mh-^Rfobv{Ufp_$_*e3%4GfW+k^IKhO7B*e{32qA#r6GSP$hq_RP8rIiOSD*uH8 z_!k`-+;4Bq|J!x!P(L!O#2QaT`{MYanuDPvRcz5u234vOFBy*MEX+UxQtP)u+IYV? zhk*1%Fh<}~0#Xw|p_yy|da=j+FHpvV=(3jU7MR)1+c)9>jJ1>K(?s)#{&;L|${7AA zeLqfA^*!7pZY3`R^8*A=BKpprR6i`ilf$zN65eYU|4D5$KrXJf&p6m(g9?1nzSkn9 zB zk*{ljeHdN-5?;RL?L z$oO^Ie^|3LFS2GOG=qR*pGv9e!1bk#N>M6it65;;N7q3^Z!-+___iq576h5BEC-)`a|-rNu)zXRvP`FkOqoHbRr=AFvaqLW z2`))lgrO?Uw3{}6CJTTuOs<%Yf2uYL_ll_Qa6rNS*iD+ZuXmHN!*_s&ipi z?KvFQ&qi&P-z*DJhVom3Vyi~fr{1~r^pVf%jCbnnT2rPTOJ}xWN2^yH-JpUPYz^ok z4`KaD3|(*2Wick=bvn??fZw2mJjJi(K@%Pr&ckQtb)f^BL{mpU;0q5m^M5`FF5K7M zhR)6o(V;!2=$-%QKd0iwr;cEBb6V0Y(y@SO1Ae?YA4`uG9^Rtj$i+M{<(XoD>sV5M z0;5mn@Jm6uQI`mAvjF@XvvUq%bCF%xL!Sd)&7RC{N#H}@u6HpSuww{4{_n2FVU>cjr&@uBWz$ zG30u8uy@!vsv`DkwFO+HJSE`(p}qEjIxdNj*^KeH6P!5c$Pl5U?o>7Hlbyd3$0HAb zg*`+9`h;y5d;6|kC&;cTckDWGUT<_J2#MIqYSjYS1j(*7=9AFQ{MmHJsVG@gNGwkR z8)H$wSvKi~382tEOrM(EM~F0aY~F}(#ZvLNUmu4>I6L+Pe)P>ObXJs6T|022+t z-EjZlh|d4zC_hqCVystS_4Jp2Kt+b@3;2nqVIP65YzZr41w>`HC;%m7y$t_;v^4$9 zypLA$MUn3Z_b5=8|7-dR6TN;Hm7KBh24Q1sIhP)MBqMTOfhQCTa}aOtUA`=2uIXXF z2)b{A?fabw2R>La+Kr2M86trs2t(Kg=H5p56sq}Z6Sa{HzHl9!2{(g^e=o?yP@e#MaZgn04chq4)UudZ@v*o*CSk3#TKe z%}}hvo!Q;k{|lAsCOc=bZB*CjUwJuhyn2~Oo!~w)R0CmpQ^V6h2|GYR_bzM1ngceK zXSu;;ln=D@UV0hP+zCRRz7usQ@<&Jh<64IslxI8*9sr8!aF>g3u)Y% z2h-4d))mPtgac`jupeFl3zX5O2}EPcB3cdX2R!{oF>asJg2xus;`8&xjPiO1?-v|zZl8un4Jd!d~3Mks*_8FL4` z!n@=&j~|kgdGrQ;-wbPkuU!jA=!O&=`neaKMVEM15Uz$(V0`Z;@S?Y9sHeuuy_=vI K7~lRl?D#+FPY`JU diff --git a/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Resources/StringResources.resx b/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Resources/StringResources.resx index e16382daab..d57792d837 100644 --- a/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Resources/StringResources.resx +++ b/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Resources/StringResources.resx @@ -128,8 +128,16 @@ _Cancel + For testing outside SD _OK + For testing outside SD + + + Unspecified + + + Uncategorized \ No newline at end of file diff --git a/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Src/Data/ShortcutsFinder.cs b/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Src/Data/ShortcutsFinder.cs index 5ad775841e..28574e9ad9 100644 --- a/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Src/Data/ShortcutsFinder.cs +++ b/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Src/Data/ShortcutsFinder.cs @@ -261,7 +261,8 @@ namespace ICSharpCode.ShortcutsManagement.Data } } - return category.IsVisible = (forseMatch.HasValue && forseMatch.Value) || isSubElementVisible; + // Show category if has sub elements, forced or matches search filter + return category.IsVisible = (forseMatch.HasValue && forseMatch.Value && (category.SubCategories.Count > 0 || category.Shortcuts.Count > 0)) || isSubElementVisible; } } } diff --git a/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Src/Dialogs/Resources.xaml b/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Src/Dialogs/Resources.xaml index 3730597311..0d837dda4b 100644 --- a/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Src/Dialogs/Resources.xaml +++ b/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Src/Dialogs/Resources.xaml @@ -128,6 +128,7 @@ + diff --git a/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Src/Dialogs/ShortcutsManagementOptionsPanel.xaml.cs b/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Src/Dialogs/ShortcutsManagementOptionsPanel.xaml.cs index 48e52606fa..b009e5bde5 100644 --- a/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Src/Dialogs/ShortcutsManagementOptionsPanel.xaml.cs +++ b/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Src/Dialogs/ShortcutsManagementOptionsPanel.xaml.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Collections.ObjectModel; using System.Text.RegularExpressions; using System.Windows.Controls; using System.Windows.Input; @@ -17,131 +16,128 @@ namespace ICSharpCode.ShortcutsManagement /// public partial class ShortcutsManagementOptionsPanel : UserControl, IOptionPanel { + /// + /// Stores shortcut entry to input binding convertion map + /// private readonly Dictionary shortcutsMap = new Dictionary(); - private static InputGestureCollection GetGestures(string gesturesString) - { - var converter = new InputGestureCollectionConverter(); - return (InputGestureCollection)converter.ConvertFromInvariantString(gesturesString); - } - - public ShortcutsManagementOptionsPanel() { ResourceService.RegisterStrings("ICSharpCode.ShortcutsManagement.Resources.StringResources", GetType().Assembly); InitializeComponent(); - - // Test data - var rootEntries = new ObservableCollection(); - - var addin1 = new ShortcutManagement.AddIn("SharpDevelop"); - rootEntries.Add(addin1); - addin1.Categories.Add(new ShortcutCategory("Editing")); - addin1.Categories[0].Shortcuts.Add(new Shortcut("Copy", GetGestures("Ctrl + C"))); - addin1.Categories[0].Shortcuts.Add(new Shortcut("Paste", GetGestures("Ctrl + V | Ctrl+Insert"))); - addin1.Categories[0].Shortcuts.Add(new Shortcut("Cut", GetGestures("Ctrl + X"))); - addin1.Categories[0].Shortcuts.Add(new Shortcut("Undo", GetGestures("Ctrl + Z"))); - addin1.Categories[0].Shortcuts.Add(new Shortcut("Redo", GetGestures("Ctrl + Y"))); - addin1.Categories.Add(new ShortcutCategory("Building")); - addin1.Categories[1].Shortcuts.Add(new Shortcut("Build", GetGestures("Ctrl + Shift+B"))); - addin1.Categories[1].Shortcuts.Add(new Shortcut("Run", GetGestures("F5"))); - addin1.Categories[1].Shortcuts.Add(new Shortcut("Run without debuger", GetGestures("Ctrl + F5"))); - addin1.Categories[1].Shortcuts.Add(new Shortcut("Attach debuger", GetGestures("Ctrl + F8"))); - addin1.Categories.Add(new ShortcutCategory("Uncategorized")); - addin1.Categories[2].Shortcuts.Add(new Shortcut("Attach debuger", GetGestures("Ctrl + F8"))); - - var addin2 = new ShortcutManagement.AddIn("Search & replace"); - rootEntries.Add(addin2); - addin2.Categories.Add(new ShortcutCategory("Uncategorized")); - addin2.Categories[0].Shortcuts.Add(new Shortcut("Quick find", GetGestures("Ctrl + F"))); - addin2.Categories[0].Shortcuts.Add(new Shortcut("Quick replace", GetGestures("Ctrl + H"))); - addin2.Categories[0].SubCategories.Add(new ShortcutCategory("Subcategory 3")); - addin2.Categories[0].SubCategories[0].SubCategories.Add(new ShortcutCategory("Subcategory 4")); - addin2.Categories[0].SubCategories[0].SubCategories[0].Shortcuts.Add(new Shortcut("Shortcut N", GetGestures("Ctrl + N"))); - addin2.Categories[0].SubCategories[0].SubCategories[0].Shortcuts.Add(new Shortcut("Shortcut O", GetGestures("Ctrl + O"))); - addin2.Categories[0].SubCategories[0].Shortcuts.Add(new Shortcut("Shortcut L", GetGestures("Ctrl + L"))); - addin2.Categories[0].SubCategories[0].Shortcuts.Add(new Shortcut("Shortcut M", GetGestures("Ctrl + M"))); - addin2.Categories[0].Shortcuts.Add(new Shortcut("Find in files", GetGestures("Ctrl + Shift + F | Ctrl + Shift + H | Ctrl + I"))); - addin2.Categories[0].Shortcuts.Add(new Shortcut("Replace in files", GetGestures("Ctrl + Shift + H"))); - addin2.Categories[0].Shortcuts.Add(new Shortcut("Find symbol", null)); - - var addin3 = new ShortcutManagement.AddIn("Unspecified"); - rootEntries.Add(addin3); - addin3.Categories.Add(new ShortcutCategory("Uncategorized")); - addin3.Categories[0].Shortcuts.Add(new Shortcut("Test regex expression", null)); - - var rootCategory = new ShortcutCategory("Without addin"); - rootEntries.Add(rootCategory); - rootCategory.SubCategories.Add(new ShortcutCategory("Subcategory 1")); - rootCategory.SubCategories[0].Shortcuts.Add(new Shortcut("Shortcut H", GetGestures("Ctrl + H"))); - rootCategory.SubCategories[0].Shortcuts.Add(new Shortcut("Shortcut I", GetGestures("Ctrl + I"))); - rootCategory.SubCategories.Add(new ShortcutCategory("Subcategory 2")); - rootCategory.SubCategories[0].Shortcuts.Add(new Shortcut("Shortcut J", GetGestures("Ctrl + J"))); - rootCategory.SubCategories[0].Shortcuts.Add(new Shortcut("Shortcut K", GetGestures("Ctrl + K"))); - rootCategory.Shortcuts.Add(new Shortcut("Shortcut A", GetGestures("Ctrl + A"))); - rootCategory.Shortcuts.Add(new Shortcut("Shortcut B", GetGestures("Ctrl + B"))); - rootCategory.Shortcuts.Add(new Shortcut("Shortcut C", GetGestures("Ctrl + C"))); - rootCategory.Shortcuts.Add(new Shortcut("Shortcut D", GetGestures("Ctrl + D"))); - rootCategory.Shortcuts.Add(new Shortcut("Shortcut E", null)); - - rootEntries.Add(new Shortcut("Shortcut F", GetGestures("Ctrl + F"))); - rootEntries.Add(new Shortcut("Shortcut G", GetGestures("Ctrl + G"))); - - shortcutsManagementOptionsPanel.DataContext = rootEntries; } public void LoadOptions() { - // Load shortcuts for real - var unspecifiedAddInSection = new ShortcutManagement.AddIn("Unspecified"); - unspecifiedAddInSection.Categories.Add(new ShortcutCategory("Uncategorized")); - - var rootEntries = new List(); - rootEntries.Add(unspecifiedAddInSection); + // Root shortcut tree entries + var rootEntries = new List(); + // Stores SD add-in to add-in section convertion map var addInsMap = new Dictionary(); - var categoriesMap = new Dictionary>(); + // Stores SD input binding category to category section convertion map + var categoriesMap = new Dictionary>(); + + // Create default add-in for input bindings which don't specify add-in + var unspecifiedAddInSection = new ShortcutManagement.AddIn(StringParser.Parse("${res:ShortcutsManagement.UnspecifiedAddInName}")); + unspecifiedAddInSection.Categories.Add(new ShortcutCategory(StringParser.Parse("${res:ShortcutsManagement.UnspecifiedCategoryName}"))); + rootEntries.Add(unspecifiedAddInSection); + + // Go through all input bindings var inputBindingInfos = CommandsRegistry.FindInputBindingInfos(null, null, null); foreach(var inputBindingInfo in inputBindingInfos) { + // Find appropriate or create new add-in section for input binding ShortcutManagement.AddIn addinSection; - if(inputBindingInfo.AddIn == null) { + if (inputBindingInfo.AddIn == null) { addinSection = unspecifiedAddInSection; } else if (addInsMap.ContainsKey(inputBindingInfo.AddIn)) { addinSection = addInsMap[inputBindingInfo.AddIn]; } else { addinSection = new ShortcutManagement.AddIn(inputBindingInfo.AddIn.Name); - addinSection.Categories.Add(new ShortcutCategory("Uncategorized")); + addinSection.Categories.Add(new ShortcutCategory(StringParser.Parse("${res:ShortcutsManagement.UnspecifiedCategoryName}"))); addInsMap.Add(inputBindingInfo.AddIn, addinSection); - categoriesMap.Add(addinSection, new Dictionary()); + categoriesMap.Add(addinSection, new Dictionary()); rootEntries.Add(addinSection); } - ShortcutCategory categorySection; - if(string.IsNullOrEmpty(inputBindingInfo.CategoryName)) { - categorySection = addinSection.Categories[0]; - } else if(!categoriesMap[addinSection].ContainsKey(inputBindingInfo.CategoryName)) { - categorySection = new ShortcutCategory(inputBindingInfo.CategoryName); - addinSection.Categories.Add(categorySection); - categoriesMap[addinSection].Add(inputBindingInfo.CategoryName, categorySection); + // Find appropriate or create new category sections within add-in section for input binding + var shortcutCategorySections = new List(); + if (inputBindingInfo.Categories.Count == 0) { + // If no category specified assign to "Uncotegorized" category + shortcutCategorySections.Add(addinSection.Categories[0]); } else { - categorySection = categoriesMap[addinSection][inputBindingInfo.CategoryName]; + // Go throu all categories and find or create appropriate category sections + foreach (var bindingCategory in inputBindingInfo.Categories) { + ShortcutCategory categorySection; + if (categoriesMap[addinSection].ContainsKey(bindingCategory)) { + // If found appropriate category assign shortcut to it + categorySection = categoriesMap[addinSection][bindingCategory]; + } else { + // Create appropriate category section and root category sections + + // Create direct category to which shortcut will be assigned + var categoryName = StringParser.Parse(bindingCategory.Name); + categorySection = new ShortcutCategory(categoryName); + categoriesMap[addinSection].Add(bindingCategory, categorySection); + + // Go down to root level and create all parent categories + var currentBindingCategory = bindingCategory; + var currentShortcutCategory = categorySection; + while (currentBindingCategory.ParentCategory != null) { + ShortcutCategory parentCategorySection; + + if (!categoriesMap[addinSection].ContainsKey(currentBindingCategory.ParentCategory)) { + // Create parent category section if it's not created yet + var parentCategoryName = StringParser.Parse(currentBindingCategory.ParentCategory.Name); + parentCategorySection = new ShortcutCategory(parentCategoryName); + + categoriesMap[addinSection].Add(currentBindingCategory.ParentCategory, parentCategorySection); + } else { + // Use existing category section as parent category section + parentCategorySection = categoriesMap[addinSection][currentBindingCategory.ParentCategory]; + } + + // Add current category section to root category section children + if (!parentCategorySection.SubCategories.Contains(currentShortcutCategory)) { + parentCategorySection.SubCategories.Add(currentShortcutCategory); + } + + currentShortcutCategory = parentCategorySection; + currentBindingCategory = currentBindingCategory.ParentCategory; + } + + // Add root category section to add-in categories list + if (!addinSection.Categories.Contains(currentShortcutCategory)) { + addinSection.Categories.Add(currentShortcutCategory); + } + } + + shortcutCategorySections.Add(categorySection); + } + } + + // Get shortcut entry text. Normaly shortcut entry text is equalt to routed command text + // but this value can be overriden through InputBindingInfo.RoutedCommandText value + var shortcutText = inputBindingInfo.RoutedCommand.Text; + if (!string.IsNullOrEmpty(inputBindingInfo.RoutedCommandText)) { + shortcutText = inputBindingInfo.RoutedCommandText; } - var shortcutText = !string.IsNullOrEmpty(inputBindingInfo.RoutedCommandText) - ? inputBindingInfo.RoutedCommandText - : inputBindingInfo.RoutedCommand.Text; shortcutText = StringParser.Parse(shortcutText); - + // Some commands have "&" sign to mark alternative key used to call this command from menu - // Strip this sign + // Strip this sign from shortcut entry text shortcutText = Regex.Replace(shortcutText, @"&([^\s])", @"$1"); var shortcut = new Shortcut(shortcutText, inputBindingInfo.Gestures); - categorySection.Shortcuts.Add(shortcut); + + // Assign shortcut to all categories it is registered in + foreach (var categorySection in shortcutCategorySections) { + categorySection.Shortcuts.Add(shortcut); + } - shortcutsMap.Add(shortcut, inputBindingInfo); + shortcutsMap.Add(shortcut, inputBindingInfo); } rootEntries.Sort(); @@ -154,10 +150,9 @@ namespace ICSharpCode.ShortcutsManagement shortcutsManagementOptionsPanel.DataContext = rootEntries; } - public bool SaveOptions() { - - foreach (var pair in shortcutsMap) - { + public bool SaveOptions() + { + foreach (var pair in shortcutsMap) { var shortcut = pair.Key; var inputBindingInfo = pair.Value; diff --git a/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Window1.xaml.cs b/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Window1.xaml.cs index 2a8c63544a..45dec338c1 100644 --- a/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Window1.xaml.cs +++ b/src/AddIns/Misc/ShortcutsManagement/ShortcutsManagement/Window1.xaml.cs @@ -1,4 +1,9 @@ -using System.Windows; +using System.Collections.ObjectModel; +using System.Windows; +using System.Windows.Input; +using ICSharpCode.Core.Presentation; +using ICSharpCode.ShortcutsManagement.Data; +using AddInSection = ICSharpCode.ShortcutsManagement.Data.AddIn; namespace ICSharpCode.ShortcutsManagement { @@ -10,6 +15,70 @@ namespace ICSharpCode.ShortcutsManagement public Window1() { InitializeComponent(); + + // Test data + var rootEntries = new ObservableCollection(); + + var addin1 = new AddInSection("SharpDevelop"); + rootEntries.Add(addin1); + addin1.Categories.Add(new ShortcutCategory("Editing")); + addin1.Categories[0].Shortcuts.Add(new Shortcut("Copy", GetGestures("Ctrl + C"))); + addin1.Categories[0].Shortcuts.Add(new Shortcut("Paste", GetGestures("Ctrl + V | Ctrl+Insert"))); + addin1.Categories[0].Shortcuts.Add(new Shortcut("Cut", GetGestures("Ctrl + X"))); + addin1.Categories[0].Shortcuts.Add(new Shortcut("Undo", GetGestures("Ctrl + Z"))); + addin1.Categories[0].Shortcuts.Add(new Shortcut("Redo", GetGestures("Ctrl + Y"))); + addin1.Categories.Add(new ShortcutCategory("Building")); + addin1.Categories[1].Shortcuts.Add(new Shortcut("Build", GetGestures("Ctrl + Shift+B"))); + addin1.Categories[1].Shortcuts.Add(new Shortcut("Run", GetGestures("F5"))); + addin1.Categories[1].Shortcuts.Add(new Shortcut("Run without debuger", GetGestures("Ctrl + F5"))); + addin1.Categories[1].Shortcuts.Add(new Shortcut("Attach debuger", GetGestures("Ctrl + F8"))); + addin1.Categories.Add(new ShortcutCategory("Uncategorized")); + addin1.Categories[2].Shortcuts.Add(new Shortcut("Attach debuger", GetGestures("Ctrl + F8"))); + + var addin2 = new AddInSection("Search & replace"); + rootEntries.Add(addin2); + addin2.Categories.Add(new ShortcutCategory("Uncategorized")); + addin2.Categories[0].Shortcuts.Add(new Shortcut("Quick find", GetGestures("Ctrl + F"))); + addin2.Categories[0].Shortcuts.Add(new Shortcut("Quick replace", GetGestures("Ctrl + H"))); + addin2.Categories[0].SubCategories.Add(new ShortcutCategory("Subcategory 3")); + addin2.Categories[0].SubCategories[0].SubCategories.Add(new ShortcutCategory("Subcategory 4")); + addin2.Categories[0].SubCategories[0].SubCategories[0].Shortcuts.Add(new Shortcut("Shortcut N", GetGestures("Ctrl + N"))); + addin2.Categories[0].SubCategories[0].SubCategories[0].Shortcuts.Add(new Shortcut("Shortcut O", GetGestures("Ctrl + O"))); + addin2.Categories[0].SubCategories[0].Shortcuts.Add(new Shortcut("Shortcut L", GetGestures("Ctrl + L"))); + addin2.Categories[0].SubCategories[0].Shortcuts.Add(new Shortcut("Shortcut M", GetGestures("Ctrl + M"))); + addin2.Categories[0].Shortcuts.Add(new Shortcut("Find in files", GetGestures("Ctrl + Shift + F | Ctrl + Shift + H | Ctrl + I"))); + addin2.Categories[0].Shortcuts.Add(new Shortcut("Replace in files", GetGestures("Ctrl + Shift + H"))); + addin2.Categories[0].Shortcuts.Add(new Shortcut("Find symbol", null)); + + var addin3 = new AddInSection("Unspecified"); + rootEntries.Add(addin3); + addin3.Categories.Add(new ShortcutCategory("Uncategorized")); + addin3.Categories[0].Shortcuts.Add(new Shortcut("Test regex expression", null)); + + var rootCategory = new ShortcutCategory("Without addin"); + rootEntries.Add(rootCategory); + rootCategory.SubCategories.Add(new ShortcutCategory("Subcategory 1")); + rootCategory.SubCategories[0].Shortcuts.Add(new Shortcut("Shortcut H", GetGestures("Ctrl + H"))); + rootCategory.SubCategories[0].Shortcuts.Add(new Shortcut("Shortcut I", GetGestures("Ctrl + I"))); + rootCategory.SubCategories.Add(new ShortcutCategory("Subcategory 2")); + rootCategory.SubCategories[0].Shortcuts.Add(new Shortcut("Shortcut J", GetGestures("Ctrl + J"))); + rootCategory.SubCategories[0].Shortcuts.Add(new Shortcut("Shortcut K", GetGestures("Ctrl + K"))); + rootCategory.Shortcuts.Add(new Shortcut("Shortcut A", GetGestures("Ctrl + A"))); + rootCategory.Shortcuts.Add(new Shortcut("Shortcut B", GetGestures("Ctrl + B"))); + rootCategory.Shortcuts.Add(new Shortcut("Shortcut C", GetGestures("Ctrl + C"))); + rootCategory.Shortcuts.Add(new Shortcut("Shortcut D", GetGestures("Ctrl + D"))); + rootCategory.Shortcuts.Add(new Shortcut("Shortcut E", null)); + + rootEntries.Add(new Shortcut("Shortcut F", GetGestures("Ctrl + F"))); + rootEntries.Add(new Shortcut("Shortcut G", GetGestures("Ctrl + G"))); + + optionsPanel.DataContext = rootEntries; + } + + private static InputGestureCollection GetGestures(string gesturesString) + { + var converter = new InputGestureCollectionConverter(); + return (InputGestureCollection)converter.ConvertFromInvariantString(gesturesString); } } } diff --git a/src/Main/Base/Project/Src/Commands/MenuItemBuilders.cs b/src/Main/Base/Project/Src/Commands/MenuItemBuilders.cs index 558d57bf56..70af13b05e 100644 --- a/src/Main/Base/Project/Src/Commands/MenuItemBuilders.cs +++ b/src/Main/Base/Project/Src/Commands/MenuItemBuilders.cs @@ -471,7 +471,7 @@ namespace ICSharpCode.SharpDevelop.Commands } var routedCommandName = "SDViewCommands.ShowView_" + padContent.Class; - var routedCommandText = "Show view \"" + MenuService.ConvertLabel(StringParser.Parse(padContent.Title)) + "\""; + var routedCommandText = MenuService.ConvertLabel(StringParser.Parse(padContent.Title)); // TODO: fix this hack if(!bindingsAssigned.Contains(routedCommandName)) { @@ -497,7 +497,7 @@ namespace ICSharpCode.SharpDevelop.Commands inputBindingInfo.ContextName = CommandsRegistry.DefaultContextName; inputBindingInfo.RoutedCommandName = routedCommandName; inputBindingInfo.Gestures = gestures; - inputBindingInfo.CategoryName = "Views"; + inputBindingInfo.Categories.AddRange(CommandsRegistry.RegisterInputBindingCategories("Menu Items/Views")); inputBindingInfo.AddIn = addIn; CommandsRegistry.RegisterInputBinding(inputBindingInfo); diff --git a/src/Main/ICSharpCode.Core.Presentation/CommandsService/CommandsRegistry.cs b/src/Main/ICSharpCode.Core.Presentation/CommandsService/CommandsRegistry.cs index 9dc6838350..6f067afa8b 100644 --- a/src/Main/ICSharpCode.Core.Presentation/CommandsService/CommandsRegistry.cs +++ b/src/Main/ICSharpCode.Core.Presentation/CommandsService/CommandsRegistry.cs @@ -5,6 +5,8 @@ using System.Windows; using ICSharpCode.Core; using System.Threading; using System.Text; +using System.Text.RegularExpressions; +using System.Linq; namespace ICSharpCode.Core.Presentation { @@ -39,6 +41,8 @@ namespace ICSharpCode.Core.Presentation private static Dictionary>> commandBindingsUpdateHandlers = new Dictionary>>(); private static Dictionary>> inputBindingsUpdateHandlers = new Dictionary>>(); + private static List categories = new List(); + /// /// Get reference to routed UI command by name /// @@ -110,12 +114,20 @@ namespace ICSharpCode.Core.Presentation } } + /// + /// Register input binding by specifying this binding parameters + /// + /// Input binding parameters public static void RegisterInputBinding(InputBindingInfo inputBindingInfo) { inputBidnings.Add(inputBindingInfo); CommandsRegistry.InvokeCommandBindingUpdateHandlers(inputBindingInfo.ContextName, null); } + /// + /// Unregister input binding + /// + /// Input binding parameters public static void UnregisterInputBinding(InputBindingInfo inputBindingInfo) { inputBidnings.Remove(inputBindingInfo); @@ -129,7 +141,6 @@ namespace ICSharpCode.Core.Presentation /// Context class full name /// Unregister binding assigned to specific context instance /// Routed UI command name - /// Gesture public static ICollection FindInputBindingInfos(string contextName, UIElement contextInstance, string routedCommandName) { var foundBindings = new List(); for(int i = inputBidnings.Count - 1; i >= 0; i--) { @@ -228,9 +239,9 @@ namespace ICSharpCode.Core.Presentation } /// - /// Remove all managed input bindings from input bindings collection + /// Remove all managed input bindings from /// - /// + /// Input binding cllection containing managed input bindings public static void RemoveManagedInputBindings(InputBindingCollection inputBindingCollection) { for(int i = inputBindingCollection.Count - 1; i >= 0; i--) { if(inputBindingCollection[i] is ManagedInputBinding) { @@ -239,10 +250,18 @@ namespace ICSharpCode.Core.Presentation } } + /// + /// Register command binding by specifying command binding parameters + /// + /// Command binding parameters public static void RegisterCommandBinding(CommandBindingInfo commandBindingInfo) { commandBindings.Add(commandBindingInfo); } + /// + /// Unregister command binding + /// + /// Command binding parameters public static void UnregisterCommandBinding(CommandBindingInfo commandBindingInfo) { commandBindings.Remove(commandBindingInfo); } @@ -331,9 +350,9 @@ namespace ICSharpCode.Core.Presentation } /// - /// Remove all managed command bindungs from command bindings collection + /// Remove all managed command bindings from /// - /// + /// Command binding cllection containing managed input bindings public static void RemoveManagedCommandBindings(CommandBindingCollection commandBindingsCollection) { for(int i = commandBindingsCollection.Count - 1; i >= 0; i--) { if(commandBindingsCollection[i] is ManagedCommandBinding) { @@ -343,9 +362,9 @@ namespace ICSharpCode.Core.Presentation } /// - /// Load all registered commands in addin + /// Load all registered commands in add-in /// - /// Addin + /// Add-in public static void LoadAddinCommands(AddIn addIn) { foreach(var binding in commandBindings) { if(binding.AddIn != addIn) continue; @@ -363,7 +382,8 @@ namespace ICSharpCode.Core.Presentation } /// - /// Load command + /// Register command object (either instance of or ) + /// which can be identified by command name /// /// Command name /// Command instance @@ -379,7 +399,7 @@ namespace ICSharpCode.Core.Presentation } /// - /// Load context + /// Register binding owner instance which can be identified by unique name /// /// Context class full name /// Context class instance @@ -479,5 +499,46 @@ namespace ICSharpCode.Core.Presentation return gestures; } + + /// + /// Register input binding category + /// + /// Format: + /// , - Separates categories + /// / - Describes hierarchy meaning category to the left is child of the category to the right + /// + /// parent/child + /// + /// String representing list of categories. + /// Returns list of categories which can be assigned to input binding + public static List RegisterInputBindingCategories(string categoriesString) { + var registeredCategories = new List(); + + // Split categories + var categoryPaths = Regex.Split(categoriesString, @"\s*\,\s*"); + foreach(var categoryPath in categoryPaths) { + // Split category path + var pathEntries = Regex.Split(categoryPath, @"\s*\/\s*").ToList(); + + var accumulatedPath = ""; + InputBindingCategory parentCategory = null; + + // In a loop create category hierarchy specified in a path + foreach(var categoryPathEntry in pathEntries) { + accumulatedPath += "/" + categoryPathEntry; + var matchingCategory = categories.FirstOrDefault(c => c.Path == accumulatedPath); + if(matchingCategory == null) { + matchingCategory = new InputBindingCategory(categoryPathEntry, parentCategory); + matchingCategory.Path = accumulatedPath; + categories.Add(matchingCategory); + } + parentCategory = matchingCategory; + } + + registeredCategories.Add(parentCategory); + } + + return registeredCategories; + } } } diff --git a/src/Main/ICSharpCode.Core.Presentation/CommandsService/CommandsService.cs b/src/Main/ICSharpCode.Core.Presentation/CommandsService/CommandsService.cs index 206b9e73bd..698c9e5745 100644 --- a/src/Main/ICSharpCode.Core.Presentation/CommandsService/CommandsService.cs +++ b/src/Main/ICSharpCode.Core.Presentation/CommandsService/CommandsService.cs @@ -70,7 +70,7 @@ namespace ICSharpCode.Core.Presentation } if(!string.IsNullOrEmpty(desc.Category)) { - inputBindingInfo.CategoryName = desc.Category; + inputBindingInfo.Categories.AddRange(CommandsRegistry.RegisterInputBindingCategories(desc.Category)); } CommandsRegistry.RegisterInputBinding(inputBindingInfo); @@ -96,7 +96,7 @@ namespace ICSharpCode.Core.Presentation } if(!string.IsNullOrEmpty(desc.Category)) { - inputBindingInfo.CategoryName = desc.Category; + inputBindingInfo.Categories.AddRange(CommandsRegistry.RegisterInputBindingCategories(desc.Category)); } CommandsRegistry.RegisterInputBinding(inputBindingInfo); diff --git a/src/Main/ICSharpCode.Core.Presentation/CommandsService/InputBindingCategory.cs b/src/Main/ICSharpCode.Core.Presentation/CommandsService/InputBindingCategory.cs new file mode 100644 index 0000000000..398a37502e --- /dev/null +++ b/src/Main/ICSharpCode.Core.Presentation/CommandsService/InputBindingCategory.cs @@ -0,0 +1,47 @@ +using System; + +namespace ICSharpCode.Core.Presentation +{ + /// + /// Stores input binding category description + /// + public class InputBindingCategory + { + /// + /// Creates new instance of + /// + /// Category name + /// Parent category (null - root level category) + public InputBindingCategory(string name, InputBindingCategory parentCategory) + { + Name = name; + ParentCategory = parentCategory; + } + + /// + /// Category name + /// + public string Name + { + get; set; + } + + /// + /// Reference to parent category + /// + public InputBindingCategory ParentCategory + { + get; set; + } + + /// + /// Category path is used to specify hierarchical category position + /// + /// Format: + /// /category/subcategory + /// + internal string Path { + get; set; + } + } +} diff --git a/src/Main/ICSharpCode.Core.Presentation/CommandsService/InputBindingInfo.cs b/src/Main/ICSharpCode.Core.Presentation/CommandsService/InputBindingInfo.cs index 2a728bf7c8..1e0cb6468e 100644 --- a/src/Main/ICSharpCode.Core.Presentation/CommandsService/InputBindingInfo.cs +++ b/src/Main/ICSharpCode.Core.Presentation/CommandsService/InputBindingInfo.cs @@ -1,6 +1,7 @@ using System; using System.Windows; using System.Windows.Input; +using System.Collections.Generic; namespace ICSharpCode.Core.Presentation { @@ -13,6 +14,7 @@ namespace ICSharpCode.Core.Presentation public InputBindingInfo() { ContextName = CommandsRegistry.DefaultContextName; + Categories = new List(); } /// @@ -90,8 +92,8 @@ namespace ICSharpCode.Core.Presentation get; set; } - public string CategoryName { - get; set; + public List Categories { + get; private set; } } } diff --git a/src/Main/ICSharpCode.Core.Presentation/ICSharpCode.Core.Presentation.csproj b/src/Main/ICSharpCode.Core.Presentation/ICSharpCode.Core.Presentation.csproj index 4cc38cb758..f82cac30b8 100644 --- a/src/Main/ICSharpCode.Core.Presentation/ICSharpCode.Core.Presentation.csproj +++ b/src/Main/ICSharpCode.Core.Presentation/ICSharpCode.Core.Presentation.csproj @@ -71,6 +71,7 @@ + diff --git a/src/Main/ICSharpCode.Core.Presentation/Menu/MenuCommand.cs b/src/Main/ICSharpCode.Core.Presentation/Menu/MenuCommand.cs index 91a044dd32..cd875ce082 100644 --- a/src/Main/ICSharpCode.Core.Presentation/Menu/MenuCommand.cs +++ b/src/Main/ICSharpCode.Core.Presentation/Menu/MenuCommand.cs @@ -153,7 +153,7 @@ namespace ICSharpCode.Core.Presentation var shortcut = codon.Properties["shortcut"]; var inputBindingInfo = new InputBindingInfo(); inputBindingInfo.AddIn = codon.AddIn; - inputBindingInfo.CategoryName = "Menu items"; + inputBindingInfo.Categories.AddRange(CommandsRegistry.RegisterInputBindingCategories("Menu Items")); inputBindingInfo.ContextName = CommandsRegistry.DefaultContextName; inputBindingInfo.RoutedCommandName = routedCommandName; inputBindingInfo.Gestures = (InputGestureCollection)new InputGestureCollectionConverter().ConvertFromInvariantString(codon.Properties["gestures"]);