Beispiel #1
0
        public void FromDevice(DriveInfo d, string device)
        {
            VolumeMonitor volmon = GLib.VolumeMonitor.Default;

            // Search in mounts first
            foreach (Mount m in volmon.Mounts) {
                if ((m.Volume != null) && (m.Volume.GetIdentifier(G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE) == device)) {
                    FillDriveInfoFromMount(d, m, device);
                    return;
                }
            }

            // Search in volumes (unmounted or else the device would have been found in volmon.Mounts)
            foreach (Volume v in volmon.Volumes) {
                if (v.GetIdentifier(G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE) == device) {
                    d.device = device;
                    d.driveType = GuessDriveType(null, v.Icon, v.Drive);
                    return;
                }
            }

            // Search in drives (does not contain media or else the device
            // would have been found in volmon.Mounts/Volumes) (e.g. cdrom-drives))
            foreach (Drive dr in volmon.ConnectedDrives) {
                if (dr.GetIdentifier(G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE) == device) {
                    d.device = device;
                    d.driveType = GuessDriveType(null, dr.Icon, dr);
                    return;
                }
            }

            throw new ArgumentException("Can't find drive for specified device", "device");
        }
        protected virtual void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    if (this.IsBusy)
                    {
                        throw new InvalidOperationException("Scan in progress");
                    }

                    /*
                     * if (m_db != null)
                     *      m_db.Close(); // TODO ? leavOpen in Scanworker.ctor after database parameter? PASS THIS leavOpen PARAMETER AT THE BufferedItemWriter INSTANCIATION AS WELL!!!
                     * m_media.Dispose(); // TODO ? (in case m_media implements IDisposable because of the MediaDB reference anytime later)
                     */
                }

                drive          = null;
                database       = null;
                volume         = null;
                volumeInfo     = null;
                options        = null;
                asyncOperation = null;

                disposed = true;
            }
        }
Beispiel #3
0
        public static VolumeProbeResult ProbeVolume(PlatformIO.DriveInfo drive)
        {
            VolumeProbeResult result = VolumeProbeResult.Unknown;

            if (drive == null)
            {
                throw new ArgumentNullException("drive");
            }

            if (!drive.IsReady)
            {
                throw new ArgumentException("Drive is not ready", "drive");
            }

            // check for audio cd first -
            // win32 also mounts audio cds as filesystems
            if (drive.HasAudioCdVolume)
            {
                return(VolumeProbeResult.AudioCd);
            }
            else if (drive.IsMounted)
            {
                return(VolumeProbeResult.Filesystem);
            }

            return(result);
        }
Beispiel #4
0
        public virtual void FromPath(DriveInfo d, string rootPath)
        {
            if (!rootPath.EndsWith("\\")) // e.g. "D:" -> "D:\"
                rootPath += "\\";

            // throws ArgumentException if drive can't be found
            System.IO.DriveInfo di = new System.IO.DriveInfo(rootPath);

            FillDriveInfo(d, di);
        }
Beispiel #5
0
        public DriveSelection()
        {
            BuildGui();

            btnOk.Sensitive = false;
            isDestroyed = false;
            selectedDrive = null;

            RefreshListAsync();
        }
Beispiel #6
0
 // note:
 // do not allow to modify the constuctor parameters
 // (i.e. database, options)
 // through public properties later, since the scanner
 // may already use them after scanning has been started,
 // and some stuff has been initialized depending on the
 // options in the ctor already.
 public AudioCdVolumeScanner(Platform.Common.IO.DriveInfo drive,
                             VolumeDatabase database,
                             AudioCdScannerOptions options)
     : base(drive, database, options)
 {
     if (!drive.HasAudioCdVolume)
     {
         throw new ArgumentException("No audio cd present in drive");
     }
 }
Beispiel #7
0
 private static string GetLabel(DriveInfo d)
 {
     string label = d.VolumeLabel;
     if (label.Length > 0)
         return label;
     else if(d.IsMounted && d.RootPath == "/")
         return S._("Filesystem");
     else
         return "--";
 }
