public virtual void CopyAsync(Uri[] src, Uri[] dest, IFileOperationProgress progress) {
			if(src.Length != dest.Length) throw new ArgumentException();
			for(int i = 0; i < src.Length; ++i) {
				progress.UpdateProgress(i + 1, src.Length, (double)i/(double)src.Length, null, null);
				Copy(src[i], dest[i]);
			}
			progress.Finish();
		}
			public static void CopyAsync(Uri[] src, Uri[] dest, IFileOperationProgress progress) {
				FileSystemHandler fsSrc = null, fsDst = null;
				FileSystemHandler fsSrc2 = null, fsDst2 = null;
				
				Console.WriteLine("{0}, {1}", src[0], dest[0]);
				
				if(src.Length != dest.Length) throw new ArgumentException();
				for(int i = 0; i < src.Length; ++i) {
					foreach(FileSystemHandler handler in CommonUtil.fsHandlers) {
						if(handler.HandlesUriType(src[i])) {
							fsSrc = handler;
						}
						
						Uri parent = new Uri(dest[i], "..");
						if(handler.HandlesUriType(parent)) {
							fsDst = handler;
						}
					}
					
					if(fsSrc == null || fsDst == null) {
						throw new PluginNotFoundException();
					}
					
					if(i > 0) {
						if(fsSrc != fsSrc2 || fsDst != fsDst2) {
							throw new ArgumentException();
						}
					}
					else {
						fsSrc2 = fsSrc;
						fsDst2 = fsDst;
					}
				}
				
				if(fsSrc == fsDst) {
					progress.Start();
					Thread thread = new Thread(delegate() {
						fsSrc.CopyAsync(src, dest, progress);
					});
					thread.Start();
				}
				else {
					throw new NotImplementedException();
				}
			}
		public static void PasteButtonClicked(IDisplayInterfaceControl control, IFileOperationProgress progress) {
			ClipboardHandler handler = PrimaryClipboardHandler;
			if(handler == null) return;
			handler.RequestPaths(delegate(object sender, EventArgs e) {
				ClipboardData data = handler.PastePaths();
				if(data.Paths.Count > 0) {
					switch(data.Operation) {
						case ClipboardOperationType.Copy:
							FileSystem.CopyAsync(data.Paths.ToArray(), (from uri in data.Paths select FileSystem.CopyOperationDestination(uri, control.CurrentLocation)).ToArray(), progress);
							break;
						
						case ClipboardOperationType.Cut:
							break;
					}
				}
			});
		}
		public override void CopyAsync(Uri[] src, Uri[] dest, IFileOperationProgress progress) {
			if(OS == PluginOSType.Windows) {
				//FileSystem.CopyDirectory(
			}
			else if(OS == PluginOSType.Unix) {
				long? prev = null;
				DateTime? prevTime = null;
				
				Gnome.Vfs.Uri[] src2 = (from uri in src select new Gnome.Vfs.Uri(Gnome.Vfs.Uri.GetUriFromLocalPath(uri.GetScrubbedLocalPath()))).ToArray();
				Gnome.Vfs.Uri[] dest2 = (from uri in dest select new Gnome.Vfs.Uri(Gnome.Vfs.Uri.GetUriFromLocalPath(uri.GetScrubbedLocalPath()))).ToArray();
				Gnome.Vfs.Xfer.XferUriList(src2,
			                           dest2,
			                           Gnome.Vfs.XferOptions.Recursive, Gnome.Vfs.XferErrorMode.Query, Gnome.Vfs.XferOverwriteMode.Query,
			                           delegate(Gnome.Vfs.XferProgressInfo info) {
					switch(info.Status) {
						case Gnome.Vfs.XferProgressStatus.Ok:
							double? bitrate = null;
							TimeSpan? etr = null;
							DateTime time = DateTime.Now;
							if(prev != null && prevTime != null) {
								double sec = (time - (DateTime)prevTime).TotalSeconds;
								bitrate = ((double)info.TotalBytesCopied - (double)prev) / sec;
								try { etr = TimeSpan.FromSeconds((info.BytesTotal - info.TotalBytesCopied)/(double)bitrate); }
								catch {}
							}
							prev = info.TotalBytesCopied;
							prevTime = time;
							
							progress.UpdateProgress((int)info.FileIndex, 0, (double)info.TotalBytesCopied/(double)info.BytesTotal, bitrate, etr);
							return 1;
						case Gnome.Vfs.XferProgressStatus.Vfserror:
							Console.WriteLine(info.VfsStatus.ToString());
							switch(progress.OnError(null, null)) {
								case FileErrorAction.Abort:
									return (int)Gnome.Vfs.XferErrorAction.Abort;
								case FileErrorAction.Retry:
									return (int)Gnome.Vfs.XferErrorAction.Retry;
								case FileErrorAction.Ignore:
									return (int)Gnome.Vfs.XferErrorAction.Skip;
							}
							return (int)Gnome.Vfs.XferErrorAction.Abort;
						case Gnome.Vfs.XferProgressStatus.Overwrite:
							bool applytoall;
							bool? overwrite = progress.OnOverwrite(new Uri(info.SourceName), new Uri(info.TargetName), out applytoall);
							if(overwrite == null) return (int)Gnome.Vfs.XferOverwriteAction.Abort;
							if((bool)overwrite) {
								if(applytoall) {
									return (int)Gnome.Vfs.XferOverwriteAction.ReplaceAll;
								}
								else {
									return (int)Gnome.Vfs.XferOverwriteAction.Replace;
								}
							}
							else {
								if(applytoall) {
									return (int)Gnome.Vfs.XferOverwriteAction.SkipAll;
								}
								else {
									return (int)Gnome.Vfs.XferOverwriteAction.Skip;
								}
							}
					}
					return 0;
				});
				progress.Finish();
			}
			else {
				base.CopyAsync(src, dest, progress);
			
			}
		}