# View speichern/laden beim schließen



## SegFault (12. Jan 2010)

Ich habe folgendes Problem. Ich benutze eine View in der ich eine Tabelle habe. Diese benenutzt init(...,Memento memento) und saveState(Memento memento) Um den zustand der Tabelle (Spaltenbreite und Anordnung) zu speichern und beim öffnen wieder zu laden. 
Nun das problem, die View soll Schließbar sein. Beim schließen wird aber nicht saveState ausgeführt. Sondern nur beim Workbench close. Wie kann ich also beim Schließen der View das Speichern auslösen (Ideal wäre wirklich das saveState was auch beim Workbench close verwendet wird, aber da müsste ich frisch an das Memento ran). 

Dann ist das Problem der anzeige meiner view. es gibt eine .showView(ViewID) funktion. Dummerweise wird beim öffnen einer View damit auch nicht das init ausgelöst. Wie mache ich das also richtig das beim Schließen einer View der status wirklich gesichert wird und beim neu öffnen auf jeden fall wieder geladen.

Ich weiß das es dafür einen weg gibt. Denn der Klassenfenster von Eclipse nutzt das ganze. Das kann ich schließen und beim öffnen sind auch wieder die Pfade Expanded die es vorher schon waren.


----------



## Gast2 (12. Jan 2010)

Ja bei der View gibts ein attribut restoreable vielleicht hilft dir das... Keiner Ahnung was das genau macht


----------



## SegFault (12. Jan 2010)

Das restorable Attribut gibt nur an ob die View beim öffnen des Workbenches wieder her gestellt wird. Das hat nichts mit dem schließen und öffnen zu tun. In dem Zusammenhang fällt mir noch eine Frage ein, wie kann ich eine View ausserhalb einer Perspektive Factory an bestimmter Stelle öffnen? Wie macht das Eclipse im allgemeinen. Der Package Explorer wird immer links angedockt, auch wenn ich ihn schließe und neu öffne. Wie mache ich sowas? Unabhängig von der Frage wie ich eine View komplett wiederherstellen kann. Denn das ini wird beim händischen öffnen einer View nicht mehr mit dem memento ausgeführt.


----------



## Wildcard (12. Jan 2010)

View Positionen können im Layout als Platzhalter angelegt werden. Wenn die View dann in der Perspektive geöffnet wird, erscheint sie dort wo den den Platzhalter für die View positioniert hast.


----------



## Gast2 (12. Jan 2010)

Wildcard hat gesagt.:


> View Positionen können im Layout als Platzhalter angelegt werden. Wenn die View dann in der Perspektive geöffnet wird, erscheint sie dort wo den den Platzhalter für die View positioniert hast.



Wie ist es dann wenn man sie woanders hin verschiebt und neu öffnet??? Noch nie ausprobiert Oo...  Morgen mal testen, ob die neues Postion gespeichert wird ^^...


----------



## SegFault (13. Jan 2010)

Hmm... die view immer wieder in den gleichen zustand zurück zu setzen ist also schwerer als ich dachte. Die Sachen vom RCP Erfordern das die View noch geöffnet ist. Beim neu öffnen der View wird diese nie mit init initialisiert (nur beim öffnen des Workbenches). Beim schließen der View wird nichts gespeichert, nur beim schließen des Workbenches. Wenn nicht jemand da wirklich net tolle sache kennt wie man sowas umsetzt muss ich mir was eigenes einfallen lassen. Dafür kommen auch noch ein paar fragen auf die kommen aber in einen anderen Thread. Ich lass das noch ein Tag offen ggf kommen noch ein paar gute Ideen wie man eine View dauerhaft Persisiteren kann. Ansonsten mach ichs demnächst als "erledigt"


----------



## SegFault (13. Jan 2010)

Wie schon gesagt, so einfach scheints nicht zu gehen. Meine Idee ist gerade folgende:
Ich bau einen Service bei dem die einzelnen Views einen State anfordern können (Mit einer unique id) aus diesen werden die entsprechenden Werte geladen. Zusätzlich müssen die Views garantieren das sie alle änderungen direkt wieder in dieses State Objekt schreiben. Nach dem schließen des Views ist dieses State Objekt ja noch vorhanden da es vom Service verwaltet wird. Beim schließen des Workbenches kann ich dann alle der State Objekte speichern bzw beim startup wieder initialisieren. Sofern niemanden was besseres einfällt wäre das wohl ne möglichkeit das ganze so um zu setzen.


----------



## SegFault (14. Jan 2010)