Beispiel #8
0
        public virtual List<DriveInfo> GetAll(bool readyDrivesOnly)
        {
            List<DriveInfo> drives = new List<DriveInfo>();

            System.IO.DriveInfo[] ioDrives = System.IO.DriveInfo.GetDrives();
            foreach (System.IO.DriveInfo di in ioDrives) {
                if (!(readyDrivesOnly && !di.IsReady)) {
                    DriveInfo d = new DriveInfo();
                    FillDriveInfo(d, di);
                    drives.Add(d);
                }
            }

            return drives;
        }
Beispiel #9
0
        // <summary>
        /// Probes a volume, creates the appropriate VolumeScanner and returns a general interface to it.
        /// </summary>
        /// <param name="drive">Drive to be scanned</param>
        /// <param name="database">VolumeDatabase object</param>
        /// <param name="options">ScannerOptions for all possible scanners</param>
        /// <returns>Interface to the proper VolumeScanner</returns>
        public static IVolumeScanner GetScannerForVolume(PlatformIO.DriveInfo drive,
                                                         VolumeDatabase database,
                                                         ScannerOptions[] options)
        {
            if (drive == null)
            {
                throw new ArgumentNullException("drive");
            }

            if (!drive.IsReady)
            {
                throw new ArgumentException("Drive is not ready", "drive");
            }

            if (options == null)
            {
                throw new ArgumentNullException("options");
            }

            IVolumeScanner    scanner = null;
            VolumeProbeResult result  = ProbeVolume(drive);

            switch (result)
            {
            case VolumeProbeResult.Filesystem:
                scanner = new FilesystemVolumeScanner(drive,
                                                      database,
                                                      GetOptions <FilesystemScannerOptions>(options));
                break;

            case VolumeProbeResult.AudioCd:
                scanner = new AudioCdVolumeScanner(drive,
                                                   database,
                                                   GetOptions <AudioCdScannerOptions>(options));
                break;

            case VolumeProbeResult.Unknown:
                throw new ArgumentException("Volume is of an unknown type");

            default:
                throw new NotImplementedException(string.Format("VolumeProbeResult {0} is not implemented", result.ToString()));
            }

            return(scanner);
        }
Beispiel #10
0
        public void FromPath(DriveInfo d, string rootPath)
        {
            VolumeMonitor volmon = GLib.VolumeMonitor.Default;

            // Remove endling slash from path
            if ((rootPath.Length > 1) && (rootPath[rootPath.Length - 1] == System.IO.Path.DirectorySeparatorChar))
                rootPath = rootPath.Substring(0, rootPath.Length - 1);

            foreach (Mount m in volmon.Mounts) {
                if (m.Root.Path == rootPath) {
                    FillDriveInfoFromMount (d, m,
                        (m.Volume != null) ? m.Volume.GetIdentifier (G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE) : string.Empty);
                    return;
                }
            }

            throw new ArgumentException("Can't find drive for specified path", "rootPath");
        }
Beispiel #11
0
        // note:
        // do not allow to modify the constuctor parameters
        // (i.e. database, options)
        // through public properties later, since the scanner
        // may already use them after scanning has been started,
        // and some stuff has been initialized depending on the
        // options in the ctor already.
        internal AbstractVolumeScanner(PlatformIO.DriveInfo drive,
                                       VolumeDatabase database,
                                       TOpts options)
        {
            if (drive == null)
            {
                throw new ArgumentNullException("drive");
            }

            if (!drive.IsReady)
            {
                throw new ArgumentException("Drive is not ready", "drive");
            }

            if (options == null)
            {
                throw new ArgumentNullException("options");
            }

            /* don't test database for null -- database is optional */

            if ((options.BufferSize < 1) && (database != null))
            {
                throw new ArgumentOutOfRangeException("BufferSize");
            }

            this.isRunning = false;
            //m_cancellationRequested = false;

            this.scanSucceeded = false;
            this.disposed      = false;

            this.drive    = drive;
            this.database = database;

            // copy options reference so that they can't be modified
            // while the scanner is running already.
            this.options = new TOpts();
            options.CopyTo(this.options);

            this.itemID     = VolumeDatabase.ID_NONE;
            this.volume     = CreateVolumeObject(drive, database, options.ComputeHashs);
            this.volumeInfo = CreateInstance <TVolumeInfo>(volume);
        }
