Compare commits

...

18 Commits
2.5 ... 3.0

Author SHA1 Message Date
Frank Skare
6fc0ebf9d5 - 2019-04-20 23:49:05 +02:00
Frank Skare
a0e30ff982 - 2019-04-20 23:27:14 +02:00
Frank Skare
c2a0d95851 - 2019-04-20 23:07:04 +02:00
Frank Skare
34d5d2fff5 - 2019-04-20 20:56:54 +02:00
Frank Skare
baa669fe1e - 2019-04-20 20:51:05 +02:00
Frank Skare
67ecebbf6b - 2019-04-20 04:34:44 +02:00
Frank Skare
e9a8b1962a - 2019-04-16 19:04:52 +02:00
Frank Skare
3ded1573f4 - 2019-04-16 18:46:24 +02:00
Frank Skare
d38adcfc70 - 2019-04-16 18:42:03 +02:00
Frank Skare
5f1f5b3811 - 2019-04-16 18:31:39 +02:00
Frank Skare
c18c70c2af - 2019-04-16 18:14:22 +02:00
Frank Skare
9cdd76e1ac - 2019-04-13 03:07:57 +02:00
Frank Skare
7db8a1e534 - 2019-04-13 03:05:07 +02:00
Frank Skare
b93972c7f7 - 2019-04-12 18:01:23 +02:00
Frank Skare
1bae00051b - 2019-04-12 16:12:12 +02:00
Frank Skare
b75bae3c38 2.6 2019-04-09 05:09:43 +02:00
Frank Skare
2e435dcdc0 2.6 2019-04-09 04:57:29 +02:00
Frank Skare
fec33056cb 2.5 2019-04-08 04:38:17 +02:00
43 changed files with 1218 additions and 774 deletions

View File

@@ -11,7 +11,7 @@ Public Class CSScriptAddon
Implements IAddon Implements IAddon
Sub New() Sub New()
Dim scriptDir = mp.mpvConfFolderPath + "scripts" Dim scriptDir = mp.MpvConfFolderPath + "scripts"
If Not Directory.Exists(scriptDir) Then Return If Not Directory.Exists(scriptDir) Then Return
Dim csFiles = Directory.GetFiles(scriptDir, "*.cs").ToList Dim csFiles = Directory.GetFiles(scriptDir, "*.cs").ToList
csFiles.AddRange(Directory.GetFiles(Application.StartupPath + "\\Scripts", "*.cs")) csFiles.AddRange(Directory.GetFiles(Application.StartupPath + "\\Scripts", "*.cs"))

View File