Die Version mit dem Service klappt ganz gut. Ich hab auch Vererbung eingesetzt damit ich beliebige Objekt Per Memento Persistieren kann. Ich denke mal das lager ich aus um es ggf bei anderen Projekten weiterverwenden zu können.


----------



## dzim (14. Jan 2010)

Hm, ich finde das ist tatsächlich eine interessante Frage, die du hier aufwirfst... Ich geb zu, ich hab nur die Hälfte verstanden, da ich bisher weder mit Mementos noch Services gearbeitet habe.
Kannst du mir da ein paar Tipps geben?

Ich habe, damit ich nicht völlig unwissend daher komme gerade mal versucht einen View mit Memento zu versehen und hinterher die Daten erst einmal nur auszulesen, aber mein Test (ich starte immer aus eclipse heraus mein product) hat nirgendwo die workbench.xml angelegt und somit auch hinter nichts wieder auslesen können...

Folgendes habe ich zum speichern genutzt (kA ob ich wirklich alle sachen so bekommen hätte, wie gewollt, aber irgendwas hätte kommen müssen):

```
public void saveState(IMemento memento) {
	IMemento pShelfMemento = memento.createChild(PHSELF_MEMENTO);

	PShelfItem selectedPShelfItem = shelf.getSelection();
	pShelfMemento
			.createChild(SELECTED_PLUGIN, selectedPShelfItem.getText());
		Control[] pShelfItemControls = selectedPShelfItem.getBody()
			.getChildren();

	if (pShelfItemControls.length == 1
			&& pShelfItemControls[0] instanceof Tree) {
		Tree pluginTree = (Tree) pShelfItemControls[0];

		TreeItem[] selection = pluginTree.getSelection();

		if (selection != null) {
			for (TreeItem taskItem : selection) {
				pShelfMemento
						.createChild(SELECTED_TASK, taskItem.getText());
			}
		}
	}
}
```

Mus da noch was beachtet werden?

Danke und Grüße,
Daniel


----------



## SegFault (15. Jan 2010)

ich hab aktuell wenig Zeit... ich schau mir das heut nachmittag an. Das mit den Mementos geht halt gut hat aber einige Tücken. Die da wären:

Sobald eine View komplett geschlossen wird und innerhalb der sitzung wieder geöffnet wird, wird sie nicht aus dem Memento geladen. (In diesen fall ist memento==null beim init).
Ist die View geschlossen wenn der Workbench geschlossen wird kann auch nichts gespeichert werden. D.H. die einstellungen für diese View sind nach dem neustart weg (das xml file wird jedesmal neu angelegt).

Bei deinen Code fällt mir folgendes auf. Du legst ein child im PShelfMemento an ohne dir die Referenz darauf zu geben.

```
IMemento mymemento = pShelfMemento
            .createChild(SELECTED_PLUGIN, selectedPShelfItem.getText());
        Control[] pShelfItemControls = selectedPShelfItem.getBody()
            .getChildren();
```

da dies die Wurzel für alle Tasks ist kannst du dann machen


```
mymemento.putString(taskItem.getText())
```

Du musst dir immer vorstellen dass du einen baum aufbaust. CreateChild erstellt einen weiteren Knoten und gibts dir eine Referenz zurück. mit .putString .putInteger etc kannst du in den Knoten daten speichern. Mit getString... lieferst du die entsprechenden Werte die in den Knoten gespeichert sind wieder zurück.

zum auslesen machst du dann etwas wie:


```
//liefert alle subnodes von PSHELF_MEMENTO
IMemento[] items = memento.getChilds(PHSELF_MEMENTO);
for ( IMemento mem : items )
{
   IMemento[] selecteditems = mem.getChilds(SELECTED_PLUGIN);
   for ( IMemento item : selecteditems )
   {
        ... item.getString(SELECTED_TASK);
   }
}
```

Der Code ist weder getestes noch wirklich vollständig. Ist nur ein ansatz wie man das macht. Kann (und wird) also noch einige fehler enthalten.

