@@ -23,11 +23,14 @@ namespace Terminal.Gui {
2323 internal class DirListView : View {
2424 int top , selected ;
2525 DirectoryInfo dirInfo ;
26- List < ( string , bool ) > infos ;
26+ List < ( string , bool , bool ) > infos ;
27+ internal bool canChooseFiles = true ;
28+ internal bool canChooseDirectories = false ;
29+ internal bool allowsMultipleSelection = false ;
2730
2831 public DirListView ( )
2932 {
30- infos = new List < ( string , bool ) > ( ) ;
33+ infos = new List < ( string , bool , bool ) > ( ) ;
3134 CanFocus = true ;
3235 }
3336
@@ -43,14 +46,14 @@ bool IsAllowed (FileSystemInfo fsi)
4346 return false ;
4447 }
4548
46- void Reload ( )
49+ internal void Reload ( )
4750 {
4851 dirInfo = new DirectoryInfo ( directory . ToString ( ) ) ;
4952 infos = ( from x in dirInfo . GetFileSystemInfos ( )
5053 where IsAllowed ( x )
5154 orderby ( ! x . Attributes . HasFlag ( FileAttributes . Directory ) ) + x . Name
52- select ( x . Name , x . Attributes . HasFlag ( FileAttributes . Directory ) ) ) . ToList ( ) ;
53- infos . Insert ( 0 , ( ".." , true ) ) ;
55+ select ( x . Name , x . Attributes . HasFlag ( FileAttributes . Directory ) , false ) ) . ToList ( ) ;
56+ infos . Insert ( 0 , ( ".." , true , false ) ) ;
5457 top = 0 ;
5558 selected = 0 ;
5659 SetNeedsDisplay ( ) ;
@@ -78,7 +81,7 @@ void DrawString (int line, string str)
7881 var width = f . Width ;
7982 var ustr = ustring . Make ( str ) ;
8083
81- Move ( 2 , line ) ;
84+ Move ( allowsMultipleSelection ? 3 : 2 , line ) ;
8285 int byteLen = ustr . Length ;
8386 int used = 0 ;
8487 for ( int i = 0 ; i < byteLen ; ) {
@@ -121,6 +124,10 @@ public override void Redraw (Rect region)
121124 var fi = infos [ item ] ;
122125
123126 Driver . AddRune ( isSelected ? '>' : ' ' ) ;
127+
128+ if ( allowsMultipleSelection )
129+ Driver . AddRune ( fi . Item3 ? '*' : ' ' ) ;
130+
124131 if ( fi . Item2 )
125132 Driver . AddRune ( '/' ) ;
126133 else
@@ -134,8 +141,10 @@ public override void Redraw (Rect region)
134141
135142 void SelectionChanged ( )
136143 {
137- if ( SelectedChanged != null )
138- SelectedChanged ( infos [ selected ] ) ;
144+ if ( SelectedChanged != null ) {
145+ var sel = infos [ selected ] ;
146+ SelectedChanged ( ( sel . Item1 , sel . Item2 ) ) ;
147+ }
139148 }
140149
141150 public override bool ProcessKey ( KeyEvent keyEvent )
@@ -201,6 +210,19 @@ public override bool ProcessKey (KeyEvent keyEvent)
201210 SetNeedsDisplay ( ) ;
202211 }
203212 return true ;
213+
214+ case Key . Space :
215+ case Key . ControlT :
216+ if ( allowsMultipleSelection ) {
217+ if ( ( canChooseFiles && infos [ selected ] . Item2 == false ) ||
218+ ( canChooseDirectories && infos [ selected ] . Item2 &&
219+ infos [ selected ] . Item1 != ".." ) ) {
220+ infos [ selected ] = ( infos [ selected ] . Item1 , infos [ selected ] . Item2 , ! infos [ selected ] . Item3 ) ;
221+ SelectionChanged ( ) ;
222+ SetNeedsDisplay ( ) ;
223+ }
224+ }
225+ return true ;
204226 }
205227 return base . ProcessKey ( keyEvent ) ;
206228 }
@@ -214,13 +236,42 @@ public string [] AllowedFileTypes {
214236 }
215237 }
216238
239+ public string MakePath ( string relativePath )
240+ {
241+ return Path . GetFullPath ( Path . Combine ( Directory . ToString ( ) , relativePath ) ) ;
242+ }
243+
244+ public IReadOnlyList < string > FilePaths {
245+ get {
246+ if ( allowsMultipleSelection ) {
247+ var res = new List < string > ( ) ;
248+ foreach ( var item in infos )
249+ if ( item . Item3 )
250+ res . Add ( MakePath ( item . Item1 ) ) ;
251+ return res ;
252+ } else {
253+ if ( infos [ selected ] . Item2 ) {
254+ if ( canChooseDirectories )
255+ return new List < string > ( ) { MakePath ( infos [ selected ] . Item1 ) } ;
256+ return Array . Empty < string > ( ) ;
257+ } else {
258+ if ( canChooseFiles )
259+ return new List < string > ( ) { MakePath ( infos [ selected ] . Item1 ) } ;
260+ return Array . Empty < string > ( ) ;
261+ }
262+ }
263+ }
264+ }
217265 }
218266
267+ /// <summary>
268+ /// Base class for the OpenDialog and the SaveDialog
269+ /// </summary>
219270 public class FileDialog : Dialog {
220271 Button prompt , cancel ;
221272 Label nameFieldLabel , message , dirLabel ;
222273 TextField dirEntry , nameEntry ;
223- DirListView dirListView ;
274+ internal DirListView dirListView ;
224275
225276 public FileDialog ( ustring title , ustring prompt , ustring nameFieldLabel , ustring message ) : base ( title , Driver . Cols - 20 , Driver . Rows - 5 , null )
226277 {
@@ -358,6 +409,19 @@ public SaveDialog (ustring title, ustring message) : base (title, prompt: "Save"
358409 }
359410 }
360411
412+ /// <summary>
413+ /// The Open Dialog provides an interactive dialog box for users to select files or directories.
414+ /// </summary>
415+ /// <remarks>
416+ /// <para>
417+ /// The open dialog can be used to select files for opening, it can be configured to allow
418+ /// multiple items to be selected (based on the AllowsMultipleSelection) variable and
419+ /// you can control whether this should allow files or directories to be selected.
420+ /// </para>
421+ /// <para>
422+ /// To select more than one file, users can use the spacebar, or control-t.
423+ /// </para>
424+ /// </remarks>
361425 public class OpenDialog : FileDialog {
362426 public OpenDialog ( ustring title , ustring message ) : base ( title , prompt : "Open" , nameFieldLabel : "Open" , message : message )
363427 {
@@ -366,25 +430,45 @@ public OpenDialog (ustring title, ustring message) : base (title, prompt: "Open"
366430 /// <summary>
367431 /// Gets or sets a value indicating whether this <see cref="T:Terminal.Gui.OpenDialog"/> can choose files.
368432 /// </summary>
369- /// <value><c>true</c> if can choose files; otherwise, <c>false</c>.</value>
370- public bool CanChooseFiles { get ; set ; }
433+ /// <value><c>true</c> if can choose files; otherwise, <c>false</c>. Defaults to <c>true</c></value>
434+ public bool CanChooseFiles {
435+ get => dirListView . canChooseFiles ;
436+ set {
437+ dirListView . canChooseDirectories = value ;
438+ dirListView . Reload ( ) ;
439+ }
440+ }
371441
372442 /// <summary>
373443 /// Gets or sets a value indicating whether this <see cref="T:Terminal.Gui.OpenDialog"/> can choose directories.
374444 /// </summary>
375- /// <value><c>true</c> if can choose directories; otherwise, <c>false</c>.</value>
376- public bool CanChooseDirectories { get ; set ; }
445+ /// <value><c>true</c> if can choose directories; otherwise, <c>false</c> defaults to <c>false</c>.</value>
446+ public bool CanChooseDirectories {
447+ get => dirListView . canChooseDirectories ;
448+ set {
449+ dirListView . canChooseDirectories = value ;
450+ dirListView . Reload ( ) ;
451+ }
452+ }
377453
378454 /// <summary>
379455 /// Gets or sets a value indicating whether this <see cref="T:Terminal.Gui.OpenDialog"/> allows multiple selection.
380456 /// </summary>
381- /// <value><c>true</c> if allows multiple selection; otherwise, <c>false</c>.</value>
382- public bool AllowsMultipleSelection { get ; set ; }
457+ /// <value><c>true</c> if allows multiple selection; otherwise, <c>false</c>, defaults to false.</value>
458+ public bool AllowsMultipleSelection {
459+ get => dirListView . allowsMultipleSelection ;
460+ set {
461+ dirListView . allowsMultipleSelection = value ;
462+ dirListView . Reload ( ) ;
463+ }
464+ }
383465
384466 /// <summary>
385- /// Gets the file paths selected
467+ /// Returns the selected files, or an empty list if nothing has been selected
386468 /// </summary>
387469 /// <value>The file paths.</value>
388- public IReadOnlyList < ustring > FilePaths { get ; }
470+ public IReadOnlyList < string > FilePaths {
471+ get => dirListView . FilePaths ;
472+ }
389473 }
390474}
0 commit comments