@@ -22,7 +22,8 @@ Table of contents
- Customizable context menu defined in the same file as the key bindings - Customizable context menu defined in the same file as the key bindings
- Searchable options dialog with modern UI as mpv compatible standalone application - Searchable options dialog with modern UI as mpv compatible standalone application
- Searchable input (key/mouse) binding editor with modern UI as mpv compatible standalone application - Searchable input (key/mouse) binding editor with modern UI as mpv compatible standalone application
- Rich addon API for .NET languages - Modern UI using the OS theme color and dark mode
- Rich addon/extension API for .NET languages, over 700 available mpv properties
- Rich scripting API for Python, C#, Lua, JavaScript and PowerShell - Rich scripting API for Python, C#, Lua, JavaScript and PowerShell
- mpv's OSC (on screen controller (play control bar)), IPC, conf files - mpv's OSC (on screen controller (play control bar)), IPC, conf files
@@ -30,6 +31,8 @@ Table of contents
![](https://raw.githubusercontent.com/stax76/mpv.net/master/screenshots/mpvnet.png) ![](https://raw.githubusercontent.com/stax76/mpv.net/master/screenshots/mpvnet.png)
![](https://raw.githubusercontent.com/stax76/mpv.net/master/screenshots/mpvnetContextMenu.png)
![](https://raw.githubusercontent.com/stax76/mpv.net/master/screenshots/mpvConfEdit.png) ![](https://raw.githubusercontent.com/stax76/mpv.net/master/screenshots/mpvConfEdit.png)
![](https://raw.githubusercontent.com/stax76/mpv.net/master/screenshots/mpvInputEdit.png) ![](https://raw.githubusercontent.com/stax76/mpv.net/master/screenshots/mpvInputEdit.png)
@@ -56,47 +59,53 @@ if it's missing mpv.net generates it with the following defaults:
### Scripting ### Scripting
Scripting is supported for Python, C#, Lua, JavaScript and PowerShell Scripting is supported via Python, C#, Lua, JavaScript and PowerShell
https://github.com/stax76/mpv.net/wiki/Scripting-(CSharp,-Python,-JavaScript,-Lua,-PowerShell) [Scripting wiki page](https://github.com/stax76/mpv.net/wiki/Scripting-(CSharp,-Python,-JavaScript,-Lua,-PowerShell))
### Support ### Support
<https://forum.doom9.org/showthread.php?t=174841> [Support thread in Doom9 forum](https://forum.doom9.org/showthread.php?t=174841)
<https://forum.videohelp.com/threads/392514-mpv-net-a-extendable-media-player-for-windows> [Support thread in VideoHelp forum](https://forum.videohelp.com/threads/392514-mpv-net-a-extendable-media-player-for-windows)
<https://github.com/stax76/mpv.net/issues> [Issue tracker to report bugs and request features](https://github.com/stax76/mpv.net/issues)
### Changelog ### Changelog
### 2.5 (2019-04-08) ### 3.0 (2019-04-20)
- in case the input conf don't contain a menu definition mpv.net creates the default menu instead no menu like before - the history feature logs now only files that were opened longer than 90 seconds
- all message boxes were migrated to use the TaskDialog API - the default input command for cycling the audio tracks was replaced with an
- an improvement in the previous release unfortunately introduced a bug mpv.net command that shows detailed track info and has no 'no audio' track
causing the conf editor not to save settings - new web site for mpv.net <https://mpv-net.github.io/mpv.net-web-site/>
- the Tracks menu supports now MKV edition selection. [Default binding](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/input.conf.txt#L106).
- the Navigate menu supports now chapter selection. [Default binding](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/input.conf.txt#L57).
- opening the context menu was crashing if the default binding for Tracks was missing
### 2.4 (2019-04-06) [go to download page](https://github.com/stax76/mpv.net/releases)
- new options added to the conf GUI editor: gpu-context, gpu-api, scale, cscale, ### 2.9 (2019-04-16)
dscale, dither-depth, correct-downscaling, sigmoid-upscaling, deband
- the conf edit GUI has a 'Apply' feature added to write the conf to mpv.conf
without the need to close the conf edit GUI
- the input edit GUI shows a message box when a duplicate is detected and it has
a new feature to reduce the filter scope to eather of input, menu or command and
the editor writes always the same help on top of input.conf as it is found in the defaults
- the conf edit GUI was often starting out of working area bounds and is now starting with center screen
- the startup size was reduced and a issue was fixed that when the screen property
was defined for a screen that isn't connected the startup size wasn't applied
- added feature to load external audio and subtitle files in the menu under:
Open > Load external audio|subtitle files (default binding at:
[input.conf](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/input.conf.txt))
- previously the conf edit GUI removed settings from the conf file if the setting
was set to the default, the new behavior is not to remove anything
- the autofit mpv property was partly implemented, you can use 'autofit = 50%' in mpv.conf or
'--autofit=50%' on the command line, WxH isn't implemented and only percent values are accepted.
There is a new wiki page explaining the mpv.net limitations compared to the original mpv:
[Limitations](https://github.com/stax76/mpv.net/wiki/Limitations)
[go to download page](https://github.com/stax76/mpv.net/releases) - clicking the right top corner in full screen mode
closes the player but it did not work on all displays
- the info display was changed to display the filename on top
so it's not displayed in the middle of the screen
- on start up of the conf editor all text is now selected in the
search text box so it's ready for a new search to be typed
- the conf editor was changed to write the settings to disk
only if the settings were actually modified, also the message
that says that the settings will be available on next start
is now only shown if the settings were actually modified.
- there was an instance in the context menu where the sub menu
arrow was overlapping with the text
- in the input editor when only one character is entered in the
search text box the search is performed only in the input and
not in the command or menu
- in the input editor the routine that generates the input string
was completely rewritten because it was adding Shift where it
wasn't necessary (it took a huge amount of time to implement)
- the context menu has a new track menu where the active track
can be seen and selected, it shows video, audio and subtitle
tracks with various metadata. [Menu default definition](https://github.com/stax76/mpv.net/blob/master/mpv.net/Resources/input.conf.txt#L104).
The screenshots were updated showing the [new track menu](https://github.com/stax76/mpv.net#screenshots).

View File

@@ -10,7 +10,7 @@ namespace RatingAddon
[Export(typeof(IAddon))] [Export(typeof(IAddon))]
public class RatingAddon : IAddon public class RatingAddon : IAddon
{ {
private Dictionary<string, int> Dic = new Dictionary<string, int>(); Dictionary<string, int> Dic = new Dictionary<string, int>();
public RatingAddon() public RatingAddon()
{ {
@@ -18,7 +18,7 @@ namespace RatingAddon
mp.Shutdown += mpv_Shutdown; mp.Shutdown += mpv_Shutdown;
} }
private void mpv_Shutdown() void mpv_Shutdown()
{ {
foreach (var i in Dic) foreach (var i in Dic)
{ {
@@ -44,7 +44,7 @@ namespace RatingAddon
} }
} }
private void mpv_ClientMessage(string[] args) void mpv_ClientMessage(string[] args)
{ {
int rating; int rating;

View File

@@ -58,7 +58,7 @@ Public Class Msg
End Try End Try
End Sub End Sub
Private Shared ShownMessages As String Shared ShownMessages As String
Public Shared Sub ShowWarning(mainInstruction As String, Public Shared Sub ShowWarning(mainInstruction As String,
Optional content As String = Nothing, Optional content As String = Nothing,
@@ -421,11 +421,11 @@ Public Class TaskDialog(Of T)
Private ExitTickCount As Integer Private ExitTickCount As Integer
Private Function DialogProc(hwnd As IntPtr, Function DialogProc(hwnd As IntPtr,
msg As UInteger, msg As UInteger,
wParam As IntPtr, wParam As IntPtr,
lParam As IntPtr, lParam As IntPtr,
lpRefData As IntPtr) As Integer lpRefData As IntPtr) As Integer
Select Case msg Select Case msg
Case TDN_BUTTON_CLICKED, TDN_RADIO_BUTTON_CLICKED Case TDN_BUTTON_CLICKED, TDN_RADIO_BUTTON_CLICKED
If TypeOf SelectedValue Is MsgResult Then If TypeOf SelectedValue Is MsgResult Then
@@ -464,7 +464,7 @@ Public Class TaskDialog(Of T)
End Select End Select
End Function End Function
Private Sub MarshalDialogControlStructs() Sub MarshalDialogControlStructs()
If Not Buttons Is Nothing AndAlso Buttons.Count > 0 Then If Not Buttons Is Nothing AndAlso Buttons.Count > 0 Then
ButtonArray = AllocateAndMarshalButtons(Buttons) ButtonArray = AllocateAndMarshalButtons(Buttons)
Config.pButtons = ButtonArray Config.pButtons = ButtonArray
@@ -478,7 +478,7 @@ Public Class TaskDialog(Of T)
End If End If
End Sub End Sub
Private Shared Function AllocateAndMarshalButtons(structs As List(Of TASKDIALOG_BUTTON)) As IntPtr Shared Function AllocateAndMarshalButtons(structs As List(Of TASKDIALOG_BUTTON)) As IntPtr
Dim initialPtr = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(TASKDIALOG_BUTTON)) * structs.Count) Dim initialPtr = Marshal.AllocHGlobal(Marshal.SizeOf(GetType(TASKDIALOG_BUTTON)) * structs.Count)
Dim currentPtr = initialPtr Dim currentPtr = initialPtr

View File

@@ -19,7 +19,8 @@
<DefineDebug>true</DefineDebug> <DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace> <DefineTrace>true</DefineTrace>
<OutputPath>bin\Debug\</OutputPath> <OutputPath>bin\Debug\</OutputPath>
<DocumentationFile>VBNET.xml</DocumentationFile> <DocumentationFile>
</DocumentationFile>
<NoWarn>42353,42354,42355</NoWarn> <NoWarn>42353,42354,42355</NoWarn>
<WarningsAsErrors>41999,42016,42017,42018,42019,42020,42021,42022,42032,42036</WarningsAsErrors> <WarningsAsErrors>41999,42016,42017,42018,42019,42020,42021,42022,42032,42036</WarningsAsErrors>
</PropertyGroup> </PropertyGroup>
@@ -29,7 +30,8 @@
<DefineTrace>true</DefineTrace> <DefineTrace>true</DefineTrace>
<Optimize>true</Optimize> <Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath> <OutputPath>bin\Release\</OutputPath>
<DocumentationFile>VBNET.xml</DocumentationFile> <DocumentationFile>
</DocumentationFile>
<NoWarn>42353,42354,42355</NoWarn> <NoWarn>42353,42354,42355</NoWarn>
<WarningsAsErrors>41999,42016,42017,42018,42019,42020,42021,42022,42032,42036</WarningsAsErrors> <WarningsAsErrors>41999,42016,42017,42018,42019,42020,42021,42022,42032,42036</WarningsAsErrors>
</PropertyGroup> </PropertyGroup>

View File

@@ -14,7 +14,7 @@ namespace mpvnet
[ImportMany] [ImportMany]
public IEnumerable<IAddon> Addons = null; public IEnumerable<IAddon> Addons = null;
private readonly CompositionContainer CompositionContainer; readonly CompositionContainer CompositionContainer;
public Addon() public Addon()
{ {
@@ -28,7 +28,7 @@ namespace mpvnet
foreach (string i in Directory.GetDirectories(dir)) foreach (string i in Directory.GetDirectories(dir))
catalog.Catalogs.Add(new DirectoryCatalog(i, "*Addon.dll")); catalog.Catalogs.Add(new DirectoryCatalog(i, "*Addon.dll"));
dir = mp.mpvConfFolderPath + "\\Addons"; dir = mp.MpvConfFolderPath + "\\Addons";
if (Directory.Exists(dir)) if (Directory.Exists(dir))
foreach (string i in Directory.GetDirectories(dir)) foreach (string i in Directory.GetDirectories(dir))

View File

@@ -2,8 +2,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using VBNET; using VBNET;
@@ -15,7 +15,7 @@ namespace mpvnet
public string Name { get; set; } public string Name { get; set; }
public Action<string[]> Action { get; set; } public Action<string[]> Action { get; set; }
private static List<Command> commands; static List<Command> commands;
public static List<Command> Commands public static List<Command> Commands
{ {
@@ -58,7 +58,7 @@ namespace mpvnet
public static void open_conf_folder(string[] args) public static void open_conf_folder(string[] args)
{ {
Process.Start(mp.mpvConfFolderPath); Process.Start(mp.MpvConfFolderPath);
} }
public static void show_input_editor(string[] args) public static void show_input_editor(string[] args)
@@ -73,7 +73,7 @@ namespace mpvnet
public static void show_history(string[] args) public static void show_history(string[] args)
{ {
var fp = mp.mpvConfFolderPath + "history.txt"; var fp = mp.MpvConfFolderPath + "history.txt";
if (File.Exists(fp)) if (File.Exists(fp))
Process.Start(fp); Process.Start(fp);
@@ -83,41 +83,15 @@ namespace mpvnet
File.WriteAllText(fp, ""); File.WriteAllText(fp, "");
} }
public static void shell_execute(string[] args) public static void shell_execute(string[] args) => Process.Start(args[0]);
{
Process.Start(args[0]);
}
public static void set_setting(string[] args)
{
bool changed = false;
var lines = File.ReadAllLines(mp.mpvConfPath);
for (int i = 0; i < lines.Length; i++)
{
if (lines[i].Contains("=") &&
lines[i].Substring(0, lines[i].IndexOf("=")).Trim("# ".ToCharArray()) == args[0])
{
lines[i] = args[0] + " = " + args[1];
changed = true;
}
}
if (changed)
File.WriteAllText(mp.mpvConfPath, String.Join(Environment.NewLine, lines));
else
File.WriteAllText(mp.mpvConfPath, File.ReadAllText(mp.mpvConfPath) + Environment.NewLine + args[0] + " = " + args[1]);
Msg.Show("Changed settings are available on next startup.");
}
public static void show_info(string[] args) public static void show_info(string[] args)
{ {
try try
{ {
var fileInfo = new FileInfo(mp.get_property_string("path")); FileInfo fileInfo = new FileInfo(mp.get_property_string("path"));
using (var mediaInfo = new MediaInfo(fileInfo.FullName)) using (MediaInfo mediaInfo = new MediaInfo(fileInfo.FullName))
{ {
string width = mediaInfo.GetInfo(MediaInfoStreamKind.Video, "Width"); string width = mediaInfo.GetInfo(MediaInfoStreamKind.Video, "Width");
@@ -133,11 +107,11 @@ namespace mpvnet
string text = ""; string text = "";
if (performer != "") text += "Artist: " + performer + "\n"; if (performer != "") text += "Artist: " + performer + "\n";
if (title != "") text += "Title: " + title + "\n"; if (title != "") text += "Title: " + title + "\n";
if (album != "") text += "Album: " + album + "\n"; if (album != "") text += "Album: " + album + "\n";
if (genre != "") text += "Genre: " + genre + "\n"; if (genre != "") text += "Genre: " + genre + "\n";
if (date != "") text += "Year: " + date + "\n"; if (date != "") text += "Year: " + date + "\n";
if (duration != "") text += "Length: " + duration + "\n"; if (duration != "") text += "Length: " + duration + "\n";
mp.commandv("show-text", text, "5000"); mp.commandv("show-text", text, "5000");
} }
@@ -151,17 +125,19 @@ namespace mpvnet
if (bitrate == "") if (bitrate == "")
bitrate = "0"; bitrate = "0";
var bitrate2 = Convert.ToDouble(bitrate) / 1000.0 / 1000.0; double bitrate2 = Convert.ToDouble(bitrate) / 1000.0 / 1000.0;
var videoCodec = mp.get_property_string("video-format").ToUpper(); string videoCodec = mp.get_property_string("video-format").ToUpper();
var filename = fileInfo.Name; string filename = fileInfo.Name;
var text = string text = filename + "\n" +
FormatTime(position.TotalMinutes) + ":" + FormatTime(position.TotalMinutes) + ":" +
FormatTime(position.Seconds) + " / " + FormatTime(position.Seconds) + " / " +
FormatTime(duration.TotalMinutes) + ":" + FormatTime(duration.TotalMinutes) + ":" +
FormatTime(duration.Seconds) + "\n" + FormatTime(duration.Seconds) + "\n" +
Convert.ToInt32(fileInfo.Length / 1024 / 1024).ToString() + $"{width} x {height}\n" +
$" MB - {width} x {height}\n{videoCodec} - {bitrate2.ToString("f1")} Mb/s" + "\n" + filename; $"{bitrate2.ToString("f1")} Mb/s\n" +
Convert.ToInt32(fileInfo.Length / 1024 / 1024).ToString() + " MB\n" +
$"{videoCodec}\n";
mp.commandv("show-text", text, "5000"); mp.commandv("show-text", text, "5000");
} }
@@ -221,5 +197,22 @@ namespace mpvnet
} }
})); }));
} }
public static void cycle_audio(string[] args)
{
string filePath = mp.get_property_string("path", false);
if (!File.Exists(filePath)) return;
using (MediaInfo mi = new MediaInfo(filePath))
{
MediaTrack[] audTracks = mp.MediaTracks.Where(track => track.Type == "a").ToArray();
if (audTracks.Length < 2) return;
int aid = mp.get_property_int("aid");
aid += 1;
if (aid > audTracks.Length) aid = 1;
mp.commandv("set", "aid", aid.ToString());
mp.commandv("show-text", audTracks[aid - 1].Text.Substring(3), "5000");
}
}
} }
} }

View File

@@ -6,24 +6,35 @@ using System.Threading;
using System.Windows.Forms; using System.Windows.Forms;
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using VBNET; using VBNET;
using System.Globalization;
namespace mpvnet namespace mpvnet
{ {
public partial class MainForm : Form public partial class MainForm : Form
{ {
public static MainForm Instance { get; set; } public static MainForm Instance { get; set; }
public static IntPtr Hwnd; public static IntPtr Hwnd { get; set; }
private Point LastCursorPosChanged; public new ContextMenuStripEx ContextMenu;
private int LastCursorChangedTickCount;
private bool IgnoreDpiChanged = true;
private float mpvAutofit = 0.42f;
private bool mpvFullscreen;
private int mpvScreen = -1;
public ContextMenuStripEx CMS; MenuItemEx TracksMenu;
MenuItemEx ChaptersMenu;
Point LastCursorPosChanged;
int LastCursorChangedTickCount;
bool IgnoreDpiChanged = true;
float MpvAutofit = 0.50f;
bool MpvFullscreen;
int MpvScreen = -1;
string MpvNetDarkMode = "system";
string MpvSid = "";
string MpvAid = "";
string MpvVid = "";
int MpvEdition;
public MainForm() public MainForm()
{ {
@@ -43,12 +54,15 @@ namespace mpvnet
foreach (var i in mp.mpvConf) foreach (var i in mp.mpvConf)
ProcessMpvProperty(i.Key, i.Value); ProcessMpvProperty(i.Key, i.Value);
foreach (var i in mp.mpvNetConf)
ProcessMpvNetProperty(i.Key, i.Value);
ProcessCommandLineEarly(); ProcessCommandLineEarly();
if (mpvScreen == -1) mpvScreen = Array.IndexOf(Screen.AllScreens, Screen.PrimaryScreen); if (MpvScreen == -1) MpvScreen = Array.IndexOf(Screen.AllScreens, Screen.PrimaryScreen);
SetScreen(mpvScreen); SetScreen(MpvScreen);
ChangeFullscreen(mpvFullscreen); ChangeFullscreen(MpvFullscreen);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -56,6 +70,77 @@ namespace mpvnet
} }
} }
void ContextMenu_Opening(object sender, CancelEventArgs e)
{
lock (mp.MediaTracks)
{
if (TracksMenu != null)
TracksMenu.DropDownItems.Clear();
MediaTrack[] audTracks = mp.MediaTracks.Where(track => track.Type == "a").ToArray();
MediaTrack[] subTracks = mp.MediaTracks.Where(track => track.Type == "s").ToArray();
MediaTrack[] vidTracks = mp.MediaTracks.Where(track => track.Type == "v").ToArray();
MediaTrack[] ediTracks = mp.MediaTracks.Where(track => track.Type == "e").ToArray();
foreach (MediaTrack track in vidTracks)
{
MenuItemEx mi = ContextMenu.Add("Track > " + track.Text);
mi.Action = () => { mp.commandv("set", "vid", track.ID.ToString()); };
mi.Checked = MpvVid == track.ID.ToString();
}
if (vidTracks.Length > 0)
ContextMenu.Add("Track > -");
foreach (MediaTrack track in audTracks)
{
MenuItemEx mi = ContextMenu.Add("Track > " + track.Text);
mi.Action = () => { mp.commandv("set", "aid", track.ID.ToString()); };
mi.Checked = MpvAid == track.ID.ToString();
}
if (subTracks.Length > 0)
ContextMenu.Add("Track > -");
foreach (MediaTrack track in subTracks)
{
MenuItemEx mi = ContextMenu.Add("Track > " + track.Text);
mi.Action = () => { mp.commandv("set", "sid", track.ID.ToString()); };
mi.Checked = MpvSid == track.ID.ToString();
}
if (subTracks.Length > 0)
{
MenuItemEx mi = ContextMenu.Add("Track > S: No subtitles");
mi.Action = () => { mp.commandv("set", "sid", "no"); };
mi.Checked = MpvSid == "no";
}
if (ediTracks.Length > 0)
ContextMenu.Add("Track > -");
foreach (MediaTrack track in ediTracks)
{
MenuItemEx mi = ContextMenu.Add("Track > " + track.Text);
mi.Action = () => { mp.commandv("set", "edition", track.ID.ToString()); };
mi.Checked = MpvEdition == track.ID;
}
}
lock (mp.Chapters)
{
if (ChaptersMenu != null)
ChaptersMenu.DropDownItems.Clear();
foreach (var i in mp.Chapters)
{
MenuItemEx mi = ContextMenu.Add("Navigate > Chapters > " + i.Key);
mi.ShortcutKeyDisplayString = TimeSpan.FromSeconds(i.Value).ToString().Substring(0, 8) + " ";
mi.Action = () => { mp.commandv("seek", i.Value.ToString(CultureInfo.InvariantCulture), "absolute"); };
}
}
}
protected void SetScreen(int targetIndex) protected void SetScreen(int targetIndex)
{ {
Screen[] screens = Screen.AllScreens; Screen[] screens = Screen.AllScreens;
@@ -76,7 +161,7 @@ namespace mpvnet
{ {
if (IsFullscreen || mp.VideoSize.Width == 0) return; if (IsFullscreen || mp.VideoSize.Width == 0) return;
Screen screen = Screen.FromControl(this); Screen screen = Screen.FromControl(this);
int height = Convert.ToInt32(screen.Bounds.Height * mpvAutofit); int height = Convert.ToInt32(screen.Bounds.Height * MpvAutofit);
int width = Convert.ToInt32(height * mp.VideoSize.Width / (double)mp.VideoSize.Height); int width = Convert.ToInt32(height * mp.VideoSize.Width / (double)mp.VideoSize.Height);
Point middlePos = new Point(Left + Width / 2, Top + Height / 2); Point middlePos = new Point(Left + Width / 2, Top + Height / 2);
var rect = new Native.RECT(new Rectangle(screen.Bounds.X, screen.Bounds.Y, width, height)); var rect = new Native.RECT(new Rectangle(screen.Bounds.X, screen.Bounds.Y, width, height));
@@ -91,7 +176,7 @@ namespace mpvnet
if (IsFullscreen || mp.VideoSize.Width == 0) return; if (IsFullscreen || mp.VideoSize.Width == 0) return;
Screen screen = Screen.FromControl(this); Screen screen = Screen.FromControl(this);
int height = ClientSize.Height; int height = ClientSize.Height;
if (height > screen.Bounds.Height * 0.9) height = Convert.ToInt32(screen.Bounds.Height * mpvAutofit); if (height > screen.Bounds.Height * 0.9) height = Convert.ToInt32(screen.Bounds.Height * MpvAutofit);
int width = Convert.ToInt32(height * mp.VideoSize.Width / (double)mp.VideoSize.Height); int width = Convert.ToInt32(height * mp.VideoSize.Width / (double)mp.VideoSize.Height);
Point middlePos = new Point(Left + Width / 2, Top + Height / 2); Point middlePos = new Point(Left + Width / 2, Top + Height / 2);
var rect = new Native.RECT(new Rectangle(screen.Bounds.X, screen.Bounds.Y, width, height)); var rect = new Native.RECT(new Rectangle(screen.Bounds.X, screen.Bounds.Y, width, height));
@@ -118,6 +203,7 @@ namespace mpvnet
string left = i.Substring(2, i.IndexOf("=") - 2); string left = i.Substring(2, i.IndexOf("=") - 2);
string right = i.Substring(left.Length + 3); string right = i.Substring(left.Length + 3);
ProcessMpvProperty(left, right); ProcessMpvProperty(left, right);
ProcessMpvNetProperty(left, right);
} }
else else
{ {
@@ -127,7 +213,7 @@ namespace mpvnet
{ {
case "fs": case "fs":
case "fullscreen": case "fullscreen":
mpvFullscreen = true; MpvFullscreen = true;
break; break;
} }
} }
@@ -142,14 +228,24 @@ namespace mpvnet
case "autofit": case "autofit":
if (value.Length == 3 && value.EndsWith("%")) if (value.Length == 3 && value.EndsWith("%"))
if (int.TryParse(value.Substring(0, 2), out int result)) if (int.TryParse(value.Substring(0, 2), out int result))
mpvAutofit = result / 100f; MpvAutofit = result / 100f;
break; break;
case "fs": case "fs":
case "fullscreen": case "fullscreen":
mpvFullscreen = value == "yes"; MpvFullscreen = value == "yes";
break; break;
case "screen": case "screen":
mpvScreen = Convert.ToInt32(value); MpvScreen = Convert.ToInt32(value);
break;
}
}
void ProcessMpvNetProperty(string name, string value)
{
switch (name)
{
case "dark-mode":
MpvNetDarkMode = value;
break; break;
} }
} }
@@ -195,7 +291,7 @@ namespace mpvnet
else else
input = ""; input = "";
var menuItem = CMS.Add(path, () => { MenuItemEx menuItem = ContextMenu.Add(path, () => {
try { try {
mp.command_string(command); mp.command_string(command);
} }
@@ -205,51 +301,46 @@ namespace mpvnet
}); });
if (menuItem != null) if (menuItem != null)
menuItem.ShortcutKeyDisplayString = input.Replace("_","") + " "; {
menuItem.ShortcutKeyDisplayString = input.Replace("_", "") + " ";
if (TracksMenu == null && menuItem.Text.StartsWith("Track ") &&
menuItem.Text.Trim() == "Track")
TracksMenu = menuItem;
if (ChaptersMenu == null && menuItem.Text.StartsWith("Chapters ") &&
menuItem.Text.Trim() == "Chapters")
ChaptersMenu = menuItem;
}
} }
} }
private void CMS_Opened(object sender, EventArgs e) => CursorHelp.Show(); void ContextMenu_Opened(object sender, EventArgs e) => CursorHelp.Show();
private string LastHistory; void mp_PlaybackRestart() => BeginInvoke(new Action(() => { Text = Path.GetFileName(mp.get_property_string("path")) + " - mpv.net " + Application.ProductVersion; }));
private void mp_PlaybackRestart() void Mp_Idle() => BeginInvoke(new Action(() => { Text = "mpv.net " + Application.ProductVersion; }));
{
var filename = mp.get_property_string("filename");
BeginInvoke(new Action(() => { Text = filename + " - mpv.net " + Application.ProductVersion; }));
var historyFilepath = mp.mpvConfFolderPath + "history.txt";
if (LastHistory != filename && File.Exists(historyFilepath)) void CM_Popup(object sender, EventArgs e) => CursorHelp.Show();
{
File.AppendAllText(historyFilepath, DateTime.Now.ToString() + " " +
Path.GetFileNameWithoutExtension(filename) + "\r\n");
LastHistory = filename;
}
}
private void Mp_Idle() void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
BeginInvoke(new Action(() => { Text = "mpv.net " + Application.ProductVersion; }));
}
private void CM_Popup(object sender, EventArgs e) => CursorHelp.Show();
private void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{ {
Msg.ShowException(e.Exception); Msg.ShowException(e.Exception);
} }
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{ {
Msg.ShowError(e.ExceptionObject.ToString()); Msg.ShowError(e.ExceptionObject.ToString());
} }
private void mp_VideoSizeChanged() void mp_VideoSizeChanged()
{ {
BeginInvoke(new Action(() => SetFormPositionAndSizeKeepHeight())); BeginInvoke(new Action(() => SetFormPositionAndSizeKeepHeight()));
} }
private void mp_Shutdown() void mp_Shutdown()
{ {
BeginInvoke(new Action(() => Close())); BeginInvoke(new Action(() => Close()));
} }
@@ -363,11 +454,7 @@ namespace mpvnet
Native.PostMessage(Handle, 0xA1 /* WM_NCLBUTTONDOWN */, HTCAPTION, IntPtr.Zero); Native.PostMessage(Handle, 0xA1 /* WM_NCLBUTTONDOWN */, HTCAPTION, IntPtr.Zero);
} }
var sb = Screen.FromControl(this).Bounds; if (Width - e.Location.X < 10 && e.Location.Y < 10)
var p1 = new Point(sb.Width, 0);
var p2 = PointToScreen(e.Location);
if (Math.Abs(p1.X - p2.X) < 10 && Math.Abs(p1.Y - p2.Y) < 10)
mp.commandv("quit"); mp.commandv("quit");
} }
@@ -380,12 +467,9 @@ namespace mpvnet
CursorHelp.Show(); CursorHelp.Show();
} }
bool IsMouseInOSC() bool IsMouseInOSC() => PointToClient(Control.MousePosition).Y > ClientSize.Height * 0.9;
{
return PointToClient(Control.MousePosition).Y > ClientSize.Height * 0.9;
}
private void Timer_Tick(object sender, EventArgs e) void Timer_Tick(object sender, EventArgs e)
{ {
if (CursorHelp.IsPosDifferent(LastCursorPosChanged)) if (CursorHelp.IsPosDifferent(LastCursorPosChanged))
{ {
@@ -394,7 +478,7 @@ namespace mpvnet
} }
else if (Environment.TickCount - LastCursorChangedTickCount > 1500 && else if (Environment.TickCount - LastCursorChangedTickCount > 1500 &&
!IsMouseInOSC() && ClientRectangle.Contains(PointToClient(MousePosition)) && !IsMouseInOSC() && ClientRectangle.Contains(PointToClient(MousePosition)) &&
Form.ActiveForm == this && !CMS.Visible) Form.ActiveForm == this && !ContextMenu.Visible)
{ {
CursorHelp.Hide(); CursorHelp.Hide();
} }
@@ -414,6 +498,10 @@ namespace mpvnet
mp.Init(); mp.Init();
mp.observe_property_bool("fullscreen", mpPropChangeFullscreen); mp.observe_property_bool("fullscreen", mpPropChangeFullscreen);
mp.observe_property_bool("ontop", mpPropChangeOnTop); mp.observe_property_bool("ontop", mpPropChangeOnTop);
mp.observe_property_string("sid", mpPropChangeSid);
mp.observe_property_string("aid", mpPropChangeAid);
mp.observe_property_string("vid", mpPropChangeVid);
mp.observe_property_int("edition", mpPropChangeEdition);
mp.Shutdown += mp_Shutdown; mp.Shutdown += mp_Shutdown;
mp.VideoSizeChanged += mp_VideoSizeChanged; mp.VideoSizeChanged += mp_VideoSizeChanged;
mp.PlaybackRestart += mp_PlaybackRestart; mp.PlaybackRestart += mp_PlaybackRestart;
@@ -422,13 +510,24 @@ namespace mpvnet
void mpPropChangeOnTop(bool value) => BeginInvoke(new Action(() => TopMost = value)); void mpPropChangeOnTop(bool value) => BeginInvoke(new Action(() => TopMost = value));
void mpPropChangeAid(string value) => MpvAid = value;
void mpPropChangeSid(string value) => MpvSid = value;
void mpPropChangeVid(string value) => MpvVid = value;
void mpPropChangeEdition(int value) => MpvEdition = value;
protected override void OnShown(EventArgs e) protected override void OnShown(EventArgs e)
{ {
base.OnShown(e); base.OnShown(e);
CMS = new ContextMenuStripEx(components); if ((MpvNetDarkMode == "system" && Misc.IsDarkTheme) || MpvNetDarkMode == "always")
CMS.Opened += CMS_Opened; ToolStripRendererEx.ColorTheme = Color.Black;
ContextMenuStrip = CMS; ContextMenu = new ContextMenuStripEx(components);
ContextMenu.Opened += ContextMenu_Opened;
ContextMenu.Opening += ContextMenu_Opening;
BuildMenu(); BuildMenu();
ContextMenuStrip = ContextMenu;
IgnoreDpiChanged = false; IgnoreDpiChanged = false;
CheckYouTube(); CheckYouTube();
} }
@@ -437,7 +536,7 @@ namespace mpvnet
{ {
base.OnFormClosed(e); base.OnFormClosed(e);
mp.commandv("quit"); mp.commandv("quit");
mp.AutoResetEvent.WaitOne(3000); mp.AutoResetEvent.WaitOne(3000);
} }
protected override void OnLostFocus(EventArgs e) protected override void OnLostFocus(EventArgs e)
@@ -452,14 +551,14 @@ namespace mpvnet
CheckYouTube(); CheckYouTube();
} }
private string LastURL;
void CheckYouTube() void CheckYouTube()
{ {
string clipboard = Clipboard.GetText(); string clipboard = Clipboard.GetText();
if (clipboard.StartsWith("https://www.youtube.com/watch?") && LastURL != clipboard && Visible)
if (clipboard.StartsWith("https://www.youtube.com/watch?") && RegistryHelp.GetValue("HKCU\\Software\\" + Application.ProductName, "LastYouTubeURL") != clipboard && Visible)
{ {
LastURL = clipboard; RegistryHelp.SetValue("HKCU\\Software\\" + Application.ProductName, "LastYouTubeURL", clipboard);
if (Msg.ShowQuestion("Play YouTube URL?") == MsgResult.OK) if (Msg.ShowQuestion("Play YouTube URL?") == MsgResult.OK)
mp.LoadURL(clipboard); mp.LoadURL(clipboard);
} }

View File

@@ -3,8 +3,8 @@ using System.Runtime.InteropServices;
public class MediaInfo : IDisposable public class MediaInfo : IDisposable
{ {
private IntPtr Handle; IntPtr Handle;
private static bool Loaded; static bool Loaded;
public MediaInfo(string sourcepath) public MediaInfo(string sourcepath)
{ {
@@ -25,7 +25,24 @@ public class MediaInfo : IDisposable
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, streamKind, 0, parameter, MediaInfoInfoKind.Text, MediaInfoInfoKind.Name)); return Marshal.PtrToStringUni(MediaInfo_Get(Handle, streamKind, 0, parameter, MediaInfoInfoKind.Text, MediaInfoInfoKind.Name));
} }
private bool Disposed; public int GetCount(MediaInfoStreamKind streamKind) => MediaInfo_Count_Get(Handle, streamKind, -1);
public string GetVideo(int streamNumber, string parameter)
{
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, MediaInfoStreamKind.Video, streamNumber, parameter, MediaInfoInfoKind.Text, MediaInfoInfoKind.Name));
}
public string GetAudio(int streamNumber, string parameter)
{
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, MediaInfoStreamKind.Audio, streamNumber, parameter, MediaInfoInfoKind.Text, MediaInfoInfoKind.Name));
}
public string GetText(int streamNumber, string parameter)
{
return Marshal.PtrToStringUni(MediaInfo_Get(Handle, MediaInfoStreamKind.Text, streamNumber, parameter, MediaInfoInfoKind.Text, MediaInfoInfoKind.Name));
}
bool Disposed;
public void Dispose() public void Dispose()
{ {
@@ -37,28 +54,35 @@ public class MediaInfo : IDisposable
} }
} }
~MediaInfo() ~MediaInfo() { Dispose(); }
{
Dispose();
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)] [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr LoadLibrary(string path); static extern IntPtr LoadLibrary(string path);
[DllImport("MediaInfo.dll")] [DllImport("MediaInfo.dll")]
private static extern IntPtr MediaInfo_New(); static extern IntPtr MediaInfo_New();
[DllImport("MediaInfo.dll")] [DllImport("MediaInfo.dll")]
private static extern void MediaInfo_Delete(IntPtr Handle); static extern void MediaInfo_Delete(IntPtr handle);
[DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)] [DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)]
private static extern int MediaInfo_Open(IntPtr Handle, string FileName); static extern int MediaInfo_Open(IntPtr handle, string fileName);
[DllImport("MediaInfo.dll")] [DllImport("MediaInfo.dll")]
private static extern int MediaInfo_Close(IntPtr Handle); static extern int MediaInfo_Close(IntPtr handle);
[DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)] [DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr MediaInfo_Get(IntPtr Handle, MediaInfoStreamKind StreamKind, int StreamNumber, string Parameter, MediaInfoInfoKind KindOfInfo, MediaInfoInfoKind KindOfSearch); static extern IntPtr MediaInfo_Get(IntPtr handle,
MediaInfoStreamKind streamKind,
int streamNumber,
string parameter,
MediaInfoInfoKind kindOfInfo,
MediaInfoInfoKind kindOfSearch);
[DllImport("MediaInfo.dll", CharSet = CharSet.Unicode)]
static extern int MediaInfo_Count_Get(IntPtr handle,
MediaInfoStreamKind streamKind,
int streamNumber);
} }
public enum MediaInfoStreamKind public enum MediaInfoStreamKind
@@ -67,8 +91,10 @@ public enum MediaInfoStreamKind
Video, Video,
Audio, Audio,
Text, Text,
Chapters, Other,
Image Image,
Menu,
Max,
} }
public enum MediaInfoInfoKind public enum MediaInfoInfoKind

