Skip to content

Commit fe23c95

Browse files
Merge pull request #605 from Belphemur/profile-tray-menu
feat(Profile::TrayIcon) Add profile to the tray icon menu
2 parents 6d3c40c + 8d2abc9 commit fe23c95

21 files changed

+371
-139
lines changed

README.de.md

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ Sie möchten etwas verbessern oder eine neue Sprache hinzufügen? Übersetzungen
9797
- Portuguese (Brazilian) translation [#258](https://github.com/Belphemur/SoundSwitch/pull/258) [@aleczk](https://github.com/aleczk)
9898
- Awesome Logo [#278](https://github.com/Belphemur/SoundSwitch/pull/278) [@linadesteem](https://github.com/linadesteem)
9999
- Icons [Pastel SVG icon set](https://codefisher.org/pastel-svg/), by Michael Buckley ([CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/))
100+
- Icons [Font Awesome](https://fontawesome.com/), Creative Commons Attribution 4.0 International license: [License](https://fontawesome.com/license)
100101

101102
### 🤝 JetBrains ![JetBrain Tooling](https://i.imgur.com/SN2qAuL.png "JetBrain Tooling")
102103

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ Improve an existing or add another language? Translations are online editable [r
101101
- Awesome Logo [#278](https://github.com/Belphemur/SoundSwitch/pull/278) [@linadesteem](https://github.com/linadesteem)
102102
- Icons [Pastel SVG icon set](https://codefisher.org/pastel-svg/), by Michael Buckley ([CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/))
103103
- Discovered and reported a security vulnerability with the updater and its code signature checker [#415](https://github.com/Belphemur/SoundSwitch/issues/415) [@JarLob](https://github.com/JarLob)
104-
104+
- Free Icons from [Font Awesome](https://fontawesome.com/), Creative Commons Attribution 4.0 International license: [License](https://fontawesome.com/license)
105105
### 🤝 JetBrains ![JetBrain Tooling](https://i.imgur.com/SN2qAuL.png "JetBrain Tooling")
106106

107107
Thanks for their Open-Source licence to their amazing IDEs and addons like [ReSharper](https://www.jetbrains.com/resharper) for Visual Studio.

SoundSwitch/Framework/Profile/ProfileManager.cs

+6-4
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ private void RegisterTriggers(Profile profile, bool onInit = false)
7676
}
7777

7878
SwitchAudio(profile);
79-
}, () => { _profilesByUwpApp.Add(trigger.WindowName.ToLower(), (profile, trigger)); });
79+
}, () => { _profilesByUwpApp.Add(trigger.WindowName.ToLower(), (profile, trigger)); },
80+
() => {});
8081
}
8182
}
8283

@@ -92,7 +93,8 @@ private void UnRegisterTriggers(Profile profile)
9293
() => { _profilesByWindowName.Remove(trigger.WindowName.ToLower()); },
9394
() => { _profileByApplication.Remove(trigger.ApplicationPath.ToLower()); },
9495
() => { _steamProfile = null; }, () => { },
95-
() => { _profilesByUwpApp.Remove(trigger.WindowName.ToLower()); });
96+
() => { _profilesByUwpApp.Remove(trigger.WindowName.ToLower()); },
97+
() => {});
9698
}
9799
}
98100

@@ -292,7 +294,7 @@ private void SwitchAudio(Profile profile, uint processId)
292294
}
293295

294296

295-
private void SwitchAudio(Profile profile)
297+
public void SwitchAudio(Profile profile)
296298
{
297299
_notificationManager.NotifyProfileChanged(profile, null);
298300
foreach (var device in profile.Devices)
@@ -425,7 +427,7 @@ private Result<string, VoidSuccess> ValidateProfile(Profile profile)
425427
}
426428