P.S.: Sofern dich der andere Weg interessiert (also das man views restoren kann selbst wenn die View zwischendrinn geschlossen wird, kann ich gerne meinen Ansatz über den Dienst mal näher ausführen.


----------



## dzim (15. Jan 2010)

Hi,

ich hab (auch zu dem kurzfristigen Speichern) folgenden link bei, googlen entdeckt:
FAQ How does a view persist its state between sessions? - Eclipsepedia

Aber ich würde auch gern deine Variante hören!

Ich hab das Speichern jetzt so abgeändert:

```
@Override
	public void saveState(IMemento memento) {
		super.saveState(memento);

		IMemento pShelfMemento = memento.createChild(PHSELF_MEMENTO);

		PShelfItem selectedPShelfItem = shelf.getSelection();
		pShelfMemento.putString(SELECTED_PLUGIN, selectedPShelfItem.getText());

		Control[] pShelfItemControls = selectedPShelfItem.getBody()
				.getChildren();

		if (pShelfItemControls.length == 1
				&& pShelfItemControls[0] instanceof Tree) {
			Tree pluginTree = (Tree) pShelfItemControls[0];

			TreeItem[] selection = pluginTree.getSelection();

			if (selection != null) {
				for (TreeItem taskItem : selection) {
					pShelfMemento.putString(SELECTED_TASK, taskItem.getText());
				}
			}
		}
	}
```

Allerdings wird noch immer nichts gespeichert... Ich habe aber noch zwei Ideen, warum evtl. nicht:
1) Ich habe das setSaveAndRestore(true) in der initialize-Methode des ApplikationWorkbenchAdvisor nicht eingeschaltet
2) Der View an dem ich es teste, ist nur ein ShortView, der über die Extension eingestellt und normalerweise nicht standardmäßig sichtbar ist.

Ich werde das mal austesten...


----------



## SegFault (15. Jan 2010)

1.) ist das Problem. Du musst Save and Restore auf on schalten ansonsten wird das init mit den memento nicht aufgerufen. 
Das Tutorial was du gefunden hast hatte ich auch gefunden. Wie schon gesagt, das hat seine tücken wenn das View schließbar ist. Sobald du dein Workbench beendest während das View geschlossen ist werden keine Einstellungen zu dem View gespeichert. Mit unter ist so etwas gewollt aber bei mir nicht. Ich würde gerne den Aufbau der View auch dann speichern wenn sie geschlossen wurde.


----------



## dzim (15. Jan 2010)

Ok, dann danke für den Tipp mit dem ersten Problem.

Wenn du irgendwann noch mir verraten kannst, wie das über die Services gelöst hast, wäre das Klasse - das heißt, wenn du darfst ;-)


----------



## SegFault (15. Jan 2010)

Das ganze wird jetzt etwas ausführlicher. Der Code für den Service:

```
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map.Entry;

import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.XMLMemento;

import de.weiss.werksattclient.tools.persistableitems.PersistableNode;
import de.weiss.werkstattclient.service.IPersistableItemService;

public class PersistableItemService implements IPersistableItemService
{

	private static final String ROOT_ID = "STORABLE_DATA";
	private HashMap<String,PersistableNode> data = new HashMap<String,PersistableNode>();
	
	
	public PersistableItemService() 
	{
		
	}

	public PersistableNode getNode(String id)
	{
		return data.get(id);
	}
	
	public void addNode(String id, PersistableNode node)
	{
		data.put(id, node);
	}
	
	
	public void saveState()
	{
		try
		{
			XMLMemento memento = XMLMemento.createWriteRoot(ROOT_ID);
			for ( Entry<String, PersistableNode> ent : data.entrySet())
			{
				IMemento child = memento.createChild("PERSISTABLE_ITEM",ent.getKey());
				child.putString("classname", ent.getValue().getClass().getName());
				ent.getValue().saveState(child);
			}
			Writer writer = new FileWriter(PersistableItemService.getMementoFile());
			memento.save(writer);
		}
		catch ( Exception ex )
		{
			Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Fehler beim öffnen von Memento", ex));
		}
	}
	
	public void loadState()
	{
		try
		{
			data.clear();
			FileReader reader = new FileReader(PersistableItemService.getMementoFile());
			XMLMemento memento = XMLMemento.createReadRoot(reader);
			IMemento[] childs = memento.getChildren("PERSISTABLE_ITEM");
			if ( childs == null)return;
			for (IMemento child : childs)
			{
				String classname = child.getString("classname");
				PersistableNode node = (PersistableNode)Class.forName(classname).newInstance();
				node.restoreState(child);
				data.put(child.getID(), node);
			}
		}
		catch ( Exception ex )
		{
			Activator.getDefault().getLog().log(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Fehler beim laden von Memento", ex));
		}
	}
	
	private static File getMementoFile() throws IOException
	{
		File mementofile = Activator.getDefault().getStateLocation().append("persistable_data.xml").toFile();
		if (!mementofile.exists())
		{
			mementofile.createNewFile();
			XMLMemento memento = XMLMemento.createWriteRoot(ROOT_ID);
			Writer writer = new FileWriter(mementofile);
			memento.save(writer);
				
		}
		return mementofile;
	}
}
```
Zusätzlich gibts es ein interface was Klassen Implementieren müsse welche gespeichert werden sollen:

```
public interface PersistableNode 
{
	public abstract void saveState(IMemento memento);
	public abstract void restoreState(IMemento memento);
};
```
Hier mal eine Implementierung davon (Ist dazu das die Ordnung innerhalb einer Tabelle und die Tabellenspalten dazu zu speichern), saveState und restoreState hat im grunde den selben sinn wie beim View

```
public class PersistableTableData implements PersistableNode 
{
	private LinkedList<TableHeading> heading = new LinkedList<TableHeading>();
	private int[] columnpos;
	
	public void setColumnPos(int[] cp)
	{
		this.columnpos = cp;
	}
	
	public int[] getColumnPos()
	{
		return this.columnpos;
	}
	
	public LinkedList<TableHeading> getHeading()
	{
		return heading;
	}
	
	public void addHeading(TableHeading heading)
	{
		this.heading.add(heading);
	}
	
	public void clearHeading()
	{
		this.heading.clear();
	}
	
	@Override
	public void restoreState(IMemento memento) 
	{
		clearHeading();
		IMemento[] pos =  memento.getChildren("pos");
		this.columnpos = new int[pos.length];
		for ( int i = 0; i < pos.length; ++i)this.columnpos[i] = pos[i].getInteger("p");
		IMemento[] columndata = memento.getChildren("columns");
		for ( IMemento mem : columndata)
		{
			String name = mem.getString("name");
			int width = mem.getInteger("width");
			boolean resizable = mem.getBoolean("resizeable");
			boolean moveable = mem.getBoolean("moveable");
			TableHeading th = new TableHeading(name,width,resizable,moveable);
			this.heading.add(th);
		}
		
	}

	@Override
	public void saveState(IMemento memento)
	{
		for ( int i : this.columnpos)
		{
			IMemento pos = memento.createChild("pos");
			pos.putInteger("p", i);
		}
		for ( TableHeading th : this.heading)
		{
			IMemento columns = memento.createChild("columns");
			columns.putString("name",th.heading);
			columns.putInteger("width", th.size);
			columns.putBoolean("resizeable", th.resizeable);
			columns.putBoolean("moveable", th.moveable);
		}
		
	}
	

}
```
Und hier der Aktivator des Plugins welcher den Service anbietet. (Laden des zustandes beim starten und speichern beim beenden)


```
/*
 * (non-Javadoc)
 * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext)
 */
public void start(BundleContext context) throws Exception {
	super.start(context);
	System.out.println("Activating tools");
	plugin = this;
	pis = new PersistableItemService();
	context.registerService(IDataProviderService.class.getName(), new DataProvider(), null);
	context.registerService(IPersistableItemService.class.getName(), pis, null);
	pis.loadState();
}

/*
 * (non-Javadoc)
 * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext)
 */
public void stop(BundleContext context) throws Exception {
	System.out.println("Deactivating tools");
	pis.saveState();
	plugin = null;
	super.stop(context);
}
```
Und hier mal eine Beispielmethode in der ich den Service nutze um die Daten für meine Tabelle an zu fordern

```
private void restore()
{
	ServiceReference ref = Activator.getDefault().getBundle().getBundleContext().getServiceReference(IPersistableItemService.class.getName()); 
	IPersistableItemService pis = (IPersistableItemService)Activator.getDefault().getBundle().getBundleContext().getService(ref);
	ptd = (PersistableTableData) pis.getNode(id);
	if ( ptd != null )
	{
		heading = ptd.getHeading();
		createHeaders();
	}
	else
	{
		ptd = new PersistableTableData();
		pis.addNode(this.id, ptd);
	}
}
```
Im grunde läuft der Service darauf hinaus das man bei ihm über eine id instanz von PersistableData anfordern kann. Die kann dann natürlich alles enthalten. Es muss nur sichergestellt werden das in die instanz von PersistableData alle änderungen wieder reingeschrieben werden, die dann gespeichert werden sollen. Sofern die id über die man die PersistableData anforder noch nicht vergeben ist wird null zurück gegeben. Dann muss man die natürlich anlegen.

Ein paar Tücken hat das ganze.: Es könnten id's enthalten sein die gar nicht mehr benötigt werden. Diese muss man wohl von hand aus der entsprechenden xml Datei löschen oder das ganze Prinzip noch etwas aufbohren. Aktuell hat das ganze noch ein paar Probleme (Siehe meinen Beitrag zum Thema das ich das XMLMemento noch nicht korrekt lesen kann) aber ich denke das krieg ich behoben.


----------



## dzim (18. Jan 2010)

Hey vielen Dank!
Ich versuch das dann mal aus!


----------