Beispiel #12
0
        /// <summary>
        // Returns a specific Volume object, but preassigns properties of the Volume baseclass only.
        // Filling properties of the specific, derived object, is job of the specific VolumeScanner implementation.
        /// </summary>
        private static TVolume CreateVolumeObject(PlatformIO.DriveInfo d,
                                                  VolumeDatabase database,
                                                  bool isHashed)
        {
            // TODO : check here whether everything is still filled correctly after media class reorganisation

            long volumeID = VolumeDatabase.ID_NONE;

            if (database != null)
            {
                // TODO : this is neither threadsave nor multi-instance save in general!
                // maybe the db (physical db, VolumeDatabase object? this scanner?) should be locked during scanning?
                volumeID = database.GetNextVolumeID();
            }

            TVolume v = CreateInstance <TVolume>(database);

//			  /* v = new TVolume(database); */
//			  v = (TVolume)VolumeDB.Volume.CreateInstance(volumeType, database);
//
            // initialize fields of the Volume base class.
            // don't initialize via properties. initializing via properties is error-prone
            // as the compiler won't error if a new field is added to the base class
            // and forgotten to be initialized here.
            v.SetVolumeFields(
                volumeID,
                d.IsMounted ? d.VolumeLabel : string.Empty,
                DateTime.Now,
                /*di.VolumeSerialNumber,*/
                isHashed,
                volumeID.ToString(),
                d.DriveType.ToVolumeDriveType(),
                null,
                DateTime.MinValue,
                DateTime.MinValue,
                null,
                null,
                null
                );

            return(v);
        }
        internal override void ScanningThreadMain(Platform.Common.IO.DriveInfo drive,
                                                  FileSystemVolume volume,
                                                  BufferedVolumeItemWriter writer)
        {
            try {
                if (Options.GenerateThumbnails)
                {
                    paths.volumeDataPath = DbData.CreateVolumeDataPath(paths.dbDataPath, volume.VolumeID);
                    paths.thumbnailPath  = DbData.CreateVolumeDataThumbsPath(paths.volumeDataPath);
                }

                string rootPath = drive.RootPath;
                // remove possible ending path seperator except for _system_ root paths
                rootPath = RemoveEndingSlash(rootPath);
                //			  if ((rootPath.Length > 1) && (rootPath[rootPath.Length - 1] == Path.DirectorySeparatorChar))
                //				  rootPath = rootPath.Substring(0, rootPath.Length - 1);

                // make sure the root path exists
                // (media may have been removed after scanner construction)
                if (!Directory.Exists(rootPath))
                {
                    throw new DirectoryNotFoundException("Root path does not exist");
                }

                DirectoryInfo dir = new DirectoryInfo(rootPath);
                RecursiveDump(rootPath, dir, writer, VolumeDatabase.ID_NONE);
                symLinkHelper.InsertSymLinkItems(writer, volume.VolumeID);

                volume.SetFileSystemVolumeFields(VolumeInfo.Files, VolumeInfo.Directories, VolumeInfo.Size);
            } catch (Exception) {
                // try to cleanup
                try {
                    if ((paths.volumeDataPath != null) && Directory.Exists(paths.volumeDataPath))
                    {
                        Directory.Delete(paths.volumeDataPath, true);
                    }
                } catch (Exception) { /* just shut up */ }

                // rethrow initial exception
                throw;
            }
        }