427429
return null;
428-
});
430+
}, () => null);
429431
if (error != null)
430432
{
431433
return error;

SoundSwitch/Framework/Profile/Trigger/TriggerEnumExtensions.cs

+12-47
Original file line numberDiff line numberDiff line change
@@ -7,64 +7,29 @@ public static class TriggerEnumExtensions
77
/// <summary>
88
/// Switch on the given enum
99
/// </summary>
10-
/// <param name="enum"></param>
11-
/// <param name="hotkey"></param>
12-
/// <param name="window"></param>
13-
/// <param name="process"></param>
14-
/// <param name="steam"></param>
15-
/// <param name="startup"></param>
16-
/// <param name="uwpApp"></param>
1710
/// <exception cref="ArgumentOutOfRangeException"></exception>
18-
public static void Switch(this TriggerFactory.Enum @enum, Action hotkey, Action window, Action process, Action steam, Action startup, Action uwpApp)
11+
public static void Switch(this TriggerFactory.Enum @enum, Action hotkey, Action window, Action process, Action steam, Action startup, Action uwpApp, Action trayMenu)
1912
{
20-
switch (@enum)
21-
{
22-
case TriggerFactory.Enum.HotKey:
23-
hotkey();
24-
break;
25-
case TriggerFactory.Enum.Window:
26-
window();
27-
break;
28-
case TriggerFactory.Enum.Process:
29-
process();
30-
break;
31-
case TriggerFactory.Enum.Steam:
32-
steam();
33-
break;
34-
case TriggerFactory.Enum.Startup:
35-
startup();
36-
break;
37-
case TriggerFactory.Enum.UwpApp:
38-
uwpApp();
39-
break;
40-
default:
41-
throw new ArgumentOutOfRangeException(nameof(@enum), @enum, null);
42-
}
13+
Match(@enum, () => hotkey, () => window, () => process, () => steam, () => startup, () => uwpApp, () => trayMenu)();
4314
}
4415

4516
/// <summary>
4617
/// Match on the enum
4718
/// </summary>
48-
/// <param name="enum"></param>
49-
/// <param name="hotkey"></param>
50-
/// <param name="window"></param>
51-
/// <param name="process"></param>
52-
/// <param name="steam"></param>
53-
/// <param name="startup"></param>
54-
/// <param name="uwpApp"></param>
55-
/// <typeparam name="T"></typeparam>
5619
/// <returns></returns>
57-
public static T Match<T>(this TriggerFactory.Enum @enum, Func<T> hotkey, Func<T> window, Func<T> process, Func<T> steam, Func<T> startup, Func<T> uwpApp)
20+
/// <exception cref="ArgumentOutOfRangeException"></exception>
21+
public static T Match<T>(this TriggerFactory.Enum @enum, Func<T> hotkey, Func<T> window, Func<T> process, Func<T> steam, Func<T> startup, Func<T> uwpApp, Func<T> trayMenu)
5822
{
5923
return @enum switch
6024
{
61-
TriggerFactory.Enum.HotKey => hotkey(),
62-
TriggerFactory.Enum.Window => window(),
63-
TriggerFactory.Enum.Process => process(),
64-
TriggerFactory.Enum.Steam => steam(),
65-
TriggerFactory.Enum.Startup => startup(),
66-
TriggerFactory.Enum.UwpApp => uwpApp(),
67-
_ => throw new ArgumentOutOfRangeException(nameof(@enum), @enum, null)
25+
TriggerFactory.Enum.HotKey => hotkey(),
26+
TriggerFactory.Enum.Window => window(),
27+
TriggerFactory.Enum.Process => process(),
28+
TriggerFactory.Enum.Steam => steam(),
29+
TriggerFactory.Enum.Startup => startup(),
30+
TriggerFactory.Enum.UwpApp => uwpApp(),
31+
TriggerFactory.Enum.TrayMenu => trayMenu(),
32+
_ => throw new ArgumentOutOfRangeException(nameof(@enum), @enum, null)
6833
};
6934
}
7035
}
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
using System;
2-
using System.Windows.Forms;
3-
using SoundSwitch.Framework.Factory;
1+
using SoundSwitch.Framework.Factory;
42
using SoundSwitch.Localization;
5-
using SoundSwitch.Util;
63

74
namespace SoundSwitch.Framework.Profile.Trigger
85
{
@@ -15,7 +12,8 @@ public enum Enum
1512
Process,
1613
Steam,
1714
Startup,
18-
UwpApp
15+
UwpApp,
16+
TrayMenu
1917
}
2018

2119
private static readonly IEnumImplList<Enum, ITriggerDefinition> Impl =
@@ -26,7 +24,8 @@ public enum Enum
2624
new WindowTrigger(),
2725
new SteamBigPictureTrigger(),
2826
new Startup(),
29-
new UwpApp()
27+
new UwpApp(),
28+
new TrayMenu()
3029
};
3130

3231
public TriggerFactory() : base(Impl)
@@ -66,68 +65,76 @@ public override string ToString()
6665
return Label;
6766
}
6867