View File

@@ -23,110 +23,23 @@ public class ContextMenuStripEx : ContextMenuStrip
Renderer = new ToolStripRendererEx(); Renderer = new ToolStripRendererEx();
} }
public ActionMenuItem Add(string path) public MenuItemEx Add(string path)
{ {
return Add(path, null); return Add(path, null);
} }
public ActionMenuItem Add(string path, Action action) public MenuItemEx Add(string path, Action action, bool enabled = true)
{ {
return Add(path, action, true); MenuItemEx ret = MenuItemEx.Add(Items, path, action);
} if (ret == null) return null;
public ActionMenuItem Add(string path, Action action, bool enabled)
{
var ret = ActionMenuItem.Add(Items, path, action);
if (ret == null)
return null;
ret.Enabled = enabled; ret.Enabled = enabled;
return ret; return ret;
} }
public ActionMenuItem Add(string path, Action action, Func<bool> enabledFunc)
{
var ret = ActionMenuItem.Add(Items, path, action);
return ret;
}
}
public class ActionMenuItem : MenuItemEx
{
private Action Action;
public ActionMenuItem()
{
}
public ActionMenuItem(string text, Action action)
{
this.Text = text;
this.Action = action;
}
protected override void OnClick(EventArgs e)
{
Application.DoEvents();
if (Action != null)
Action();
base.OnClick(e);
}
public static ActionMenuItem Add<T>(ToolStripItemCollection items, string path, Action<T> action, T value)
{
return Add(items, path, () => action(value));
}
public static ActionMenuItem Add(ToolStripItemCollection items, string path, Action action)
{
var a = path.Split(new[] { " > ", " | " }, StringSplitOptions.RemoveEmptyEntries);
var l = items;
for (var x = 0; x <= a.Length - 1; x++)
{
var found = false;
foreach (var i in l.OfType<ToolStripMenuItem>())
{
if (x < a.Length - 1)
{
if (i.Text == a[x] + " ")
{
found = true;
l = i.DropDownItems;
}
}
}
if (!found)
{
if (x == a.Length - 1)
{
if (a[x] == "-")
l.Add(new ToolStripSeparator());
else
{
ActionMenuItem item = new ActionMenuItem(a[x] + " ", action);
l.Add(item);
l = item.DropDownItems;
return item;
}
}
else
{
ActionMenuItem item = new ActionMenuItem();
item.Text = a[x] + " ";
l.Add(item);
l = item.DropDownItems;
}
}
}
return null;
}
} }
public class MenuItemEx : ToolStripMenuItem public class MenuItemEx : ToolStripMenuItem
{ {
public static bool UseTooltips { get; set; } public Action Action { get; set; }
public MenuItemEx() public MenuItemEx()
{ {
@@ -136,20 +49,81 @@ public class MenuItemEx : ToolStripMenuItem
{ {
} }
public MenuItemEx(string text, Action action) : base(text)
{
Action = action;
}
protected override void OnClick(EventArgs e)
{
Application.DoEvents();
Action?.Invoke();
base.OnClick(e);
}
public static MenuItemEx Add<T>(ToolStripItemCollection items, string path, Action<T> action, T value)
{
return Add(items, path, () => action(value));
}
public static MenuItemEx Add(ToolStripItemCollection items, string path, Action action)
{
string[] a = path.Split(new[] { " > ", " | " }, StringSplitOptions.RemoveEmptyEntries);
var itemsCollection = items;
for (int x = 0; x < a.Length; x++)
{
bool found = false;
foreach (var i in itemsCollection.OfType<ToolStripMenuItem>())
{
if (x < a.Length - 1)
{
if (i.Text == a[x] + " ")
{
found = true;
itemsCollection = i.DropDownItems;
}
}
}
if (!found)
{
if (x == a.Length - 1)
{
if (a[x] == "-")
itemsCollection.Add(new ToolStripSeparator());
else
{
MenuItemEx item = new MenuItemEx(a[x] + " ", action);
itemsCollection.Add(item);
itemsCollection = item.DropDownItems;
return item;
}
}
else
{
MenuItemEx item = new MenuItemEx();
item.Text = a[x] + " ";
itemsCollection.Add(item);
itemsCollection = item.DropDownItems;
}
}
}
return null;
}
public override Size GetPreferredSize(Size constrainingSize) public override Size GetPreferredSize(Size constrainingSize)
{ {
var ret = base.GetPreferredSize(constrainingSize); Size size = base.GetPreferredSize(constrainingSize);
ret.Height = Convert.ToInt32(Font.Height * 1.4); size.Height = Convert.ToInt32(Font.Height * 1.4);
return ret; return size;
} }
public void CloseAll(object item) public void CloseAll(object item)
{ {
if (item is ToolStripItem) if (item is ToolStripItem)
{ CloseAll(((ToolStripItem)item).Owner);
var d = (ToolStripItem)item;
CloseAll(d.Owner);
}
if (item is ToolStripDropDown) if (item is ToolStripDropDown)
{ {
@@ -158,20 +132,16 @@ public class MenuItemEx : ToolStripMenuItem
CloseAll(d.OwnerItem); CloseAll(d.OwnerItem);
} }
} }
protected override void OnClick(EventArgs e)
{
Application.DoEvents();
base.OnClick(e);
}
} }
public class ToolStripRendererEx : ToolStripSystemRenderer public class ToolStripRendererEx : ToolStripSystemRenderer
{ {
public static Color ColorForeground { get; set; } = Color.Black;
public static Color ColorTheme { get; set; } = Color.Empty;
public static Color ColorChecked { get; set; } public static Color ColorChecked { get; set; }
public static Color ColorBorder { get; set; } public static Color ColorBorder { get; set; }
public static Color ColorTop { get; set; } public static Color ColorTop { get; set; }
public static Color ColorBottom { get; set; } public static Color ColorSelection { get; set; }
public static Color ColorBackground { get; set; } public static Color ColorBackground { get; set; }
public static Color ColorToolStrip1 { get; set; } public static Color ColorToolStrip1 { get; set; }
@@ -179,24 +149,37 @@ public class ToolStripRendererEx : ToolStripSystemRenderer
public static Color ColorToolStrip3 { get; set; } public static Color ColorToolStrip3 { get; set; }
public static Color ColorToolStrip4 { get; set; } public static Color ColorToolStrip4 { get; set; }
private int TextOffset; int TextOffset;
public ToolStripRendererEx() public ToolStripRendererEx()
{ {
var argb = Convert.ToInt32(Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM", "ColorizationColor", 0)); var argb = Convert.ToInt32(Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\DWM", "ColorizationColor", 0));
if (argb == 0) if (argb == 0)
argb = Color.LightBlue.ToArgb(); argb = Color.LightBlue.ToArgb();
InitColors(Color.FromArgb(argb)); if (ColorTheme == Color.Empty)
InitColors(Color.FromArgb(argb));
else
InitColors(ColorTheme);
} }
public static void InitColors(Color c) public static void InitColors(Color c)
{ {
ColorBorder = HSLColor.Convert(c).ToColorSetLuminosity(100); ColorBorder = HSLColor.Convert(c).ToColorSetLuminosity(100);
ColorChecked = HSLColor.Convert(c).ToColorSetLuminosity(200); ColorChecked = HSLColor.Convert(c).ToColorSetLuminosity(160);
ColorBottom = HSLColor.Convert(c).ToColorSetLuminosity(220); ColorSelection = HSLColor.Convert(c).ToColorSetLuminosity(180);
ColorBackground = HSLColor.Convert(c).ToColorSetLuminosity(230); ColorBackground = HSLColor.Convert(c).ToColorSetLuminosity(210);
ColorTop = HSLColor.Convert(c).ToColorSetLuminosity(240); ColorTop = HSLColor.Convert(c).ToColorSetLuminosity(240);
if (ColorTheme == Color.Black)
{
ColorBorder = Color.White;
ColorBackground = Color.FromArgb(50, 50, 50);
ColorSelection = Color.FromArgb(80, 80, 80);
ColorForeground = Color.White;
ColorChecked = Color.FromArgb(90, 90, 90);
}
ColorToolStrip1 = ControlPaint.LightLight(ControlPaint.LightLight(ControlPaint.Light(ColorBorder, 1))); ColorToolStrip1 = ControlPaint.LightLight(ControlPaint.LightLight(ControlPaint.Light(ColorBorder, 1)));
ColorToolStrip2 = ControlPaint.LightLight(ControlPaint.LightLight(ControlPaint.Light(ColorBorder, 0.7f))); ColorToolStrip2 = ControlPaint.LightLight(ControlPaint.LightLight(ControlPaint.Light(ColorBorder, 0.7f)));
ColorToolStrip3 = ControlPaint.LightLight(ControlPaint.LightLight(ControlPaint.Light(ColorBorder, 0.1f))); ColorToolStrip3 = ControlPaint.LightLight(ControlPaint.LightLight(ControlPaint.Light(ColorBorder, 0.1f)));
@@ -205,7 +188,10 @@ public class ToolStripRendererEx : ToolStripSystemRenderer
protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e) protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e)
{ {
ControlPaint.DrawBorder(e.Graphics, e.AffectedBounds, Color.FromArgb(160, 175, 195), ButtonBorderStyle.Solid); Rectangle r = e.AffectedBounds;
r.Inflate(-1, -1);
ControlPaint.DrawBorder(e.Graphics, r, ColorBackground, ButtonBorderStyle.Solid);
ControlPaint.DrawBorder(e.Graphics, e.AffectedBounds, ColorBorder, ButtonBorderStyle.Solid);
} }
protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e) protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e)
@@ -222,6 +208,7 @@ public class ToolStripRendererEx : ToolStripSystemRenderer
else else
TextOffset = Convert.ToInt32(e.Item.Height * 0.2); TextOffset = Convert.ToInt32(e.Item.Height * 0.2);
e.TextColor = ColorForeground;
e.TextRectangle = new Rectangle(TextOffset, Convert.ToInt32((e.Item.Height - rect.Height) / 2.0), rect.Width, rect.Height); e.TextRectangle = new Rectangle(TextOffset, Convert.ToInt32((e.Item.Height - rect.Height) / 2.0), rect.Width, rect.Height);
} }
@@ -235,118 +222,88 @@ public class ToolStripRendererEx : ToolStripSystemRenderer
Rectangle r = new Rectangle(-1, -1, e.AffectedBounds.Width, e.AffectedBounds.Height); Rectangle r = new Rectangle(-1, -1, e.AffectedBounds.Width, e.AffectedBounds.Height);
using (SolidBrush b = new SolidBrush(ColorToolStrip2)) using (SolidBrush b = new SolidBrush(ColorToolStrip2))
{
e.Graphics.FillRectangle(b, r); e.Graphics.FillRectangle(b, r);
}
} }
} }
protected override void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e) protected override void OnRenderMenuItemBackground(ToolStripItemRenderEventArgs e)
{ {
e.Item.ForeColor = Color.Black; Rectangle rect = new Rectangle(Point.Empty, e.Item.Size);
var r = new Rectangle(Point.Empty, e.Item.Size);
var g = e.Graphics;
if (!(e.Item.Owner is MenuStrip)) if (!(e.Item.Owner is MenuStrip))
g.Clear(ColorBackground); e.Graphics.Clear(ColorBackground);
if (e.Item.Selected && e.Item.Enabled) if (e.Item.Selected && e.Item.Enabled)
{ {
if (e.Item.Owner is MenuStrip) e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
DrawButton(e); rect = new Rectangle(rect.X + 2, rect.Y, rect.Width - 4, rect.Height - 1);
else rect.Inflate(-1, -1);
{ using (SolidBrush b = new SolidBrush(ColorSelection))
g.SmoothingMode = SmoothingMode.AntiAlias; e.Graphics.FillRectangle(b, rect);
var r2 = new Rectangle(r.X + 2, r.Y, r.Width - 4, r.Height - 1);
using (Pen pen = new Pen(ColorBorder))
{
g.DrawRectangle(pen, r2);
}
r2.Inflate(-1, -1);
using (SolidBrush b = new SolidBrush(ColorBottom))
{
g.FillRectangle(b, r2);
}
}
} }
} }
public void DrawButton(ToolStripItemRenderEventArgs e)
{
var gx = e.Graphics;
var rect = new Rectangle(Point.Empty, e.Item.Size);
var rect2 = new Rectangle(rect.X, rect.Y, rect.Width - 1, rect.Height - 1);
using (Pen pen = new Pen(ColorBorder))
gx.DrawRectangle(pen, rect2);
rect2.Inflate(-1, -1);
var tsb = e.Item as ToolStripButton;
if (tsb != null && tsb.Checked)
using (SolidBrush brush = new SolidBrush(ColorChecked))
gx.FillRectangle(brush, rect2);
else
using (SolidBrush brush = new SolidBrush(ColorBottom))
gx.FillRectangle(brush, rect2);
}
protected override void OnRenderDropDownButtonBackground(ToolStripItemRenderEventArgs e)
{
if (e.Item.Selected)
DrawButton(e);
}
protected override void OnRenderButtonBackground(ToolStripItemRenderEventArgs e)
{
var button = (ToolStripButton)e.Item;
if (e.Item.Selected || button.Checked)
DrawButton(e);
}
protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e) protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e)
{ {
var value = e.Direction == ArrowDirection.Down ? 0x36 : 0x34; if (e.Direction == ArrowDirection.Down) throw new NotImplementedException();
var s = Convert.ToChar(value).ToString(); float x1 = e.Item.Width - e.Item.Height * 0.6f;
var font = new Font("Marlett", e.Item.Font.Size - 2); float y1 = e.Item.Height * 0.25f;
var size = e.Graphics.MeasureString(s, font); float x2 = x1 + e.Item.Height * 0.25f;
var x = Convert.ToInt32(e.Item.Width - size.Width); float y2 = e.Item.Height / 2f;
var y = Convert.ToInt32((e.Item.Height - size.Height) / 2.0) + 1; float x3 = x1;
e.Graphics.DrawString(s, font, Brushes.Black, x, y); float y3 = e.Item.Height * 0.75f;
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
using (Brush b = new SolidBrush(ColorForeground))
{
using (Pen p = new Pen(b, Control.DefaultFont.Height / 20f))
{
e.Graphics.DrawLine(p, x1, y1, x2, y2);
e.Graphics.DrawLine(p, x2, y2, x3, y3);
}
}
} }
protected override void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e) protected override void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e)
{ {
int x = Convert.ToInt32(e.ImageRectangle.Height * 0.2); if (e.Item.GetType() != typeof(MenuItemEx))
e.Graphics.DrawImage(e.Image, new Point(x, x)); return;
MenuItemEx item = e.Item as MenuItemEx;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
if (!item.Checked)
return;
Rectangle rect = new Rectangle(Point.Empty, e.Item.Size);
rect = new Rectangle(rect.X + 2, rect.Y, rect.Height - 1, rect.Height - 1);
rect.Inflate(-1, -1);
using (Brush brush = new SolidBrush(ColorChecked))
e.Graphics.FillRectangle(brush, rect);
float ellipseWidth = rect.Height / 3f;
RectangleF rectF = new RectangleF(rect.X + rect.Height / 2f - ellipseWidth / 2f,
rect.Y + rect.Height / 2f - ellipseWidth / 2f,
ellipseWidth,
ellipseWidth);
using (Brush brush = new SolidBrush(ColorForeground))
e.Graphics.FillEllipse(brush, rectF);
} }
protected override void OnRenderSeparator(ToolStripSeparatorRenderEventArgs e) protected override void OnRenderSeparator(ToolStripSeparatorRenderEventArgs e)
{ {
if (e.Item.IsOnDropDown) e.Graphics.Clear(ColorBackground);
{ int top = e.Item.Height / 2;
e.Graphics.Clear(ColorBackground); top -= 1;
int right = e.Item.Width - Convert.ToInt32(TextOffset / 5.0); int offset = Convert.ToInt32(e.Item.Font.Height * 0.7);
int top = e.Item.Height / 2; using (Pen p = new Pen(ColorBorder))
top -= 1; e.Graphics.DrawLine(p,
using (Pen p = new Pen(Color.Gray)) new Point(offset, top),
e.Graphics.DrawLine(p, new Point(TextOffset, top), new Point(right, top)); new Point(e.Item.Width - offset, top));
}
else if (e.Vertical)
{
var bounds = e.Item.Bounds;
using (Pen p = new Pen(SystemColors.ControlDarkDark))
e.Graphics.DrawLine(p,
Convert.ToInt32(bounds.Width / 2.0),
Convert.ToInt32(bounds.Height * 0.15),
Convert.ToInt32(bounds.Width / 2.0),
Convert.ToInt32(bounds.Height * 0.85));
}
} }
} }
@@ -364,31 +321,28 @@ public struct HSLColor
Luminosity = l; Luminosity = l;
} }
private double hue; double _Hue;
public int Hue public int Hue {
{ get => System.Convert.ToInt32(_Hue * 240);
get => System.Convert.ToInt32(hue * 240); set => _Hue = CheckRange(value / 240.0);
set => hue = CheckRange(value / 240.0);
} }
private double saturation; double _Saturation;
public int Saturation public int Saturation {
{ get => System.Convert.ToInt32(_Saturation * 240);
get => System.Convert.ToInt32(saturation * 240); set => _Saturation = CheckRange(value / 240.0);
set => saturation = CheckRange(value / 240.0);
} }
private double luminosity; double _Luminosity;
public int Luminosity public int Luminosity {
{ get => System.Convert.ToInt32(_Luminosity * 240);
get => System.Convert.ToInt32(luminosity * 240); set => _Luminosity = CheckRange(value / 240.0);
set => luminosity = CheckRange(value / 240.0);
} }
private double CheckRange(double value) double CheckRange(double value)
{ {
if (value < 0) if (value < 0)
value = 0; value = 0;
@@ -413,31 +367,31 @@ public struct HSLColor
{ {
double r = 0, g = 0, b = 0; double r = 0, g = 0, b = 0;
if (luminosity != 0) if (_Luminosity != 0)
{ {
if (saturation == 0) if (_Saturation == 0)
{ {
b = luminosity; b = _Luminosity;
g = luminosity; g = _Luminosity;
r = luminosity; r = _Luminosity;
} }
else else
{ {
double temp2 = GetTemp2(this); double temp2 = GetTemp2(this);
double temp1 = 2.0 * luminosity - temp2; double temp1 = 2.0 * _Luminosity - temp2;
r = GetColorComponent(temp1, temp2, hue + 1.0 / 3.0); r = GetColorComponent(temp1, temp2, _Hue + 1.0 / 3.0);
g = GetColorComponent(temp1, temp2, hue); g = GetColorComponent(temp1, temp2, _Hue);
b = GetColorComponent(temp1, temp2, hue - 1.0 / 3.0); b = GetColorComponent(temp1, temp2, _Hue - 1.0 / 3.0);
} }
} }
return Color.FromArgb( return Color.FromArgb(
System.Convert.ToInt32(255 * r), System.Convert.ToInt32(255 * r),
System.Convert.ToInt32(255 * g), System.Convert.ToInt32(255 * g),
System.Convert.ToInt32(255 * b)); System.Convert.ToInt32(255 * b));
} }
private static double GetColorComponent(double temp1, double temp2, double temp3) static double GetColorComponent(double temp1, double temp2, double temp3)
{ {
temp3 = MoveIntoRange(temp3); temp3 = MoveIntoRange(temp3);
@@ -451,7 +405,7 @@ public struct HSLColor
return temp1; return temp1;
} }
private static double MoveIntoRange(double temp3) static double MoveIntoRange(double temp3)
{ {
if (temp3 < 0) if (temp3 < 0)
temp3 += 1; temp3 += 1;
@@ -460,14 +414,14 @@ public struct HSLColor
return temp3; return temp3;
} }
private static double GetTemp2(HSLColor hslColor) static double GetTemp2(HSLColor hslColor)
{ {
double temp2; double temp2;
if (hslColor.luminosity < 0.5) if (hslColor._Luminosity < 0.5)
temp2 = hslColor.luminosity * (1.0 + hslColor.saturation); temp2 = hslColor._Luminosity * (1.0 + hslColor._Saturation);
else else
temp2 = hslColor.luminosity + hslColor.saturation - (hslColor.luminosity * hslColor.saturation); temp2 = hslColor._Luminosity + hslColor._Saturation - (hslColor._Luminosity * hslColor._Saturation);
return temp2; return temp2;
} }
@@ -475,17 +429,17 @@ public struct HSLColor
public static HSLColor Convert(Color c) public static HSLColor Convert(Color c)
{ {
HSLColor r = new HSLColor(); HSLColor r = new HSLColor();
r.hue = c.GetHue() / 360.0; r._Hue = c.GetHue() / 360.0;
r.luminosity = c.GetBrightness(); r._Luminosity = c.GetBrightness();
r.saturation = c.GetSaturation(); r._Saturation = c.GetSaturation();
return r; return r;
} }
public void SetRGB(int red, int green, int blue) public void SetRGB(int red, int green, int blue)
{ {
HSLColor hc = HSLColor.Convert(Color.FromArgb(red, green, blue)); HSLColor hc = HSLColor.Convert(Color.FromArgb(red, green, blue));
hue = hc.hue; _Hue = hc._Hue;
saturation = hc.saturation; _Saturation = hc._Saturation;
luminosity = hc.luminosity; _Luminosity = hc._Luminosity;
} }
} }