Beispiel #14
0
        private static void FillDriveInfo(DriveInfo d, System.IO.DriveInfo di)
        {
            if (di.IsReady) {
                d.volumeLabel = di.VolumeLabel;
                d.totalSize = di.TotalSize;
                d.filesystem = di.DriveFormat;
            }

            d.rootPath = di.RootDirectory.FullName;
            // should return e.g. "D:", not "D:\"
            d.device = d.rootPath[d.rootPath.Length - 1] == System.IO.Path.DirectorySeparatorChar ? d.rootPath.Substring(0, d.rootPath.Length - 1) : d.rootPath;
            d.isMounted = true;
            d.isReady = di.IsReady;

            switch (di.DriveType) {
                case System.IO.DriveType.CDRom:
                    d.driveType = DriveType.CDRom;
                    if (d.isReady)
                        d.hasAudioCdVolume = AudioCdWin32.IsAudioCd(d.device);
                    break;
                case System.IO.DriveType.Fixed:
                    d.driveType = DriveType.Fixed;
                    break;
                case System.IO.DriveType.Network:
                    d.driveType = DriveType.Network;
                    break;
                case System.IO.DriveType.Ram:
                    d.driveType = DriveType.Ram;
                    break;
                case System.IO.DriveType.Removable:
                    d.driveType = DriveType.Removable;
                    break;
                case System.IO.DriveType.NoRootDirectory:
                    d.driveType = DriveType.Unknown;
                    d.isMounted = false;
                    break;
                case System.IO.DriveType.Unknown:
                    d.driveType = DriveType.Unknown;
                    break;
            }
        }
        // note:
        // do not allow to modify the constuctor parameters
        // (i.e. database, options)
        // through public properties later, since the scanner
        // may already use them after scanning has been started,
        // and some stuff has been initialized depending on the
        // options in the ctor already.
        public FilesystemVolumeScanner(Platform.Common.IO.DriveInfo drive,
                                       VolumeDatabase database,
                                       FilesystemScannerOptions options)
            : base(drive, database, options)
        {
            if (!drive.IsMounted)
            {
                throw new ArgumentException("Drive is not mounted", "drive");
            }

            if (Options.GenerateThumbnails && string.IsNullOrEmpty(Options.DbDataPath))
            {
                throw new ArgumentException("DbDataPath",
                                            "Thumbnail generation requires the DbDataPath option to be set");
            }

            disposed = false;
            //this.mimeInfo			  = new MimeInfo(false);
            this.sbPathFixer   = new StringBuilder(1024);
            this.paths         = new Paths(Options.DbDataPath, null, null);
            this.symLinkHelper = new SymLinkHelper(this);
            this.thumbGen      = new ThumbnailGenerator();
        }
Beispiel #16
0
        // keep in sync with VolumeView.GetVolumeIcon()
        public static Icons.Icon GetDriveIcon(DriveInfo d)
        {
            //// DriveInfo.DriveType is not supported on linux
            //if (CurrentPlatform.IsUnix)
            //	  return Stock.Harddisk;

            Icons.Icon icon;
            switch(d.DriveType) {
                case DriveType.CDRom:
                    //name = Gtk.Stock.Cdrom;
                    icon = Icons.Icon.Stock_Cdrom;
                    break;
                case DriveType.Fixed:
                    //name = Gtk.Stock.Harddisk;
                    icon = Icons.Icon.Stock_Harddisk;
                    break;
                case DriveType.Ram:
                    //name = Gtk.Stock.Harddisk; // FIXME : is there a more suitable icon?
                    icon = Icons.Icon.Stock_Harddisk; // FIXME : is there a more suitable icon?
                    break;
                case DriveType.Network:
                    //name = Gtk.Stock.Network;
                    icon = Icons.Icon.Stock_Network;
                    break;
                case DriveType.Removable:
                    //name = "drive-removable-media";
                    icon = Icons.Icon.DriveRemovableMedia;
                    break;
                case DriveType.Unknown:
                    //name = Gtk.Stock.Harddisk; // FIXME : is there a more suitable icon?
                    icon = Icons.Icon.Stock_Harddisk; // FIXME : is there a more suitable icon?
                    break;
                default:
                    throw new Exception("Invalid DriveType.");
            }
            return icon;
        }
Beispiel #17
0
        private static void FillDriveInfoFromMount(DriveInfo d, GLib.Mount m, string device)
        {
            if (m.Volume != null) {
                // Only get size and format info from physical volumes
                // (System.IO.DriveInfo throws an exception on network mounts)
                System.IO.DriveInfo di = new System.IO.DriveInfo (m.Root.Path);
                d.totalSize = di.TotalSize;
                d.filesystem = di.DriveFormat;
            }

            d.volumeLabel = m.Name;
            d.rootPath = m.Root.Path;
            d.device = device;
            d.driveType = GuessDriveType(m.Root.UriScheme, m.Icon, m.Drive);
            d.isMounted = true;
            d.isReady = true;
            d.hasAudioCdVolume = (m.Root.UriScheme == "cdda");
        }
Beispiel #18
0
        public static DriveInfo FromDevice(string device)
        {
            if (device == null)
                throw new ArgumentNullException("device");

            DriveInfo d = new DriveInfo();
            dip.FromDevice(d, device);
            return d;
        }