69-
public virtual TriggerFactory.Enum TypeEnum { get; }
70-
public virtual string Label { get; }
71-
public virtual int MaxOccurence { get; } = -1;
72-
public virtual int MaxGlobalOccurence { get; } = -1;
73-
public abstract string Description { get; }
74-
public virtual bool CanRestoreDevices { get; } = false;
75-
public virtual bool AlwaysDefaultAndRestoreDevice { get; } = false;
68+
public virtual TriggerFactory.Enum TypeEnum { get; }
69+
public virtual string Label { get; }
70+
public virtual int MaxOccurence => -1;
71+
public virtual int MaxGlobalOccurence => -1;
72+
public abstract string Description { get; }
73+
public virtual bool CanRestoreDevices => false;
74+
public virtual bool AlwaysDefaultAndRestoreDevice => false;
7675
}
7776

7877
public class HotKeyTrigger : BaseTrigger
7978
{
80-
public override TriggerFactory.Enum TypeEnum { get; } = TriggerFactory.Enum.HotKey;
81-
public override string Label => SettingsStrings.hotkeys;
82-
public override string Description { get; } = SettingsStrings.profile_trigger_hotkey_desc;
83-
public override int MaxOccurence { get; } = 1;
79+
public override TriggerFactory.Enum TypeEnum => TriggerFactory.Enum.HotKey;
80+
public override string Label => SettingsStrings.hotkeys;
81+
public override string Description { get; } = SettingsStrings.profile_trigger_hotkey_desc;
82+
public override int MaxOccurence => 1;
8483
}
8584

8685
public class WindowTrigger : BaseTrigger
8786
{
88-
public override TriggerFactory.Enum TypeEnum { get; } = TriggerFactory.Enum.Window;
89-
public override string Label => SettingsStrings.profile_trigger_window;
90-
public override string Description { get; } = SettingsStrings.profile_trigger_window_desc;
91-
public override bool CanRestoreDevices { get; } = true;
87+
public override TriggerFactory.Enum TypeEnum => TriggerFactory.Enum.Window;
88+
public override string Label => SettingsStrings.profile_trigger_window;
89+
public override string Description { get; } = SettingsStrings.profile_trigger_window_desc;
90+
public override bool CanRestoreDevices => true;
9291
}
9392

9493
public class ProcessTrigger : BaseTrigger
9594
{
96-
public override TriggerFactory.Enum TypeEnum { get; } = TriggerFactory.Enum.Process;
97-
public override string Label => SettingsStrings.profile_trigger_process;
98-
public override string Description { get; } = SettingsStrings.profile_trigger_process_desc;
99-
public override bool CanRestoreDevices { get; } = true;
95+
public override TriggerFactory.Enum TypeEnum => TriggerFactory.Enum.Process;
96+
public override string Label => SettingsStrings.profile_trigger_process;
97+
public override string Description { get; } = SettingsStrings.profile_trigger_process_desc;
98+
public override bool CanRestoreDevices => true;
10099
}
101100

102101
public class SteamBigPictureTrigger : BaseTrigger
103102
{
104-
public override TriggerFactory.Enum TypeEnum { get; } = TriggerFactory.Enum.Steam;
105-
public override string Label => SettingsStrings.profile_trigger_steam;
106-
107-
public override int MaxOccurence { get; } = 1;
108-
public override int MaxGlobalOccurence { get; } = 1;
109-
public override string Description { get; } = SettingsStrings.profile_trigger_steam_desc;
110-
public override bool CanRestoreDevices { get; } = true;
111-
public override bool AlwaysDefaultAndRestoreDevice { get; } = true;
103+
public override TriggerFactory.Enum TypeEnum => TriggerFactory.Enum.Steam;
104+
public override string Label => SettingsStrings.profile_trigger_steam;
105+
106+
public override int MaxOccurence => 1;
107+
public override int MaxGlobalOccurence => 1;
108+
public override string Description { get; } = SettingsStrings.profile_trigger_steam_desc;
109+
public override bool CanRestoreDevices => true;
110+
public override bool AlwaysDefaultAndRestoreDevice => true;
112111
}
113112

114113
public class Startup : BaseTrigger
115114
{
116-
public override TriggerFactory.Enum TypeEnum { get; } = TriggerFactory.Enum.Startup;
117-
public override string Label => SettingsStrings.profile_trigger_startup;
115+
public override TriggerFactory.Enum TypeEnum => TriggerFactory.Enum.Startup;
116+
public override string Label => SettingsStrings.profile_trigger_startup;
118117

119-
public override int MaxOccurence { get; } = 1;
120-
public override int MaxGlobalOccurence { get; } = 1;
118+
public override int MaxOccurence => 1;
119+
public override int MaxGlobalOccurence => 1;
121120

122121
public override string Description { get; } = SettingsStrings.profile_trigger_startup_desc;
123122
}
124123