View File

@@ -15,6 +15,14 @@ namespace mpvnet
public static string GetFilter(IEnumerable<string> values) => "*." + public static string GetFilter(IEnumerable<string> values) => "*." +
String.Join(";*.", values) + "|*." + String.Join(";*.", values) + "|All Files|*.*"; String.Join(";*.", values) + "|*." + String.Join(";*.", values) + "|All Files|*.*";
public static bool IsDarkTheme {
get {
object value = Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", 1);
if (value is null) value = 1;
return (int)value == 0;
}
}
} }
public class StringLogicalComparer : IComparer, IComparer<string> public class StringLogicalComparer : IComparer, IComparer<string>
@@ -28,16 +36,6 @@ namespace mpvnet
int IComparer<string>.Compare(string x, string y) => IComparerOfString_Compare(x, y); int IComparer<string>.Compare(string x, string y) => IComparerOfString_Compare(x, y);
} }
public class OSVersion
{
public static float Windows7 { get; } = 6.1f;
public static float Windows8 { get; } = 6.2f;
public static float Windows81 { get; } = 6.3f;
public static float Windows10 { get; } = 10f;
public static float Current => Environment.OSVersion.Version.Major + Environment.OSVersion.Version.Minor / 10f;
}
public class FileAssociation public class FileAssociation
{ {
static string ExePath = Application.ExecutablePath; static string ExePath = Application.ExecutablePath;
@@ -101,6 +99,15 @@ namespace mpvnet
rk.SetValue(name, value); rk.SetValue(name, value);
} }
public static string GetValue(string path, string name)
{
using (RegistryKey rk = GetRootKey(path).OpenSubKey(path.Substring(5)))
if (rk != null)
return rk.GetValue(name, "").ToString();
else
return "";
}
public static void RemoveKey(string path) public static void RemoveKey(string path)
{ {
GetRootKey(path).DeleteSubKeyTree(path.Substring(5), false); GetRootKey(path).DeleteSubKeyTree(path.Substring(5), false);
@@ -124,4 +131,11 @@ namespace mpvnet
} }
} }
} }
public class MediaTrack
{
public string Text { get; set; }
public string Type { get; set; }
public int ID { get; set; }
}
} }

View File

