public void btnEdit_ClickTest() { DlgQueryEditCheckedInFile target = new DlgQueryEditCheckedInFile("Dummy.txt"); MethodInfo method = typeof(DlgQueryEditCheckedInFile).GetMethod("btnEdit_Click", BindingFlags.NonPublic | BindingFlags.Instance); method.Invoke(target, new object[] {null, null} ); Assert.AreEqual(target.Answer, DlgQueryEditCheckedInFile.qecifEditInMemory); }
/// <summary> /// Called by projects and editors before modifying a file /// The function allows the source control systems to take the necessary actions (checkout, flip attributes) /// to make the file writable in order to allow the edit to continue /// /// There are a lot of cases to deal with during QueryEdit/QuerySave. /// - called in commmand line mode, when UI cannot be displayed /// - called during builds, when save shoudn't probably be allowed /// - called during projects migration, when projects are not open and not registered yet with source control /// - checking out files may bring new versions from vss database which may be reloaded and the user may lose in-memory changes; some other files may not be reloadable /// - not all editors call QueryEdit when they modify the file the first time (buggy editors!), and the files may be already dirty in memory when QueryEdit is called /// - files on disk may be modified outside IDE and may have attributes incorrect for their scc status /// - checkouts may fail /// The sample provider won't deal with all these situations, but a real source control provider should! /// </summary> public int QueryEditFiles([InAttribute] uint rgfQueryEdit, [InAttribute] int cFiles, [InAttribute] string[] rgpszMkDocuments, [InAttribute] uint[] rgrgf, [InAttribute] VSQEQS_FILE_ATTRIBUTE_DATA[] rgFileInfo, out uint pfEditVerdict, out uint prgfMoreInfo) { // Initialize output variables pfEditVerdict = (uint)tagVSQueryEditResult.QER_EditOK; prgfMoreInfo = 0; // In non-UI mode just allow the edit, because the user cannot be asked what to do with the file if (_sccProvider.InCommandLineMode()) { return VSConstants.S_OK; } try { //Iterate through all the files for (int iFile = 0; iFile < cFiles; iFile++) { uint fEditVerdict = (uint)tagVSQueryEditResult.QER_EditNotOK; uint fMoreInfo = 0; // Because of the way we calculate the status, it is not possible to have a // checked in file that is writtable on disk, or a checked out file that is read-only on disk // A source control provider would need to deal with those situations, too SourceControlStatus status = GetFileStatus(rgpszMkDocuments[iFile]); bool fileExists = File.Exists(rgpszMkDocuments[iFile]); bool isFileReadOnly = false; if (fileExists) { isFileReadOnly = (( File.GetAttributes(rgpszMkDocuments[iFile]) & FileAttributes.ReadOnly) == FileAttributes.ReadOnly); } // Allow the edits if the file does not exist or is writable if (!fileExists || !isFileReadOnly) { fEditVerdict = (uint)tagVSQueryEditResult.QER_EditOK; } else { // If the IDE asks about a file that was already approved for in-memory edit, allow the edit without asking the user again if (_approvedForInMemoryEdit.ContainsKey(rgpszMkDocuments[iFile].ToLower())) { fEditVerdict = (uint)tagVSQueryEditResult.QER_EditOK; fMoreInfo = (uint)(tagVSQueryEditResultFlags.QER_InMemoryEdit); } else { switch (status) { case SourceControlStatus.scsCheckedIn: if ((rgfQueryEdit & (uint)tagVSQueryEditFlags.QEF_ReportOnly) != 0) { fMoreInfo = (uint)(tagVSQueryEditResultFlags.QER_EditNotPossible | tagVSQueryEditResultFlags.QER_ReadOnlyUnderScc); } else { DlgQueryEditCheckedInFile dlgAskCheckout = new DlgQueryEditCheckedInFile(rgpszMkDocuments[iFile]); if ((rgfQueryEdit & (uint)tagVSQueryEditFlags.QEF_SilentMode) != 0) { // When called in silent mode, attempt the checkout // (The alternative is to deny the edit and return QER_NoisyPromptRequired and expect for a non-silent call) dlgAskCheckout.Answer = DlgQueryEditCheckedInFile.qecifCheckout; } else { dlgAskCheckout.ShowDialog(); } if (dlgAskCheckout.Answer == DlgQueryEditCheckedInFile.qecifCheckout) { // Checkout the file, and since it cannot fail, allow the edit CheckoutFileAndRefreshProjectGlyphs(rgpszMkDocuments[iFile]); fEditVerdict = (uint)tagVSQueryEditResult.QER_EditOK; fMoreInfo = (uint)tagVSQueryEditResultFlags.QER_MaybeCheckedout; // Do not forget to set QER_Changed if the content of the file on disk changes during the query edit // Do not forget to set QER_Reloaded if the source control reloads the file from disk after such changing checkout. } else if (dlgAskCheckout.Answer == DlgQueryEditCheckedInFile.qecifEditInMemory) { // Allow edit in memory fEditVerdict = (uint)tagVSQueryEditResult.QER_EditOK; fMoreInfo = (uint)(tagVSQueryEditResultFlags.QER_InMemoryEdit); // Add the file to the list of files approved for edit, so if the IDE asks again about this file, we'll allow the edit without asking the user again // UNDONE: Currently, a file gets removed from _approvedForInMemoryEdit list only when the solution is closed. Consider intercepting the // IVsRunningDocTableEvents.OnAfterSave/OnAfterSaveAll interface and removing the file from the approved list after it gets saved once. _approvedForInMemoryEdit[rgpszMkDocuments[iFile].ToLower()] = true; } else { fEditVerdict = (uint)tagVSQueryEditResult.QER_NoEdit_UserCanceled; fMoreInfo = (uint)(tagVSQueryEditResultFlags.QER_ReadOnlyUnderScc | tagVSQueryEditResultFlags.QER_CheckoutCanceledOrFailed); } } break; case SourceControlStatus.scsCheckedOut: // fall through case SourceControlStatus.scsUncontrolled: if (fileExists && isFileReadOnly) { if ((rgfQueryEdit & (uint)tagVSQueryEditFlags.QEF_ReportOnly) != 0) { fMoreInfo = (uint)(tagVSQueryEditResultFlags.QER_EditNotPossible | tagVSQueryEditResultFlags.QER_ReadOnlyNotUnderScc); } else { bool fChangeAttribute = false; if ((rgfQueryEdit & (uint)tagVSQueryEditFlags.QEF_SilentMode) != 0) { // When called in silent mode, deny the edit and return QER_NoisyPromptRequired and expect for a non-silent call) // (The alternative is to silently make the file writable and accept the edit) fMoreInfo = (uint)(tagVSQueryEditResultFlags.QER_EditNotPossible | tagVSQueryEditResultFlags.QER_ReadOnlyNotUnderScc | tagVSQueryEditResultFlags.QER_NoisyPromptRequired ); } else { // This is a controlled file, warn the user IVsUIShell uiShell = (IVsUIShell)_sccProvider.GetService(typeof(SVsUIShell)); Guid clsid = Guid.Empty; int result = VSConstants.S_OK; string messageText = Resources.ResourceManager.GetString("QEQS_EditUncontrolledReadOnly"); string messageCaption = Resources.ResourceManager.GetString("ProviderName"); if (uiShell.ShowMessageBox(0, ref clsid, messageCaption, String.Format(CultureInfo.CurrentUICulture, messageText, rgpszMkDocuments[iFile]), string.Empty, 0, OLEMSGBUTTON.OLEMSGBUTTON_YESNO, OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, OLEMSGICON.OLEMSGICON_QUERY, 0, // false = application modal; true would make it system modal out result) == VSConstants.S_OK && result == (int)DialogResult.Yes) { fChangeAttribute = true; } } if (fChangeAttribute) { // Make the file writable and allow the edit File.SetAttributes(rgpszMkDocuments[iFile], FileAttributes.Normal); fEditVerdict = (uint)tagVSQueryEditResult.QER_EditOK; } } } else { fEditVerdict = (uint)tagVSQueryEditResult.QER_EditOK; } break; } } } // It's a bit unfortunate that we have to return only one set of flags for all the files involved in the operation // The edit can continue if all the files were approved for edit prgfMoreInfo |= fMoreInfo; pfEditVerdict |= fEditVerdict; } } catch(Exception) { // If an exception was caught, do not allow the edit pfEditVerdict = (uint)tagVSQueryEditResult.QER_EditNotOK; prgfMoreInfo = (uint)tagVSQueryEditResultFlags.QER_EditNotPossible; } return VSConstants.S_OK; }
public void ConstructorTest() { DlgQueryEditCheckedInFile target = new DlgQueryEditCheckedInFile("Dummy.txt"); Assert.IsNotNull(target, "DlgQueryEditCheckedInFile cannot be created"); }