125124
public class UwpApp : BaseTrigger
126125
{
127-
public override TriggerFactory.Enum TypeEnum { get; } = TriggerFactory.Enum.UwpApp;
128-
public override string Label => SettingsStrings.profile_trigger_uwp;
129-
public override bool CanRestoreDevices { get; } = true;
130-
public override string Description => SettingsStrings.profile_trigger_uwp_desc;
131-
public override bool AlwaysDefaultAndRestoreDevice { get; } = true;
126+
public override TriggerFactory.Enum TypeEnum => TriggerFactory.Enum.UwpApp;
127+
public override string Label => SettingsStrings.profile_trigger_uwp;
128+
public override bool CanRestoreDevices => true;
129+
public override string Description => SettingsStrings.profile_trigger_uwp_desc;
130+
public override bool AlwaysDefaultAndRestoreDevice => true;
131+
}
132+
133+
public class TrayMenu : BaseTrigger
134+
{
135+
public override string Description => SettingsStrings.profile_trigger_trayMenu_desc;
136+
public override TriggerFactory.Enum TypeEnum => TriggerFactory.Enum.TrayMenu;
137+
public override string Label => SettingsStrings.profile_trigger_trayMenu;
138+
public override int MaxOccurence => 1;
132139
}
133140
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/********************************************************************
2+
* Copyright (C) 2015 Jeroen Pelgrims
3+
* Copyright (C) 2015-2017 Antoine Aflalo
4+
*
5+
* This program is free software; you can redistribute it and/or
6+
* modify it under the terms of the GNU General Public License
7+
* as published by the Free Software Foundation; either version 2
8+
* of the License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
********************************************************************/
15+
16+
using System;
17+
using System.Drawing;
18+
using System.Windows.Forms;
19+
20+
namespace SoundSwitch.Framework.Profile.UI
21+
{
22+
internal class ProfileToolStripMenuItem : ToolStripMenuItem
23+
{
24+
25+
public ProfileToolStripMenuItem(Profile profile, Image image, Action<Profile> onClick)
26+
: base(profile.Name, image, (_, _) => onClick(profile))
27+
{
28+
}
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Drawing;
4+
using System.Linq;
5+
using System.Windows.Forms;
6+
using SoundSwitch.Common.Framework.Icon;
7+
using SoundSwitch.Framework.Profile.Trigger;
8+
using SoundSwitch.Model;
9+
10+
namespace SoundSwitch.Framework.Profile.UI
11+
{
12+
public class ProfileTrayIconBuilder
13+
{
14+
private ProfileManager ProfileManager => AppModel.Instance.ProfileManager;
15+
16+
private IAudioDeviceLister AudioDeviceLister => AppModel.Instance.ActiveAudioDeviceLister;
17+
18+
/// <summary>
19+
/// Get the menu items for profile that needs to be shown in the menu
20+
/// </summary>
21+
/// <returns></returns>
22+
public IEnumerable<ToolStripMenuItem> GetMenuItems()
23+
{
24+
return ProfileManager.Profiles
25+
.Where(profile => profile.Triggers.Any(trigger => trigger.Type == TriggerFactory.Enum.TrayMenu))
26+
.Select(BuildMenuItem);
27+
}
28+
29+
private ProfileToolStripMenuItem BuildMenuItem(Profile profile)
30+
{
31+
Image image = null;
32+
try
33+
{
34+
var appTrigger = profile.Triggers.FirstOrDefault(trigger => trigger.ApplicationPath != null);
35+
if (appTrigger != null)
36+
{
37+
image = IconExtractor.Extract(appTrigger.ApplicationPath, 0, false).ToBitmap();
38+
}
39+
}
40+
catch (Exception)
41+
{
42+
// ignored
43+
}
44+
45+
foreach (var wrapper in profile.Devices)
46+
{
47+
if (image != null)
48+
break;
49+
50+
try
51+
{
52+
var device = AudioDeviceLister.PlaybackDevices.FirstOrDefault(info => info.Equals(wrapper.DeviceInfo));
53+
image = device?.SmallIcon.ToBitmap();
54+
}
55+
catch (Exception)
56+
{
57+
// ignored
58+
}
59+
}
60+
61+
return new ProfileToolStripMenuItem(profile, image, profileClicked => ProfileManager.SwitchAudio(profileClicked));
62+
}
63+
}
64+
}

SoundSwitch/Localization/SettingsStrings.Designer.cs

+18
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)