@@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("mpv.net")] [assembly: AssemblyProduct("mpv.net")]
[assembly: AssemblyCopyright("Copyright © 2017 stax76")] [assembly: AssemblyCopyright("Copyright © 2017-2019 stax76")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.5.0.0")] [assembly: AssemblyVersion("3.0.0.0")]
[assembly: AssemblyFileVersion("2.5.0.0")] [assembly: AssemblyFileVersion("3.0.0.0")]

View File

@@ -34,25 +34,27 @@
Space cycle pause #menu: Play/Pause Space cycle pause #menu: Play/Pause
s stop #menu: Stop s stop #menu: Stop
_ ignore #menu: - _ ignore #menu: -
f cycle fullscreen #menu: Toggle Fullscreen Enter cycle fullscreen #menu: Toggle Fullscreen
F11 playlist-prev #menu: Navigate > Previous F11 playlist-prev #menu: Navigate > Previous
F12 playlist-next #menu: Navigate > Next F12 playlist-next #menu: Navigate > Next
_ ignore #menu: Navigate > - _ ignore #menu: Navigate > -
PGUP add chapter 1 #menu: Navigate > Next Chapter PGUP add chapter 1 #menu: Navigate > Next Chapter
PGDWN add chapter -1 #menu: Navigate > Previous Chapter PGDWN add chapter -1 #menu: Navigate > Previous Chapter
_ ignore #menu: Navigate > -
. frame-step #menu: Seek > Next Frame . frame-step #menu: Navigate > Jump Next Frame
, frame-back-step #menu: Seek > Previous Frame , frame-back-step #menu: Navigate > Jump Previous Frame
_ ignore #menu: Seek > - _ ignore #menu: Navigate > -
Right no-osd seek 7 #menu: Seek > 7 sec forward Right no-osd seek 7 #menu: Navigate > Jump 7 sec forward
Left no-osd seek -7 #menu: Seek > 7 sec backward Left no-osd seek -7 #menu: Navigate > Jump 7 sec backward
_ ignore #menu: Seek > - _ ignore #menu: Navigate > -
Up no-osd seek 40 #menu: Seek > 40 sec forward Up no-osd seek 40 #menu: Navigate > Jump 40 sec forward
Down no-osd seek -40 #menu: Seek > 40 sec backward Down no-osd seek -40 #menu: Navigate > Jump 40 sec backward
_ ignore #menu: Seek > - _ ignore #menu: Navigate > -
Ctrl+Right no-osd seek 300 #menu: Seek > 5 min forward Ctrl+Right no-osd seek 300 #menu: Navigate > Jump 5 min forward
Ctrl+Left no-osd seek -300 #menu: Seek > 5 min backward Ctrl+Left no-osd seek -300 #menu: Navigate > Jump 5 min backward
_ ignore #menu: Navigate > -
_ ignore #menu: Navigate > Chapters
Ctrl++ add video-zoom 0.1 #menu: Pan & Scan > Increase Size Ctrl++ add video-zoom 0.1 #menu: Pan & Scan > Increase Size
Ctrl+- add video-zoom -0.1 #menu: Pan & Scan > Decrease Size Ctrl+- add video-zoom -0.1 #menu: Pan & Scan > Decrease Size
@@ -84,7 +86,7 @@
d cycle deinterlace #menu: Video > Toggle Deinterlace d cycle deinterlace #menu: Video > Toggle Deinterlace
a cycle-values video-aspect "16:9" "4:3" "2.35:1" "-1" #menu: Video > Cycle Aspect Ratio a cycle-values video-aspect "16:9" "4:3" "2.35:1" "-1" #menu: Video > Cycle Aspect Ratio
KP7 cycle audio #menu: Audio > Cycle/Next KP7 script-message mpv.net cycle-audio #menu: Audio > Cycle/Next
_ ignore #menu: Audio > - _ ignore #menu: Audio > -
KP6 add audio-delay 0.100 #menu: Audio > Delay +0.1 KP6 add audio-delay 0.100 #menu: Audio > Delay +0.1
KP9 add audio-delay -0.100 #menu: Audio > Delay -0.1 KP9 add audio-delay -0.100 #menu: Audio > Delay -0.1
@@ -101,6 +103,8 @@
_ add sub-scale -0.1 #menu: Subtitle > Decrease Subtitle Font Size _ add sub-scale -0.1 #menu: Subtitle > Decrease Subtitle Font Size
_ add sub-scale 0.1 #menu: Subtitle > Increase Subtitle Font Size _ add sub-scale 0.1 #menu: Subtitle > Increase Subtitle Font Size
_ ignore #menu: Track
+ add volume 10 #menu: Volume > Up + add volume 10 #menu: Volume > Up
- add volume -10 #menu: Volume > Down - add volume -10 #menu: Volume > Down
_ ignore #menu: Volume > - _ ignore #menu: Volume > -
@@ -114,32 +118,30 @@
_ ignore #menu: Speed > - _ ignore #menu: Speed > -
BS set speed 1 #menu: Speed > Reset BS set speed 1 #menu: Speed > Reset
KP0 script-message rate-file 0 #menu: Addons > Rating > 0stars KP0 script-message rate-file 0 #menu: Extensions > Rating > 0stars
KP1 script-message rate-file 1 #menu: Addons > Rating > 1stars KP1 script-message rate-file 1 #menu: Extensions > Rating > 1stars
KP2 script-message rate-file 2 #menu: Addons > Rating > 2stars KP2 script-message rate-file 2 #menu: Extensions > Rating > 2stars
KP3 script-message rate-file 3 #menu: Addons > Rating > 3stars KP3 script-message rate-file 3 #menu: Extensions > Rating > 3stars
KP4 script-message rate-file 4 #menu: Addons > Rating > 4stars KP4 script-message rate-file 4 #menu: Extensions > Rating > 4stars
KP5 script-message rate-file 5 #menu: Addons > Rating > 5stars KP5 script-message rate-file 5 #menu: Extensions > Rating > 5stars
Ctrl+t set ontop yes #menu: View > On Top > Enable Ctrl+t set ontop yes #menu: View > On Top > Enable
Ctrl+T set ontop no #menu: View > On Top > Disable Ctrl+T set ontop no #menu: View > On Top > Disable
i script-message mpv.net show-info #menu: View > File Info i script-message mpv.net show-info #menu: View > File Info
t script-binding stats/display-stats #menu: View > Show Statistics t script-binding stats/display-stats #menu: View > Show Statistics
T script-binding stats/display-stats-toggle #menu: View > Toggle Statistics T script-binding stats/display-stats-toggle #menu: View > Toggle Statistics
Del script-binding osc/visibility #menu: View > Toggle OSC Visibility
F8 show-text ${playlist} 5000 #menu: View > Show Playlist
F9 show-text ${track-list} 5000 #menu: View > Show Audio/Video/Subtitle List
_ script-message mpv.net set-setting hwdec yes #menu: Settings > Hardware Decoding > Enable
_ script-message mpv.net set-setting hwdec no #menu: Settings > Hardware Decoding > Disable
Ctrl+c script-message mpv.net show-conf-editor #menu: Settings > Show Config Editor Ctrl+c script-message mpv.net show-conf-editor #menu: Settings > Show Config Editor
Ctrl+i script-message mpv.net show-input-editor #menu: Settings > Show Input Editor Ctrl+i script-message mpv.net show-input-editor #menu: Settings > Show Input Editor
Ctrl+f script-message mpv.net open-conf-folder #menu: Settings > Open Config Folder Ctrl+f script-message mpv.net open-conf-folder #menu: Settings > Open Config Folder
h script-message mpv.net show-history #menu: Tools > Show History h script-message mpv.net show-history #menu: Tools > Show History
l ab-loop #menu: Tools > Set/clear A-B loop points l ab-loop #menu: Tools > Set/clear A-B loop points
L cycle-values loop-file "inf" "no" #menu: Tools > Toggle Infinite Looping L cycle-values loop-file "inf" "no" #menu: Tools > Toggle infinite file looping
Del script-binding osc/visibility #menu: Tools > Toggle OSC Visibility
Ctrl+h cycle-values hwdec "auto" "no" #menu: Tools > Cycle Hardware Decoding Ctrl+h cycle-values hwdec "auto" "no" #menu: Tools > Cycle Hardware Decoding
F8 show-text ${playlist} 5000 #menu: Tools > Show Playlist
F9 show-text ${track-list} 5000 #menu: Tools > Show Audio/Video/Subtitle List
_ script-message mpv.net execute-mpv-command #menu: Tools > Execute mpv command... _ script-message mpv.net execute-mpv-command #menu: Tools > Execute mpv command...
_ script-message mpv.net shell-execute https://mpv.io/manual/stable/ #menu: Help > Show mpv manual _ script-message mpv.net shell-execute https://mpv.io/manual/stable/ #menu: Help > Show mpv manual
@@ -152,7 +154,6 @@
Q quit-watch-later #menu: Exit Watch Later Q quit-watch-later #menu: Exit Watch Later
> playlist-next > playlist-next
< playlist-prev < playlist-prev
Enter cycle pause
Power quit Power quit
Play cycle pause Play cycle pause
Pause cycle pause Pause cycle pause

View File

@@ -10,6 +10,7 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Forms; using System.Windows.Forms;
using VBNET; using VBNET;
using static mpvnet.libmpv; using static mpvnet.libmpv;
using static mpvnet.Native; using static mpvnet.Native;
@@ -52,18 +53,23 @@ namespace mpvnet
public static event Action QueueOverflow; // MPV_EVENT_QUEUE_OVERFLOW public static event Action QueueOverflow; // MPV_EVENT_QUEUE_OVERFLOW
public static event Action Hook; // MPV_EVENT_HOOK public static event Action Hook; // MPV_EVENT_HOOK
public static IntPtr MpvHandle; public static IntPtr MpvHandle { get; set; }
public static IntPtr MpvWindowHandle; public static IntPtr MpvWindowHandle { get; set; }
public static Addon Addon; public static Addon Addon { get; set; }
public static List<KeyValuePair<string, Action<bool>>> BoolPropChangeActions = new List<KeyValuePair<string, Action<bool>>>(); public static List<KeyValuePair<string, Action<bool>>> BoolPropChangeActions { get; set; } = new List<KeyValuePair<string, Action<bool>>>();
public static Size VideoSize = new Size(1920, 1080); public static List<KeyValuePair<string, Action<int>>> IntPropChangeActions { get; set; } = new List<KeyValuePair<string, Action<int>>>();
public static string mpvConfFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\mpv\\"; public static List<KeyValuePair<string, Action<string>>> StringPropChangeActions { get; set; } = new List<KeyValuePair<string, Action<string>>>();
public static string InputConfPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\mpv\\input.conf"; public static Size VideoSize { get; set; } = new Size(1920, 1080);
public static string mpvConfPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\mpv\\mpv.conf"; public static string MpvConfFolderPath { get; set; } = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\mpv\\";
public static List<PythonScript> PythonScripts => new List<PythonScript>(); public static string InputConfPath { get; set; } = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\mpv\\input.conf";
public static AutoResetEvent AutoResetEvent = new AutoResetEvent(false); public static string MpvConfPath { get; set; } = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\mpv\\mpv.conf";
public static string MpvNetConfPath { get; set; } = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\mpv\\mpvnet.conf";
public static List<PythonScript> PythonScripts { get; set; } = new List<PythonScript>();
public static AutoResetEvent AutoResetEvent { get; set; } = new AutoResetEvent(false);
public static List<MediaTrack> MediaTracks { get; set; } = new List<MediaTrack>();
public static List<KeyValuePair<string, double>> Chapters { get; set; } = new List<KeyValuePair<string, double>>();
private static Dictionary<string, string> _mpvConf; static Dictionary<string, string> _mpvConf;
public static Dictionary<string, string> mpvConf { public static Dictionary<string, string> mpvConf {
get { get {
@@ -71,8 +77,8 @@ namespace mpvnet
{ {
_mpvConf = new Dictionary<string, string>(); _mpvConf = new Dictionary<string, string>();
if (File.Exists(mpvConfPath)) if (File.Exists(MpvConfPath))
foreach (var i in File.ReadAllLines(mpvConfPath)) foreach (var i in File.ReadAllLines(MpvConfPath))
if (i.Contains("=") && ! i.StartsWith("#")) if (i.Contains("=") && ! i.StartsWith("#"))
_mpvConf[i.Substring(0, i.IndexOf("=")).Trim()] = i.Substring(i.IndexOf("=") + 1).Trim(); _mpvConf[i.Substring(0, i.IndexOf("=")).Trim()] = i.Substring(i.IndexOf("=") + 1).Trim();
} }
@@ -80,13 +86,30 @@ namespace mpvnet
} }
} }
static Dictionary<string, string> _mpvNetConf;
public static Dictionary<string, string> mpvNetConf {
get {
if (_mpvNetConf == null)
{
_mpvNetConf = new Dictionary<string, string>();
if (File.Exists(MpvNetConfPath))
foreach (string i in File.ReadAllLines(MpvNetConfPath))
if (i.Contains("=") && !i.StartsWith("#"))
_mpvNetConf[i.Substring(0, i.IndexOf("=")).Trim()] = i.Substring(i.IndexOf("=") + 1).Trim();
}
return _mpvNetConf;
}
}
public static void Init() public static void Init()
{ {
if (!Directory.Exists(mp.mpvConfFolderPath)) if (!Directory.Exists(mp.MpvConfFolderPath))
Directory.CreateDirectory(mp.mpvConfFolderPath); Directory.CreateDirectory(mp.MpvConfFolderPath);
if (!File.Exists(mp.mpvConfPath)) if (!File.Exists(mp.MpvConfPath))
File.WriteAllText(mp.mpvConfPath, Properties.Resources.mpv_conf); File.WriteAllText(mp.MpvConfPath, Properties.Resources.mpv_conf);
if (!File.Exists(mp.InputConfPath)) if (!File.Exists(mp.InputConfPath))
File.WriteAllText(mp.InputConfPath, Properties.Resources.input_conf); File.WriteAllText(mp.InputConfPath, Properties.Resources.input_conf);
@@ -123,7 +146,7 @@ namespace mpvnet
if (Path.GetExtension(scriptPath) == ".ps1") if (Path.GetExtension(scriptPath) == ".ps1")
PowerShellScript.Init(scriptPath); PowerShellScript.Init(scriptPath);
foreach (var scriptPath in Directory.GetFiles(mp.mpvConfFolderPath + "Scripts")) foreach (var scriptPath in Directory.GetFiles(mp.MpvConfFolderPath + "Scripts"))
if (Path.GetExtension(scriptPath) == ".py") if (Path.GetExtension(scriptPath) == ".py")
PythonScripts.Add(new PythonScript(File.ReadAllText(scriptPath))); PythonScripts.Add(new PythonScript(File.ReadAllText(scriptPath)));
else if (Path.GetExtension(scriptPath) == ".ps1") else if (Path.GetExtension(scriptPath) == ".ps1")
@@ -148,6 +171,7 @@ namespace mpvnet
{ {
case mpv_event_id.MPV_EVENT_SHUTDOWN: case mpv_event_id.MPV_EVENT_SHUTDOWN:
Shutdown?.Invoke(); Shutdown?.Invoke();
WriteHistory(null);
AutoResetEvent.Set(); AutoResetEvent.Set();
return; return;
case mpv_event_id.MPV_EVENT_LOG_MESSAGE: case mpv_event_id.MPV_EVENT_LOG_MESSAGE:
@@ -235,12 +259,22 @@ namespace mpvnet
Seek?.Invoke(); Seek?.Invoke();
break; break;
case mpv_event_id.MPV_EVENT_PROPERTY_CHANGE: case mpv_event_id.MPV_EVENT_PROPERTY_CHANGE:
var event_propertyData = (mpv_event_property)Marshal.PtrToStructure(evt.data, typeof(mpv_event_property)); var propData = (mpv_event_property)Marshal.PtrToStructure(evt.data, typeof(mpv_event_property));
if (event_propertyData.format == mpv_format.MPV_FORMAT_FLAG) if (propData.format == mpv_format.MPV_FORMAT_FLAG)
foreach (var i in BoolPropChangeActions) foreach (var i in BoolPropChangeActions)
if (i.Key== event_propertyData.name) if (i.Key== propData.name)
i.Value.Invoke(Marshal.PtrToStructure<int>(event_propertyData.data) == 1); i.Value.Invoke(Marshal.PtrToStructure<int>(propData.data) == 1);
if (propData.format == mpv_format.MPV_FORMAT_STRING)
foreach (var i in StringPropChangeActions)
if (i.Key == propData.name)
i.Value.Invoke(StringFromNativeUtf8(Marshal.PtrToStructure<IntPtr>(propData.data)));
if (propData.format == mpv_format.MPV_FORMAT_INT64)
foreach (var i in IntPropChangeActions)
if (i.Key == propData.name)
i.Value.Invoke(Marshal.PtrToStructure<int>(propData.data));
break; break;
case mpv_event_id.MPV_EVENT_PLAYBACK_RESTART: case mpv_event_id.MPV_EVENT_PLAYBACK_RESTART:
PlaybackRestart?.Invoke(); PlaybackRestart?.Invoke();
@@ -250,7 +284,12 @@ namespace mpvnet
{ {
VideoSize = s; VideoSize = s;
VideoSizeChanged?.Invoke(); VideoSizeChanged?.Invoke();
} }
Task.Run(new Action(() => {
WriteHistory(mp.get_property_string("path"));
ReadMetaData();
}));
break; break;
case mpv_event_id.MPV_EVENT_CHAPTER_CHANGE: case mpv_event_id.MPV_EVENT_CHAPTER_CHANGE:
ChapterChange?.Invoke(); ChapterChange?.Invoke();
@@ -270,7 +309,7 @@ namespace mpvnet
} }
} }
private static List<PythonEventObject> PythonEventObjects = new List<PythonEventObject>(); static List<PythonEventObject> PythonEventObjects = new List<PythonEventObject>();
public static void register_event(string name, PyRT.PythonFunction pyFunc) public static void register_event(string name, PyRT.PythonFunction pyFunc)
{ {
@@ -360,7 +399,7 @@ namespace mpvnet
if (err < 0 && throwOnException) if (err < 0 && throwOnException)
throw new Exception($"{name}: {(mpv_error)err}"); throw new Exception($"{name}: {(mpv_error)err}");
var ret = StringFromNativeUtf8(lpBuffer); string ret = StringFromNativeUtf8(lpBuffer);
mpv_free(lpBuffer); mpv_free(lpBuffer);
return ret; return ret;
@@ -412,6 +451,16 @@ namespace mpvnet
throw new Exception($"{name}: {(mpv_error)err}"); throw new Exception($"{name}: {(mpv_error)err}");
} }
public static void observe_property_int(string name, Action<int> action)
{
int err = mpv_observe_property(MpvHandle, (ulong)action.GetHashCode(), name, mpv_format.MPV_FORMAT_INT64);
if (err < 0)
throw new Exception($"{name}: {(mpv_error)err}");
else
IntPropChangeActions.Add(new KeyValuePair<string, Action<int>>(name, action));
}
public static void observe_property_bool(string name, Action<bool> action) public static void observe_property_bool(string name, Action<bool> action)
{ {
int err = mpv_observe_property(MpvHandle, (ulong)action.GetHashCode(), name, mpv_format.MPV_FORMAT_FLAG); int err = mpv_observe_property(MpvHandle, (ulong)action.GetHashCode(), name, mpv_format.MPV_FORMAT_FLAG);
@@ -422,16 +471,14 @@ namespace mpvnet
BoolPropChangeActions.Add(new KeyValuePair<string, Action<bool>>(name, action)); BoolPropChangeActions.Add(new KeyValuePair<string, Action<bool>>(name, action));
} }
public static void unobserve_property_bool(string name, Action<bool> action) public static void observe_property_string(string name, Action<string> action)
{ {
foreach (var i in BoolPropChangeActions.ToArray()) int err = mpv_observe_property(MpvHandle, (ulong)action.GetHashCode(), name, mpv_format.MPV_FORMAT_STRING);
if (i.Value == action)
BoolPropChangeActions.Remove(i);
int err = mpv_unobserve_property(MpvHandle, (ulong)action.GetHashCode());
if (err < 0) if (err < 0)
throw new Exception($"{name}: {(mpv_error)err}"); throw new Exception($"{name}: {(mpv_error)err}");
else
StringPropChangeActions.Add(new KeyValuePair<string, Action<string>>(name, action));
} }
protected static void ProcessCommandLine() protected static void ProcessCommandLine()
@@ -486,9 +533,9 @@ namespace mpvnet
mp.LoadFolder(); mp.LoadFolder();
} }
private static bool WasFolderLoaded; static bool WasFolderLoaded;
public static void LoadFolder() static void LoadFolder()
{ {
if (WasFolderLoaded) if (WasFolderLoaded)
return; return;
@@ -514,7 +561,7 @@ namespace mpvnet
WasFolderLoaded = true; WasFolderLoaded = true;
} }
public static IntPtr AllocateUtf8IntPtrArrayWithSentinel(string[] arr, out IntPtr[] byteArrayPointers) static IntPtr AllocateUtf8IntPtrArrayWithSentinel(string[] arr, out IntPtr[] byteArrayPointers)
{ {
int numberOfStrings = arr.Length + 1; // add extra element for extra null pointer last (sentinel) int numberOfStrings = arr.Length + 1; // add extra element for extra null pointer last (sentinel)
byteArrayPointers = new IntPtr[numberOfStrings]; byteArrayPointers = new IntPtr[numberOfStrings];
@@ -532,7 +579,7 @@ namespace mpvnet
return rootPointer; return rootPointer;
} }
public static string[] NativeUtf8StrArray2ManagedStrArray(IntPtr pUnmanagedStringArray, int StringCount) static string[] NativeUtf8StrArray2ManagedStrArray(IntPtr pUnmanagedStringArray, int StringCount)
{ {
IntPtr[] pIntPtrArray = new IntPtr[StringCount]; IntPtr[] pIntPtrArray = new IntPtr[StringCount];
string[] ManagedStringArray = new string[StringCount]; string[] ManagedStringArray = new string[StringCount];
@@ -544,7 +591,7 @@ namespace mpvnet
return ManagedStringArray; return ManagedStringArray;
} }
public static string StringFromNativeUtf8(IntPtr nativeUtf8) static string StringFromNativeUtf8(IntPtr nativeUtf8)
{ {
int len = 0; int len = 0;
while (Marshal.ReadByte(nativeUtf8, len) != 0) ++len; while (Marshal.ReadByte(nativeUtf8, len) != 0) ++len;
@@ -553,7 +600,124 @@ namespace mpvnet
return Encoding.UTF8.GetString(buffer); return Encoding.UTF8.GetString(buffer);
} }
public static byte[] GetUtf8Bytes(string s) => Encoding.UTF8.GetBytes(s + "\0"); static byte[] GetUtf8Bytes(string s) => Encoding.UTF8.GetBytes(s + "\0");
static string LastHistoryPath;
static DateTime LastHistoryStartDateTime;
static void WriteHistory(string filePath)
{
int totalMinutes = Convert.ToInt32((DateTime.Now - LastHistoryStartDateTime).TotalMinutes);
if (File.Exists(LastHistoryPath) && totalMinutes > 1)
{
string historyFilepath = mp.MpvConfFolderPath + "history.txt";
File.AppendAllText(historyFilepath, DateTime.Now.ToString().Substring(0, 16) +
" " + totalMinutes.ToString().PadLeft(3) + " " +
Path.GetFileNameWithoutExtension(LastHistoryPath) + "\r\n");
}
LastHistoryPath = filePath;
LastHistoryStartDateTime = DateTime.Now;
}
static void ReadMetaData()
{
lock (MediaTracks)
{
MediaTracks.Clear();
using (MediaInfo mi = new MediaInfo(mp.get_property_string("path")))
{
int count = mi.GetCount(MediaInfoStreamKind.Video);
for (int i = 0; i < count; i++)
{
MediaTrack track = new MediaTrack();
Add(track, mi.GetVideo(i, "Format"));
Add(track, mi.GetVideo(i, "Format_Profile"));
Add(track, mi.GetVideo(i, "Width") + "x" + mi.GetVideo(i, "Height"));
Add(track, mi.GetVideo(i, "FrameRate") + " FPS");
Add(track, mi.GetVideo(i, "Language/String"));
Add(track, mi.GetVideo(i, "Forced") == "Yes" ? "Forced" : "");
Add(track, mi.GetVideo(i, "Default") == "Yes" ? "Default" : "");
Add(track, mi.GetVideo(i, "Title"));
track.Text = "V: " + track.Text.Trim(" ,".ToCharArray());
track.Type = "v";
track.ID = i + 1;
MediaTracks.Add(track);
}
count = mi.GetCount(MediaInfoStreamKind.Audio);
for (int i = 0; i < count; i++)
{
MediaTrack track = new MediaTrack();
Add(track, mi.GetAudio(i, "Language/String"));
Add(track, mi.GetAudio(i, "Format"));
Add(track, mi.GetAudio(i, "Format_Profile"));
Add(track, mi.GetAudio(i, "BitRate/String"));
Add(track, mi.GetAudio(i, "Channel(s)/String"));
Add(track, mi.GetAudio(i, "SamplingRate/String"));
Add(track, mi.GetAudio(i, "Forced") == "Yes" ? "Forced" : "");
Add(track, mi.GetAudio(i, "Default") == "Yes" ? "Default" : "");
Add(track, mi.GetAudio(i, "Title"));
track.Text = "A: " + track.Text.Trim(" ,".ToCharArray());
track.Type = "a";
track.ID = i + 1;
MediaTracks.Add(track);
}
count = mi.GetCount(MediaInfoStreamKind.Text);
for (int i = 0; i < count; i++)
{
MediaTrack track = new MediaTrack();
Add(track, mi.GetText(i, "Language/String"));
Add(track, mi.GetText(i, "Format"));
Add(track, mi.GetText(i, "Format_Profile"));
Add(track, mi.GetText(i, "Forced") == "Yes" ? "Forced" : "");
Add(track, mi.GetText(i, "Default") == "Yes" ? "Default" : "");
Add(track, mi.GetText(i, "Title"));
track.Text = "S: " + track.Text.Trim(" ,".ToCharArray());
track.Type = "s";
track.ID = i + 1;
MediaTracks.Add(track);
}
count = get_property_int("edition-list/count");
for (int i = 0; i < count; i++)
{
MediaTrack track = new MediaTrack();
track.Text = "E: " + get_property_string($"edition-list/{i}/title");
track.Type = "e";
track.ID = i;
MediaTracks.Add(track);
}
void Add(MediaTrack track, string val)
{
if (!string.IsNullOrEmpty(val) && !(track.Text != null && track.Text.Contains(val)))
track.Text += " " + val + ",";
}
}
}
lock (Chapters)
{
Chapters.Clear();
int count = get_property_int("chapter-list/count");
for (int x = 0; x < count; x++)
{
string text = get_property_string($"chapter-list/{x}/title");
double time = get_property_number($"chapter-list/{x}/time");
Chapters.Add(new KeyValuePair<string, double>(text, time));
}
}
}
} }
public enum EndFileEventMode public enum EndFileEventMode

View File

@@ -1,7 +1,7 @@
<Application x:Class="DynamicGUI.App" <Application x:Class="mpvConfEdit.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DynamicGUI" xmlns:Controls="clr-namespace:Controls"
StartupUri="MainWindow.xaml"> StartupUri="MainWindow.xaml">
<Application.Resources> <Application.Resources>
<Style TargetType="TextBox"> <Style TargetType="TextBox">
@@ -19,7 +19,7 @@
<Setter Property="BorderBrush" TargetName="border" Value="#FF7EB4EA"/> <Setter Property="BorderBrush" TargetName="border" Value="#FF7EB4EA"/>
</Trigger> </Trigger>
<Trigger Property="IsFocused" Value="true"> <Trigger Property="IsFocused" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{x:Static SystemParameters.WindowGlassBrush}"/> <Setter Property="BorderBrush" TargetName="border" Value="{x:Static Controls:Controls.ThemeBrush}"/>
</Trigger> </Trigger>
</ControlTemplate.Triggers> </ControlTemplate.Triggers>
</ControlTemplate> </ControlTemplate>

View File

