diff --git a/Filtration/Extensions/EnumerationExtension.cs b/Filtration.Common/Extensions/EnumerationExtension.cs similarity index 94% rename from Filtration/Extensions/EnumerationExtension.cs rename to Filtration.Common/Extensions/EnumerationExtension.cs index 3498157..93630bd 100644 --- a/Filtration/Extensions/EnumerationExtension.cs +++ b/Filtration.Common/Extensions/EnumerationExtension.cs @@ -3,9 +3,9 @@ using System.ComponentModel; using System.Linq; using System.Windows.Markup; -namespace Filtration.Extensions +namespace Filtration.Common.Extensions { - internal class EnumerationExtension : MarkupExtension + public class EnumerationExtension : MarkupExtension { private Type _enumType; diff --git a/Filtration/Extensions/HyperlinkExtensions.cs b/Filtration.Common/Extensions/HyperlinkExtensions.cs similarity index 97% rename from Filtration/Extensions/HyperlinkExtensions.cs rename to Filtration.Common/Extensions/HyperlinkExtensions.cs index fd46841..140236c 100644 --- a/Filtration/Extensions/HyperlinkExtensions.cs +++ b/Filtration.Common/Extensions/HyperlinkExtensions.cs @@ -4,7 +4,7 @@ using System.Windows; using System.Windows.Documents; using System.Windows.Navigation; -namespace Filtration.Extensions +namespace Filtration.Common.Extensions { public static class HyperlinkExtensions { diff --git a/Filtration.Common/Filtration.Common.csproj b/Filtration.Common/Filtration.Common.csproj index 13f3165..38b47e8 100644 --- a/Filtration.Common/Filtration.Common.csproj +++ b/Filtration.Common/Filtration.Common.csproj @@ -75,6 +75,8 @@ + + diff --git a/Filtration.ItemFilterPreview.Tests/Services/TestItemFilterProcessor.cs b/Filtration.ItemFilterPreview.Tests/Services/TestItemFilterProcessor.cs index 2a20555..6d8296c 100644 --- a/Filtration.ItemFilterPreview.Tests/Services/TestItemFilterProcessor.cs +++ b/Filtration.ItemFilterPreview.Tests/Services/TestItemFilterProcessor.cs @@ -67,6 +67,7 @@ namespace Filtration.ItemFilterPreview.Tests.Services } [Test] + [Ignore("Outdated item filter")] public void ProcessItemsAgainstItemFilterScript_IntegrationTest() { //Arrange @@ -102,6 +103,7 @@ namespace Filtration.ItemFilterPreview.Tests.Services } [Test] + [Ignore("Outdated item filter")] public void ProcessItemsAgainstItemFilterScript_IntegrationTest_10Items() { //Arrange diff --git a/Filtration.ObjectModel.Tests/Filtration.ObjectModel.Tests.csproj b/Filtration.ObjectModel.Tests/Filtration.ObjectModel.Tests.csproj index 69dd6ef..62cfed3 100644 --- a/Filtration.ObjectModel.Tests/Filtration.ObjectModel.Tests.csproj +++ b/Filtration.ObjectModel.Tests/Filtration.ObjectModel.Tests.csproj @@ -66,6 +66,9 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Permanent + Temporary + + + + + + diff --git a/Filtration/App.xaml.cs b/Filtration/App.xaml.cs index 6690e58..624f22e 100644 --- a/Filtration/App.xaml.cs +++ b/Filtration/App.xaml.cs @@ -46,6 +46,12 @@ namespace Filtration cfg.ConstructServicesUsing(_container.Resolve); cfg.CreateMap().ConstructUsingServiceLocator(); cfg.CreateMap().ReverseMap(); + cfg.CreateMap().ReverseMap(); + cfg.CreateMap().ReverseMap(); + cfg.CreateMap().ReverseMap(); + cfg.CreateMap().ReverseMap(); + cfg.CreateMap().ReverseMap(); + cfg.CreateMap().ReverseMap(); cfg.CreateMap(); }); diff --git a/Filtration/Converters/AvailableThemeComponentsConverter.cs b/Filtration/Converters/AvailableThemeComponentsConverter.cs index 0d4b3f3..e4c5d61 100644 --- a/Filtration/Converters/AvailableThemeComponentsConverter.cs +++ b/Filtration/Converters/AvailableThemeComponentsConverter.cs @@ -16,7 +16,7 @@ namespace Filtration.Converters var themeComponentsList = values[0] as ThemeComponentCollection; if (themeComponentsList == null || themeComponentsList.Count == 0) return null; - var blockItem = values[1] as ColorBlockItem; + var blockItem = values[1] as BlockItemBase; if (blockItem == null) return null; ThemeComponentType themeComponentType; @@ -33,6 +33,26 @@ namespace Filtration.Converters { themeComponentType = ThemeComponentType.BorderColor; } + else if (blockItem.GetType() == typeof(FontSizeBlockItem)) + { + themeComponentType = ThemeComponentType.FontSize; + } + else if (blockItem.GetType() == typeof(SoundBlockItem) || blockItem.GetType() == typeof(PositionalSoundBlockItem)) + { + themeComponentType = ThemeComponentType.AlertSound; + } + else if (blockItem.GetType() == typeof(CustomSoundBlockItem)) + { + themeComponentType = ThemeComponentType.CustomSound; + } + else if (blockItem.GetType() == typeof(MapIconBlockItem)) + { + themeComponentType = ThemeComponentType.Icon; + } + else if (blockItem.GetType() == typeof(PlayEffectBlockItem)) + { + themeComponentType = ThemeComponentType.Effect; + } else { return null; diff --git a/Filtration/Converters/IconShapeToSourceConverter.cs b/Filtration/Converters/IconShapeToSourceConverter.cs new file mode 100644 index 0000000..c727045 --- /dev/null +++ b/Filtration/Converters/IconShapeToSourceConverter.cs @@ -0,0 +1,37 @@ +using Filtration.ObjectModel.Enums; +using System; +using System.Globalization; +using System.Windows.Data; + +namespace Filtration.Converters +{ + internal class IconShapeToSourceConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var iconShape = (IconShape)(int)value; + switch (iconShape) + { + case IconShape.Circle: + return "/Filtration;component/Resources/DropIcons/Circle.png"; + case IconShape.Diamond: + return "/Filtration;component/Resources/DropIcons/Diamond.png"; + case IconShape.Hexagon: + return "/Filtration;component/Resources/DropIcons/Hexagon.png"; + case IconShape.Square: + return "/Filtration;component/Resources/DropIcons/Square.png"; + case IconShape.Star: + return "/Filtration;component/Resources/DropIcons/Star.png"; + case IconShape.Triangle: + return "/Filtration;component/Resources/DropIcons/Triangle.png"; + } + + return "/Filtration;component/Resources/DropIcons/NoIcon.png"; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Filtration/Converters/SizeColorToRectConverter.cs b/Filtration/Converters/SizeColorToRectConverter.cs new file mode 100644 index 0000000..0be1814 --- /dev/null +++ b/Filtration/Converters/SizeColorToRectConverter.cs @@ -0,0 +1,33 @@ +using Filtration.ObjectModel.Enums; +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace Filtration.Converters +{ + internal class SizeColorToRectConverter : IMultiValueConverter + { + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + var size = (int)(values[0]); + var color = (int)(values[1]); + + if (size < 0 || color < 0) + return new Rect(0, 0, 0, 0); + + Rect cropArea = new Rect(); + cropArea.Width = 64; + cropArea.Height = 64; + cropArea.X = 0 + size * 64; + cropArea.Y = 0 + color * 64; + + return cropArea; + } + + public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/Filtration/Filtration.csproj b/Filtration/Filtration.csproj index 4e0f402..6a1ac98 100644 --- a/Filtration/Filtration.csproj +++ b/Filtration/Filtration.csproj @@ -167,11 +167,11 @@ + + - - @@ -190,6 +190,9 @@ EditableListBoxControl.xaml + + ImageComboBoxControl.xaml + ItemPreviewControl.xaml @@ -230,6 +233,10 @@ Designer MSBuild:Compile + + Designer + MSBuild:Compile + Designer MSBuild:Compile @@ -538,6 +545,13 @@ + + + + + + + diff --git a/Filtration/Resources/DropIcons/Circle.png b/Filtration/Resources/DropIcons/Circle.png new file mode 100644 index 0000000..7b59891 Binary files /dev/null and b/Filtration/Resources/DropIcons/Circle.png differ diff --git a/Filtration/Resources/DropIcons/Diamond.png b/Filtration/Resources/DropIcons/Diamond.png new file mode 100644 index 0000000..ab917dd Binary files /dev/null and b/Filtration/Resources/DropIcons/Diamond.png differ diff --git a/Filtration/Resources/DropIcons/Hexagon.png b/Filtration/Resources/DropIcons/Hexagon.png new file mode 100644 index 0000000..549fa94 Binary files /dev/null and b/Filtration/Resources/DropIcons/Hexagon.png differ diff --git a/Filtration/Resources/DropIcons/NoIcon.png b/Filtration/Resources/DropIcons/NoIcon.png new file mode 100644 index 0000000..fb56f36 Binary files /dev/null and b/Filtration/Resources/DropIcons/NoIcon.png differ diff --git a/Filtration/Resources/DropIcons/Square.png b/Filtration/Resources/DropIcons/Square.png new file mode 100644 index 0000000..9e143cf Binary files /dev/null and b/Filtration/Resources/DropIcons/Square.png differ diff --git a/Filtration/Resources/DropIcons/Star.png b/Filtration/Resources/DropIcons/Star.png new file mode 100644 index 0000000..9158e90 Binary files /dev/null and b/Filtration/Resources/DropIcons/Star.png differ diff --git a/Filtration/Resources/DropIcons/Triangle.png b/Filtration/Resources/DropIcons/Triangle.png new file mode 100644 index 0000000..3d8d6d9 Binary files /dev/null and b/Filtration/Resources/DropIcons/Triangle.png differ diff --git a/Filtration/Resources/ItemBaseTypes.txt b/Filtration/Resources/ItemBaseTypes.txt index b056064..c978d72 100644 --- a/Filtration/Resources/ItemBaseTypes.txt +++ b/Filtration/Resources/ItemBaseTypes.txt @@ -1,5 +1,6 @@ A Mother's Parting Gift Abandoned Wealth +Aberrant Fossil Abyssal Axe Abyssal Cry Abyssal Sceptre @@ -10,6 +11,7 @@ Added Cold Damage Support Added Fire Damage Support Added Lightning Damage Support Additional Accuracy Support +Aetheric Fossil Agate Amulet Albino Rhoa Feather Alchemy Shard @@ -111,9 +113,11 @@ Battle Sword Bazaar Map Beach Map Bear Trap +Beauty Through Death Behemoth Mace Belfry Map Bestel's Manuscript +Bestiary Orb Beyond Leaguestone Binding Shard Birth of the Three @@ -131,6 +135,7 @@ Blast Rain Blessed Orb Blessing of Chayula Blessing of Esh +Blessing of God Blessing of Tul Blessing of Uul-Netol Blessing of Xoph @@ -146,6 +151,7 @@ Blood Raiment Blood Sceptre Bloodlines Leaguestone Bloodlust Support +Bloodstained Fossil Blue Pearl Amulet Blunt Arrow Quiver Boarding Axe @@ -159,9 +165,11 @@ Bone Helmet Bone Offering Bone Spirit Shield Bonespire Talisman +Boon of the First Ones Boot Blade Boot Knife Bottled Storm +Bound Fossil Boundless Realms Bowyer's Dream Branded Kite Shield @@ -244,6 +252,7 @@ Chaos Orb Chaos Shard Chaotic Disposition Charged Dash +Charged Traps Support Chateau Map Chayula's Breachstone Chest Splitter @@ -290,6 +299,7 @@ Conjurer Boots Conjurer Gloves Conjurer's Vestment Conquest Chainmail +Consecrated Path Conservatory Map Contagion Controlled Destruction Support @@ -306,6 +316,7 @@ Core Map Coronal Leather Coronal Maul Corroded Blade +Corroded Fossil Corroded Tower Shield Corrugated Buckler Corsair Sword @@ -385,6 +396,7 @@ Defiled Cathedral Map Deicide Mask Demon Dagger Demon's Horn +Dense Fossil Desecrate Desert Brigandine Desert Map @@ -460,6 +472,8 @@ Emperor of Purity Emperor's Luck Empower Support Enameled Buckler +Enchanted Fossil +Encrusted Fossil Endurance Charge on Melee Stun Support Enduring Cry Enfeeble @@ -492,6 +506,7 @@ Exalted Orb Exalted Shard Excavation Map Explosive Arrow +Explosive Trap Exquisite Blade Exquisite Leather Eye Gouger @@ -504,6 +519,7 @@ Ezomyte Dagger Ezomyte Spiked Shield Ezomyte Staff Ezomyte Tower Shield +Faceted Fossil Factory Map Fancy Foil Fangjaw Talisman @@ -531,8 +547,10 @@ Flame Dash Flame Surge Flame Totem Flameblast +Flamethrower Trap Flammability Flanged Mace +Flashpowder Keg Flaying Knife Flesh Offering Fleshripper @@ -540,11 +558,13 @@ Flicker Strike Flooded Mine Map Fluted Bascinet Footman Sword +Forbidden Power Forge of the Phoenix Map Fork Support Fortify Support Fossilised Spirit Shield Foul Staff +Fractured Fossil Fragment of the Chimera Fragment of the Hydra Fragment of the Minotaur @@ -554,6 +574,7 @@ Freezing Pulse Frenzy Fright Claw Fright Maul +Frigid Fossil Frontier Leather Frost Blades Frost Bomb @@ -583,6 +604,7 @@ Giant Mana Flask Gift of the Gemling Queen Gilded Axe Gilded Buckler +Gilded Fossil Gilded Sallet Girded Tower Shield Glacial Cascade @@ -595,6 +617,7 @@ Glassblower's Bauble Glimmer of Hope Glorious Leather Glorious Plate +Glyphic Fossil Gnarled Branch Goat's Horn Goathide Boots @@ -654,6 +677,7 @@ Harbinger's Orb Harbinger's Shard Harlequin Mask Harmonic Spirit Shield +Harmony of Souls Harpy Rapier Haste Hatred @@ -667,8 +691,10 @@ Heavy Quiver Heavy Strike Hellion's Paw Her Mask +Herald of Agony Herald of Ash Herald of Ice +Herald of Purity Herald of Thunder Heterochromia Hexclaw Talisman @@ -676,6 +702,7 @@ Highborn Bow Highborn Staff Highland Blade Hinekora's Hair +Hollow Fossil Holy Chainmail Hook Sword Hope @@ -705,6 +732,7 @@ Ignite Proliferation Support Imbued Wand Immolate Support Immortal Call +Immortal Resolve Imp Dagger Imperial Bow Imperial Buckler @@ -712,6 +740,7 @@ Imperial Claw Imperial Maul Imperial Skean Imperial Staff +Imprinted Bestiary Orb Incinerate Increased Area of Effect Support Increased Critical Damage Support @@ -749,6 +778,7 @@ Jade Chopper Jade Flask Jade Hatchet Jagged Foil +Jagged Fossil Jagged Maul Jasper Axe Jasper Chopper @@ -813,6 +843,7 @@ Light Quiver Lighthouse Map Lightning Arrow Lightning Penetration Support +Lightning Spire Trap Lightning Strike Lightning Tendrils Lightning Trap @@ -832,6 +863,7 @@ Lordly Plate Loricated Ringmail Lost Worlds Loyalty +Lucent Fossil Lucky Connections Lucky Deck Lunaris Circlet @@ -873,6 +905,7 @@ Merciless Armament Mesa Map Mesh Boots Mesh Gloves +Metallic Fossil Miasmeter Midnight Blade Might is Right @@ -923,6 +956,7 @@ Muttering Essence of Woe Nailed Fist Necromancer Circlet Necromancer Silks +Necromancy Net Necropolis Map Nemesis Leaguestone Nightmare Bascinet @@ -983,12 +1017,13 @@ Penetrating Arrow Quiver Peninsula Map Perandus Coin Perandus Leaguestone +Perfect Fossil +Perfection Pernarch Petrified Club Phantasmagoria Map Phantom Mace Phase Run -Physical Projectile Attack Damage Support Physical to Lightning Support Pier Map Pierce Support @@ -1018,19 +1053,29 @@ Port Map Portal Portal Scroll Portal Shredder +Potent Alchemical Resonator +Potent Chaotic Resonator Power Charge On Critical Support Power Siphon +Powerful Alchemical Resonator +Powerful Chaotic Resonator Praetor Crown Precinct Map Prehistoric Claw Pride Before the Fall Primal Skull Talisman +Prime Alchemical Resonator +Prime Chaotic Resonator Primeval Rapier +Primitive Alchemical Resonator +Primitive Chaotic Resonator Primitive Staff Primordial Pool Map Primordial Staff +Prismatic Fossil Prismatic Jewel Prismatic Ring +Pristine Fossil Profane Wand Projectile Weakness Promenade Map @@ -1082,7 +1127,10 @@ Regal Orb Regal Shard Regicide Mask Reinforced Greaves +Reinforced Iron Net Reinforced Kite Shield +Reinforced Rope Net +Reinforced Steel Net Reinforced Tower Shield Rejuvenation Totem Relic Chambers Map @@ -1141,6 +1189,7 @@ Sambar Sceptre Samite Gloves Samite Helmet Samite Slippers +Sanctified Fossil Sanctified Life Flask Sanctified Mana Flask Sand of Eternity @@ -1157,7 +1206,9 @@ Scarlet Round Shield Scholar Boots Scholar of the Seas Scholar's Robe +Scorched Fossil Scorching Ray +Scourge Arrow Screaming Essence Screaming Essence of Anger Screaming Essence of Anguish @@ -1185,6 +1236,7 @@ Scroll of Wisdom Searching Eye Jewel Searing Bond Secutor Helm +Seismic Trap Sekhem Sekhema Feather Sentinel Jacket @@ -1195,6 +1247,7 @@ Serpentscale Boots Serpentscale Gauntlets Serrated Arrow Quiver Serrated Foil +Serrated Fossil Shabby Jerkin Shackled Boots Shadow Axe @@ -1204,7 +1257,6 @@ Shagreen Gloves Shagreen Tower Shield Shaper's Orb Shaper's Orb (Tier 1) -Shaper's Orb (Tier 10) Shaper's Orb (Tier 2) Shaper's Orb (Tier 3) Shaper's Orb (Tier 4) @@ -1213,6 +1265,7 @@ Shaper's Orb (Tier 6) Shaper's Orb (Tier 7) Shaper's Orb (Tier 8) Shaper's Orb (Tier 9) +Shaper's Orb (Tier 10) Shard of Fate Sharkskin Boots Sharkskin Gloves @@ -1248,6 +1301,7 @@ Shrieking Essence of Woe Shrieking Essence of Wrath Shrieking Essence of Zeal Shrine Map +Shuddering Fossil Siege Axe Siege Ballista Siege Helmet @@ -1264,8 +1318,12 @@ Silver Coin Silver Flask Silver Key Silver Locket +Simple Iron Net Simple Robe +Simple Rope Net +Simple Steel Net Sinner Tricorne +Siphoning Trap Skean Skinning Knife Slaughter Knife @@ -1278,6 +1336,7 @@ Small Hybrid Flask Small Life Flask Small Mana Flask Smallsword +Smite Smoke Mine Sniper Bow Solar Maul @@ -1292,6 +1351,7 @@ Sovereign Spiked Shield Spark Sparkling Claw Spectral Axe +Spectral Shield Throw Spectral Sword Spectral Throw Spell Cascade Support @@ -1341,6 +1401,7 @@ Stibnite Flask Stiletto Stone Axe Stone Hammer +Stone of Passage Storm Barrier Support Storm Blade Storm Burst @@ -1349,6 +1410,9 @@ Strand Map Strapped Boots Strapped Leather Strapped Mitts +Strong Iron Net +Strong Rope Net +Strong Steel Net Struck by Lightning Studded Belt Studded Round Shield @@ -1359,8 +1423,10 @@ Sulphur Vents Map Summit Map Summon Chaos Golem Summon Flame Golem +Summon Holy Relic Summon Ice Golem Summon Lightning Golem +Summon Phantasm on Kill Support Summon Raging Spirit Summon Skeleton Summon Stone Golem @@ -1375,8 +1441,10 @@ Sweep Swift Affliction Support Talisman Leaguestone Talon Axe +Tangled Fossil Tarnished Spirit Shield Teak Round Shield +Tectonic Slam Tempered Foil Tempest Leaguestone Tempest Shield @@ -1386,38 +1454,50 @@ Tenderizer Terrace Map Terror Claw Terror Maul +Thaumaturgical Net Thaumetic Emblem Thaumetic Sulphite +The Admirer The Aesthete The Arena Champion +The Army of Blood The Artist The Avenger The Battle Born +The Beast The Betrayal The Black Flag The Blazing Fire The Body +The Breach The Brittle Emperor +The Cacophony The Calling The Carrion Crow The Cartographer The Cataclysm The Catalyst The Celestial Justicar +The Celestial Stone The Chains that Bind The Coming Storm The Conduit The Cursed King The Dapper Prodigy The Dark Mage +The Darkest Dream +The Deceiver The Demoness The Devastator The Doctor The Doppelganger The Dragon The Dragon's Heart +The Dreamer +The Dreamland The Drunken Aristocrat The Encroaching Darkness +The Endless Darkness The Endurance The Enlightened The Ethereal @@ -1425,6 +1505,7 @@ The Explorer The Eye of Desire The Eye of Fury The Eye of the Dragon +The Fathomless Depths The Feast The Fiend The Fletcher @@ -1437,15 +1518,20 @@ The Garish Power The Gemcutter The Gentleman The Gladiator +The Hale Heart The Harvester The Hermit The Hoarder The Hunger The Immortal The Incantation +The Innocent The Inoculated +The Insatiable The Inventor +The Iron Bard The Jester +The Jeweller's Boon The King's Blade The King's Heart The Last One Standing @@ -1454,9 +1540,12 @@ The Lion The Lord in Black The Lover The Lunaris Priestess +The Master +The Mayor The Mercenary The Metalsmith's Gift The Oath +The Obscured The Offering The One With All The Opulent @@ -1466,17 +1555,23 @@ The Penitent The Poet The Polymath The Porcupine +The Price of Protection +The Professor +The Puzzle The Queen The Rabid Rhoa The Realm The Risk +The Rite of Elements The Road to Power The Ruthless Ceinture The Saint's Treasure +The Samurai's Eye The Scarred Meadow The Scavenger The Scholar The Sephirot +The Shaper's Key The Sigil The Siren The Soul @@ -1490,14 +1585,18 @@ The Sun The Surgeon The Surveyor The Survivalist +The Sword King's Salute The Teardrop The Thaumaturgist The Throne The Tower The Traitor The Trial +The Twilight Moon The Twins The Tyrant +The Undaunted +The Undisputed The Union The Valkyrie The Valley of Steel Boxes @@ -1508,11 +1607,14 @@ The Warden The Warlord The Watcher The Web +The Wilted Rose The Wind +The Witch The Wolf The Wolf's Shadow The Wolven King's Bite The Wolverine +The World Eater The Wrath The Wretched Thicket Bow @@ -1523,6 +1625,7 @@ Thorn Rapier Three Faces in the Dark Three Hands Talisman Three Rat Talisman +Three Voices Thresher Claw Throat Stabber Thunderous Skies @@ -1532,6 +1635,7 @@ Tiger's Paw Timber Axe Time-Lost Relic Timeworn Claw +Timeworn Reliquary Key Titan Gauntlets Titan Greaves Titanium Spirit Shield @@ -1547,6 +1651,7 @@ Torture Chamber Map Totemic Maul Tower Key Tower Map +Toxic Rain Toxic Sewer Map Tranquillity Transmutation Shard @@ -1587,9 +1692,12 @@ Unset Ring Unshaping Orb Ursine Pelt Uul-Netol's Breachstone +Vaal Ancestral Warchief Vaal Arc Vaal Axe Vaal Blade +Vaal Blade Vortex +Vaal Blight Vaal Breach Vaal Buckler Vaal Burning Arrow @@ -1600,6 +1708,7 @@ Vaal Cyclone Vaal Detonate Dead Vaal Discipline Vaal Double Strike +Vaal Earthquake Vaal Fireball Vaal Flameblast Vaal Gauntlets @@ -1612,6 +1721,9 @@ Vaal Haste Vaal Hatchet Vaal Ice Nova Vaal Immortal Call +Vaal Impurity of Fire +Vaal Impurity of Ice +Vaal Impurity of Lightning Vaal Lightning Strike Vaal Lightning Trap Vaal Lightning Warp @@ -1640,7 +1752,17 @@ Vault Map Velvet Gloves Velvet Slippers Vengeance +Vial of Awakening +Vial of Consequence +Vial of Dominance +Vial of Fate Vial Of Power +Vial of Sacrifice +Vial of Summoning +Vial of the Ghost +Vial of the Ritual +Vial of Transcendence +Vicious Projectiles Support Vigilant Strike Vile Staff Vile Toxins Support @@ -1718,6 +1840,7 @@ Wild Leather Wild Strike Wings of Vastiri Wither +Withering Touch Support Wolf Pelt Woodful Staff Woodsplitter diff --git a/Filtration/Resources/ItemClasses.txt b/Filtration/Resources/ItemClasses.txt index fd87671..d38526b 100644 --- a/Filtration/Resources/ItemClasses.txt +++ b/Filtration/Resources/ItemClasses.txt @@ -9,6 +9,7 @@ Bows Claws Currency Daggers +Delve Stackable Currency Divination Card Fishing Rods Flasks @@ -16,6 +17,7 @@ Gems Gloves Helmets Hybrid Flasks +Incursion Item Jewel Labyrinth Item Labyrinth Map Item diff --git a/Filtration/UserControls/BlockItemControl.xaml b/Filtration/UserControls/BlockItemControl.xaml index c9dfba3..1a0d699 100644 --- a/Filtration/UserControls/BlockItemControl.xaml +++ b/Filtration/UserControls/BlockItemControl.xaml @@ -7,7 +7,7 @@ xmlns:commonConverters="clr-namespace:Filtration.Common.Converters;assembly=Filtration.Common" xmlns:blockItemBaseTypes="clr-namespace:Filtration.ObjectModel.BlockItemBaseTypes;assembly=Filtration.ObjectModel" xmlns:blockItemTypes="clr-namespace:Filtration.ObjectModel.BlockItemTypes;assembly=Filtration.ObjectModel" - xmlns:extensions="clr-namespace:Filtration.Extensions" + xmlns:extensions="clr-namespace:Filtration.Common.Extensions;assembly=Filtration.Common" xmlns:enums="clr-namespace:Filtration.ObjectModel.Enums;assembly=Filtration.ObjectModel" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:views="clr-namespace:Filtration.Views" @@ -75,12 +75,39 @@ - + + + + + + - + + + + + + Permanent + Temporary + + + + + + + + + + + + + @@ -100,9 +127,21 @@ - - - + + + + + + + + + + + + + + + @@ -113,6 +152,14 @@ + + + + + + + + @@ -124,8 +171,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Filtration/UserControls/BlockItemControl.xaml.cs b/Filtration/UserControls/BlockItemControl.xaml.cs index 5af5c2f..db0909c 100644 --- a/Filtration/UserControls/BlockItemControl.xaml.cs +++ b/Filtration/UserControls/BlockItemControl.xaml.cs @@ -6,6 +6,8 @@ using System.Windows; using Filtration.Annotations; using Filtration.ObjectModel; using Filtration.ObjectModel.BlockItemBaseTypes; +using Filtration.ObjectModel.Enums; +using Filtration.ObjectModel.ThemeEditor; using Filtration.Views; using GalaSoft.MvvmLight.CommandWpf; using Xceed.Wpf.Toolkit; @@ -20,10 +22,10 @@ namespace Filtration.UserControls // ReSharper disable once PossibleNullReferenceException (Content as FrameworkElement).DataContext = this; - SetBlockColorCommand = new RelayCommand(OnSetBlockColorCommmand); + SetBlockValueCommand = new RelayCommand(OnSetBlockValueCommmand); } - public RelayCommand SetBlockColorCommand { get; private set; } + public RelayCommand SetBlockValueCommand { get; private set; } public static readonly DependencyProperty BlockItemProperty = DependencyProperty.Register( "BlockItem", @@ -88,12 +90,45 @@ namespace Filtration.UserControls "ShFusing", "ShRegal", "ShVaal" }; - private void OnSetBlockColorCommmand() - { - var blockItem = BlockItem as ColorBlockItem; - if (blockItem?.ThemeComponent == null) return; + private void OnSetBlockValueCommmand() + { + var blockItemWithTheme = BlockItem as IBlockItemWithTheme; + if (blockItemWithTheme?.ThemeComponent == null) return; - blockItem.Color = blockItem.ThemeComponent.Color; + var componentType = ((IBlockItemWithTheme)BlockItem).ThemeComponent.ComponentType; + switch(componentType) + { + case ThemeComponentType.BackgroundColor: + case ThemeComponentType.BorderColor: + case ThemeComponentType.TextColor: + var colorBlockItem = BlockItem as ColorBlockItem; + colorBlockItem.Color = ((ColorThemeComponent)colorBlockItem.ThemeComponent).Color; + break; + case ThemeComponentType.FontSize: + var integerBlockItem = BlockItem as IntegerBlockItem; + integerBlockItem.Value = ((IntegerThemeComponent)integerBlockItem.ThemeComponent).Value; + break; + case ThemeComponentType.AlertSound: + var strIntBlockItem = BlockItem as StrIntBlockItem; + strIntBlockItem.Value = ((StrIntThemeComponent)strIntBlockItem.ThemeComponent).Value; + strIntBlockItem.SecondValue = ((StrIntThemeComponent)strIntBlockItem.ThemeComponent).SecondValue; + break; + case ThemeComponentType.CustomSound: + var stringBlockItem = BlockItem as StringBlockItem; + stringBlockItem.Value = ((StringThemeComponent)stringBlockItem.ThemeComponent).Value; + break; + case ThemeComponentType.Icon: + var iconBlockItem = BlockItem as IconBlockItem; + iconBlockItem.Size = ((IconThemeComponent)iconBlockItem.ThemeComponent).IconSize; + iconBlockItem.Color = ((IconThemeComponent)iconBlockItem.ThemeComponent).IconColor; + iconBlockItem.Shape = ((IconThemeComponent)iconBlockItem.ThemeComponent).IconShape; + break; + case ThemeComponentType.Effect: + var effectColorBlockItem = BlockItem as EffectColorBlockItem; + effectColorBlockItem.Color = ((EffectColorThemeComponent)effectColorBlockItem.ThemeComponent).EffectColor; + effectColorBlockItem.Temporary = ((EffectColorThemeComponent)effectColorBlockItem.ThemeComponent).Temporary; + break; + } } public event PropertyChangedEventHandler PropertyChanged; diff --git a/Filtration/UserControls/ImageComboBoxControl.xaml b/Filtration/UserControls/ImageComboBoxControl.xaml new file mode 100644 index 0000000..88b1726 --- /dev/null +++ b/Filtration/UserControls/ImageComboBoxControl.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + diff --git a/Filtration/UserControls/ImageComboBoxControl.xaml.cs b/Filtration/UserControls/ImageComboBoxControl.xaml.cs new file mode 100644 index 0000000..0921355 --- /dev/null +++ b/Filtration/UserControls/ImageComboBoxControl.xaml.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Filtration.UserControls +{ + /// + /// Interaction logic for ImageComboBoxControl.xaml + /// + public partial class ImageComboBoxControl : UserControl + { + public ImageComboBoxControl() + { + InitializeComponent(); + } + } +} diff --git a/Filtration/UserControls/ItemPreviewControl.xaml b/Filtration/UserControls/ItemPreviewControl.xaml index f9232fe..0ba9de4 100644 --- a/Filtration/UserControls/ItemPreviewControl.xaml +++ b/Filtration/UserControls/ItemPreviewControl.xaml @@ -8,7 +8,7 @@ d:DataContext="{d:DesignInstance Type=userControls:ItemPreviewControl}" d:DesignHeight="35" d:DesignWidth="170"> - + diff --git a/Filtration/UserControls/NumericFilterPredicateControl.xaml b/Filtration/UserControls/NumericFilterPredicateControl.xaml index 32ce079..8d8d03f 100644 --- a/Filtration/UserControls/NumericFilterPredicateControl.xaml +++ b/Filtration/UserControls/NumericFilterPredicateControl.xaml @@ -5,7 +5,7 @@ xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" - xmlns:extensions="clr-namespace:Filtration.Extensions" + xmlns:extensions="clr-namespace:Filtration.Common.Extensions;assembly=Filtration.Common" xmlns:userControls="clr-namespace:Filtration.UserControls" xmlns:enums="clr-namespace:Filtration.ObjectModel.Enums;assembly=Filtration.ObjectModel" mc:Ignorable="d" diff --git a/Filtration/UserControls/ThemeComponentSelectionControl.xaml b/Filtration/UserControls/ThemeComponentSelectionControl.xaml index 19d17ab..ae2f094 100644 --- a/Filtration/UserControls/ThemeComponentSelectionControl.xaml +++ b/Filtration/UserControls/ThemeComponentSelectionControl.xaml @@ -4,6 +4,7 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:userControls="clr-namespace:Filtration.UserControls" + xmlns:themeEditor="clr-namespace:Filtration.ObjectModel.ThemeEditor;assembly=Filtration.ObjectModel" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" mc:Ignorable="d" d:DataContext="{d:DesignInstance Type=userControls:ThemeComponentSelectionControl}" @@ -21,7 +22,7 @@ - + @@ -32,44 +33,59 @@ Visibility="{Binding ShowThemeComponentComboBox, Converter={StaticResource BooleanToVisibilityConverter}}"> - + - - - - - - - - - + + + + + + + + + diff --git a/Filtration/ViewModels/ItemFilterBlockViewModel.cs b/Filtration/ViewModels/ItemFilterBlockViewModel.cs index 5cf0c0c..ac2411a 100644 --- a/Filtration/ViewModels/ItemFilterBlockViewModel.cs +++ b/Filtration/ViewModels/ItemFilterBlockViewModel.cs @@ -2,14 +2,17 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Collections.Specialized; +using System.IO; using System.Linq; using System.Windows.Media; using Filtration.ObjectModel; using Filtration.ObjectModel.BlockItemBaseTypes; using Filtration.ObjectModel.BlockItemTypes; +using Filtration.ObjectModel.Enums; using Filtration.Services; using Filtration.Views; using GalaSoft.MvvmLight.CommandWpf; +using Microsoft.Win32; using Xceed.Wpf.Toolkit; namespace Filtration.ViewModels @@ -28,6 +31,7 @@ namespace Filtration.ViewModels private readonly IStaticDataService _staticDataService; private readonly IReplaceColorsViewModel _replaceColorsViewModel; private readonly MediaPlayer _mediaPlayer = new MediaPlayer(); + public static ObservableCollection _customSoundsAvailable; private bool _displaySettingsPopupOpen; private bool _isExpanded; @@ -47,6 +51,16 @@ namespace Filtration.ViewModels SwitchBlockItemsViewCommand = new RelayCommand(OnSwitchBlockItemsViewCommand); PlaySoundCommand = new RelayCommand(OnPlaySoundCommand, () => HasSound); PlayPositionalSoundCommand = new RelayCommand(OnPlayPositionalSoundCommand, () => HasPositionalSound); + PlayCustomSoundCommand = new RelayCommand(OnPlayCustomSoundCommand, () => HasCustomSound); + CustomSoundFileDialogCommand = new RelayCommand(OnCustomSoundFileDialog); + + if(_customSoundsAvailable == null || _customSoundsAvailable.Count < 1) + { + _customSoundsAvailable = new ObservableCollection { + "1maybevaluable.mp3", "2currency.mp3", "3uniques.mp3", "4maps.mp3", "5highmaps.mp3", + "6veryvaluable.mp3", "7chancing.mp3", "12leveling.mp3", "placeholder.mp3" + }; + } } public override void Initialise(IItemFilterBlockBase itemFilterBlockBase, IItemFilterScriptViewModel parentScriptViewModel) @@ -66,9 +80,16 @@ namespace Filtration.ViewModels foreach (var blockItem in itemFilterBlock.BlockItems) { blockItem.PropertyChanged += OnBlockItemChanged; + + var customSoundBlockItem = blockItem as CustomSoundBlockItem; + if (customSoundBlockItem != null) + { + if (!string.IsNullOrWhiteSpace(customSoundBlockItem.Value) && _customSoundsAvailable.IndexOf(customSoundBlockItem.Value) < 0) + { + _customSoundsAvailable.Add(customSoundBlockItem.Value); + } + } } - - base.Initialise(itemFilterBlock, parentScriptViewModel); } @@ -81,6 +102,8 @@ namespace Filtration.ViewModels public RelayCommand PlaySoundCommand { get; } public RelayCommand PlayPositionalSoundCommand { get; } public RelayCommand SwitchBlockItemsViewCommand { get; } + public RelayCommand CustomSoundFileDialogCommand { get; } + public RelayCommand PlayCustomSoundCommand { get; } public IItemFilterBlock Block { get; private set; } @@ -159,7 +182,12 @@ namespace Filtration.ViewModels typeof (CorruptedBlockItem), typeof (ElderItemBlockItem), typeof (ShaperItemBlockItem), - typeof (ShapedMapBlockItem) + typeof (MapTierBlockItem), + typeof (ShapedMapBlockItem), + typeof (ElderMapBlockItem), + typeof (GemLevelBlockItem), + typeof (StackSizeBlockItem), + typeof (HasExplicitModBlockItem) }; public List AudioVisualBlockItemTypesAvailable => new List @@ -169,7 +197,11 @@ namespace Filtration.ViewModels typeof (BorderColorBlockItem), typeof (FontSizeBlockItem), typeof (SoundBlockItem), - typeof (PositionalSoundBlockItem) + typeof (PositionalSoundBlockItem), + typeof (DisableDropSoundBlockItem), + typeof (MapIconBlockItem), + typeof (PlayEffectBlockItem), + typeof (CustomSoundBlockItem) }; public bool BlockEnabled @@ -205,13 +237,20 @@ namespace Filtration.ViewModels public ObservableCollection AvailableColors => PathOfExileColors.DefaultColors; + public ObservableCollection CustomSoundsAvailable => _customSoundsAvailable; + public Color DisplayTextColor => Block.DisplayTextColor; public Color DisplayBackgroundColor => Block.DisplayBackgroundColor; public Color DisplayBorderColor => Block.DisplayBorderColor; public double DisplayFontSize => Block.DisplayFontSize/1.8; - + public int DisplayIconSize => Block.DisplayIconSize; + public int DisplayIconColor => Block.DisplayIconColor; + public int DisplayIconShape => Block.DisplayIconShape; + public Color DisplayEffectColor => Block.DisplayEffectColor; + public bool HasSound => Block.HasBlockItemOfType(); public bool HasPositionalSound => Block.HasBlockItemOfType(); + public bool HasCustomSound => Block.HasBlockItemOfType(); public bool HasAudioVisualBlockItems => AudioVisualBlockItems.Any(); @@ -419,7 +458,16 @@ namespace Filtration.ViewModels { IsDirty = true; } - + var customSoundBlockItem = sender as CustomSoundBlockItem; + if (customSoundBlockItem != null) + { + if (!string.IsNullOrWhiteSpace(customSoundBlockItem.Value) && _customSoundsAvailable.IndexOf(customSoundBlockItem.Value) < 0) + { + _customSoundsAvailable.Add(customSoundBlockItem.Value); + } + RaisePropertyChanged(nameof(CustomSoundsAvailable)); + } + Block.IsEdited = true; //if (sender is IAudioVisualBlockItem) //{ RefreshBlockPreview(); @@ -432,7 +480,13 @@ namespace Filtration.ViewModels RaisePropertyChanged(nameof(DisplayBackgroundColor)); RaisePropertyChanged(nameof(DisplayBorderColor)); RaisePropertyChanged(nameof(DisplayFontSize)); + RaisePropertyChanged(nameof(DisplayIconSize)); + RaisePropertyChanged(nameof(DisplayIconColor)); + RaisePropertyChanged(nameof(DisplayIconShape)); + RaisePropertyChanged(nameof(DisplayEffectColor)); RaisePropertyChanged(nameof(HasSound)); + RaisePropertyChanged(nameof(HasPositionalSound)); + RaisePropertyChanged(nameof(HasCustomSound)); } private void OnBlockItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) @@ -442,5 +496,53 @@ namespace Filtration.ViewModels RaisePropertyChanged(nameof(AudioVisualBlockItems)); RaisePropertyChanged(nameof(HasAudioVisualBlockItems)); } + + private void OnCustomSoundFileDialog() + { + OpenFileDialog fileDialog = new OpenFileDialog(); + fileDialog.DefaultExt = ".mp3"; + var poePath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments).ToString() + @"\My Games\Path of Exile\"; + fileDialog.InitialDirectory = poePath; + + Nullable result = fileDialog.ShowDialog(); + if (result == true) + { + var fileName = fileDialog.FileName; + if(fileName.StartsWith(poePath)) + { + fileName = fileName.Replace(poePath, ""); + } + + var customSoundBlockItem = BlockItems.First(b => b.GetType() == typeof(CustomSoundBlockItem)) as CustomSoundBlockItem; + + if (CustomSoundsAvailable.IndexOf(fileName) < 0) + { + CustomSoundsAvailable.Add(fileName); + RaisePropertyChanged(nameof(CustomSoundsAvailable)); + } + customSoundBlockItem.Value = fileName; + } + } + + private void OnPlayCustomSoundCommand() + { + var poePath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments).ToString() + @"\My Games\Path of Exile\"; + var identifier = BlockItems.OfType().First().Value; + + if(!Path.IsPathRooted(identifier)) + { + identifier = poePath + identifier; + } + + try + { + _mediaPlayer.Open(new Uri(identifier, UriKind.Absolute)); + _mediaPlayer.Play(); + } + catch + { + MessageBox.Show("Couldn't play the file. Please be sure it is a valid audio file."); + } + } } } diff --git a/Filtration/ViewModels/ItemFilterBlockViewModelBase.cs b/Filtration/ViewModels/ItemFilterBlockViewModelBase.cs index 86dcfcb..395bbe1 100644 --- a/Filtration/ViewModels/ItemFilterBlockViewModelBase.cs +++ b/Filtration/ViewModels/ItemFilterBlockViewModelBase.cs @@ -9,13 +9,15 @@ namespace Filtration.ViewModels { void Initialise(IItemFilterBlockBase itemfilterBlock, IItemFilterScriptViewModel itemFilterScriptViewModel); IItemFilterBlockBase BaseBlock { get; } - bool IsDirty { get; set; } + bool IsDirty { get; set; } + bool IsVisible { get; set; } event EventHandler BlockBecameDirty; } internal abstract class ItemFilterBlockViewModelBase : ViewModelBase, IItemFilterBlockViewModelBase { private bool _isDirty; + private bool _isVisible; public ItemFilterBlockViewModelBase() { @@ -28,6 +30,8 @@ namespace Filtration.ViewModels MoveBlockDownCommand = new RelayCommand(OnMoveBlockDownCommand); MoveBlockToTopCommand = new RelayCommand(OnMoveBlockToTopCommand); MoveBlockToBottomCommand = new RelayCommand(OnMoveBlockToBottomCommand); + + _isVisible = true; } @@ -64,6 +68,16 @@ namespace Filtration.ViewModels BlockBecameDirty?.Invoke(this, EventArgs.Empty); } } + } + + public bool IsVisible + { + get => _isVisible; + set + { + _isVisible = value; + RaisePropertyChanged(); + } } private void OnCopyBlockCommand() diff --git a/Filtration/ViewModels/ItemFilterCommentBlockViewModel.cs b/Filtration/ViewModels/ItemFilterCommentBlockViewModel.cs index 08f5f23..618065a 100644 --- a/Filtration/ViewModels/ItemFilterCommentBlockViewModel.cs +++ b/Filtration/ViewModels/ItemFilterCommentBlockViewModel.cs @@ -1,15 +1,29 @@ -using Filtration.ObjectModel; - +using Filtration.ObjectModel; +using GalaSoft.MvvmLight.CommandWpf; +using System; + namespace Filtration.ViewModels { internal interface IItemFilterCommentBlockViewModel : IItemFilterBlockViewModelBase { IItemFilterCommentBlock ItemFilterCommentBlock { get; } - string Comment { get; } + string Comment { get; } + bool IsExpanded { get; set; } + bool HasChild { get; set; } } internal class ItemFilterCommentBlockViewModel : ItemFilterBlockViewModelBase, IItemFilterCommentBlockViewModel - { + { + private bool _isExpanded; + private bool _hasChild; + + public ItemFilterCommentBlockViewModel() + { + _isExpanded = true; + + ToggleSectionCommand = new RelayCommand(OnToggleSectionCommand); + } + public override void Initialise(IItemFilterBlockBase itemfilterBlock, IItemFilterScriptViewModel itemFilterScriptViewModel) { _parentScriptViewModel = itemFilterScriptViewModel; @@ -17,7 +31,9 @@ namespace Filtration.ViewModels BaseBlock = ItemFilterCommentBlock; base.Initialise(itemfilterBlock, itemFilterScriptViewModel); - } + } + + public RelayCommand ToggleSectionCommand { get; } public IItemFilterCommentBlock ItemFilterCommentBlock { get; private set; } @@ -34,8 +50,60 @@ namespace Filtration.ViewModels ItemFilterCommentBlock.Comment = value; IsDirty = true; RaisePropertyChanged(); + RaisePropertyChanged("Header"); } } } + + public string Header + { + get + { + string[] commentLines = ItemFilterCommentBlock.Comment.Split(new[] { Environment.NewLine }, StringSplitOptions.None); + var titleOffset = 1; + if (commentLines.Length > 1 && (commentLines[0].TrimStart(' ').StartsWith(@"============") || commentLines[0].TrimStart(' ').StartsWith(@"------------"))) + { + titleOffset = 3; + commentLines[0] = commentLines[1]; + } + + commentLines[0] = commentLines[0].TrimStart(' '); + if (commentLines[0].Length > 80) + { + commentLines[0] = commentLines[0].Substring(0, 80) + " (...)"; + } + else if (commentLines.Length > titleOffset) + { + commentLines[0] = commentLines[0] + " (...)"; + } + + return commentLines[0]; + } + } + + public bool IsExpanded + { + get => _isExpanded; + set + { + _isExpanded = value; + RaisePropertyChanged(); + } + } + + public bool HasChild + { + get => _hasChild; + set + { + _hasChild = value; + RaisePropertyChanged(); + } + } + + private void OnToggleSectionCommand() + { + _parentScriptViewModel.ToggleSection(this); + } } } \ No newline at end of file diff --git a/Filtration/ViewModels/ItemFilterScriptViewModel.cs b/Filtration/ViewModels/ItemFilterScriptViewModel.cs index e76f10f..01564a4 100644 --- a/Filtration/ViewModels/ItemFilterScriptViewModel.cs +++ b/Filtration/ViewModels/ItemFilterScriptViewModel.cs @@ -6,6 +6,7 @@ using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using System.Threading.Tasks; using System.Windows; using System.Windows.Data; @@ -44,11 +45,16 @@ namespace Filtration.ViewModels void SetDirtyFlag(); bool HasSelectedEnabledBlock(); bool HasSelectedDisabledBlock(); + bool HasSelectedCommentBlock(); RelayCommand AddBlockCommand { get; } RelayCommand AddSectionCommand { get; } RelayCommand DisableBlockCommand { get; } RelayCommand EnableBlockCommand { get; } + RelayCommand DisableSectionCommand { get; } + RelayCommand EnableSectionCommand { get; } + RelayCommand ExpandSectionCommand { get; } + RelayCommand CollapseSectionCommand { get; } RelayCommand DeleteBlockCommand { get; } RelayCommand MoveBlockUpCommand { get; } RelayCommand MoveBlockDownCommand { get; } @@ -60,6 +66,8 @@ namespace Filtration.ViewModels RelayCommand PasteBlockStyleCommand { get; } RelayCommand ExpandAllBlocksCommand { get; } RelayCommand CollapseAllBlocksCommand { get; } + RelayCommand ExpandAllSectionsCommand { get; } + RelayCommand CollapseAllSectionsCommand { get; } RelayCommand ToggleShowAdvancedCommand { get; } RelayCommand ClearFilterCommand { get; } @@ -74,6 +82,7 @@ namespace Filtration.ViewModels void MoveBlockUp(IItemFilterBlockViewModelBase targetBlockViewModelBase); void MoveBlockDown(IItemFilterBlockViewModelBase targetBlockViewModelBase); void MoveBlockToBottom(IItemFilterBlockViewModelBase targetBlockViewModelBase); + void ToggleSection(IItemFilterCommentBlockViewModel targetCommentBlockViewModelBase, bool updateViewModels = true); } internal class ItemFilterScriptViewModel : PaneViewModel, IItemFilterScriptViewModel @@ -92,6 +101,7 @@ namespace Filtration.ViewModels private IItemFilterBlockViewModelBase _selectedBlockViewModel; private IItemFilterCommentBlockViewModel _sectionBrowserSelectedBlockViewModel; private readonly ObservableCollection _itemFilterBlockViewModels; + private ObservableCollection _viewItemFilterBlockViewModels; private ICollectionView _itemFilterBlockViewModelsCollectionView; private Predicate _blockFilterPredicate; private ICommandManager _scriptCommandManager; @@ -131,18 +141,26 @@ namespace Filtration.ViewModels AddSectionCommand = new RelayCommand(OnAddCommentBlockCommand, () => SelectedBlockViewModel != null); DisableBlockCommand = new RelayCommand(OnDisableBlockCommand, HasSelectedEnabledBlock); EnableBlockCommand = new RelayCommand(OnEnableBlockCommand, HasSelectedDisabledBlock); + DisableSectionCommand = new RelayCommand(OnDisableSectionCommand, HasSelectedCommentBlock); + EnableSectionCommand = new RelayCommand(OnEnableSectionCommand, HasSelectedCommentBlock); + ExpandSectionCommand = new RelayCommand(OnExpandSectionCommand, HasSelectedCommentBlock); + CollapseSectionCommand = new RelayCommand(OnCollapseSectionCommand, HasSelectedCommentBlock); CopyBlockCommand = new RelayCommand(OnCopyBlockCommand, () => SelectedBlockViewModel != null); CopyBlockStyleCommand = new RelayCommand(OnCopyBlockStyleCommand, () => SelectedBlockViewModel != null); PasteBlockCommand = new RelayCommand(OnPasteBlockCommand, () => SelectedBlockViewModel != null); PasteBlockStyleCommand = new RelayCommand(OnPasteBlockStyleCommand, () => SelectedBlockViewModel != null); ExpandAllBlocksCommand = new RelayCommand(OnExpandAllBlocksCommand); CollapseAllBlocksCommand = new RelayCommand(OnCollapseAllBlocksCommand); + ExpandAllSectionsCommand = new RelayCommand(ExpandAllSections); + CollapseAllSectionsCommand = new RelayCommand(CollapseAllSections); var icon = new BitmapImage(); icon.BeginInit(); icon.UriSource = new Uri("pack://application:,,,/Filtration;component/Resources/Icons/script_icon.png"); icon.EndInit(); IconSource = icon; + + _viewItemFilterBlockViewModels = new ObservableCollection(); } public void Initialise(IItemFilterScript itemFilterScript, bool newScript) @@ -164,6 +182,9 @@ namespace Filtration.ViewModels Title = Filename; ContentId = "ScriptContentId"; + + CollapseAllSections(); + UpdateBlockModelsForView(); } private void ItemFilterBlocksOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs) @@ -186,6 +207,8 @@ namespace Filtration.ViewModels break; } } + + UpdateBlockModelsForView(); } private void AddItemFilterBlockViewModels(IEnumerable itemFilterBlocks, int addAtIndex) @@ -245,12 +268,18 @@ namespace Filtration.ViewModels public RelayCommand AddSectionCommand { get; } public RelayCommand EnableBlockCommand { get; } public RelayCommand DisableBlockCommand { get; } + public RelayCommand DisableSectionCommand { get; } + public RelayCommand EnableSectionCommand { get; } + public RelayCommand ExpandSectionCommand { get; } + public RelayCommand CollapseSectionCommand { get; } public RelayCommand CopyBlockCommand { get; } public RelayCommand CopyBlockStyleCommand { get; } public RelayCommand PasteBlockCommand { get; } public RelayCommand PasteBlockStyleCommand { get; } public RelayCommand ExpandAllBlocksCommand { get; } public RelayCommand CollapseAllBlocksCommand { get; } + public RelayCommand ExpandAllSectionsCommand { get; } + public RelayCommand CollapseAllSectionsCommand { get; } public bool IsActiveDocument { @@ -264,6 +293,19 @@ namespace Filtration.ViewModels } } + public ObservableCollection ViewItemFilterBlockViewModels + { + get + { + return _viewItemFilterBlockViewModels; + } + set + { + _viewItemFilterBlockViewModels = value; + RaisePropertyChanged(); + } + } + public ObservableCollection ItemFilterBlockViewModels { get @@ -359,6 +401,13 @@ namespace Filtration.ViewModels return SelectedBlockViewModel != null; } + public bool HasSelectedCommentBlock() + { + var selectedBlockViewModel = SelectedBlockViewModel as IItemFilterCommentBlockViewModel; + + return selectedBlockViewModel != null; + } + public IItemFilterBlockViewModelBase SelectedBlockViewModel { get => _selectedBlockViewModel; @@ -514,7 +563,7 @@ namespace Filtration.ViewModels Script.ThemeComponents.Where( t => Script.ItemFilterBlocks.OfType().Count( - b => b.BlockItems.OfType().Count(i => i.ThemeComponent == t) > 0) == 0).ToList(); + b => b.BlockItems.OfType().Count(i => i.ThemeComponent == t) > 0) == 0).ToList(); if (unusedThemeComponents.Count <= 0) return true; @@ -634,7 +683,15 @@ namespace Filtration.ViewModels private void OnCopyBlockCommand() { - CopyBlock(SelectedBlockViewModel); + var commentBlockViewModel = SelectedBlockViewModel as IItemFilterCommentBlockViewModel; + if (commentBlockViewModel == null || commentBlockViewModel.IsExpanded) + { + CopyBlock(SelectedBlockViewModel); + } + else + { + CopySection(commentBlockViewModel); + } } public void CopyBlock(IItemFilterBlockViewModelBase targetBlockViewModel) @@ -649,6 +706,26 @@ namespace Filtration.ViewModels } } + public void CopySection(IItemFilterCommentBlockViewModel targetCommentBlockViewModel) + { + var sectionStart = ItemFilterBlockViewModels.IndexOf(targetCommentBlockViewModel) + 1; + var copyText = _blockTranslator.TranslateItemFilterBlockBaseToString(targetCommentBlockViewModel.BaseBlock); + while (sectionStart < ItemFilterBlockViewModels.Count && ItemFilterBlockViewModels[sectionStart] as IItemFilterCommentBlockViewModel == null) + { + copyText += Environment.NewLine + "##CopySection##" + Environment.NewLine + _blockTranslator.TranslateItemFilterBlockBaseToString(ItemFilterBlockViewModels[sectionStart].BaseBlock); + sectionStart++; + } + + try + { + _clipboardService.SetClipboardText(copyText); + } + catch + { + _messageBoxService.Show("Clipboard Error", "Failed to access the clipboard, copy command not completed.", MessageBoxButton.OK, MessageBoxImage.Error); + } + } + private void OnCopyBlockStyleCommand() { var selectedBlockViewModel = SelectedBlockViewModel as IItemFilterBlockViewModel; @@ -709,15 +786,65 @@ namespace Filtration.ViewModels public void PasteBlock(IItemFilterBlockViewModelBase targetBlockViewModelBase) { + var commentBlock = targetBlockViewModelBase as IItemFilterCommentBlockViewModel; + if(commentBlock != null && !commentBlock.IsExpanded) + { + var blockIndex = ItemFilterBlockViewModels.IndexOf(targetBlockViewModelBase) + 1; + while (blockIndex < ItemFilterBlockViewModels.Count && (ItemFilterBlockViewModels[blockIndex] as IItemFilterCommentBlockViewModel) == null) + { + blockIndex++; + } + targetBlockViewModelBase = ItemFilterBlockViewModels[blockIndex - 1]; + } try { var clipboardText = _clipboardService.GetClipboardText(); if (string.IsNullOrEmpty(clipboardText)) return; - - var translatedBlock = _blockTranslator.TranslateStringToItemFilterBlock(clipboardText, Script, true); // TODO: Doesn't handle pasting comment blocks? - if (translatedBlock == null) return; - _scriptCommandManager.ExecuteCommand(new PasteBlockCommand(Script, translatedBlock, targetBlockViewModelBase.BaseBlock)); + string[] blockTexts = clipboardText.Split(new string[] { Environment.NewLine + "##CopySection##" + Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); + + List blocksToPaste = new List(); + foreach (var curBlock in blockTexts) + { + IItemFilterBlockBase translatedBlock; + var preparedString = PrepareBlockForParsing(curBlock); + var isCommentBlock = true; + string[] textLines = preparedString.Split(new[] { Environment.NewLine }, StringSplitOptions.None); + foreach(var line in textLines) + { + if(!line.StartsWith(@"#")) + { + isCommentBlock = false; + break; + } + } + + if (isCommentBlock) + { + translatedBlock = _blockTranslator.TranslateStringToItemFilterCommentBlock(preparedString, Script, curBlock); + } + else + { + translatedBlock = _blockTranslator.TranslateStringToItemFilterBlock(preparedString, Script, curBlock, true); + } + + if (translatedBlock == null) continue; + + blocksToPaste.Add(translatedBlock); + } + + if (blocksToPaste.Count < 1) + return; + + var blockIndex = ItemFilterBlockViewModels.IndexOf(targetBlockViewModelBase) + 1; + _scriptCommandManager.ExecuteCommand(new PasteSectionCommand(Script, blocksToPaste, targetBlockViewModelBase.BaseBlock)); + SelectedBlockViewModel = ItemFilterBlockViewModels[blockIndex]; + RaisePropertyChanged("SelectedBlockViewModel"); + var firstBlockAsComment = blocksToPaste[0] as IItemFilterCommentBlock; + if (firstBlockAsComment != null) + { + OnCollapseSectionCommand(); + } } catch (Exception e) { @@ -728,39 +855,250 @@ namespace Filtration.ViewModels } } + private string PrepareBlockForParsing(string inputString) + { + inputString = inputString.Replace("\t", ""); + var lines = Regex.Split(inputString, "\r\n|\r|\n").ToList(); + for (var i = 0; i < lines.Count; i++) + { + if (lines[i].Length == 0) + { + lines.RemoveAt(i--); + } + else + break; + } + for (var i = lines.Count - 1; i >= 0; i--) + { + if (lines[i].Length == 0) + { + lines.RemoveAt(i++); + } + else + break; + } + var allCommented = true; + for (var i = 0; i < lines.Count; i++) + { + lines[i] = Regex.Replace(lines[i], @"\s+", " "); + if(lines[i][0] == '#') + { + if (lines[i].Length > 1 && lines[i][1] != ' ') + { + lines[i] = "# " + lines[i].Substring(1); + } + } + else + { + allCommented = false; + } + } + + var disabledBlock = -1; + if (allCommented) + { + for (var i = 0; i < lines.Count; i++) + { + if (lines[i].StartsWith("#")) + { + string curLine = Regex.Replace(lines[i].Substring(1), @"\s+", ""); + if ((curLine.StartsWith("Show") || curLine.StartsWith("Hide")) && (curLine.Length == 4 || curLine[4] == '#')) + { + lines[i] = lines[i].Substring(0, 6) + "Disabled" + lines[i].Substring(6); + disabledBlock = i; + break; + } + } + } + } + + if(disabledBlock >= 0) + { + for (var i = disabledBlock; i < lines.Count; i++) + { + lines[i] = lines[i].Substring(2); + } + } + + return string.Join(Environment.NewLine, lines); + } + private void OnMoveBlockToTopCommand() { - MoveBlockToTop(SelectedBlockViewModel); + var commentBlockViewModel = SelectedBlockViewModel as IItemFilterCommentBlockViewModel; + if (commentBlockViewModel == null || commentBlockViewModel.IsExpanded) + { + MoveBlockToTop(SelectedBlockViewModel); + } + else + { + MoveSectionToTop(commentBlockViewModel); + } } private void OnMoveBlockUpCommand() { - MoveBlockUp(SelectedBlockViewModel); + var commentBlockViewModel = SelectedBlockViewModel as IItemFilterCommentBlockViewModel; + if(commentBlockViewModel == null || commentBlockViewModel.IsExpanded) + { + MoveBlockUp(SelectedBlockViewModel); + } + else + { + MoveSectionUp(commentBlockViewModel); + } } public void MoveBlockUp(IItemFilterBlockViewModelBase targetBlockViewModelBase) { - _scriptCommandManager.ExecuteCommand(new MoveBlockUpCommand(Script, targetBlockViewModelBase?.BaseBlock)); + var blockIndex = ItemFilterBlockViewModels.IndexOf(targetBlockViewModelBase); + if (ItemFilterBlockViewModels[blockIndex - 1].IsVisible) + { + _scriptCommandManager.ExecuteCommand(new MoveBlockUpCommand(Script, targetBlockViewModelBase?.BaseBlock)); + SelectedBlockViewModel = ItemFilterBlockViewModels[blockIndex - 1]; + RaisePropertyChanged("SelectedBlockViewModel"); + } + else + { + var aboveSectionStart = blockIndex - 1; + while(ItemFilterBlockViewModels[aboveSectionStart] as IItemFilterCommentBlockViewModel == null) + { + aboveSectionStart--; + } + _scriptCommandManager.ExecuteCommand(new MoveSectionToIndexCommand(Script, blockIndex, 1, aboveSectionStart)); + SelectedBlockViewModel = ItemFilterBlockViewModels[aboveSectionStart]; + RaisePropertyChanged("SelectedBlockViewModel"); + } + } + + public void MoveSectionUp(IItemFilterCommentBlockViewModel targetCommentBlockViewModel) + { + var sectionStart = ItemFilterBlockViewModels.IndexOf(targetCommentBlockViewModel); + var sectionEnd = sectionStart + 1; + while(sectionEnd < ItemFilterBlockViewModels.Count && ItemFilterBlockViewModels[sectionEnd] as IItemFilterCommentBlockViewModel == null) + { + sectionEnd++; + } + + var newLocation = sectionStart - 1; + if (ItemFilterBlockViewModels[newLocation].IsVisible) + { + _scriptCommandManager.ExecuteCommand(new MoveSectionToIndexCommand(Script, sectionStart, sectionEnd - sectionStart, newLocation)); + } + else + { + while (ItemFilterBlockViewModels[newLocation] as IItemFilterCommentBlockViewModel == null) + { + newLocation--; + } + _scriptCommandManager.ExecuteCommand(new MoveSectionToIndexCommand(Script, sectionStart, sectionEnd - sectionStart, newLocation)); + } + + ToggleSection(ItemFilterBlockViewModels[newLocation] as IItemFilterCommentBlockViewModel); + SelectedBlockViewModel = ItemFilterBlockViewModels[newLocation]; + RaisePropertyChanged("SelectedBlockViewModel"); } private void OnMoveBlockDownCommand() { - MoveBlockDown(SelectedBlockViewModel); + var commentBlockViewModel = SelectedBlockViewModel as IItemFilterCommentBlockViewModel; + if (commentBlockViewModel == null || commentBlockViewModel.IsExpanded) + { + MoveBlockDown(SelectedBlockViewModel); + } + else + { + MoveSectionDown(commentBlockViewModel); + } } public void MoveBlockDown(IItemFilterBlockViewModelBase targetBlockViewModelBase) { - _scriptCommandManager.ExecuteCommand(new MoveBlockDownCommand(Script, targetBlockViewModelBase?.BaseBlock)); + var blockIndex = ItemFilterBlockViewModels.IndexOf(targetBlockViewModelBase); + var beloveBlockAsComment = ItemFilterBlockViewModels[blockIndex + 1] as IItemFilterCommentBlockViewModel; + if (beloveBlockAsComment == null || beloveBlockAsComment.IsExpanded) + { + _scriptCommandManager.ExecuteCommand(new MoveBlockDownCommand(Script, targetBlockViewModelBase?.BaseBlock)); + SelectedBlockViewModel = ItemFilterBlockViewModels[blockIndex + 1]; + RaisePropertyChanged("SelectedBlockViewModel"); + } + else + { + var beloveSectionEnd = blockIndex + 2; + while (beloveSectionEnd < ItemFilterBlockViewModels.Count && ItemFilterBlockViewModels[beloveSectionEnd] as IItemFilterCommentBlockViewModel == null) + { + beloveSectionEnd++; + } + _scriptCommandManager.ExecuteCommand(new MoveSectionToIndexCommand(Script, blockIndex, 1, beloveSectionEnd - 1)); + SelectedBlockViewModel = ItemFilterBlockViewModels[beloveSectionEnd - 1]; + RaisePropertyChanged("SelectedBlockViewModel"); + } + } + + public void MoveSectionDown(IItemFilterCommentBlockViewModel targetCommentBlockViewModel) + { + var sectionStart = ItemFilterBlockViewModels.IndexOf(targetCommentBlockViewModel); + var sectionEnd = sectionStart + 1; + while (sectionEnd < ItemFilterBlockViewModels.Count && ItemFilterBlockViewModels[sectionEnd] as IItemFilterCommentBlockViewModel == null) + { + sectionEnd++; + } + + if (sectionEnd >= ItemFilterBlockViewModels.Count) + return; + + var sectionSize = sectionEnd - sectionStart; + + var newLocation = sectionStart + 1; + var beloveBlockAsComment = ItemFilterBlockViewModels[sectionEnd] as IItemFilterCommentBlockViewModel; + if (beloveBlockAsComment == null || beloveBlockAsComment.IsExpanded) + { + _scriptCommandManager.ExecuteCommand(new MoveSectionToIndexCommand(Script, sectionStart, sectionSize, newLocation)); + } + else + { + while ((newLocation + sectionSize) < ItemFilterBlockViewModels.Count && ItemFilterBlockViewModels[newLocation + sectionSize] as IItemFilterCommentBlockViewModel == null) + { + newLocation++; + } + _scriptCommandManager.ExecuteCommand(new MoveSectionToIndexCommand(Script, sectionStart, sectionEnd - sectionStart, newLocation)); + } + + ToggleSection(ItemFilterBlockViewModels[newLocation] as IItemFilterCommentBlockViewModel); + SelectedBlockViewModel = ItemFilterBlockViewModels[newLocation]; + RaisePropertyChanged("SelectedBlockViewModel"); } private void OnMoveBlockToBottomCommand() { - MoveBlockToBottom(SelectedBlockViewModel); + var commentBlockViewModel = SelectedBlockViewModel as IItemFilterCommentBlockViewModel; + if (commentBlockViewModel == null || commentBlockViewModel.IsExpanded) + { + MoveBlockToBottom(SelectedBlockViewModel); + } + else + { + MoveSectionToBottom(commentBlockViewModel); + } } private void OnAddBlockCommand() { - AddBlock(SelectedBlockViewModel); + var selectedBlockAsCommentBlock = SelectedBlockViewModel as IItemFilterCommentBlockViewModel; + if(selectedBlockAsCommentBlock == null || selectedBlockAsCommentBlock.IsExpanded) + { + AddBlock(SelectedBlockViewModel); + } + else + { + var sectionStart = ItemFilterBlockViewModels.IndexOf(selectedBlockAsCommentBlock); + var sectionEnd = sectionStart + 1; + while (sectionEnd < ItemFilterBlockViewModels.Count && ItemFilterBlockViewModels[sectionEnd] as IItemFilterCommentBlockViewModel == null) + { + sectionEnd++; + } + AddBlock(ItemFilterBlockViewModels[sectionEnd - 1]); + } } public void AddBlock(IItemFilterBlockViewModelBase targetBlockViewModelBase) @@ -776,7 +1114,22 @@ namespace Filtration.ViewModels public void DeleteBlock(IItemFilterBlockViewModelBase targetBlockViewModelBase) { - _scriptCommandManager.ExecuteCommand(new RemoveBlockCommand(Script, targetBlockViewModelBase.BaseBlock)); + var commentBlockViewModel = SelectedBlockViewModel as IItemFilterCommentBlockViewModel; + if (commentBlockViewModel == null || commentBlockViewModel.IsExpanded) + { + _scriptCommandManager.ExecuteCommand(new RemoveBlockCommand(Script, targetBlockViewModelBase.BaseBlock)); + } + else + { + var sectionStart = ItemFilterBlockViewModels.IndexOf(targetBlockViewModelBase); + var sectionEnd = sectionStart + 1; + while (sectionEnd < ItemFilterBlockViewModels.Count && ItemFilterBlockViewModels[sectionEnd] as IItemFilterCommentBlockViewModel == null) + { + sectionEnd++; + } + + _scriptCommandManager.ExecuteCommand(new RemoveSectionCommand(Script, sectionStart, sectionEnd - sectionStart)); + } } public void MoveBlockToBottom(IItemFilterBlockViewModelBase targetBlockViewModelBase) @@ -784,11 +1137,43 @@ namespace Filtration.ViewModels _scriptCommandManager.ExecuteCommand(new MoveBlockToBottomCommand(Script, targetBlockViewModelBase.BaseBlock)); } + public void MoveSectionToBottom(IItemFilterCommentBlockViewModel targetCommentBlockViewModel) + { + var sectionStart = ItemFilterBlockViewModels.IndexOf(targetCommentBlockViewModel); + var sectionEnd = sectionStart + 1; + while (sectionEnd < ItemFilterBlockViewModels.Count && ItemFilterBlockViewModels[sectionEnd] as IItemFilterCommentBlockViewModel == null) + { + sectionEnd++; + } + + var newLocation = ItemFilterBlockViewModels.Count - (sectionEnd - sectionStart); + _scriptCommandManager.ExecuteCommand(new MoveSectionToIndexCommand(Script, sectionStart, sectionEnd - sectionStart, newLocation)); + + ToggleSection(ItemFilterBlockViewModels[newLocation] as IItemFilterCommentBlockViewModel); + SelectedBlockViewModel = ItemFilterBlockViewModels[newLocation]; + } + public void MoveBlockToTop(IItemFilterBlockViewModelBase targetBlockViewModelBase) { _scriptCommandManager.ExecuteCommand(new MoveBlockToTopCommand(Script, targetBlockViewModelBase.BaseBlock)); } + public void MoveSectionToTop(IItemFilterCommentBlockViewModel targetCommentBlockViewModel) + { + var sectionStart = ItemFilterBlockViewModels.IndexOf(targetCommentBlockViewModel); + var sectionEnd = sectionStart + 1; + while (sectionEnd < ItemFilterBlockViewModels.Count && ItemFilterBlockViewModels[sectionEnd] as IItemFilterCommentBlockViewModel == null) + { + sectionEnd++; + } + + var newLocation = 0; + _scriptCommandManager.ExecuteCommand(new MoveSectionToIndexCommand(Script, sectionStart, sectionEnd - sectionStart, newLocation)); + + ToggleSection(ItemFilterBlockViewModels[newLocation] as IItemFilterCommentBlockViewModel); + SelectedBlockViewModel = ItemFilterBlockViewModels[newLocation]; + } + private void OnBlockBecameDirty(object sender, EventArgs e) { SetDirtyFlag(); @@ -796,7 +1181,21 @@ namespace Filtration.ViewModels private void OnAddCommentBlockCommand() { - AddCommentBlock(SelectedBlockViewModel); + var selectedBlockAsCommentBlock = SelectedBlockViewModel as IItemFilterCommentBlockViewModel; + if (selectedBlockAsCommentBlock == null || selectedBlockAsCommentBlock.IsExpanded) + { + AddCommentBlock(SelectedBlockViewModel); + } + else + { + var sectionStart = ItemFilterBlockViewModels.IndexOf(selectedBlockAsCommentBlock); + var sectionEnd = sectionStart + 1; + while (sectionEnd < ItemFilterBlockViewModels.Count && ItemFilterBlockViewModels[sectionEnd] as IItemFilterCommentBlockViewModel == null) + { + sectionEnd++; + } + AddCommentBlock(ItemFilterBlockViewModels[sectionEnd - 1]); + } } private void OnExpandAllBlocksCommand() @@ -837,5 +1236,181 @@ namespace Filtration.ViewModels selectedBlockViewModel.BlockEnabled = true; } } + + private void OnDisableSectionCommand() + { + var selectedBlockViewModel = SelectedBlockViewModel as IItemFilterCommentBlockViewModel; + if (selectedBlockViewModel != null) + { + var sectionIndex = ItemFilterBlockViewModels.IndexOf(selectedBlockViewModel); + for (int i = sectionIndex + 1; i < ItemFilterBlockViewModels.Count; i++) + { + var block = ItemFilterBlockViewModels[i] as IItemFilterBlockViewModel; + if (block != null) + { + block.BlockEnabled = false; + } + else + break; + } + } + } + + private void OnEnableSectionCommand() + { + var selectedBlockViewModel = SelectedBlockViewModel as IItemFilterCommentBlockViewModel; + if (selectedBlockViewModel != null) + { + var sectionIndex = ItemFilterBlockViewModels.IndexOf(selectedBlockViewModel); + for (int i = sectionIndex + 1; i < ItemFilterBlockViewModels.Count; i++) + { + var block = ItemFilterBlockViewModels[i] as IItemFilterBlockViewModel; + if (block != null) + { + block.BlockEnabled = true; + } + else + break; + } + } + } + + private void OnExpandSectionCommand() + { + var selectedBlockViewModel = SelectedBlockViewModel as IItemFilterCommentBlockViewModel; + if (selectedBlockViewModel != null && !selectedBlockViewModel.IsExpanded) + { + ToggleSection(selectedBlockViewModel); + } + } + + private void OnCollapseSectionCommand() + { + var selectedBlockViewModel = SelectedBlockViewModel as IItemFilterCommentBlockViewModel; + if (selectedBlockViewModel != null && selectedBlockViewModel.IsExpanded) + { + ToggleSection(selectedBlockViewModel); + } + } + + public void ToggleSection(IItemFilterCommentBlockViewModel targetCommentBlockViewModelBase, bool updateViewModels = true) + { + var newState = !targetCommentBlockViewModelBase.IsExpanded; + targetCommentBlockViewModelBase.IsExpanded = newState; + var sectionIndex = ItemFilterBlockViewModels.IndexOf(targetCommentBlockViewModelBase); + var viewIndex = ViewItemFilterBlockViewModels.IndexOf(targetCommentBlockViewModelBase); + for (int i = sectionIndex + 1; i < ItemFilterBlockViewModels.Count; i++) + { + var block = ItemFilterBlockViewModels[i] as IItemFilterBlockViewModel; + if (block != null) + { + if (newState) + viewIndex++; + + if (newState == block.IsVisible) + { + continue; + } + + if(updateViewModels) + { + if(newState) + { + if(viewIndex < ViewItemFilterBlockViewModels.Count) + { + ViewItemFilterBlockViewModels.Insert(viewIndex, block); + } + else + { + ViewItemFilterBlockViewModels.Add(block); + } + } + else + { + ViewItemFilterBlockViewModels.RemoveAt(viewIndex + 1); + } + } + block.IsVisible = newState; + } + else + break; + } + } + + private void UpdateBlockModelsForView() + { + ObservableCollection blocksForView = new ObservableCollection(); + for (var i = 0; i < ItemFilterBlockViewModels.Count; i++) + { + var block = ItemFilterBlockViewModels[i]; + if (block.IsVisible) + { + blocksForView.Add(block); + + var blockAsComment = block as IItemFilterCommentBlockViewModel; + if(blockAsComment != null && i < (ItemFilterBlockViewModels.Count - 1)) + { + var followingBlock = ItemFilterBlockViewModels[i + 1] as IItemFilterBlockViewModel; + if(followingBlock != null) + { + blockAsComment.HasChild = true; + } + else + { + blockAsComment.HasChild = false; + } + } + } + } + + ViewItemFilterBlockViewModels = blocksForView; + } + + private void CollapseAllSections() + { + ObservableCollection blocksForView = new ObservableCollection(); + for (int i = 0; i < ItemFilterBlockViewModels.Count; i++) + { + var block = ItemFilterBlockViewModels[i] as IItemFilterCommentBlockViewModel; + if (block != null) + { + blocksForView.Add(block); + + if(block.IsExpanded) + { + ToggleSection(block, false); + } + } + } + + if (SelectedBlockViewModel == null && blocksForView.Count > 0) + { + SelectedBlockViewModel = blocksForView[0]; + } + + ViewItemFilterBlockViewModels = blocksForView; + } + + private void ExpandAllSections() + { + ObservableCollection blocksForView = new ObservableCollection(); + for (int i = 0; i < ItemFilterBlockViewModels.Count; i++) + { + blocksForView.Add(ItemFilterBlockViewModels[i]); + + var block = ItemFilterBlockViewModels[i] as IItemFilterCommentBlockViewModel; + if (block != null && !block.IsExpanded) + { + ToggleSection(block, false); + } + } + + if(SelectedBlockViewModel == null && blocksForView.Count > 0) + { + SelectedBlockViewModel = blocksForView[0]; + } + + ViewItemFilterBlockViewModels = blocksForView; + } } } diff --git a/Filtration/ViewModels/MainWindowViewModel.cs b/Filtration/ViewModels/MainWindowViewModel.cs index 7fe247c..74e6534 100644 --- a/Filtration/ViewModels/MainWindowViewModel.cs +++ b/Filtration/ViewModels/MainWindowViewModel.cs @@ -100,6 +100,10 @@ namespace Filtration.ViewModels DeleteBlockCommand = new RelayCommand(OnDeleteBlockCommand, () => ActiveDocumentIsScript && ActiveScriptHasSelectedBlock); DisableBlockCommand = new RelayCommand(OnDisableBlockCommand, () => ActiveDocumentIsScript && ActiveScriptHasSelectedEnabledBlock); EnableBlockCommand = new RelayCommand(OnEnableBlockCommand, () => ActiveDocumentIsScript && ActiveScriptHasSelectedDisabledBlock); + DisableSectionCommand = new RelayCommand(OnDisableSectionCommand, () => ActiveDocumentIsScript && ActiveScriptHasSelectedCommentBlock); + EnableSectionCommand = new RelayCommand(OnEnableSectionCommand, () => ActiveDocumentIsScript && ActiveScriptHasSelectedCommentBlock); + ExpandSectionCommand = new RelayCommand(OnExpandSectionCommand, () => ActiveDocumentIsScript && ActiveScriptHasSelectedCommentBlock); + CollapseSectionCommand = new RelayCommand(OnCollapseSectionCommand, () => ActiveDocumentIsScript && ActiveScriptHasSelectedCommentBlock); OpenAboutWindowCommand = new RelayCommand(OnOpenAboutWindowCommand); ReplaceColorsCommand = new RelayCommand(OnReplaceColorsCommand, () => ActiveDocumentIsScript); @@ -110,11 +114,19 @@ namespace Filtration.ViewModels AddTextColorThemeComponentCommand = new RelayCommand(OnAddTextColorThemeComponentCommand, () => ActiveDocumentIsTheme && ActiveThemeIsEditable); AddBackgroundColorThemeComponentCommand = new RelayCommand(OnAddBackgroundColorThemeComponentCommand, () => ActiveDocumentIsTheme && ActiveThemeIsEditable); AddBorderColorThemeComponentCommand = new RelayCommand(OnAddBorderColorThemeComponentCommand, () => ActiveDocumentIsTheme && ActiveThemeIsEditable); + AddFontSizeThemeComponentCommand = new RelayCommand(OnAddFontSizeThemeComponentCommand, () => ActiveDocumentIsTheme && ActiveThemeIsEditable); + AddAlertSoundThemeComponentCommand = new RelayCommand(OnAddAlertSoundThemeComponentCommand, () => ActiveDocumentIsTheme && ActiveThemeIsEditable); + AddCustomSoundThemeComponentCommand = new RelayCommand(OnAddCustomSoundThemeComponentCommand, () => ActiveDocumentIsTheme && ActiveThemeIsEditable); + AddIconThemeComponentCommand = new RelayCommand(OnAddIconThemeComponentCommand, () => ActiveDocumentIsTheme && ActiveThemeIsEditable); + AddEffectColorThemeComponentCommand = new RelayCommand(OnAddEffectColorThemeComponentCommand, () => ActiveDocumentIsTheme && ActiveThemeIsEditable); DeleteThemeComponentCommand = new RelayCommand(OnDeleteThemeComponentCommand, () => ActiveDocumentIsTheme && ActiveThemeIsEditable && _avalonDockWorkspaceViewModel.ActiveThemeViewModel.SelectedThemeComponent != null); ExpandAllBlocksCommand = new RelayCommand(OnExpandAllBlocksCommand, () => ActiveDocumentIsScript); CollapseAllBlocksCommand = new RelayCommand(OnCollapseAllBlocksCommand, () => ActiveDocumentIsScript); + ExpandAllSectionsCommand = new RelayCommand(OnExpandAllSectionsCommand, () => ActiveDocumentIsScript); + CollapseAllSectionsCommand = new RelayCommand(OnCollapseAllSectionsCommand, () => ActiveDocumentIsScript); + ToggleShowAdvancedCommand = new RelayCommand(OnToggleShowAdvancedCommand, s => ActiveDocumentIsScript); ClearFiltersCommand = new RelayCommand(OnClearFiltersCommand, () => ActiveDocumentIsScript); @@ -206,6 +218,11 @@ namespace Filtration.ViewModels public RelayCommand AddTextColorThemeComponentCommand { get; } public RelayCommand AddBackgroundColorThemeComponentCommand { get; } public RelayCommand AddBorderColorThemeComponentCommand { get; } + public RelayCommand AddFontSizeThemeComponentCommand { get; } + public RelayCommand AddAlertSoundThemeComponentCommand { get; } + public RelayCommand AddCustomSoundThemeComponentCommand { get; } + public RelayCommand AddIconThemeComponentCommand { get; } + public RelayCommand AddEffectColorThemeComponentCommand { get; } public RelayCommand DeleteThemeComponentCommand { get; } public RelayCommand AddBlockCommand { get; } @@ -213,6 +230,10 @@ namespace Filtration.ViewModels public RelayCommand DeleteBlockCommand { get; } public RelayCommand DisableBlockCommand { get; } public RelayCommand EnableBlockCommand { get; } + public RelayCommand DisableSectionCommand { get; } + public RelayCommand EnableSectionCommand { get; } + public RelayCommand ExpandSectionCommand { get; } + public RelayCommand CollapseSectionCommand { get; } public RelayCommand MoveBlockUpCommand { get; } public RelayCommand MoveBlockDownCommand { get; } @@ -222,6 +243,9 @@ namespace Filtration.ViewModels public RelayCommand ExpandAllBlocksCommand { get; } public RelayCommand CollapseAllBlocksCommand { get; } + public RelayCommand ExpandAllSectionsCommand { get; } + public RelayCommand CollapseAllSectionsCommand { get; } + public RelayCommand ToggleShowAdvancedCommand { get; } public RelayCommand ClearFiltersCommand { get; } @@ -260,6 +284,9 @@ namespace Filtration.ViewModels public bool ActiveScriptHasSelectedDisabledBlock => AvalonDockWorkspaceViewModel.ActiveScriptViewModel.HasSelectedDisabledBlock(); + public bool ActiveScriptHasSelectedCommentBlock => AvalonDockWorkspaceViewModel.ActiveScriptViewModel.HasSelectedCommentBlock(); + + public bool ActiveThemeIsEditable => AvalonDockWorkspaceViewModel.ActiveThemeViewModel.IsMasterTheme; private bool ActiveDocumentIsEditable() @@ -595,6 +622,26 @@ namespace Filtration.ViewModels _avalonDockWorkspaceViewModel.ActiveScriptViewModel.EnableBlockCommand.Execute(null); } + private void OnDisableSectionCommand() + { + _avalonDockWorkspaceViewModel.ActiveScriptViewModel.DisableSectionCommand.Execute(null); + } + + private void OnEnableSectionCommand() + { + _avalonDockWorkspaceViewModel.ActiveScriptViewModel.EnableSectionCommand.Execute(null); + } + + private void OnExpandSectionCommand() + { + _avalonDockWorkspaceViewModel.ActiveScriptViewModel.ExpandSectionCommand.Execute(null); + } + + private void OnCollapseSectionCommand() + { + _avalonDockWorkspaceViewModel.ActiveScriptViewModel.CollapseSectionCommand.Execute(null); + } + private void OnExpandAllBlocksCommand() { _avalonDockWorkspaceViewModel.ActiveScriptViewModel.ExpandAllBlocksCommand.Execute(null); @@ -605,6 +652,16 @@ namespace Filtration.ViewModels _avalonDockWorkspaceViewModel.ActiveScriptViewModel.CollapseAllBlocksCommand.Execute(null); } + private void OnExpandAllSectionsCommand() + { + _avalonDockWorkspaceViewModel.ActiveScriptViewModel.ExpandAllSectionsCommand.Execute(null); + } + + private void OnCollapseAllSectionsCommand() + { + _avalonDockWorkspaceViewModel.ActiveScriptViewModel.CollapseAllSectionsCommand.Execute(null); + } + private void OnToggleShowAdvancedCommand(bool showAdvanced) { _avalonDockWorkspaceViewModel.ActiveScriptViewModel.ToggleShowAdvancedCommand.Execute(showAdvanced); @@ -630,6 +687,31 @@ namespace Filtration.ViewModels _avalonDockWorkspaceViewModel.ActiveThemeViewModel.AddThemeComponentCommand.Execute(ThemeComponentType.BorderColor); } + private void OnAddFontSizeThemeComponentCommand() + { + _avalonDockWorkspaceViewModel.ActiveThemeViewModel.AddThemeComponentCommand.Execute(ThemeComponentType.FontSize); + } + + private void OnAddAlertSoundThemeComponentCommand() + { + _avalonDockWorkspaceViewModel.ActiveThemeViewModel.AddThemeComponentCommand.Execute(ThemeComponentType.AlertSound); + } + + private void OnAddCustomSoundThemeComponentCommand() + { + _avalonDockWorkspaceViewModel.ActiveThemeViewModel.AddThemeComponentCommand.Execute(ThemeComponentType.CustomSound); + } + + private void OnAddIconThemeComponentCommand() + { + _avalonDockWorkspaceViewModel.ActiveThemeViewModel.AddThemeComponentCommand.Execute(ThemeComponentType.Icon); + } + + private void OnAddEffectColorThemeComponentCommand() + { + _avalonDockWorkspaceViewModel.ActiveThemeViewModel.AddThemeComponentCommand.Execute(ThemeComponentType.Effect); + } + private void OnDeleteThemeComponentCommand() { _avalonDockWorkspaceViewModel.ActiveThemeViewModel.DeleteThemeComponentCommand.Execute( diff --git a/Filtration/ViewModels/ToolPanes/BlockGroupBrowserViewModel.cs b/Filtration/ViewModels/ToolPanes/BlockGroupBrowserViewModel.cs index a13f3c4..f205a39 100644 --- a/Filtration/ViewModels/ToolPanes/BlockGroupBrowserViewModel.cs +++ b/Filtration/ViewModels/ToolPanes/BlockGroupBrowserViewModel.cs @@ -28,6 +28,9 @@ namespace Filtration.ViewModels.ToolPanes icon.UriSource = new Uri("pack://application:,,,/Filtration;component/Resources/Icons/block_group_browser_icon.png"); icon.EndInit(); IconSource = icon; + + IsVisible = false; + Messenger.Default.Register>(this, message => { switch (message.Notification) diff --git a/Filtration/ViewModels/ToolPanes/CommentBlockBrowserViewModel.cs b/Filtration/ViewModels/ToolPanes/CommentBlockBrowserViewModel.cs index e2c9880..9dc542a 100644 --- a/Filtration/ViewModels/ToolPanes/CommentBlockBrowserViewModel.cs +++ b/Filtration/ViewModels/ToolPanes/CommentBlockBrowserViewModel.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.Windows.Data; using System.Windows.Media.Imaging; using GalaSoft.MvvmLight.Messaging; @@ -15,6 +17,8 @@ namespace Filtration.ViewModels.ToolPanes { private IEnumerable _itemFilterCommentBlockViewModels; private IItemFilterCommentBlockViewModel _selectedItemFilterCommentBlockViewModel; + private CollectionView _commentBlocksView; + private string _searchText; public CommentBlockBrowserViewModel() : base("Section Browser") { @@ -24,7 +28,9 @@ namespace Filtration.ViewModels.ToolPanes icon.UriSource = new Uri("pack://application:,,,/Filtration;component/Resources/Icons/add_section_icon.png"); icon.EndInit(); IconSource = icon; - + _searchText = ""; + _commentBlocksView = (CollectionView)CollectionViewSource.GetDefaultView(new List()); + Messenger.Default.Register(this, message => { switch (message.Notification) @@ -46,7 +52,23 @@ namespace Filtration.ViewModels.ToolPanes private set { _itemFilterCommentBlockViewModels = value; - RaisePropertyChanged(); + + _commentBlocksView = _itemFilterCommentBlockViewModels != null ? + (CollectionView)CollectionViewSource.GetDefaultView(_itemFilterCommentBlockViewModels) + : (CollectionView)CollectionViewSource.GetDefaultView(new List()); + + _commentBlocksView.Filter = SearchFilter; + _commentBlocksView.Refresh(); + RaisePropertyChanged("CommentBlocksView"); + } + } + + public CollectionView CommentBlocksView + { + get => _commentBlocksView; + private set + { + _commentBlocksView = value; } } @@ -64,6 +86,18 @@ namespace Filtration.ViewModels.ToolPanes } } + public string SearchText + { + get => _searchText; + set + { + _searchText = value; + _commentBlocksView.Refresh(); + RaisePropertyChanged(); + RaisePropertyChanged("CommentBlocksView"); + } + } + protected override void OnActiveDocumentChanged(object sender, EventArgs e) { if (AvalonDockWorkspaceViewModel.ActiveScriptViewModel != null && AvalonDockWorkspaceViewModel.ActiveDocument.IsScript) @@ -81,5 +115,24 @@ namespace Filtration.ViewModels.ToolPanes ItemFilterCommentBlockViewModels = null; SelectedItemFilterCommentBlockViewModel = null; } + + private bool SearchFilter(object obj) + { + if (string.IsNullOrEmpty(_searchText)) + return true; + + var block = obj as IItemFilterCommentBlockViewModel; + var searchWords = Regex.Split(_searchText, @"\s+"); + foreach(var word in searchWords) + { + if (string.IsNullOrEmpty(word)) + continue; + + if (block.Comment.IndexOf(word, StringComparison.OrdinalIgnoreCase) >= 0) + return true; + } + + return false; + } } } diff --git a/Filtration/Views/AboutWindow.xaml b/Filtration/Views/AboutWindow.xaml index 9b8c9ae..9862989 100644 --- a/Filtration/Views/AboutWindow.xaml +++ b/Filtration/Views/AboutWindow.xaml @@ -1,7 +1,7 @@  - + diff --git a/Filtration/Views/ItemFilterBlockView.xaml b/Filtration/Views/ItemFilterBlockView.xaml index 98b941c..af675b4 100644 --- a/Filtration/Views/ItemFilterBlockView.xaml +++ b/Filtration/Views/ItemFilterBlockView.xaml @@ -18,6 +18,8 @@ + +