Changeset 2057 for trunk/eraser


Ignore:
Timestamp:
05/04/10 08:02:41 (4 years ago)
Author:
lowjoel
Message:

Implemented drag & drop from shell virtual folders (mainly the Recycle Bin)

Location:
trunk/eraser
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/eraser/Eraser.Util/NativeMethods/Shell.cs

    r2002 r2057  
    351351            public string szTypeName; 
    352352        } 
     353 
     354        /// <summary> 
     355        /// Retrieves the path of a known folder as an ITEMIDLIST structure. 
     356        /// </summary> 
     357        /// <param name="rfid">A reference to the KNOWNFOLDERID that identifies the 
     358        /// folder. The folders associated with the known folder IDs might not exist 
     359        /// on a particular system.</param> 
     360        /// <param name="dwFlags">Flags that specify special retrieval options. This 
     361        /// value can be 0; otherwise, it is one or more of the KNOWN_FOLDER_FLAG values.</param> 
     362        /// <param name="hToken">An access token used to represent a particular user. This 
     363        /// parameter is usually set to NULL, in which case the function tries to access 
     364        /// the current user's instance of the folder. However, you may need to assign 
     365        /// a value to hToken for those folders that can have multiple users but are 
     366        /// treated as belonging to a single user. The most commonly used folder of this 
     367        /// type is Documents. 
     368        ///  
     369        /// The calling application is responsible for correct impersonation when hToken 
     370        /// is non-null. It must have appropriate security privileges for the particular 
     371        /// user, including TOKEN_QUERY and TOKEN_IMPERSONATE, and the user's registry 
     372        /// hive must be currently mounted. See Access Control for further discussion 
     373        /// of access control issues. 
     374        ///  
     375        /// Assigning the hToken parameter a value of -1 indicates the Default User. 
     376        /// This allows clients of SHGetKnownFolderIDList to find folder locations 
     377        /// (such as the Desktop folder) for the Default User. The Default User user 
     378        /// profile is duplicated when any new user account is created, and includes 
     379        /// special folders such as Documents and Desktop. Any items added to the 
     380        /// Default User folder also appear in any new user account. Note that access 
     381        /// to the Default User folders requires administrator privileges.</param> 
     382        /// <param name="ppidl">When this method returns, contains a pointer to the PIDL 
     383        /// of the folder. This parameter is passed uninitialized. The caller is 
     384        /// responsible for freeing the returned PIDL when it is no longer needed 
     385        /// by calling ILFree.</param> 
     386        [DllImport("Shell32.dll", CharSet = CharSet.Unicode, PreserveSig = true)] 
     387        public static extern void SHGetKnownFolderIDList(ref Guid rfid, int dwFlags, 
     388            IntPtr hToken, out IntPtr ppidl); 
     389 
     390        /// <summary> 
     391        /// Frees an ITEMIDLIST structure allocated by the Shell. 
     392        /// </summary> 
     393        /// <param name="pidl">A pointer to the ITEMIDLIST structure to be freed. 
     394        /// This parameter can be NULL.</param> 
     395        [DllImport("Shell32.dll")] 
     396        public static extern void ILFree(IntPtr pidl); 
     397 
     398        /// <summary> 
     399        /// Converts an item identifier list to a file system path. 
     400        /// </summary> 
     401        /// <param name="pidl">The address of an item identifier list that specifies 
     402        /// a file or directory location relative to the root of the namespace' 
     403        /// (the desktop).</param> 
     404        /// <param name="pszPath">The address of a buffer to receive the file system path. 
     405        /// This buffer must be at least MAX_PATH characters in size.</param> 
     406        /// <returns>Returns TRUE if successful; otherwise, FALSE.</returns> 
     407        /// <remarks>If the location specified by the pidl parameter is not part of the 
     408        /// file system, this function will fail. 
     409        ///  
     410        /// If the pidl parameter specifies a shortcut, the pszPath will contain the 
     411        /// path to the shortcut, not to the shortcut's target.</remarks> 
     412        [DllImport("Shell32.dll", CharSet = CharSet.Unicode)] 
     413        [return: MarshalAs(UnmanagedType.Bool)] 
     414        public static extern bool SHGetPathFromIDList(IntPtr pidl, 
     415            StringBuilder pszPath); 
    353416    } 
    354417} 
  • trunk/eraser/Eraser.Util/Shell.cs

    r2002 r2057  
    2222using System; 
    2323using System.Collections.Generic; 
     24using System.Linq; 
    2425using System.Text; 
    2526 
     
    8788            return result; 
    8889        } 
     90 
     91        /// <summary> 
     92        /// A List of known folder IDs in the shell namespace. 
     93        /// </summary> 
     94        public static class KnownFolderIDs 
     95        { 
     96            /// <summary> 
     97            /// The Known Folder ID of the Recycle Bin 
     98            /// </summary> 
     99            public static readonly Guid RecycleBin =  
     100                new Guid(0xB7534046, 0x3ECB, 0x4C18, 0xBE, 0x4E, 0x64, 0xCD, 0x4C, 0xB7, 0xD6, 0xAC); 
     101 
     102            /// <summary> 
     103            /// Gets the PIDL for the given Known folder ID. 
     104            /// </summary> 
     105            /// <param name="folderId">The known folder ID to query.</param> 
     106            /// <returns>The PIDL for the given folder.</returns> 
     107            public static ShellItemIDList GetShellItemIdList(Guid folderId) 
     108            { 
     109                Guid guid = folderId; 
     110                IntPtr pidl = IntPtr.Zero; 
     111                NativeMethods.SHGetKnownFolderIDList(ref guid, 0, IntPtr.Zero, out pidl); 
     112 
     113                try 
     114                { 
     115                    return new ShellItemIDList(pidl); 
     116                } 
     117                finally 
     118                { 
     119                    NativeMethods.ILFree(pidl); 
     120                } 
     121            } 
     122        } 
     123    } 
     124 
     125    /// <summary> 
     126    /// Retrieves the path of a known folder as an ITEMIDLIST structure. 
     127    /// </summary> 
     128    public class ShellCIDA 
     129    { 
     130        /// <summary> 
     131        /// Parses the given buffer for CIDA elements 
     132        /// </summary> 
     133        /// <param name="buffer"></param> 
     134        public ShellCIDA(byte[] buffer) 
     135        { 
     136            int offset = 0; 
     137            cidl = BitConverter.ToUInt32(buffer, offset); 
     138            aoffset = new ShellItemIDList[cidl + 1]; 
     139 
     140            for (int i = 0; i < aoffset.Length; ++i) 
     141            { 
     142                int pidlOffset = BitConverter.ToInt32(buffer, offset += sizeof(int)); 
     143 
     144                //Read the size of the IDL 
     145                aoffset[i] = new ShellItemIDList(buffer.Skip(pidlOffset).ToArray()); 
     146            } 
     147        } 
     148 
     149        /// <summary> 
     150        /// The number of PIDLs that are being transferred, not including the parent folder. 
     151        /// </summary> 
     152        public uint cidl 
     153        { 
     154            get; 
     155            private set; 
     156        } 
     157 
     158        /// <summary> 
     159        /// The first element of aoffset contains the fully-qualified PIDL of a parent folder. 
     160        /// If this PIDL is empty, the parent folder is the desktop. Each of the remaining 
     161        /// elements of the array contains an offset to one of the PIDLs to be transferred. 
     162        /// All of these PIDLs are relative to the PIDL of the parent folder. 
     163        /// </summary> 
     164        public ShellItemIDList[] aoffset 
     165        { 
     166            get; 
     167            private set; 
     168        } 
     169    } 
     170 
     171    /// <summary> 
     172    /// Contains a list of item identifiers. 
     173    /// </summary> 
     174    public class ShellItemIDList 
     175    { 
     176        public ShellItemIDList(byte[] buffer) 
     177        { 
     178            mkid = new ShellItemID(buffer); 
     179        } 
     180 
     181        public ShellItemIDList(IntPtr buffer) 
     182        { 
     183            mkid = new ShellItemID(buffer); 
     184        } 
     185 
     186        public ShellItemID mkid 
     187        { 
     188            get; 
     189            private set; 
     190        } 
     191 
     192        /// <summary> 
     193        /// The physical path to the object referenced by this IDL. 
     194        /// </summary> 
     195        /// <remarks>If this IDL references a virtual object, this will return 
     196        /// null.</remarks> 
     197        public string Path 
     198        { 
     199            get 
     200            { 
     201                IntPtr mkid = this.mkid.ToSHITEMID(); 
     202                try 
     203                { 
     204                    StringBuilder result = new StringBuilder(NativeMethods.MaxPath); 
     205                    if (NativeMethods.SHGetPathFromIDList(mkid, result)) 
     206                        return result.ToString(); 
     207                } 
     208                finally 
     209                { 
     210                    Marshal.FreeHGlobal(mkid); 
     211                } 
     212 
     213                return null; 
     214            } 
     215        } 
     216 
     217        /// <summary> 
     218        /// The GUID of the virtual folder referenced by this IDL. 
     219        /// </summary> 
     220        /// <remarks>If this IDL references a physical object, this will return 
     221        /// <see cref="Guid.Empty"/></remarks> 
     222        public Guid Guid 
     223        { 
     224            get 
     225            { 
     226                Guid[] guids = new Guid[] { 
     227                    Shell.KnownFolderIDs.RecycleBin 
     228                }; 
     229 
     230                foreach (Guid guid in guids) 
     231                { 
     232                    if (Shell.KnownFolderIDs.GetShellItemIdList(guid) == this) 
     233                        return guid; 
     234                } 
     235 
     236                return Guid.Empty; 
     237            } 
     238        } 
     239 
     240        public static bool operator==(ShellItemIDList lhs, ShellItemIDList rhs) 
     241        { 
     242            return lhs.mkid == rhs.mkid; 
     243        } 
     244 
     245        public static bool operator!=(ShellItemIDList lhs, ShellItemIDList rhs) 
     246        { 
     247            return lhs.mkid != rhs.mkid; 
     248        } 
     249 
     250        public override bool Equals(object obj) 
     251        { 
     252            if (obj is ShellItemIDList) 
     253                return this == (ShellItemIDList)obj; 
     254            return this.Equals(obj); 
     255        } 
     256 
     257        public override int GetHashCode() 
     258        { 
     259            return base.GetHashCode(); 
     260        } 
     261    } 
     262 
     263    /// <summary> 
     264    /// Defines an item identifier. (native type: SHITEMID) 
     265    /// </summary> 
     266    public class ShellItemID 
     267    { 
     268        public ShellItemID(byte[] buffer) 
     269        { 
     270            short cb = BitConverter.ToInt16(buffer, 0); 
     271            abID = new byte[cb]; 
     272            if (cb > 0) 
     273                Buffer.BlockCopy(buffer, sizeof(short), abID, 0, cb - sizeof(short)); 
     274        } 
     275 
     276        public ShellItemID(IntPtr buffer) 
     277        { 
     278            short cb = Marshal.ReadInt16(buffer); 
     279            abID = new byte[cb]; 
     280            if (cb > 0) 
     281                Marshal.Copy(new IntPtr(buffer.ToInt64() + sizeof(short)), abID, 0, cb - sizeof(short)); 
     282        } 
     283 
     284        byte[] abID; 
     285 
     286        /// <summary> 
     287        /// Converts this ShellItemID to the native SHITEMID. 
     288        /// </summary> 
     289        /// <returns>A Pointer to an unmanaged block of memory which should be 
     290        /// freed by Marshal.FreeHGlobal upon completion.</returns> 
     291        internal IntPtr ToSHITEMID() 
     292        { 
     293            //Allocate the buffer 
     294            IntPtr result = Marshal.AllocHGlobal(abID.Length + (abID.Length == 0 ? 0 : sizeof(short))); 
     295 
     296            //Write the size of the identifier 
     297            Marshal.WriteInt16(result, (short)abID.Length); 
     298 
     299            //Then copy the block of memory 
     300            Marshal.Copy(abID, 0, new IntPtr(result.ToInt64() + 2), abID.Length); 
     301            return result; 
     302        } 
     303 
     304        public static bool operator==(ShellItemID lhs, ShellItemID rhs) 
     305        { 
     306            return lhs.abID.SequenceEqual(rhs.abID); 
     307        } 
     308 
     309        public static bool operator!=(ShellItemID lhs, ShellItemID rhs) 
     310        { 
     311            return !lhs.abID.SequenceEqual(rhs.abID); 
     312        } 
     313 
     314        public override bool Equals(object obj) 
     315        { 
     316            if (obj is ShellItemID) 
     317                return this == (ShellItemID)obj; 
     318            return this.Equals(obj); 
     319        } 
     320 
     321        public override int GetHashCode() 
     322        { 
     323            return base.GetHashCode(); 
     324        } 
    89325    } 
    90326} 
  • trunk/eraser/Eraser/SchedulerPanel.cs

    r2051 r2057  
    361361            string descriptionMessage = string.Empty; 
    362362            string descriptionInsert = string.Empty; 
    363             const string descrptionPlaceholder = "%1"; 
    364             if (!e.Data.GetDataPresent(DataFormats.FileDrop)) 
    365                 e.Effect = DragDropEffects.None; 
    366             else 
    367             { 
    368                 string[] files = (string[])e.Data.GetData(DataFormats.FileDrop, false); 
    369                 bool isTaskList = true; 
    370                 foreach (string file in files) 
    371                 { 
    372                     if (descriptionInsert.Length < 259 && 
    373                         (descriptionInsert.Length < 3 || descriptionInsert.Substring(descriptionInsert.Length - 3) != "...")) 
     363            string descriptionItemFormat = S._("{0}, "); 
     364            const string descriptionPlaceholder = "%1"; 
     365             
     366            bool recycleBinIncluded = false; 
     367            List<string> files = e.Data.GetDataPresent(DataFormats.FileDrop) ? 
     368                new List<string>((string[])e.Data.GetData(DataFormats.FileDrop, false)) : 
     369                new List<string>(); 
     370            if (e.Data.GetDataPresent("Shell IDList Array")) 
     371            { 
     372                MemoryStream stream = (MemoryStream)e.Data.GetData("Shell IDList Array"); 
     373                byte[] buffer = new byte[stream.Length]; 
     374                stream.Read(buffer, 0, buffer.Length); 
     375                ShellCIDA cida = new ShellCIDA(buffer); 
     376 
     377                if (cida.cidl > 0) 
     378                { 
     379                    for (int i = 1; i <= cida.cidl; ++i) 
    374380                    { 
    375                         string append = string.Format(CultureInfo.InvariantCulture, "{0}, ", 
    376                             Path.GetFileNameWithoutExtension(file)); 
    377                         if (descriptionInsert.Length + append.Length > 259) 
     381                        if (cida.aoffset[i].Guid != Guid.Empty) 
    378382                        { 
    379                             descriptionInsert += "....."; 
     383                            if (cida.aoffset[i].Guid == Shell.KnownFolderIDs.RecycleBin) 
     384                            { 
     385                                descriptionInsert += string.Format(CultureInfo.InvariantCulture, 
     386                                    descriptionItemFormat, S._("Recycle Bin")); 
     387                                recycleBinIncluded = true; 
     388                            } 
    380389                        } 
    381390                        else 
    382391                        { 
    383                             descriptionInsert += append; 
     392                            files.Add(cida.aoffset[i].Path); 
    384393                        } 
    385394                    } 
    386  
    387                     if (Path.GetExtension(file) != ".ersx") 
    388                         isTaskList = false; 
    389                 } 
    390                 descriptionInsert = descriptionInsert.Remove(descriptionInsert.Length - 2); 
    391  
    392                 if (isTaskList) 
    393                 { 
    394                     e.Effect = DragDropEffects.Copy; 
    395                     descriptionMessage = S._("Import tasks from {0}", descrptionPlaceholder); 
    396                 } 
    397                 else 
    398                 { 
    399                     e.Effect = DragDropEffects.Move; 
    400                     descriptionMessage = S._("Erase {0}", descrptionPlaceholder); 
    401                 } 
     395                } 
     396            } 
     397 
     398            bool isTaskList = !recycleBinIncluded; 
     399            foreach (string file in files) 
     400            { 
     401                if (descriptionInsert.Length < 259 && 
     402                    (descriptionInsert.Length < 3 || descriptionInsert.Substring(descriptionInsert.Length - 3) != "...")) 
     403                { 
     404                    string append = string.Format(CultureInfo.InvariantCulture, 
     405                        descriptionItemFormat, Path.GetFileNameWithoutExtension(file)); 
     406                    if (descriptionInsert.Length + append.Length > 259) 
     407                    { 
     408                        descriptionInsert += "....."; 
     409                    } 
     410                    else 
     411                    { 
     412                        descriptionInsert += append; 
     413                    } 
     414                } 
     415 
     416                if (Path.GetExtension(file) != ".ersx") 
     417                    isTaskList = false; 
     418            } 
     419            descriptionInsert = descriptionInsert.Remove(descriptionInsert.Length - 2); 
     420 
     421            if (!recycleBinIncluded && files.Count == 0) 
     422                e.Effect = DragDropEffects.None; 
     423            else if (isTaskList) 
     424            { 
     425                e.Effect = DragDropEffects.Copy; 
     426                descriptionMessage = S._("Import tasks from {0}", descriptionPlaceholder); 
     427            } 
     428            else 
     429            { 
     430                e.Effect = DragDropEffects.Move; 
     431                descriptionMessage = S._("Erase {0}", descriptionPlaceholder); 
    402432            } 
    403433 
     
    422452        private void scheduler_DragDrop(object sender, DragEventArgs e) 
    423453        { 
    424             if (!e.Data.GetDataPresent(DataFormats.FileDrop)) 
    425             { 
    426                 e.Effect = DragDropEffects.None; 
    427                 return; 
    428             } 
    429  
    430454            DropTargetHelper.Drop(e.Data, new Point(e.X, e.Y), e.Effect); 
     455            if (e.Effect == DragDropEffects.None) 
     456                return; 
     457 
     458            bool recycleBinIncluded = false; 
     459            List<string> files = e.Data.GetDataPresent(DataFormats.FileDrop) ? 
     460                new List<string>((string[])e.Data.GetData(DataFormats.FileDrop, false)) : 
     461                new List<string>(); 
     462            if (e.Data.GetDataPresent("Shell IDList Array")) 
     463            { 
     464                MemoryStream stream = (MemoryStream)e.Data.GetData("Shell IDList Array"); 
     465                byte[] buffer = new byte[stream.Length]; 
     466                stream.Read(buffer, 0, buffer.Length); 
     467                ShellCIDA cida = new ShellCIDA(buffer); 
     468 
     469                if (cida.cidl > 0) 
     470                { 
     471                    for (int i = 1; i <= cida.cidl; ++i) 
     472                    { 
     473                        if (cida.aoffset[i].Guid != Guid.Empty) 
     474                        { 
     475                            if (cida.aoffset[i].Guid == Shell.KnownFolderIDs.RecycleBin) 
     476                                recycleBinIncluded = true; 
     477                        } 
     478                        else 
     479                        { 
     480                            files.Add(cida.aoffset[i].Path); 
     481                        } 
     482                    } 
     483                } 
     484            } 
    431485 
    432486            //Schedule the task dialog to be shown (to get to the event loop so that 
    433487            //ComCtl32.dll v6 is used.) 
    434             BeginInvoke((Action<DragDropEffects, string[]>)scheduler_DragDropConfirm, 
    435                 e.Effect, e.Data.GetData(DataFormats.FileDrop, false)); 
     488            BeginInvoke((Action<DragDropEffects, List<string>, bool>)scheduler_DragDropConfirm, 
     489                e.Effect, files, recycleBinIncluded); 
    436490        } 
    437491 
     
    441495        /// <param name="effect">The Drag/drop effect of the operation.</param> 
    442496        /// <param name="files">The files which were dropped into the program.</param> 
    443         private void scheduler_DragDropConfirm(DragDropEffects effect, string[] files) 
     497        /// <param name="recycleBinIncluded">True if the recycle bin was among the 
     498        /// items dropped.</param> 
     499        private void scheduler_DragDropConfirm(DragDropEffects effect, List<string> files, 
     500            bool recycleBinIncluded) 
    444501        { 
    445502            //Determine whether we are importing a task list or dragging files for 
     
    470527                //Create a task with the default settings 
    471528                Task task = new Task(); 
    472                 foreach (string file in files) 
    473                 {    
    474                     FileSystemObjectErasureTarget target; 
    475                     if ((File.GetAttributes(file) & FileAttributes.Directory) != 0) 
    476                         target = new FolderErasureTarget(); 
    477                     else 
    478                         target = new FileErasureTarget(); 
    479                     target.Path = file; 
    480  
    481                     task.Targets.Add(target); 
    482                 } 
     529                if (files != null) 
     530                    foreach (string file in files) 
     531                    {    
     532                        FileSystemObjectErasureTarget target; 
     533                        if ((File.GetAttributes(file) & FileAttributes.Directory) != 0) 
     534                            target = new FolderErasureTarget(); 
     535                        else 
     536                            target = new FileErasureTarget(); 
     537                        target.Path = file; 
     538 
     539                        task.Targets.Add(target); 
     540                    } 
     541 
     542                //Add the recycle bin if it was specified 
     543                if (recycleBinIncluded) 
     544                    task.Targets.Add(new RecycleBinErasureTarget()); 
    483545 
    484546                //Add the task, asking the user for his intent. 
  • trunk/eraser/Eraser/Strings.en.resx

    r2053 r2057  
    223223    <value>Task executed</value> 
    224224  </data> 
     225  <data name="{0}, " xml:space="preserve"> 
     226    <value>{0}, </value> 
     227  </data> 
     228  <data name="Recycle Bin" xml:space="preserve"> 
     229    <value>Recycle Bin</value> 
     230  </data> 
    225231  <data name="Import tasks from {0}" xml:space="preserve"> 
    226232    <value>Import tasks from {0}</value> 
  • trunk/eraser/Eraser/Strings.it.resx

    r2053 r2057  
    223223    <value>Operazione eseguita</value> 
    224224  </data> 
     225  <data name="{0}, " xml:space="preserve"> 
     226    <value>(Untranslated)</value> 
     227  </data> 
     228  <data name="Recycle Bin" xml:space="preserve"> 
     229    <value>(Untranslated)</value> 
     230  </data> 
    225231  <data name="Import tasks from {0}" xml:space="preserve"> 
    226232    <value>Importazione operazioni da {0}</value> 
  • trunk/eraser/Eraser/Strings.nl.resx

    r2053 r2057  
    223223    <value>(Untranslated)</value> 
    224224  </data> 
     225  <data name="{0}, " xml:space="preserve"> 
     226    <value>(Untranslated)</value> 
     227  </data> 
     228  <data name="Recycle Bin" xml:space="preserve"> 
     229    <value>(Untranslated)</value> 
     230  </data> 
    225231  <data name="Import tasks from {0}" xml:space="preserve"> 
    226232    <value>(Untranslated)</value> 
  • trunk/eraser/Eraser/Strings.pl.resx

    r2053 r2057  
    223223    <value>Zadanie wykonano</value> 
    224224  </data> 
     225  <data name="{0}, " xml:space="preserve"> 
     226    <value>(Untranslated)</value> 
     227  </data> 
     228  <data name="Recycle Bin" xml:space="preserve"> 
     229    <value>(Untranslated)</value> 
     230  </data> 
    225231  <data name="Import tasks from {0}" xml:space="preserve"> 
    226232    <value>Importuj zadania z {0}</value> 
  • trunk/eraser/Eraser/Strings.resx

    r2053 r2057  
    223223    <value>Task executed</value> 
    224224  </data> 
     225  <data name="{0}, " xml:space="preserve"> 
     226    <value>{0}, </value> 
     227  </data> 
     228  <data name="Recycle Bin" xml:space="preserve"> 
     229    <value>Recycle Bin</value> 
     230  </data> 
    225231  <data name="Import tasks from {0}" xml:space="preserve"> 
    226232    <value>Import tasks from {0}</value> 
Note: See TracChangeset for help on using the changeset viewer.