Beispiel #19
0
        internal override void ScanningThreadMain(PlatformIO.DriveInfo drive,
                                                  AudioCdVolume volume,
                                                  BufferedVolumeItemWriter writer)
        {
            if (Options.ComputeHashs)
            {
                SendScannerWarning(S._("Hashcode generation not implemented for audio cds yet."));

                volume.IsHashed = false;
            }

            AudioCdRootVolumeItem root = GetNewVolumeItem <AudioCdRootVolumeItem>(VolumeDatabase.ID_NONE,
                                                                                  "/",
                                                                                  null,
                                                                                  MetadataStore.Empty,
                                                                                  VolumeItemType.AudioCdRootVolumeItem);

            LocalDisc localdisc = LocalDisc.GetFromDevice(drive.Device);

            if (localdisc == null)
            {
                throw new ApplicationException("Could not read contents of the audio cd");
            }

            TimeSpan[] durations = localdisc.GetTrackDurations();
            List <AudioTrackVolumeItem> items = new List <AudioTrackVolumeItem>();

            for (int i = 0; i < durations.Length; i++)
            {
                AudioTrackVolumeItem item = GetNewVolumeItem <AudioTrackVolumeItem>(root.ItemID,
                                                                                    "Track " + (i + 1),
                                                                                    MIME_TYPE_AUDIO_TRACK,
                                                                                    MetadataStore.Empty,
                                                                                    VolumeItemType.AudioTrackVolumeItem);
                item.SetAudioTrackVolumeItemFields(durations[i]);

                items.Add(item);

                VolumeInfo.Tracks++;
                VolumeInfo.Duration = VolumeInfo.Duration.Add(durations[i]);
            }

            // retrieve musicbrainz metadata
            // (the metadata field of AudioTrackVolumeItems is set
            // depending on the EnableMusicBrainz flag)
            if (Options.EnableMusicBrainz)
            {
                try {
                    // may throw MusicBrainzNotFoundException
                    Release release = Release.Query(localdisc).PerfectMatch();

                    CheckForCancellationRequest();

                    if (release == null)
                    {
                        SendScannerWarning(S._("No MusicBrainz metadata available for this disc."));
                    }
                    else
                    {
                        var tracks = release.GetTracks();

                        if (tracks.Count != items.Count)
                        {
                            SendScannerWarning(S._("The trackcount retrieved from MusicBrainz does not match the trackcount of the local disc. Skipped."));
                        }
                        else
                        {
                            string albumTitle  = release.GetTitle();
                            int    releaseYear = GetReleaseYear(release);

                            for (int i = 0; i < tracks.Count; i++)
                            {
                                items[i].Name     = tracks[i].GetTitle();
                                items[i].MetaData = GetMetadata(tracks[i], albumTitle, releaseYear);
                            }

                            volume.Title = albumTitle;

                            // preset category
                            ReleaseType rtype = release.GetReleaseType();
                            if (rtype == ReleaseType.Album ||
                                rtype == ReleaseType.EP ||
                                rtype == ReleaseType.Compilation ||
                                rtype == ReleaseType.Remix)
                            {
                                volume.Category = PRESELECTED_CATEGORY;
                            }
                        }
                    }
                } catch (MusicBrainzNotFoundException) {
                    SendScannerWarning(S._("Error connecting to MusicBrainz server."));
                }
            }

            volume.SetAudioCdVolumeFields(VolumeInfo.Tracks, VolumeInfo.Duration);

            // write items
            if (this.HasDB)
            {
                writer.Write(root);

                foreach (AudioTrackVolumeItem item in items)
                {
                    writer.Write(item);
                }
            }
        }