@@ -1,6 +1,6 @@
using System.Windows; using System.Windows;
namespace DynamicGUI namespace mpvConfEdit
{ {
public partial class App : Application public partial class App : Application
{ {

18
mpvConfEdit/Controls.cs Normal file
View File

@@ -0,0 +1,18 @@
using System;
using System.Windows;
using System.Windows.Media;
namespace Controls
{
class Controls
{
public static Brush ThemeBrush {
get {
if (Environment.OSVersion.Version.Major < 10)
return new SolidColorBrush(Colors.DimGray);
else
return SystemParameters.WindowGlassBrush;
}
}
}
}

View File

@@ -27,6 +27,7 @@ namespace DynamicGUI
baseSetting = optionSetting; baseSetting = optionSetting;
optionSetting.Default = setting["default"]; optionSetting.Default = setting["default"];
optionSetting.Value = optionSetting.Default; optionSetting.Value = optionSetting.Default;
optionSetting.StartValue = optionSetting.Default;
foreach (TomlTable option in setting["options"]) foreach (TomlTable option in setting["options"])
{ {
@@ -54,7 +55,6 @@ namespace DynamicGUI
baseSetting.Filter = setting["filter"]; baseSetting.Filter = setting["filter"];
if (setting.HasKey("help")) baseSetting.Help = setting["help"]; if (setting.HasKey("help")) baseSetting.Help = setting["help"];
if (setting.HasKey("helpurl")) baseSetting.HelpURL = setting["helpurl"]; if (setting.HasKey("helpurl")) baseSetting.HelpURL = setting["helpurl"];
if (setting.HasKey("alias")) baseSetting.Alias = setting["alias"];
if (setting.HasKey("width")) baseSetting.Width = setting["width"]; if (setting.HasKey("width")) baseSetting.Width = setting["width"];
settingsList.Add(baseSetting); settingsList.Add(baseSetting);
} }
@@ -65,8 +65,10 @@ namespace DynamicGUI
public abstract class SettingBase public abstract class SettingBase
{ {
public string Name { get; set; } public string Name { get; set; }
public string Alias { get; set; } public string Value { get; set; }
public string StartValue { get; set; }
public string Help { get; set; } public string Help { get; set; }
public string Default { get; set; }
public string HelpURL { get; set; } public string HelpURL { get; set; }
public string Filter { get; set; } public string Filter { get; set; }
public int Width { get; set; } public int Width { get; set; }
@@ -74,15 +76,11 @@ namespace DynamicGUI
public class StringSetting : SettingBase public class StringSetting : SettingBase
{ {
public string Default { get; set; }
public string Value { get; set; }
public bool IsFolder { get; set; } public bool IsFolder { get; set; }
} }
public class OptionSetting : SettingBase public class OptionSetting : SettingBase
{ {
public string Default { get; set; }
public string Value { get; set; }
public List<OptionSettingOption> Options = new List<OptionSettingOption>(); public List<OptionSettingOption> Options = new List<OptionSettingOption>();
} }

View File

@@ -8,18 +8,18 @@
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<Grid Margin="20,0"> <Grid Margin="20,0">
<StackPanel> <StackPanel>
<TextBox x:Name="TitleTextBox" FontSize="24" Margin="0,10" BorderThickness="0" IsReadOnly="True" Foreground="{x:Static SystemParameters.WindowGlassBrush}"></TextBox> <TextBox x:Name="TitleTextBox" FontSize="24" Margin="0,10" BorderThickness="0" IsReadOnly="True" Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" Background="{Binding Path=Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"></TextBox>
<ItemsControl x:Name="ItemsControl"> <ItemsControl x:Name="ItemsControl">
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate> <DataTemplate>
<WrapPanel Orientation="Vertical"> <WrapPanel Orientation="Vertical">
<RadioButton x:Name="RadioButton" VerticalContentAlignment="Center" IsChecked="{Binding IsChecked}" GroupName="{Binding OptionSetting.Name}" Content="{Binding Text}" FontSize="16" FontWeight="Normal" VerticalAlignment="Top"></RadioButton> <RadioButton x:Name="RadioButton" VerticalContentAlignment="Center" IsChecked="{Binding IsChecked}" GroupName="{Binding OptionSetting.Name}" Content="{Binding Text}" FontSize="16" FontWeight="Normal" VerticalAlignment="Top" Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"></RadioButton>
<TextBox x:Name="ItemHelpTextBox" TextWrapping="WrapWithOverflow" Text="{Binding Help}" Margin="10,0,0,0" BorderThickness="0" IsReadOnly="True" Padding="7,0,0,0" MinHeight="0"></TextBox> <TextBox x:Name="ItemHelpTextBox" TextWrapping="WrapWithOverflow" Text="{Binding Help}" Margin="10,0,0,0" BorderThickness="0" IsReadOnly="True" Padding="7,0,0,0" MinHeight="0" Foreground="{Binding Path=Foreground2, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" Background="{Binding Path=Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"></TextBox>
</WrapPanel> </WrapPanel>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>
</ItemsControl> </ItemsControl>
<TextBox x:Name="HelpTextBox" TextWrapping="WrapWithOverflow" BorderThickness="0" IsReadOnly="True" Margin="0,10,0,0"></TextBox> <TextBox x:Name="HelpTextBox" TextWrapping="WrapWithOverflow" BorderThickness="0" IsReadOnly="True" Margin="0,10,0,0" Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}" Background="{Binding Path=Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=UserControl}}"></TextBox>
<TextBlock x:Name="LinkTextBlock" Margin="0,10"> <TextBlock x:Name="LinkTextBlock" Margin="0,10">
<local:HyperlinkEx x:Name="Link"></local:HyperlinkEx> <local:HyperlinkEx x:Name="Link"></local:HyperlinkEx>
</TextBlock> </TextBlock>

View File

@@ -15,7 +15,6 @@ namespace DynamicGUI
HelpTextBox.Text = optionSetting.Help; HelpTextBox.Text = optionSetting.Help;
ItemsControl.ItemsSource = optionSetting.Options; ItemsControl.ItemsSource = optionSetting.Options;
Link.SetURL(optionSetting.HelpURL); Link.SetURL(optionSetting.HelpURL);
if (string.IsNullOrEmpty(optionSetting.HelpURL)) if (string.IsNullOrEmpty(optionSetting.HelpURL))
LinkTextBlock.Visibility = Visibility.Collapsed; LinkTextBlock.Visibility = Visibility.Collapsed;
} }

View File

@@ -9,16 +9,16 @@
d:DesignWidth="800" > d:DesignWidth="800" >
<Grid Margin="20,0"> <Grid Margin="20,0">
<StackPanel> <StackPanel>
<TextBox x:Name="TitleTextBox" FontSize="24" Margin="0,10" BorderThickness="0" IsReadOnly="True" Foreground="{x:Static SystemParameters.WindowGlassBrush}"></TextBox> <TextBox x:Name="TitleTextBox" FontSize="24" Margin="0,10" BorderThickness="0" IsReadOnly="True" Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" Background="{Binding Path=Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"></TextBox>
<Grid Margin="0,0,0,10"> <Grid Margin="0,0,0,10">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBox x:Name="ValueTextBox" Text="{Binding Path=Text, ElementName=StringSettingControl1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="150" HorizontalAlignment="Left" Height="20"/> <TextBox x:Name="ValueTextBox" Text="{Binding Path=Text, ElementName=StringSettingControl1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="150" HorizontalAlignment="Left" Height="20" Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" Background="{Binding Path=Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"/>
<Button x:Name="Button" Height="20" Grid.Column="1" Visibility="{Binding Path=Text, ElementName=StringSettingControl1}" Margin="5,0,0,0" Width="20" Click="Button_Click">...</Button> <Button x:Name="Button" Height="20" Grid.Column="1" Visibility="{Binding Path=Text, ElementName=StringSettingControl1}" Margin="5,0,0,0" Width="20" Click="Button_Click">...</Button>
</Grid> </Grid>
<TextBox x:Name="HelpTextBox" TextWrapping="WrapWithOverflow" Margin="0,0,0,10" BorderThickness="0" IsReadOnly="True"></TextBox> <TextBox x:Name="HelpTextBox" TextWrapping="WrapWithOverflow" Margin="0,0,0,10" BorderThickness="0" IsReadOnly="True" Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" Background="{Binding Path=Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"></TextBox>
<TextBlock x:Name="LinkTextBlock" Margin="0,10"> <TextBlock x:Name="LinkTextBlock" Margin="0,10">
<local:HyperlinkEx x:Name="Link"></local:HyperlinkEx> <local:HyperlinkEx x:Name="Link"></local:HyperlinkEx>
</TextBlock> </TextBlock>

View File

@@ -19,7 +19,6 @@ namespace DynamicGUI
if (!StringSetting.IsFolder) if (!StringSetting.IsFolder)
Button.Visibility = Visibility.Hidden; Button.Visibility = Visibility.Hidden;
Link.SetURL(StringSetting.HelpURL); Link.SetURL(StringSetting.HelpURL);
if (string.IsNullOrEmpty(stringSetting.HelpURL)) if (string.IsNullOrEmpty(stringSetting.HelpURL))
LinkTextBlock.Visibility = Visibility.Collapsed; LinkTextBlock.Visibility = Visibility.Collapsed;
} }

View File

@@ -4,7 +4,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" mc:Ignorable="d"
Height="600" Width="800" Loaded="MainWindow1_Loaded" WindowStartupLocation="CenterScreen"> Height="500" Width="700" Loaded="MainWindow1_Loaded" WindowStartupLocation="CenterScreen">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto" /> <RowDefinition Height="Auto" />
@@ -14,12 +14,12 @@
<ColumnDefinition Width="10*" /> <ColumnDefinition Width="10*" />
<ColumnDefinition Width="60*" /> <ColumnDefinition Width="60*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Controls:SearchTextBoxUserControl x:Name="SearchControl" Width="300" Margin="0,20,0,10" Grid.ColumnSpan="2" /> <Controls:SearchTextBoxUserControl x:Name="SearchControl" Width="250" Margin="0,20,0,10" Grid.ColumnSpan="2" />
<ScrollViewer x:Name="MainScrollViewer" VerticalScrollBarVisibility="Auto" Grid.Row="1" Grid.Column="1" Margin="0,0,0,10"> <ScrollViewer x:Name="MainScrollViewer" VerticalScrollBarVisibility="Auto" Grid.Row="1" Grid.Column="1" Margin="0,0,0,10">
<StackPanel x:Name="MainStackPanel"></StackPanel> <StackPanel x:Name="MainStackPanel"></StackPanel>
</ScrollViewer> </ScrollViewer>
<StackPanel Margin="20,0,0,0" Grid.Row="1"> <StackPanel Margin="20,0,0,0" Grid.Row="1">
<ListBox x:Name="FilterListBox" ItemsSource="{Binding FilterStrings}" BorderThickness="0" SelectionChanged="ListBox_SelectionChanged" Foreground="{x:Static SystemParameters.WindowGlassBrush}"> <ListBox x:Name="FilterListBox" ItemsSource="{Binding FilterStrings}" BorderThickness="0" SelectionChanged="ListBox_SelectionChanged" Foreground="{x:Static Controls:Controls.ThemeBrush}" Background="{Binding Path=Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate> <DataTemplate>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
@@ -28,10 +28,10 @@
</DataTemplate> </DataTemplate>
</ListBox.ItemTemplate> </ListBox.ItemTemplate>
</ListBox> </ListBox>
<TextBlock x:Name="OpenSettingsTextBlock" Margin="0,30,0,0" Cursor="Hand" TextWrapping="WrapWithOverflow" Foreground="{x:Static SystemParameters.WindowGlassBrush}" MouseUp="OpenSettingsTextBlock_MouseUp">Open settings folder</TextBlock> <TextBlock x:Name="OpenSettingsTextBlock" Margin="0,30,0,0" Cursor="Hand" TextWrapping="WrapWithOverflow" Foreground="{x:Static Controls:Controls.ThemeBrush}" MouseUp="OpenSettingsTextBlock_MouseUp">Open config folder</TextBlock>
<TextBlock x:Name="ShowManualTextBlock" Margin="0,15,0,0" Cursor="Hand" TextWrapping="WrapWithOverflow" Foreground="{x:Static SystemParameters.WindowGlassBrush}" MouseUp="ShowManualTextBlock_MouseUp">Show mpv manual</TextBlock> <TextBlock x:Name="ShowManualTextBlock" Margin="0,15,0,0" Cursor="Hand" TextWrapping="WrapWithOverflow" Foreground="{x:Static Controls:Controls.ThemeBrush}" MouseUp="ShowManualTextBlock_MouseUp">Show mpv manual</TextBlock>
<TextBlock x:Name="SupportTextBlock" Margin="0,15,0,0" Cursor="Hand" TextWrapping="WrapWithOverflow" Foreground="{x:Static SystemParameters.WindowGlassBrush}" MouseUp="SupportTextBlock_MouseUp">Show support forum</TextBlock> <TextBlock x:Name="SupportTextBlock" Margin="0,15,0,0" Cursor="Hand" TextWrapping="WrapWithOverflow" Foreground="{x:Static Controls:Controls.ThemeBrush}" MouseUp="SupportTextBlock_MouseUp">Show support forum</TextBlock>
<TextBlock x:Name="ApplyTextBlock" Margin="0,15,0,0" Cursor="Hand" TextWrapping="WrapWithOverflow" Foreground="{x:Static SystemParameters.WindowGlassBrush}" MouseUp="ApplyTextBlock_MouseUp">Write config to mpv.conf</TextBlock> <TextBlock x:Name="ApplyTextBlock" Margin="0,15,0,0" Cursor="Hand" TextWrapping="WrapWithOverflow" Foreground="{x:Static Controls:Controls.ThemeBrush}" MouseUp="ApplyTextBlock_MouseUp">Write config to disk</TextBlock>
</StackPanel> </StackPanel>
</Grid> </Grid>
</Window> </Window>

View File

@@ -8,15 +8,21 @@ using System.Reflection;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media;
using DynamicGUI; using DynamicGUI;
using Microsoft.Win32;
namespace mpvConfEdit namespace mpvConfEdit
{ {
public partial class MainWindow : Window public partial class MainWindow : Window
{ {
public string mpvConfPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\mpv\\mpv.conf"; public string MpvConfPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\mpv\\mpv.conf";
private List<SettingBase> DynamicSettings = Settings.LoadSettings(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\mpvConfEdit.toml"); public string MpvNetConfPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\mpv\\mpvnet.conf";
private List<SettingBase> MpvSettingsDefinitions = Settings.LoadSettings(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\mpvConf.toml");
private List<SettingBase> MpvNetSettingsDefinitions = Settings.LoadSettings(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\mpvNetConf.toml");
public ObservableCollection<string> FilterStrings { get; } = new ObservableCollection<string>();
private Dictionary<string, Dictionary<string, string>> Comments = new Dictionary<string, Dictionary<string, string>>();
public MainWindow() public MainWindow()
{ {
@@ -24,31 +30,65 @@ namespace mpvConfEdit
DataContext = this; DataContext = this;
Title = (Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), true)[0] as AssemblyProductAttribute).Product + " " + Assembly.GetExecutingAssembly().GetName().Version.ToString(); Title = (Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyProductAttribute), true)[0] as AssemblyProductAttribute).Product + " " + Assembly.GetExecutingAssembly().GetName().Version.ToString();
SearchControl.SearchTextBox.TextChanged += SearchTextBox_TextChanged; SearchControl.SearchTextBox.TextChanged += SearchTextBox_TextChanged;
LoadSettings(MpvSettingsDefinitions, MpvConf);
LoadSettings(MpvNetSettingsDefinitions, MpvNetConf);
SearchControl.Text = (string)Registry.GetValue(@"HKEY_CURRENT_USER\Software\mpv.net", "conf editor search", "");
SetDarkTheme();
}
foreach (var setting in DynamicSettings) public Brush Foreground2 {
get { return (Brush)GetValue(Foreground2Property); }
set { SetValue(Foreground2Property, value); }
}
public static readonly DependencyProperty Foreground2Property =
DependencyProperty.Register("Foreground2", typeof(Brush), typeof(MainWindow), new PropertyMetadata(Brushes.DarkSlateGray));
void SetDarkTheme()
{
string darkMode = MpvNetSettingsDefinitions.Where(item => item.Name == "dark-mode").First().Value;
object value = Registry.GetValue(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme", 1);
if (value is null) value = 1;
bool isDarkTheme = (int)value == 0;
if (!((darkMode == "system" && isDarkTheme) || darkMode == "always"))
return;
Foreground = Brushes.White;
Foreground2 = Brushes.Silver;
Background = Brushes.Black;
}
private void LoadSettings(List<SettingBase> settingsDefinitions,
Dictionary<string, string> confSettings)
{
foreach (var setting in settingsDefinitions)
{ {
if (!FilterStrings.Contains(setting.Filter)) if (!FilterStrings.Contains(setting.Filter))
FilterStrings.Add(setting.Filter); FilterStrings.Add(setting.Filter);
foreach (var pair in mpvConf)
foreach (var pair in confSettings)
{ {
if (setting.Name == pair.Key || setting.Alias == pair.Key) if (setting.Name == pair.Key)
switch (setting) {
{ setting.Value = pair.Value;
case StringSetting s: setting.StartValue = pair.Value;
s.Value = pair.Value; continue;
continue; }
case OptionSetting s:
s.Value = pair.Value;
break;
}
} }
switch (setting) switch (setting)
{ {
case StringSetting s: case StringSetting s:
MainStackPanel.Children.Add(new StringSettingControl(s)); var sc = new StringSettingControl(s);
sc.TitleTextBox.Foreground = Controls.Controls.ThemeBrush;
MainStackPanel.Children.Add(sc);
break; break;
case OptionSetting s: case OptionSetting s:
MainStackPanel.Children.Add(new OptionSettingControl(s)); var oc = new OptionSettingControl(s);
oc.TitleTextBox.Foreground = Controls.Controls.ThemeBrush;
MainStackPanel.Children.Add(oc);
break; break;
} }
} }
@@ -56,103 +96,109 @@ namespace mpvConfEdit
private Dictionary<string, string> _mpvConf; private Dictionary<string, string> _mpvConf;
public Dictionary<string, string> mpvConf { public Dictionary<string, string> MpvConf {
get { get {
if (_mpvConf == null) if (_mpvConf == null) _mpvConf = LoadConf(MpvConfPath);
{
_mpvConf = new Dictionary<string, string>();
if (File.Exists(mpvConfPath))
foreach (var i in File.ReadAllLines(mpvConfPath))
if (i.Contains("=") && !i.Trim().StartsWith("#"))
{
int pos = i.IndexOf("=");
_mpvConf[i.Substring(0, pos).Trim()] = i.Substring(pos + 1).Trim();
}
}
return _mpvConf; return _mpvConf;
} }
} }
public ObservableCollection<string> FilterStrings { get; } = new ObservableCollection<string>(); private Dictionary<string, string> _mpvNetConf;
public Dictionary<string, string> MpvNetConf {
get {
if (_mpvNetConf == null) _mpvNetConf = LoadConf(MpvNetConfPath);
return _mpvNetConf;
}
}
private Dictionary<string, string> LoadConf(string filePath)
{
Dictionary<string, string> conf = new Dictionary<string, string>();
Comments[filePath] = new Dictionary<string, string>();
if (File.Exists(filePath))
{
foreach (string i in File.ReadAllLines(filePath))
{
if (i.Contains("="))
{
int pos = i.IndexOf("=");
string left = i.Substring(0, pos).Replace(" ", "").ToLower();
string right = i.Substring(pos + 1).Trim();
if (left.StartsWith("#"))
{
Comments[filePath][left.TrimStart("#".ToCharArray())] = right;
continue;
}
if (left == "fs") left = "fullscreen";
if (left == "loop") left = "loop-file";
conf[left] = right;
}
}
}
return conf;
}
protected override void OnClosed(EventArgs e) protected override void OnClosed(EventArgs e)
{ {
base.OnClosed(e); base.OnClosed(e);
WriteToDisk(); WriteToDisk();
Registry.SetValue(@"HKEY_CURRENT_USER\Software\mpv.net", "conf editor search", SearchControl.Text);
} }
void WriteToDisk() void WriteToDisk()
{ {
foreach (var mpvSetting in DynamicSettings) bool isDirty = false;
{
switch (mpvSetting)
{
case StringSetting s:
if ((s.Value ?? "") != s.Default || mpvConf.ContainsKey(s.Name) || mpvConf.ContainsKey(s.Alias ?? ""))
mpvConf[s.Name] = s.Value;
break;
case OptionSetting s:
if ((s.Value ?? "") != s.Default || mpvConf.ContainsKey(s.Name) || mpvConf.ContainsKey(s.Alias ?? ""))
mpvConf[s.Name] = s.Value;
break;
}
}
if (!File.Exists(mpvConfPath)) foreach (SettingBase i in MpvSettingsDefinitions)
File.WriteAllText(mpvConfPath, ""); if (i.StartValue != i.Value)
isDirty = true;
List<string> lines = File.ReadAllLines(mpvConfPath).ToList(); foreach (SettingBase i in MpvNetSettingsDefinitions)
if (i.StartValue != i.Value)
isDirty = true;
foreach (var mpvSetting in DynamicSettings) if (!isDirty)
{ return;
foreach (var line in lines.ToArray())
{
string test = line.Replace("#", "").Replace(" ", "");
if (test.StartsWith(mpvSetting.Alias + "="))
{
lines.Remove(line);
foreach (var pair in mpvConf.ToArray())
if (test.StartsWith(pair.Key + "="))
mpvConf.Remove(pair.Key);
}
}
}
foreach (var pair in mpvConf) WriteToDisk(MpvConfPath, MpvConf, MpvSettingsDefinitions);
{ WriteToDisk(MpvNetConfPath, MpvNetConf, MpvNetSettingsDefinitions);
bool changed = false;
for (int i = 0; i < lines.Count; i++)
{
if (lines[i].Contains("=") &&
lines[i].Substring(0, lines[i].IndexOf("=")).Trim("# ".ToCharArray()) == pair.Key)
{
lines[i] = pair.Key + " = " + pair.Value;
changed = true;
}
}
if (!changed)
lines.Add(pair.Key + " = " + pair.Value);
}
foreach (var mpvSetting in DynamicSettings)
{
foreach (var line in lines.ToArray())
{
string test = line.Replace("#", "").Replace(" ", "");
if (test.StartsWith(mpvSetting.Name + "=") && !mpvConf.ContainsKey(mpvSetting.Name))
lines.Remove(line);
}
}
File.WriteAllText(mpvConfPath, String.Join(Environment.NewLine, lines));
MessageBox.Show("Changes will be available on next startup of mpv(.net).", MessageBox.Show("Changes will be available on next startup of mpv(.net).",
Title, MessageBoxButton.OK, MessageBoxImage.Information); Title, MessageBoxButton.OK, MessageBoxImage.Information);
} }
void WriteToDisk(string filePath,
Dictionary<string, string> confSettings,
List<SettingBase> settings)
{
string content = "";
foreach (var i in Comments[filePath])
content += $"#{i.Key} = {i.Value}\r\n";
foreach (var setting in settings)
{
if ((setting.Value ?? "") != setting.Default)
confSettings[setting.Name] = setting.Value;
if (confSettings.ContainsKey(setting.Name) &&
(setting.Value ?? "") == setting.Default ||
(setting.Value ?? "") == "")
{
confSettings.Remove(setting.Name);
}
}
foreach (var i in confSettings)
content = content + $"{i.Key} = {i.Value}\r\n";
File.WriteAllText(filePath, content);
}
private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e) private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
{ {
string activeFilter = ""; string activeFilter = "";
@@ -183,6 +229,7 @@ namespace mpvConfEdit
private void MainWindow1_Loaded(object sender, RoutedEventArgs e) private void MainWindow1_Loaded(object sender, RoutedEventArgs e)
{ {
SearchControl.SearchTextBox.SelectAll();
Keyboard.Focus(SearchControl.SearchTextBox); Keyboard.Focus(SearchControl.SearchTextBox);
} }
@@ -194,7 +241,7 @@ namespace mpvConfEdit
private void OpenSettingsTextBlock_MouseUp(object sender, MouseButtonEventArgs e) private void OpenSettingsTextBlock_MouseUp(object sender, MouseButtonEventArgs e)
{ {
Process.Start(Path.GetDirectoryName(mpvConfPath)); Process.Start(Path.GetDirectoryName(MpvConfPath));
} }
private void ShowManualTextBlock_MouseUp(object sender, MouseButtonEventArgs e) private void ShowManualTextBlock_MouseUp(object sender, MouseButtonEventArgs e)

View File

@@ -12,7 +12,7 @@ using System.Windows;
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("mpv(.net) conf edit")] [assembly: AssemblyProduct("mpv(.net) conf edit")]
[assembly: AssemblyCopyright("Copyright © 2017 stax76")] [assembly: AssemblyCopyright("Copyright © 2017-2019 stax76")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
@@ -51,5 +51,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.3.0.0")] [assembly: AssemblyVersion("1.8.0.0")]
[assembly: AssemblyFileVersion("1.3.0.0")] [assembly: AssemblyFileVersion("1.8.0.0")]

View File

@@ -1,13 +1,13 @@
<UserControl x:Class="Controls.SearchTextBoxUserControl" <UserControl x:Class="Controls.SearchTextBoxUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"> d:DesignHeight="450" d:DesignWidth="800">
<Grid x:Name="SearchTextBoxUserControl1" Background="White"> <Grid x:Name="SearchTextBoxUserControl1" Background="{Binding Path=Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}">
<TextBlock x:Name="SearchTextBlock" Margin="5,2" Text="Find a setting" Foreground="LightSteelBlue" VerticalAlignment="Center" /> <TextBlock x:Name="HintTextBlock" Margin="5,2" Text="Find a setting" Foreground="LightSteelBlue" VerticalAlignment="Center" Background="{Binding Path=Background, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" />
<TextBox Name="SearchTextBox" Height="25" Padding="1,2,0,0" BorderThickness="2" Background="Transparent" TextChanged="SearchTextBox_TextChanged" /> <TextBox Name="SearchTextBox" Height="25" Padding="1,2,0,0" BorderThickness="2" Background="Transparent" TextChanged="SearchTextBox_TextChanged" Foreground="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" CaretBrush="{Binding Path=Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" />
<Button x:Name="SearchClearButton" Background="Transparent" HorizontalAlignment="Right" Margin="2,0,4,0" FontSize="5" Width="17" Height="17" Visibility="Hidden" Click="SearchClearButton_Click"></Button> <Button x:Name="SearchClearButton" Background="Transparent" HorizontalAlignment="Right" Margin="2,0,4,0" FontSize="10" Width="17" Height="17" Visibility="Hidden" Click="SearchClearButton_Click" FontFamily="Marlett" Foreground="{Binding Path=Foreground2, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}">r</Button>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@@ -21,7 +21,7 @@ namespace Controls
private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e) private void SearchTextBox_TextChanged(object sender, TextChangedEventArgs e)
{ {
SearchTextBlock.Text = SearchTextBox.Text == "" ? "Find a setting" : ""; HintTextBlock.Text = SearchTextBox.Text == "" ? "Find a setting" : "";
if (SearchTextBox.Text == "") if (SearchTextBox.Text == "")
SearchClearButton.Visibility = Visibility.Hidden; SearchClearButton.Visibility = Visibility.Hidden;

38
mpvConfEdit/app.manifest Normal file
View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!--
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
-->
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>
</assembly>

View File

@@ -146,12 +146,38 @@ default = "100"
filter = "Audio" filter = "Audio"
help = "--volume=<integer> Set the startup volume. 0 means silence, 100 means no volume reduction or amplification. Negative values can be passed for compatibility, but are treated as 0. Since mpv 0.18.1, this always controls the internal mixer (aka \"softvol\"). Default: 100" help = "--volume=<integer> Set the startup volume. 0 means silence, 100 means no volume reduction or amplification. Negative values can be passed for compatibility, but are treated as 0. Since mpv 0.18.1, this always controls the internal mixer (aka \"softvol\"). Default: 100"
[[settings]]
name = "alang"
default = ""
filter = "Audio"
help = "--alang=<languagecode[,languagecode,...]> Specify a priority list of audio languages to use. Different container formats employ different language codes. DVDs use ISO 639-1 two-letter language codes, Matroska, MPEG-TS and NUT use ISO 639-2 three-letter language codes, while OGM uses a free-form identifier. See also --aid.\n\nExamples\n\nmpv dvd://1 --alang=hu,en chooses the Hungarian language track on a DVD and falls back on English if Hungarian is not available.\n\nmpv --alang=jpn example.mkv plays a Matroska file with Japanese audio."
[[settings]]
name = "audio-file-auto"
default = "no"
filter = "Audio"
help = "--audio-file-auto=<no|exact|fuzzy|all>, --no-audio-file-auto Load additional audio files matching the video filename. The parameter specifies how external audio files are matched."
options = [{ name = "no", help = "Don't automatically load external audio files (default)." },
{ name = "exact", help = "Load the media filename with audio file extension." },
{ name = "fuzzy", help = "Load all audio files containing media filename." },
{ name = "all", help = "Load all audio files in the current and --audio-file-paths directories." }]
[[settings]] [[settings]]
name = "slang" name = "slang"
default = "" default = ""
filter = "Subtitle" filter = "Subtitle"
help = "--slang=<languagecode[,languagecode,...]> Specify a priority list of subtitle languages to use. Different container formats employ different language codes. DVDs use ISO 639-1 two letter language codes, Matroska uses ISO 639-2 three letter language codes while OGM uses a free-form identifier. See also --sid." help = "--slang=<languagecode[,languagecode,...]> Specify a priority list of subtitle languages to use. Different container formats employ different language codes. DVDs use ISO 639-1 two letter language codes, Matroska uses ISO 639-2 three letter language codes while OGM uses a free-form identifier. See also --sid."
[[settings]]
name = "sub-auto"
default = "exact"
filter = "Subtitle"
help = "--sub-auto=<no|exact|fuzzy|all>, --no-sub-auto Load additional subtitle files matching the video filename. The parameter specifies how external subtitle files are matched. exact is enabled by default."
options = [{ name = "no", help = "Don't automatically load external subtitle files." },
{ name = "exact", help = "Load the media filename with subtitle file extension (Default)." },
{ name = "fuzzy", help = "Load all subs containing media filename." },
{ name = "all", help = "Load all subs in the current and --sub-file-paths directories." }]
[[settings]] [[settings]]
name = "screen" name = "screen"
default = "" default = ""
@@ -163,18 +189,31 @@ name = "osd-playing-msg"
default = "" default = ""
width = 300 width = 300
filter = "Screen" filter = "Screen"
help = "--osd-playing-msg=<value> Show a message on OSD when playback starts. The string is expanded for properties, e.g. --osd-playing-msg='file: ${filename}' will show the message file: followed by a space and the currently played filename.\n\nFor more information visit:" help = "--osd-playing-msg=<value> Show a message on OSD when playback starts. The string is expanded for properties, e.g. --osd-playing-msg='file: ${filename}' will show the message file: followed by a space and the currently played filename. For more information visit:"
helpurl = "https://mpv.io/manual/master/#property-expansion" helpurl = "https://mpv.io/manual/master/#property-expansion"
[[settings]] [[settings]]
name = "fullscreen" name = "fullscreen"
alias = "fs"
default = "no" default = "no"
filter = "Screen" filter = "Screen"
help = "--fullscreen=<yes|no>, fs=<yes|no> Start the player in fullscreen mode. Default: no." help = "--fullscreen=<yes|no>, fs=<yes|no> Start the player in fullscreen mode. Default: no."
options = [{ name = "yes" }, options = [{ name = "yes" },
{ name = "no" }] { name = "no" }]
[[settings]]
name = "screenshot-directory"
default = ""
width = 500
folder = true
filter = "Screen"
help = "--screenshot-directory=<value> Store screenshots in this directory. This path is joined with the filename generated by --screenshot-template. If the template filename is already absolute, the directory is ignored.\n\nIf the directory does not exist, it is created on the first screenshot. If it is not a directory, an error is generated when trying to write a screenshot.\n\nThis option is not set by default, and thus will write screenshots to the directory from which mpv was started. In pseudo-gui mode (see PSEUDO GUI MODE), this is set to the desktop."
[[settings]]
name = "autofit"
default = "50%"
filter = "Screen"
help = "--autofit=<percent value> Set the initial window size in percent. Please note that this setting is only partly implemented in mpv.net, accepted are only integer values with percent sign added. Default: 50%."
[[settings]] [[settings]]
name = "keep-open-pause" name = "keep-open-pause"
default = "yes" default = "yes"
@@ -194,7 +233,6 @@ options = [{ name = "yes", help = "Don't terminate if the current file is the
[[settings]] [[settings]]
name = "loop-file" name = "loop-file"
alias = "loop"
default = "" default = ""
filter = "Playback" filter = "Playback"
help = "--loop-file=<N|inf|no>, loop=<N|inf|no> Loop a single file N times. inf means forever, no means normal playback. For compatibility, --loop-file and --loop-file=yes are also accepted, and are the same as --loop-file=inf.\n\nThe difference to --loop-playlist is that this doesn't loop the playlist, just the file itself. If the playlist contains only a single file, the difference between the two option is that this option performs a seek on loop, instead of reloading the file.\n\n--loop is an alias for this option." help = "--loop-file=<N|inf|no>, loop=<N|inf|no> Loop a single file N times. inf means forever, no means normal playback. For compatibility, --loop-file and --loop-file=yes are also accepted, and are the same as --loop-file=inf.\n\nThe difference to --loop-playlist is that this doesn't loop the playlist, just the file itself. If the playlist contains only a single file, the difference between the two option is that this option performs a seek on loop, instead of reloading the file.\n\n--loop is an alias for this option."
@@ -207,14 +245,6 @@ help = "--save-position-on-quit=<yes|no> Always save the current playback positi
options = [{ name = "yes" }, options = [{ name = "yes" },
{ name = "no" }] { name = "no" }]
[[settings]]
name = "screenshot-directory"
default = ""
width = 500
folder = true
filter = "Screen"
help = "--screenshot-directory=<value> Store screenshots in this directory. This path is joined with the filename generated by --screenshot-template. If the template filename is already absolute, the directory is ignored.\n\nIf the directory does not exist, it is created on the first screenshot. If it is not a directory, an error is generated when trying to write a screenshot.\n\nThis option is not set by default, and thus will write screenshots to the directory from which mpv was started. In pseudo-gui mode (see PSEUDO GUI MODE), this is set to the desktop."
[[settings]] [[settings]]
name = "input-ar-delay" name = "input-ar-delay"
default = "" default = ""
@@ -227,12 +257,6 @@ default = ""
filter = "Input" filter = "Input"
help = "--input-ar-rate=<integer> Number of key presses to generate per second on autorepeat." help = "--input-ar-rate=<integer> Number of key presses to generate per second on autorepeat."
[[settings]]
name = "alang"
default = ""
filter = "Audio"
help = "--alang=<languagecode[,languagecode,...]> Specify a priority list of audio languages to use. Different container formats employ different language codes. DVDs use ISO 639-1 two-letter language codes, Matroska, MPEG-TS and NUT use ISO 639-2 three-letter language codes, while OGM uses a free-form identifier. See also --aid.\n\nExamples\n\nmpv dvd://1 --alang=hu,en chooses the Hungarian language track on a DVD and falls back on English if Hungarian is not available.\n\nmpv --alang=jpn example.mkv plays a Matroska file with Japanese audio."
[[settings]] [[settings]]
name = "hr-seek" name = "hr-seek"
default = "absolute" default = "absolute"
@@ -255,24 +279,4 @@ options = [{ name = "yes" },
name = "loop-playlist" name = "loop-playlist"
default = "" default = ""
filter = "Playback" filter = "Playback"
help = "--loop-playlist=<N|inf|force|no>, --loop-playlist Loops playback N times. A value of 1 plays it one time (default), 2 two times, etc. inf means forever. no is the same as 1 and disables looping. If several files are specified on command line, the entire playlist is looped. --loop-playlist is the same as --loop-playlist=inf.\n\nThe force mode is like inf, but does not skip playlist entries which have been marked as failing. This means the player might waste CPU time trying to loop a file that doesn't exist. But it might be useful for playing webradios under very bad network conditions." help = "--loop-playlist=<N|inf|force|no>, --loop-playlist Loops playback N times. A value of 1 plays it one time (default), 2 two times, etc. inf means forever. no is the same as 1 and disables looping. If several files are specified on command line, the entire playlist is looped. --loop-playlist is the same as --loop-playlist=inf.\n\nThe force mode is like inf, but does not skip playlist entries which have been marked as failing. This means the player might waste CPU time trying to loop a file that doesn't exist. But it might be useful for playing webradios under very bad network conditions."
[[settings]]
name = "audio-file-auto"
default = "no"
filter = "Audio"
help = "--audio-file-auto=<no|exact|fuzzy|all>, --no-audio-file-auto Load additional audio files matching the video filename. The parameter specifies how external audio files are matched."
options = [{ name = "no", help = "Don't automatically load external audio files (default)." },
{ name = "exact", help = "Load the media filename with audio file extension." },
{ name = "fuzzy", help = "Load all audio files containing media filename." },
{ name = "all", help = "Load all audio files in the current and --audio-file-paths directories." }]
[[settings]]
name = "sub-auto"
default = "exact"
filter = "Subtitle"
help = "--sub-auto=<no|exact|fuzzy|all>, --no-sub-auto Load additional subtitle files matching the video filename. The parameter specifies how external subtitle files are matched. exact is enabled by default."
options = [{ name = "no", help = "Don't automatically load external subtitle files." },
{ name = "exact", help = "Load the media filename with subtitle file extension (Default)." },
{ name = "fuzzy", help = "Load all subs containing media filename." },
{ name = "all", help = "Load all subs in the current and --sub-file-paths directories." }]

View File

@@ -44,9 +44,13 @@
<PropertyGroup> <PropertyGroup>
<ApplicationIcon>mpv.ico</ApplicationIcon> <ApplicationIcon>mpv.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" /> <Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
@@ -66,6 +70,7 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</ApplicationDefinition> </ApplicationDefinition>
<Compile Include="Controls.cs" />
<Compile Include="DynamicGUI\DynamicGUI.cs" /> <Compile Include="DynamicGUI\DynamicGUI.cs" />
<Compile Include="DynamicGUI\OptionSettingControl.xaml.cs"> <Compile Include="DynamicGUI\OptionSettingControl.xaml.cs">
<DependentUpon>OptionSettingControl.xaml</DependentUpon> <DependentUpon>OptionSettingControl.xaml</DependentUpon>
@@ -120,6 +125,10 @@
<Generator>ResXFileCodeGenerator</Generator> <Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput> <LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource> </EmbeddedResource>
<None Include="app.manifest" />
<None Include="mpvNetConf.toml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="Properties\Settings.settings"> <None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator> <Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput> <LastGenOutput>Settings.Designer.cs</LastGenOutput>
@@ -129,7 +138,7 @@
<None Include="App.config" /> <None Include="App.config" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="mpvConfEdit.toml"> <None Include="mpvConf.toml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory> <CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None> </None>
</ItemGroup> </ItemGroup>

View File

@@ -0,0 +1,8 @@
[[settings]]
name = "dark-mode"
default = "system"
filter = "mpv.net"
help = "Enables a dark theme."
options = [{ name = "always" },
{ name = "system" , help = "Windows 10+" },
{ name = "never" }]

View File

@@ -1,7 +1,7 @@
<Application x:Class="mpvInputEdit.App" <Application x:Class="mpvInputEdit.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:mpvInputEdit" xmlns:Controls="clr-namespace:Controls"
StartupUri="MainWindow.xaml"> StartupUri="MainWindow.xaml">
<Application.Resources> <Application.Resources>
<Style TargetType="TextBox"> <Style TargetType="TextBox">
@@ -19,7 +19,7 @@
<Setter Property="BorderBrush" TargetName="border" Value="#FF7EB4EA"/> <Setter Property="BorderBrush" TargetName="border" Value="#FF7EB4EA"/>
</Trigger> </Trigger>
<Trigger Property="IsFocused" Value="true"> <Trigger Property="IsFocused" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{x:Static SystemParameters.WindowGlassBrush}"/> <Setter Property="BorderBrush" TargetName="border" Value="{x:Static Controls:Controls.ThemeBrush}"/>
</Trigger> </Trigger>
</ControlTemplate.Triggers> </ControlTemplate.Triggers>
</ControlTemplate> </ControlTemplate>

18
mpvInputEdit/Controls.cs Normal file
View File

@@ -0,0 +1,18 @@
using System;
using System.Windows;
using System.Windows.Media;
namespace Controls
{
class Controls
{
public static Brush ThemeBrush {
get {
if (Environment.OSVersion.Version.Major < 10)
return new SolidColorBrush(Colors.DarkSlateGray);
else
return SystemParameters.WindowGlassBrush;
}
}
}
}

View File

@@ -1,5 +1,4 @@
using System; using System;
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Windows; using System.Windows;
using System.Windows.Input; using System.Windows.Input;
@@ -10,7 +9,6 @@ namespace mpvInputEdit
{ {
public partial class InputWindow : Window public partial class InputWindow : Window
{ {
string InputString = "";
public InputItem InputItem { get; set; } public InputItem InputItem { get; set; }
public string NewKey { get; set; } = ""; public string NewKey { get; set; } = "";
@@ -32,22 +30,21 @@ namespace mpvInputEdit
void OnKeyUp(WF.KeyEventArgs e) void OnKeyUp(WF.KeyEventArgs e)
{ {
if (e.KeyCode == WF.Keys.None) return;
char c = Convert.ToChar(e.KeyCode);
string text = InputString;
if (e.KeyCode == WF.Keys.ControlKey || e.KeyCode == WF.Keys.ShiftKey || if (e.KeyCode == WF.Keys.ControlKey || e.KeyCode == WF.Keys.ShiftKey ||
e.KeyCode == WF.Keys.Menu) e.KeyCode == WF.Keys.Menu || e.KeyCode == WF.Keys.None)
{
return;
}
if (text == "") return;
{
text = e.KeyCode.ToString(); string text = "";
if (text.Length == 1) uint charValue = MapVirtualKey((uint)e.KeyCode, 2);
text = text.ToLowerInvariant();
} if (charValue == 0 || (charValue & 1 << 31) == 1 << 31)
text = e.KeyCode.ToString().Trim();
else
try {
text = Convert.ToChar(charValue).ToString().ToLower().Trim();
}
catch {}
for (int i = 0; i < 13; i++) for (int i = 0; i < 13; i++)
if ("D" + i.ToString() == text) if ("D" + i.ToString() == text)
@@ -90,14 +87,6 @@ namespace mpvInputEdit
text = "Esc"; break; text = "Esc"; break;
case WF.Keys.PrintScreen: case WF.Keys.PrintScreen:
text = "Print"; break; text = "Print"; break;
case WF.Keys.Right:
text = "Right"; break;
case WF.Keys.Left:
text = "Left"; break;
case WF.Keys.Up:
text = "Up"; break;
case WF.Keys.Down:
text = "Down"; break;
case WF.Keys.Play: case WF.Keys.Play:
text = "Play"; break; text = "Play"; break;
case WF.Keys.Pause: case WF.Keys.Pause:
@@ -130,20 +119,24 @@ namespace mpvInputEdit
text = "Cancel"; break; text = "Cancel"; break;
} }
if (text == "#") bool shiftWasHandled = false;
text = "Sharp";
bool isAlt = GetKeyState(18) < (short)0; bool isAlt = GetKeyState(18) < (short)0;
bool isShift = GetKeyState(16) < (short)0; bool isShift = GetKeyState(16) < (short)0;
bool isCtrl = GetKeyState(17) < (short)0; bool isCtrl = GetKeyState(17) < (short)0;
if (!(isAlt && isCtrl && !isShift) && !(isShift && !isAlt && !isCtrl && !int.TryParse(text.Replace("F", ""), out int value))) if (text.Length == 1 && isShift && text[0] != GetModifiedKey(text[0]))
{ {
if (isAlt) text = "Alt+" + text; text = GetModifiedKey(text[0]).ToString();
if (isShift) text = "Shift+" + text; shiftWasHandled = true;
if (isCtrl) text = "Ctrl+" + text;
} }
if (text == "#") text = "Sharp";
if (isAlt) text = "Alt+" + text;
if (isShift && !shiftWasHandled) text = "Shift+" + text;
if (isCtrl) text = "Ctrl+" + text;
if (!string.IsNullOrEmpty(text)) if (!string.IsNullOrEmpty(text))
SetKey(text); SetKey(text);
} }
@@ -155,13 +148,8 @@ namespace mpvInputEdit
KeyLabel.Content = key; KeyLabel.Content = key;
} }
void OnKeyPress(WF.KeyPressEventArgs e) [DllImport("user32.dll")]
{ static extern uint MapVirtualKey(uint uCode, uint uMapType);
if (char.IsControl(e.KeyChar))
InputString = "";
else
InputString = e.KeyChar.ToString();
}
public static WF.Keys ModifierKeys { public static WF.Keys ModifierKeys {
get { get {
@@ -176,80 +164,78 @@ namespace mpvInputEdit
} }
} }
public static char GetModifiedKey(char c)
{
short vkKeyScanResult = VkKeyScan(c);
if (vkKeyScanResult == -1)
return c;
uint code = (uint)vkKeyScanResult & 0xff;
byte[] b = new byte[256];
b[0x10] = 0x80;
uint r;
if (1 != ToAscii(code, code, b, out r, 0))
return c;
return (char)r;
}
void ProcessKeyEventArgs(ref WF.Message m) void ProcessKeyEventArgs(ref WF.Message m)
{ {
int WM_CHAR = 258, WM_SYSCHAR = 262, /*WM_KEYDOWN = 256, WM_SYSKEYDOWN = 260,*/ int WM_KEYUP = 0x0101, WM_SYSKEYUP = 0x0105, WM_APPCOMMAND = 0x0319;
WM_KEYUP = 0x0101, WM_SYSKEYUP = 0x0105, WM_APPCOMMAND = 0x0319;
IntPtr newWParam = IntPtr.Zero; if (m.Msg == WM_KEYUP || m.Msg == WM_SYSKEYUP)
WF.KeyEventArgs ke = null; OnKeyUp(new WF.KeyEventArgs((WF.Keys)(unchecked((int)(long)m.WParam)) | ModifierKeys));
WF.KeyPressEventArgs kpe = null; else if (m.Msg == WM_APPCOMMAND)
if (m.Msg == WM_CHAR || m.Msg == WM_SYSCHAR)
{ {
kpe = new WF.KeyPressEventArgs(unchecked((char)(long)m.WParam)); switch ((AppCommand)(m.LParam.ToInt32() >> 16))
OnKeyPress(kpe);
newWParam = (IntPtr)kpe.KeyChar;
}
else
{
ke = new WF.KeyEventArgs((WF.Keys)(unchecked((int)(long)m.WParam)) | ModifierKeys);
if (m.Msg == WM_KEYUP || m.Msg == WM_SYSKEYUP)
OnKeyUp(ke);
if (m.Msg == WM_APPCOMMAND)
{ {
switch ((AppCommand)(m.LParam.ToInt32() >> 16)) case AppCommand.MEDIA_CHANNEL_DOWN:
{ SetKey("Channel_Down");
case AppCommand.MEDIA_CHANNEL_DOWN: break;
SetKey("Channel_Down"); case AppCommand.MEDIA_CHANNEL_UP:
break; SetKey("Channel_Up");
case AppCommand.MEDIA_CHANNEL_UP: break;
SetKey("Channel_Up"); case AppCommand.MEDIA_FAST_FORWARD:
break; SetKey("Forward");
case AppCommand.MEDIA_FAST_FORWARD: break;
SetKey("Forward"); case AppCommand.MEDIA_REWIND:
break; SetKey("Rewind");
case AppCommand.MEDIA_REWIND: break;
SetKey("Rewind"); case AppCommand.MEDIA_PAUSE:
break; SetKey("Pause");
case AppCommand.MEDIA_PAUSE: break;
SetKey("Pause"); case AppCommand.MEDIA_PLAY:
break; SetKey("Play");
case AppCommand.MEDIA_PLAY: break;
SetKey("Play"); case AppCommand.MEDIA_PLAY_PAUSE:
break; SetKey("PlayPause");
case AppCommand.MEDIA_PLAY_PAUSE: break;
SetKey("PlayPause"); case AppCommand.MEDIA_NEXTTRACK:
break; SetKey("Next");
case AppCommand.MEDIA_NEXTTRACK: break;
SetKey("Next"); case AppCommand.MEDIA_PREVIOUSTRACK:
break; SetKey("Prev");
case AppCommand.MEDIA_PREVIOUSTRACK: break;
SetKey("Prev"); case AppCommand.MEDIA_RECORD:
break; SetKey("Record");
case AppCommand.MEDIA_RECORD: break;
SetKey("Record"); case AppCommand.MEDIA_STOP:
break; SetKey("Stop");
case AppCommand.MEDIA_STOP: break;
SetKey("Stop"); case AppCommand.VolumeUp:
break; SetKey("Volume_Up");
case AppCommand.VolumeUp: break;
SetKey("Volume_Up"); case AppCommand.VolumeDown:
break; SetKey("Volume_Down");
case AppCommand.VolumeDown: break;
SetKey("Volume_Down"); case AppCommand.VolumeMute:
break; SetKey("Mute");
case AppCommand.VolumeMute: break;
SetKey("Mute");
break;
}
} }
} }
if (kpe != null)
m.WParam = newWParam;
} }
internal enum AppCommand internal enum AppCommand
@@ -273,6 +259,16 @@ namespace mpvInputEdit
[DllImport("user32.dll", CharSet = CharSet.Auto)] [DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern short GetKeyState(int keyCode); public static extern short GetKeyState(int keyCode);
[DllImport("user32.dll")]
static extern short VkKeyScan(char c);
[DllImport("user32.dll", SetLastError = true)]
static extern int ToAscii(uint uVirtKey,
uint uScanCode,
byte[] lpKeyState,
out uint lpChar,
uint flags);
private void Window_Loaded(object sender, RoutedEventArgs e) private void Window_Loaded(object sender, RoutedEventArgs e)
{ {
HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle); HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);

View File

@@ -37,15 +37,17 @@ namespace mpvInputEdit
string searchText = SearchControl.SearchTextBox.Text.ToLower(); string searchText = SearchControl.SearchTextBox.Text.ToLower();
if (searchText == "") return true; if (searchText == "") return true;
if (searchText.StartsWith("i ") || searchText.StartsWith("i:")) if (searchText.StartsWith("i ") || searchText.StartsWith("i:") || searchText.Length == 1)
{ {
searchText = searchText.Substring(2).Trim(); if (searchText.Length > 1)
searchText = searchText.Substring(2).Trim();
if (searchText.Length < 3) if (searchText.Length < 3)
return item.Input.ToLower().Replace("ctrl+", "").Replace("shift+", "").Replace("alt+", "").Contains(searchText); return item.Input.ToLower().Replace("ctrl+", "").Replace("shift+", "").Replace("alt+", "").Contains(searchText);
else else
return item.Input.ToLower().Contains(searchText); return item.Input.ToLower().Contains(searchText);
} else if (searchText.StartsWith("m ") || searchText.StartsWith("m:")) }
else if (searchText.StartsWith("m ") || searchText.StartsWith("m:"))
return item.Menu.ToLower().Contains(searchText.Substring(2).Trim()); return item.Menu.ToLower().Contains(searchText.Substring(2).Trim());
else if (searchText.StartsWith("c ") || searchText.StartsWith("c:")) else if (searchText.StartsWith("c ") || searchText.StartsWith("c:"))
return item.Command.ToLower().Contains(searchText.Substring(2).Trim()); return item.Command.ToLower().Contains(searchText.Substring(2).Trim());
@@ -86,7 +88,7 @@ namespace mpvInputEdit
Directory.CreateDirectory(backupDir); Directory.CreateDirectory(backupDir);
if (File.Exists(App.InputConfPath)) if (File.Exists(App.InputConfPath))
File.Copy(App.InputConfPath, backupDir + "input conf " + DateTime.Now.ToString("yyyy-MM-dd HH-mm") + ".conf"); File.Copy(App.InputConfPath, backupDir + "input conf " + DateTime.Now.ToString("yyyy-MM-dd HH-mm") + ".conf", true);
string text = "\r\n" + Properties.Settings.Default.input_conf_help + "\r\n\r\n"; string text = "\r\n" + Properties.Settings.Default.input_conf_help + "\r\n\r\n";

View File

@@ -8,11 +8,11 @@ using System.Windows;
// set of attributes. Change these attribute values to modify the information // set of attributes. Change these attribute values to modify the information
// associated with an assembly. // associated with an assembly.
[assembly: AssemblyTitle("mpv(.net) input edit")] [assembly: AssemblyTitle("mpv(.net) input edit")]
[assembly: AssemblyDescription("mpv(.net) key bindings editor")] [assembly: AssemblyDescription("mpv(.net) key and mouse bindings editor")]
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("mpv(.net) input edit")] [assembly: AssemblyProduct("mpv(.net) input edit")]
[assembly: AssemblyCopyright("Copyright © 2017 stax76")] [assembly: AssemblyCopyright("Copyright © 2017-2019 stax76")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
@@ -51,5 +51,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.2.0.0")] [assembly: AssemblyVersion("1.5.0.0")]
[assembly: AssemblyFileVersion("1.2.0.0")] [assembly: AssemblyFileVersion("1.5.0.0")]

View File

@@ -29,7 +29,7 @@ namespace Controls
SearchClearButton.Visibility = Visibility.Visible; SearchClearButton.Visibility = Visibility.Visible;
if (SearchTextBox.Text == "?") if (SearchTextBox.Text == "?")
MessageBox.Show("Filtering works by searching in the Input, Menu and Command but it's possible to reduce the filter scope to either of Input, Menu or Command by prefixing as follows:\n\ni <input search>\ni: <input search>\n\nm <menu search>\nm: <menu search>\n\nc <command search>\nc: <command search>", "Filtering", MessageBoxButton.OK, MessageBoxImage.Information); MessageBox.Show("Filtering works by searching in the Input, Menu and Command but it's possible to reduce the filter scope to either of Input, Menu or Command by prefixing as follows:\n\ni <input search>\ni: <input search>\n\nm <menu search>\nm: <menu search>\n\nc <command search>\nc: <command search>\n\nIf only one character is entered the search will be performed only in the input.", "Filtering", MessageBoxButton.OK, MessageBoxImage.Information);
} }
} }
} }

38
mpvInputEdit/app.manifest Normal file
View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!--
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
-->
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>
</assembly>

View File

@@ -38,6 +38,9 @@
<PropertyGroup> <PropertyGroup>
<ApplicationIcon>mpv.ico</ApplicationIcon> <ApplicationIcon>mpv.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Data" /> <Reference Include="System.Data" />
@@ -60,6 +63,7 @@
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType> <SubType>Designer</SubType>
</ApplicationDefinition> </ApplicationDefinition>
<Compile Include="Controls.cs" />
<Compile Include="SearchTextBoxUserControl.xaml.cs"> <Compile Include="SearchTextBoxUserControl.xaml.cs">
<DependentUpon>SearchTextBoxUserControl.xaml</DependentUpon> <DependentUpon>SearchTextBoxUserControl.xaml</DependentUpon>
</Compile> </Compile>
@@ -106,6 +110,10 @@
<Generator>ResXFileCodeGenerator</Generator> <Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput> <LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource> </EmbeddedResource>
<None Include="..\README.md">
<Link>README.md</Link>
</None>
<None Include="app.manifest" />
<None Include="Properties\Settings.settings"> <None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator> <Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput> <LastGenOutput>Settings.Designer.cs</LastGenOutput>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 KiB

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB