66using System . Text ;
77using System . Windows . Input ;
88using Windows . Storage . Pickers ;
9+ using Files . Shared . Helpers ;
910
1011namespace Files . App . ViewModels . Dialogs
1112{
@@ -20,45 +21,151 @@ public sealed class CreateShortcutDialogViewModel : ObservableObject
2021 // Tells whether destination path exists
2122 public bool DestinationPathExists { get ; set ; }
2223
23- // Tells wheteher the shortcut has been created
24+ // Tells whether the shortcut has been created
2425 public bool ShortcutCreatedSuccessfully { get ; private set ; }
2526
2627 // Shortcut name with extension
2728 public string ShortcutCompleteName { get ; private set ; } = string . Empty ;
2829
29- // Destination of the shortcut chosen by the user (can be a path or a URL)
30- private string _destinationItemPath ;
31- public string DestinationItemPath
30+ // Full path of the destination item
31+ public string FullPath { get ; private set ; }
32+
33+ // Arguments to be passed to the destination item if it's an executable
34+ public string Arguments { get ; private set ; }
35+
36+ // Previous path of the destination item
37+ private string _previousShortcutTargetPath ;
38+
39+ // Destination of the shortcut chosen by the user (can be a path, a command or a URL)
40+ private string _shortcutTarget ;
41+ public string ShortcutTarget
3242 {
33- get => _destinationItemPath ;
43+ get => _shortcutTarget ;
3444 set
3545 {
36- if ( ! SetProperty ( ref _destinationItemPath , value ) )
46+ if ( ! SetProperty ( ref _shortcutTarget , value ) )
3747 return ;
3848
3949 OnPropertyChanged ( nameof ( ShowWarningTip ) ) ;
40- if ( string . IsNullOrWhiteSpace ( DestinationItemPath ) )
50+ if ( string . IsNullOrWhiteSpace ( ShortcutTarget ) )
4151 {
52+ DestinationPathExists = false ;
4253 IsLocationValid = false ;
54+ _previousShortcutTargetPath = string . Empty ;
4355 return ;
4456 }
45-
4657 try
4758 {
48- DestinationPathExists = Path . Exists ( DestinationItemPath ) && DestinationItemPath != Path . GetPathRoot ( DestinationItemPath ) ;
49- if ( DestinationPathExists )
59+ var trimmed = ShortcutTarget . Trim ( ) ;
60+ // If the text starts with '"', try to parse the quoted part as path, and the rest as arguments
61+ if ( trimmed . StartsWith ( '"' ) )
5062 {
51- IsLocationValid = true ;
63+ var endQuoteIndex = trimmed . IndexOf ( '"' , 1 ) ;
64+ if ( endQuoteIndex == - 1 )
65+ {
66+ DestinationPathExists = false ;
67+ IsLocationValid = false ;
68+ _previousShortcutTargetPath = string . Empty ;
69+ return ;
70+ }
71+
72+ var quoted = trimmed [ 1 ..endQuoteIndex ] ;
73+
74+ if ( quoted == _previousShortcutTargetPath )
75+ {
76+ Arguments = ! Directory . Exists ( FullPath ) ? trimmed [ ( endQuoteIndex + 1 ) ..] : string . Empty ;
77+ return ;
78+ }
79+
80+ if ( IsValidAbsolutePath ( quoted ) )
81+ {
82+ DestinationPathExists = true ;
83+ IsLocationValid = true ;
84+ FullPath = Path . GetFullPath ( quoted ) ;
85+ Arguments = ! Directory . Exists ( FullPath ) ? trimmed [ ( endQuoteIndex + 1 ) ..] : string . Empty ;
86+ _previousShortcutTargetPath = quoted ;
87+ return ;
88+ }
89+
90+ // If the quoted part is a valid filename, try to find it in the PATH
91+ if ( quoted == Path . GetFileName ( quoted )
92+ && quoted . IndexOfAny ( Path . GetInvalidFileNameChars ( ) ) == - 1
93+ && PathHelpers . TryGetFullPath ( quoted , out var fullPath ) )
94+ {
95+ DestinationPathExists = true ;
96+ IsLocationValid = true ;
97+ FullPath = fullPath ;
98+ Arguments = trimmed [ ( endQuoteIndex + 1 ) ..] ;
99+ _previousShortcutTargetPath = quoted ;
100+ return ;
101+ }
102+
103+ var uri = new Uri ( quoted ) ;
104+ DestinationPathExists = false ;
105+ IsLocationValid = uri . IsWellFormedOriginalString ( ) ;
106+ FullPath = quoted ;
107+ Arguments = string . Empty ;
108+ _previousShortcutTargetPath = string . Empty ;
52109 }
53110 else
54111 {
55- var uri = new Uri ( DestinationItemPath ) ;
112+ var filePath = trimmed . Split ( ' ' ) [ 0 ] ;
113+
114+ if ( filePath == _previousShortcutTargetPath )
115+ {
116+ Arguments = ! Directory . Exists ( FullPath ) ? trimmed . Split ( ' ' ) [ 1 ..] . Aggregate ( string . Empty , ( current , arg ) => current + arg + " " ) : string . Empty ;
117+ return ;
118+ }
119+
120+ if ( IsValidAbsolutePath ( filePath ) )
121+ {
122+ DestinationPathExists = true ;
123+ IsLocationValid = true ;
124+ FullPath = Path . GetFullPath ( filePath ) ;
125+ Arguments = ! Directory . Exists ( FullPath ) ? trimmed . Split ( ' ' ) [ 1 ..] . Aggregate ( string . Empty , ( current , arg ) => current + arg + " " ) : string . Empty ;
126+ _previousShortcutTargetPath = filePath ;
127+ return ;
128+ }
129+
130+ // Try to parse the whole text as path
131+ if ( IsValidAbsolutePath ( trimmed ) )
132+ {
133+ DestinationPathExists = true ;
134+ IsLocationValid = true ;
135+ FullPath = Path . GetFullPath ( trimmed ) ;
136+ Arguments = string . Empty ;
137+ _previousShortcutTargetPath = string . Empty ;
138+ return ;
139+ }
140+
141+ if ( filePath == Path . GetFileName ( filePath )
142+ && filePath . IndexOfAny ( Path . GetInvalidFileNameChars ( ) ) == - 1
143+ && PathHelpers . TryGetFullPath ( filePath , out var fullPath ) )
144+ {
145+ DestinationPathExists = true ;
146+ IsLocationValid = true ;
147+ FullPath = fullPath ;
148+ Arguments = trimmed . Split ( ' ' ) [ 1 ..] . Aggregate ( string . Empty , ( current , arg ) => current + arg + " " ) ;
149+ _previousShortcutTargetPath = filePath ;
150+ return ;
151+ }
152+
153+ var uri = new Uri ( trimmed ) ;
154+ DestinationPathExists = false ;
56155 IsLocationValid = uri . IsWellFormedOriginalString ( ) ;
156+ FullPath = trimmed ;
157+ Arguments = string . Empty ;
158+ _previousShortcutTargetPath = string . Empty ;
57159 }
160+
58161 }
59162 catch ( Exception )
60163 {
164+ DestinationPathExists = false ;
61165 IsLocationValid = false ;
166+ FullPath = string . Empty ;
167+ Arguments = string . Empty ;
168+ _previousShortcutTargetPath = string . Empty ;
62169 }
63170 }
64171 }
@@ -75,7 +182,7 @@ public bool IsLocationValid
75182 }
76183 }
77184
78- public bool ShowWarningTip => ! string . IsNullOrEmpty ( DestinationItemPath ) && ! _isLocationValid ;
185+ public bool ShowWarningTip => ! string . IsNullOrEmpty ( ShortcutTarget ) && ! _isLocationValid ;
79186
80187 // Command invoked when the user clicks the 'Browse' button
81188 public ICommand SelectDestinationCommand { get ; private set ; }
@@ -86,12 +193,17 @@ public bool IsLocationValid
86193 public CreateShortcutDialogViewModel ( string workingDirectory )
87194 {
88195 WorkingDirectory = workingDirectory ;
89- _destinationItemPath = string . Empty ;
196+ _shortcutTarget = string . Empty ;
90197
91198 SelectDestinationCommand = new AsyncRelayCommand ( SelectDestination ) ;
92199 PrimaryButtonCommand = new AsyncRelayCommand ( CreateShortcutAsync ) ;
93200 }
94201
202+ private bool IsValidAbsolutePath ( string path )
203+ {
204+ return Path . Exists ( path ) && Path . IsPathFullyQualified ( path ) && path != Path . GetPathRoot ( path ) ;
205+ }
206+
95207 private Task SelectDestination ( )
96208 {
97209 Win32PInvoke . BROWSEINFO bi = new Win32PInvoke . BROWSEINFO ( ) ;
@@ -103,7 +215,7 @@ private Task SelectDestination()
103215 StringBuilder path = new StringBuilder ( 260 ) ;
104216 if ( Win32PInvoke . SHGetPathFromIDList ( pidl , path ) )
105217 {
106- DestinationItemPath = path . ToString ( ) ;
218+ ShortcutTarget = path . ToString ( ) ;
107219 }
108220 Marshal . FreeCoTaskMem ( pidl ) ;
109221 }
@@ -118,10 +230,12 @@ private async Task CreateShortcutAsync()
118230
119231 if ( DestinationPathExists )
120232 {
121- destinationName = Path . GetFileName ( DestinationItemPath ) ;
122- if ( string . IsNullOrEmpty ( destinationName ) )
233+ destinationName = Path . GetFileName ( FullPath ) ;
234+
235+ if ( string . IsNullOrEmpty ( FullPath ) )
123236 {
124- var destinationPath = DestinationItemPath . Replace ( '/' , '\\ ' ) ;
237+
238+ var destinationPath = FullPath . Replace ( '/' , '\\ ' ) ;
125239
126240 if ( destinationPath . EndsWith ( '\\ ' ) )
127241 destinationPath = destinationPath . Substring ( 0 , destinationPath . Length - 1 ) ;
@@ -131,7 +245,7 @@ private async Task CreateShortcutAsync()
131245 }
132246 else
133247 {
134- var uri = new Uri ( DestinationItemPath ) ;
248+ var uri = new Uri ( FullPath ) ;
135249 destinationName = uri . Host ;
136250 }
137251
@@ -146,7 +260,7 @@ private async Task CreateShortcutAsync()
146260 filePath = Path . Combine ( WorkingDirectory , ShortcutCompleteName ) ;
147261 }
148262
149- ShortcutCreatedSuccessfully = await FileOperationsHelpers . CreateOrUpdateLinkAsync ( filePath , DestinationItemPath ) ;
263+ ShortcutCreatedSuccessfully = await FileOperationsHelpers . CreateOrUpdateLinkAsync ( filePath , FullPath , Arguments ) ;
150264 }
151265 }
152266}
0 commit comments