Beispiel #20
0
        public List<DriveInfo> GetAll(bool readyDrivesOnly)
        {
            List<DriveInfo> drives = new List<DriveInfo> ();
            VolumeMonitor volmon = GLib.VolumeMonitor.Default;

            foreach (Mount m in volmon.Mounts) {
                DriveInfo d = new DriveInfo();

                FillDriveInfoFromMount(d, m,
                    (m.Volume != null) ? m.Volume.GetIdentifier (G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE) : string.Empty);

                drives.Add(d);
            }

            if (!readyDrivesOnly) {
                foreach (Volume v in volmon.Volumes) {
                    if (drives.FindIndex(di => (v.GetIdentifier(G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE) == di.Device)) == -1) {
                        // Volume is unmounted or else it would have been referenced via volmon.Mounts
                        DriveInfo d = new DriveInfo();

                        d.device = v.GetIdentifier(G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
                        d.driveType = GuessDriveType(null, v.Icon, v.Drive);

                        drives.Add(d);
                    }
                }

                foreach (Drive dr in volmon.ConnectedDrives) {
                    if (dr.IsMediaRemovable && !dr.HasMedia) {
                        DriveInfo d = new DriveInfo();

                        d.device = dr.GetIdentifier(G_VOLUME_IDENTIFIER_KIND_UNIX_DEVICE);
                        d.driveType = GuessDriveType(null, dr.Icon, dr);

                        drives.Add(d);
                    }
                }
            }

            return drives;
        }
Beispiel #21
0
        private void ScanningThread(PlatformIO.DriveInfo drive,
                                    TVolume volume,
                                    BufferedVolumeItemWriter writer)
        {
            TVolume   returnVolume = null;
            Exception fatalError   = null;
            bool      cancelled    = false;

            try {
                if (this.HasDB)
                {
                    Database.TransactionBegin();                      // locks VolumeDatabase
                }
                ScanningThreadMain(drive, volume, writer);
                if (this.HasDB)
                {
                    writer.Close();
                    if (!volume.IsInserted)
                    {
                        volume.InsertIntoDB();
                    }

                    returnVolume = volume;

                    database.TransactionCommit();                     // unlocks VolumeDatabase
                }

                //result = ScanningResult.Success;
                scanSucceeded = true;
            } catch (Exception ex) {
                Exception rollbackException = null;
                try {
                    // rollback all database changes
                    if (this.HasDB)
                    {
                        database.TransactionRollback();                         // unlocks VolumeDatabase
                    }
                } catch (Exception e) {
                    rollbackException = e;
                }


                if (ex is ScanCancelledException)
                {
                    //result = ScanningResult.Cancelled;
                    cancelled = true;
                }
                else
                {
                    //result = ScanningResult.FatalError;

                    /* save the error that caused the scanner to stop (scanning failure) */
                    fatalError = ex;
                    //OnError(new ErrorEventArgs(ex));
                    PostError(ex);

                    Debug.WriteLine("Details for exception in ScanningThread():\n" + ex.ToString());
                }

                // in case an error occured while rollig back,
                // post the error here, _after_ the initial error that made the scan fail.
                if (rollbackException != null)
                {
                    //OnError(new ErrorEventArgs(rollbackException));
                    PostError(rollbackException);
                }

//#if THROW_EXCEPTIONS_ON_ALL_THREADS
//				  if (!(ex is ScanCancelledException))
//					  throw;
//#endif
            } finally {
                /*
                 * TODO : unlock db / thread // (in try / catch / PostError !!) */

                //if (result == ScanningResult.Success)
                //	  m_scanSucceeded = true;

                //m_cancellationRequested = false;
                isRunning = false;

                //try { OnScanCompleted(new ScanCompletedEventArgs(result, mediaID, fatalError)); }
                //catch (Exception e) { OnError(new ErrorEventArgs(e)); }

                PostCompleted(returnVolume, fatalError, cancelled);
            }
        }
Beispiel #22
0
 abstract void ScanningThreadMain(PlatformIO.DriveInfo drive,
                                  TVolume volume,
                                  BufferedVolumeItemWriter writer);
Beispiel #23
0
 public virtual void FromDevice(DriveInfo d, string device)
 {
     string rootPath = device; // FromPath() adds an ending slash ("d:\")
     FromPath(d, rootPath); // throws ArgumentException if drive cant be found
 }
Beispiel #24
0
        private void OnTvDrivesSelectionChanged(object o, EventArgs args)
        {
            if (!IsListReady)
                return;

            TreeModel model;
            TreeIter iter;

            if (tvDrives.Selection.GetSelected(out model, out iter)) {
                // enable ok button on first selection
                if (selectedDrive == null)
                    btnOk.Sensitive = true;

                selectedDrive = (DriveInfo)model.GetValue(iter, 4);

                if (Global.EnableDebugging) {
                    Debug.WriteLine("selected drive '{0}'", selectedDrive.Device);
                }
            }
        }