Vi har tittat på många olika sätt att bidra med åtgärder till arbetsmiljön men har inte fokuserat på implementationen av en åtgärdsrun()-metod. Mekaniken för metoden beror på den aktuella åtgärden i fråga men genom att strukturera koden som en ångringsbar åtgärd kan åtgärden delta i plattformens stöd för "ångra och gör om" åtgärder.
Plattformen tillhandahåller ett ramverk för ångringsbara åtgärder i paketet org.eclipse.core.commands.operations. Genom att implementera koden i en run()-metod och skapa en IUndoableOperation, kan åtgärden göras tillgänglig för Ångra och Gör om. Konverteringen av en åtgärd till att använda åtgärder är en rättfram procedur till skillnad mot att implementera själva ångra och gör om-beteenden.
Vi börjar med att titta på ett väldigt enkelt exempel. Du kommer nog ihåg den enkla ViewActionDelegate från exemplet med readme-insticksprogrammet. När åtgärden anropas startar den helt enkelt en dialogruta som meddelar att åtgärden körts.
public void run(org.eclipse.jface.action.IAction action) { MessageDialog.openInformation(view.getSite().getShell(), MessageUtil.getString("Readme_Editor"), MessageUtil.getString("View_Action_executed")); }Genom att använda åtgärder ansvarar run-metoden för att skapa en åtgärd som utför det arbete som tidigare utfördes i run-metoden, och sedan begär att en åtgärdshistorik kör åtgärden, så att den lagras för Ångra och Gör om.
public void run(org.eclipse.jface.action.IAction action) { IUndoableOperation operation = new ReadmeOperation( view.getSite().getShell()); ... operationHistory.execute(operation, null, null); }Åtgärden kapslar in det gamla beteendet från run-metoden samt Ångra- och Gör om-informationen för åtgärden.
class ReadmeOperation extends AbstractOperation { Shell shell; public ReadmeOperation(Shell shell) { super("Readme Operation"); this.shell = shell; } public IStatus execute(IProgressMonitor monitor, IAdaptable info) { MessageDialog.openInformation(shell, MessageUtil.getString("Readme_Editor"), MessageUtil.getString("View_Action_executed")); return Status.OK_STATUS; } public IStatus undo(IProgressMonitor monitor, IAdaptable info) { MessageDialog.openInformation(shell, MessageUtil.getString("Readme_Editor"), "Undoing view action"); return Status.OK_STATUS; } public IStatus redo(IProgressMonitor monitor, IAdaptable info) { MessageDialog.openInformation(shell, MessageUtil.getString("Readme_Editor"), "Redoing view action"); return Status.OK_STATUS; } }
För enkla åtgärder kan det vara möjligt att flytta allt funktionellt arbete till åtgärdsklassen. I det här fallet kan det vara lämpligt att komprimera de tidigare åtgärdsklasserna till en enda klass som parametriseras. Åtgärden kör helt enkelt den angivna åtgärden när det är dags att köra den. Detta är på det stora hela ett tillämpningsdesignsbeslut.
När en åtgärd startar en guide skapas åtgärden vanligen som en del av guidens performFinish()-metod eller en finish()-metod för en guide. Konvertering av finish-metoden att använda åtgärder liknar konvertering av en run-metod. Metoden ansvarar för att skapa och köra en åtgärd som utför det arbete som tidigare gjordes internt.
Hittills har vi använt en åtgärdshistorik utan att egentligen förklara den. Låt oss ta en ny titt på den kod som skapar vår exempelåtgärd.
public void run(org.eclipse.jface.action.IAction action) { IUndoableOperation operation = new ReadmeOperation( view.getSite().getShell()); ... operationHistory.execute(operation, null, null); }Vand handlar åtgärdshistoriken egentligen om? IOperationHistory definierar gränssnittet för det objekt som håller ordning på alla ångringsbara åtgärder. När en åtgärdshistorik kör en åtgärd kör den först åtgärden och lägger sedan till den i ångra-historiken. Klienter som vill ångra och göra om åtgärder gör det genom att använda protokollet IOperationHistory.
Åtgärdshistoriken som används av en tillämpning kan hämtas på flera olika sätt. Det enklaste sättet är att använda OperationHistoryFactory.
IOperationHistory operationHistory = OperationHistoryFactory.getOperationHistory();
Det går även att hämta åtgärdshistoriken med hjälp av arbetsmiljön. Arbetsmiljön konfigurerar standardåtgärdshistoriken och tillhandahåller även protokoll för åtkomst till den. Följande kodstycke demonstrerar hur du erhåller åtgärdshistoriken från arbetsmiljön.
IWorkbench workbench = view.getSite().getWorkbenchWindow().getWorkbench(); IOperationHistory operationHistory = workbench.getOperationSupport().getOperationHistory();När en åtgärdshistorik har erhållits kan den användas till att ställa frågor till ångra- och gör om-historiken, ta reda på vilken åtgärd som står på tur för Ångra eller Gör om, eller till att ångra eller göra om särskilda åtgärder. Klienter kan lägga till en IOperationHistoryListener för att ta emot meddelanden om ändringar i historiken. Andra protokoll gör det möjligt för klienter att ange gränser för historiken eller meddela lyssnare om ändringar av en viss åtgärd. Innan vi tar en utförlig titt på protokollet måste vi förstå ångra-kontexten.
När en åtgärd skapas tilldelas den en ångra-kontext som beskriver den användarkontext i vilken den ursprungliga åtgärden utfördes. Ångra-kontexten beror vanligen på den vy eller redigerare som utfärdade den ångringsbara åtgärden. Ett exempel: ändringar i en redigerare är ofta lokala för den redigeraren. I det här fallet ska redigeraren skapa sin egen ångra-kontext och tilldela den kontexten till åtgärder som den lägger till i historik en. På det här sättet anses alla åtgärder som utförs i redigeraren vara lokala och halvprivata. Redigerare eller vyer som fungerar med en delad modell använder ofta en ångra-kontext som står i förhållande till den modell de manipulerar. Genom att använda en mer allmän ångra-kontext kan åtgärder som utförs av en vy eller redigerare blir tillgängliga för Ångra i en annan vy eller redigerar som fungerar med samma modell.
Ångra-kontexter är relativt enkla i sitt beteende. Protokollet för IUndoContext är ganska minimalt. Den primära rollen för en kontext är att "märka" en viss åtgärd som tillhörande i den ångra-kontexten, för att kunna urskilja den från åtgärder som skapats i andra ångra-kontexter. Det innebär att åtgärdshistoriken kan hålla ordning på den globala historiken med alla ångringsbara åtgärder som har körts, medan vyer och redigerare kan filtrera historiken för ett visst synsätt med den ångra-kontexten.
Ångra-kontexter kan skapas av det insticksprogram som skapar de ångringsbara åtgärderna, eller nås via APIt. Ett exempel: arbetsmiljön tillhandahåller åtkomst till en ångra-kontext som kan användas för åtgärder i hela arbetsmiljön. Hur den än erhålls ska ångra-kontexter tilldelas när en åtgärd skapas. Följande kodstycke visar hur readme-verktygets insticksprogram ViewActionDelegate kan tilldela en kontext i hela arbetsmiljön för dess åtgärder.
public void run(org.eclipse.jface.action.IAction action) { IUndoableOperation operation = new ReadmeOperation( view.getSite().getShell()); IWorkbench workbench = view.getSite().getWorkbenchWindow().getWorkbench(); IOperationHistory operationHistory = workbench.getOperationSupport().getOperationHistory(); IUndoContext undoContext = workbench.getOperationSupport().getUndoContext(); operation.addContext(undoContext); operationHistory.execute(operation, null, null); }
Varför använda kontexter alls? Varför inte använda separata åtgärdshistoriker för separata vyer och redigerare? Med separata åtgärdshistoriker antas det att alla vyer och redigerare underhåller en egen privat ångra-historik och att Ångra-funktionen inte har någon global betydelse i tillämpningen. Detta kan vara passande för viss tillämpningar och i vissa fall ska varje vy eller redigerare skapa sin egen separata ångra-kontext. Andra tillämpningar kanske vill implementera en global Ångra-funktion som gäller för alla användaråtgärder, oavsett vilken vy eller redigerare de härstammar från. I så fall ska arbetsmiljöns kontext användas av alla insticksprogram som lägger till åtgärder i historiken.
I mer komplicerade tillämpningar är Ångra-funktionen varken strikt lokal eller strikt global. Istället överkorsar kontexterna varandra något. Detta kan erhållas genom att tilldela flera kontexter till en åtgärd. Ett exempel: en IDE-arbetsmiljövy kan manipulera hela arbetsytan och se arbetsytan som sin egen ångra-kontext. En redigerare som är öppen för en viss resurs på arbetsytan kan se sina åtgärder som främst lokala. Däremot kan åtgärder som utförs i redigeraren påverka både den enskilda resursen och arbetsytan som helhet. (Ett bra exempel på detta är JDTs stöd för uppdelning i faktorer, som medger att strukturella ändringar i ett Java-element uppträder under redigering av källfilen). I dessa fall är det användbart att kunna lägga till båda ångra-kontexterna i åtgärden så att Ångra-funktionen kan utföras från själva redigeraren samt från de vyer som manipulerar arbetsytan.
Nu när vi förstår vad en ångra-kontext gör kan vi ta en ny titt på protokollet för IOperationHistory. Följande kodstycke används för att utföra en Ångra för en viss kontext:
IOperationHistory operationHistory = workbench.getOperationSupport().getOperationHistory(); try { IStatus status = operationHistory.undo(myContext, progressMonitor, someInfo); } catch (ExecutionException e) { // handle the exception }Historiken erhåller den senast utförda åtgärden som har den angivna kontexten och ber den ångra sig själv. Andra protokoll kan användas till att erhålla hela ångra- eller gör om-historiken för en kontext, eller till att hitta den åtgärd som ska ångras eller göras om i en viss kontext. Följande kodstycke erhåller etiketten för den åtgärd som ska ångras i en viss kontext.
IOperationHistory operationHistory = workbench.getOperationSupport().getOperationHistory(); String label = history.getUndoOperation(myContext).getLabel();
Den globala ångra-kontexten, IOperationHistory.GLOBAL_UNDO_CONTEXT, kan användas till att hänvisa till den globala ångra-historiken. Dvs. till alla åtgärder i historiken oavsett deras specifika kontext. Följande kodstycke erhåller den globala ångra-historiken.
IOperationHistory operationHistory = workbench.getOperationSupport().getOperationHistory(); IUndoableOperation [] undoHistory = operationHistory.getUndoHistory(IOperationHistory.GLOBAL_UNDO_CONTEXT);
När en åtgärd körs, ångras eller görs om med åtgärdshistorikprotokollet, kan klienter tillhandahålla en förloppsövervakare och ytterligare användargränssnittsinformation som kan behövas för att utföra åtgärden. Denna information skickas till själva åtgärden. I vårt ursprungliga exempel konstruerade readme-åtgärden en åtgärd med en skalparameter som kunde användas till att öppna dialogrutan. Istället för att lagra skalet i åtgärden, är en bättre metod att skicka parametrar till de kör-, ångra- och gör om-metoder som tillhandahåller den användargränssnittsinformation som behövs för att köra åtgärden. Dessa parametrar skickas vidare till själva åtgärden.
public void run(org.eclipse.jface.action.IAction action) { IUndoableOperation operation = new ReadmeOperation(); ... operationHistory.execute(operation, null, infoAdapter); }infoAdapter är en IAdaptable som minimalt kan tillhandahålla det skal som kan användas vid start av dialogrutor. Vår exempelåtgärd använder denna parameter på följande sätt:
public IStatus execute(IProgressMonitor monitor, IAdaptable info) { if (info != null) { Shell shell = (Shell)info.getAdapter(Shell.class); if (shell != null) { MessageDialog.openInformation(shell, MessageUtil.getString("Readme_Editor"), MessageUtil.getString("View_Action_executed")); return Status.OK_STATUS; } } // do something else... }
Plattformen tillhandahåller vanliga åtgärdshanterare som kan växla mål för ångra och gör om, som kan konfigureras av vyer och redigerar att tillhandahålla stöd för ångra och gör om för deras specifika kontext. När åtgärdshanteraren skapas, tilldelas en kontext till den så att åtgärdshistoriken filtreras på ett sätt som passar just den vyn. Åtgärdshanteraren tar hand om uppdateringen av ångra- och gör om-etiketter så att de visar den aktuella åtgärden det gäller och tillhandahåller rätt förloppsövervakning och användargränssnittsinformation till åtgärdshistoriken och kan dessutom rensa historiken när den aktuella åtgärden är ogiltig. En åtgärdsgrupp som skapar åtgärdshanterarena kan tilldela dem till de globala ångra- och gör om-åtgärderna för enkelhets skull.
new UndoRedoActionGroup(this.getSite(), undoContext, true);Den sista parametern är ett booleskt värde som anger huruvida ångra- och gör om-historikerna för den angivna kontexten ska tas bort när den åtgärd som är tillgänglig för Ångra eller Gör om inte är giltig. Inställningen för den här parametern är kopplad till den tillhandahållna ångra-kontexten och den verifieringsstrategi som används av åtgärder med den kontexten.
Tidigare tittade vi på hur ångra-kontexter kunde användas till att implementera olika typer av ångra-modeller för tillämpningar. Möjligheten att tilldela en eller flera kontexter till åtgärder gör det möjligt för tillämpningar att implementera ångra-strategier som är strikt lokala för varje vy eller redigerare, strikt globala för alla insticksprogram eller någon modell mitt emellan. Ett annat designbeslut rörande Ångra och Gör om är huruvida en åtgärd ska kunna ångras eller göras om när som helst eller om modellen är strikt linjär, där bara de senaste åtgärderna anses vara tillgängliga för Ångra eller Gör om.
IOperationHistory definierar protokoll som gör det möjligt för flexibla ångra-modeller, vilket lämnar det till de enskilda implementationerna att avgöra vad som är tillåtet. De ångra- och gör om-protokoll som vi sett hittills antar att det bara finns en implicerad åtgärd tillgänglig för Ångra eller Gör om i en viss ångra-kontext. Ytterligare protokoll tillhandahålls som gör det möjligt för klienter att köra en viss åtgärd, oavsett dess plats i historiken. Åtgärdshistoriken kan konfigureras så att den modell som passar för en tillämpning kan implementeras. Detta görs med ett gränssnitt som används till att på förhand godkänna en ångra- eller gör om-begäran innan åtgärden ångras eller görs om.
IOperationHistory history = OperationHistoryFactory.getOperationHistory(); // set an approver on the history that will disallow any undo that is not the most recent operation history.addOperationApprover(new LinearUndoEnforcer());
I det här fallet installeras åtgärdsgodkännare som tillhandahållits av ramverket LinearUndoEnforcer på historiken för att förhindra att en åtgärd ångras eller görs om som inte är den senaste ångra- eller gör om-åtgärden i alla dess ångra-kontexter.
En annan åtgärdsgodkännare, LinearUndoViolationUserApprover, känner av samma tillstånd och meddelar användaren om huruvida åtgärden ska tillåtas fortsätta. Denna åtgärdsgodkännare kan installeras på en viss arbetsmiljödel.
IOperationHistory history = OperationHistoryFactory.getOperationHistory(); // set an approver on this part that will prompt the user when the operation is not the most recent. IOperationApprover approver = new LinearUndoViolationUserApprover(myUndoContext, myWorkbenchPart); history.addOperationApprover(approver);
Utvecklare av insticksprogram kan utveckla och installera sina egna åtgärdsgodkännare för implementering av tillämpningsspecifika ångra-modeller och godkännandestrategier. I det aktuella insticksprogrammet kan det vara lämpligt att begära godkännande att utföra åtgärd, förutom Ångra och Gör om för åtgärden. I så fall bör åtgärdsgodkännaren även implementera IOperationApprover2, vilket godkänner att åtgärden utförs. När åtgärdshistoriken i plattformen får en begäran om att utföra åtgärden skickas en begäran om godkännande till en åtgärdsgodkännare som implementerar gränssnittet.
Vi har sett kodstycken som använder arbetsmiljöprotokoll för åtkomst till åtgärdshistoriken och arbetsmiljöns ångra-kontext. Detta görs med IWorkbenchOperationSupport, som kan erhållas av arbetsmiljön. Begreppet med en arbetsmiljötäckande ångra-kontext är ganska allmänt. Det är upp till arbetsmiljötillämpningen att avgöra vilken specifik omfattning som impliceras av arbetsmiljöns ångra-kontext och vilka vyer och redigerare som använder arbetsmiljökontexten vid tillhandahållning av ångra-stöd.
I fallet med Eclipse IDE-arbetsmiljön ska arbetsmiljöns ångra-kontext tilldelas alla åtgärder som påverkar IDE-arbetsytan som helhet. Denna kontext används av vyer som manipulerar arbetsytan, t.ex. resursnavigatorn. IDE-arbetsmiljön installerar en adapter på arbetsmiljön för IUndoContext som returnerar arbetsmiljöns ångra-kontext. Denna modellbaserade registrering gör det möjligt för insticksprogram som manipulerar arbetsmiljön att erhålla rätt ångra-kontext, även om de är konsollösa och inte hänvisar till några arbetsmiljöklasser.
// get the operation history IOperationHistory history = OperationHistoryFactory.getOperationHistory(); // obtain the appropriate undo context for my model IUndoContext workspaceContext = (IUndoContext)ResourcesPlugin.getWorkspace().getAdapter(IUndoContext.class); if (workspaceContext != null) { // create an operation and assign it the context }Andra insticksprogram uppmuntras använda denna teknik för registrering av modellbaserade ångra-kontexter.