Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 589a5aefb3ffdc8127b837bd0daf0dadbcd60c53 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
/*****************************************************************************
 * Copyright (c) 2013 CEA LIST.
 *
 *    
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Ansgar Radermacher (CEA LIST) Ansgar.Radermacher@cea.fr - Initial API and implementation
 *
 *****************************************************************************/
package org.eclipse.papyrus.texteditor.cdt.editor;

import org.eclipse.cdt.codan.internal.ui.cxx.CodanCReconciler;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ISourceRange;
import org.eclipse.cdt.core.model.ISourceReference;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.internal.ui.editor.CEditor;
import org.eclipse.cdt.ui.CDTUITools;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EValidator;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.papyrus.infra.core.lifecycleevents.DoSaveEvent;
import org.eclipse.papyrus.infra.core.lifecycleevents.ILifeCycleEventsProvider;
import org.eclipse.papyrus.infra.core.lifecycleevents.ISaveAndDirtyService;
import org.eclipse.papyrus.infra.core.lifecycleevents.ISaveEventListener;
import org.eclipse.papyrus.infra.core.services.ServiceException;
import org.eclipse.papyrus.infra.core.services.ServicesRegistry;
import org.eclipse.papyrus.texteditor.cdt.Activator;
import org.eclipse.papyrus.texteditor.cdt.sync.ObtainICElement;
import org.eclipse.papyrus.texteditor.cdt.sync.RevealCurrentOperation;
import org.eclipse.papyrus.texteditor.cdt.sync.SyncCDTtoModel;
import org.eclipse.papyrus.texteditor.cdt.sync.SyncModelToCDT;
import org.eclipse.papyrus.texteditor.model.texteditormodel.TextEditorModel;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IGotoMarker;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.NamedElement;

/**
 * A specialization of the CDT editor made for integration into Papyrus
 *
 */
public class PapyrusCDTEditor extends CEditor {

	/** the service registry */
	protected final ServicesRegistry registry;

	/** the papyrus text instance */
	protected final TextEditorModel papyrusTextInstance;

	/** the text listener */
	protected FocusListener focusListener;

	public static final String EDITOR_DEFAULT_NAME = "CDT Editor";

	public static final String EDITOR_TYPE = "CDTEditorInPapyrus"; //$NON-NLS-1$

	protected SyncCDTtoModel syncCpp;

	protected RevealCurrentOperation reveal;

	/**
	 * 
	 * Constructor.
	 * 
	 * @param registry
	 * @param papyrusTextInstance
	 */
	public PapyrusCDTEditor(final ServicesRegistry registry, final TextEditorModel papyrusTextInstance) {
		super();
		this.registry = registry;
		this.papyrusTextInstance = papyrusTextInstance;
		try {
			saveAndDirtyService = registry.getService(ISaveAndDirtyService.class);
			saveAndDirtyService.registerIsaveablePart(this);
			ILifeCycleEventsProvider lifeCycleEvents = registry.getService(ILifeCycleEventsProvider.class);
			ISaveEventListener preSaveEvent = new ISaveEventListener() {
				
				public void doSaveAs(DoSaveEvent event) {
					// TODO Auto-generated method stub
				}
				
				public void doSave(DoSaveEvent event) {
					syncCpp.syncCDTtoModel();		
				}
			};
			lifeCycleEvents.addAboutToDoSaveListener(preSaveEvent);
		} catch (ServiceException e) {
			Activator.log.error(e);
		}
	}
		
	/**
	 * override method in order to save & restore action bars entries
	 */
	@Override
	public void createPartControl(Composite parent) {
		// save Papyrus (GMF) handlers
		IActionBars actionBars = getEditorSite().getActionBars();
		if((actionBars != null) && (gmfUndo == null)) {
			gmfUndo = actionBars.getGlobalActionHandler(ITextEditorActionConstants.UNDO);
			gmfRedo = actionBars.getGlobalActionHandler(ITextEditorActionConstants.REDO);
		}

		super.createPartControl(parent);

		if(actionBars != null) {
			// save CDT (textEditor) handlers
			textUndo = actionBars.getGlobalActionHandler(ITextEditorActionConstants.UNDO);
			textRedo = actionBars.getGlobalActionHandler(ITextEditorActionConstants.REDO);
			// & restore Papyrus bar
			actionBars.setGlobalActionHandler(ITextEditorActionConstants.UNDO, gmfUndo);
			actionBars.setGlobalActionHandler(ITextEditorActionConstants.REDO, gmfRedo);
			actionBars.updateActionBars();
		}
		
		papyrusTextInstance.eAdapters().add(new Adapter() {

			public void notifyChanged(Notification notification) {
				// TODO Auto-generated method stub
				if (notification.getEventType() == Notification.SET) {
					if (notification.getNewValue() instanceof NamedElement) {
						gotoElement((NamedElement) notification.getNewValue());
					}

				}
			}

			public Notifier getTarget() {
				return null;
			}

			public void setTarget(Notifier newTarget) {
			}

			public boolean isAdapterForType(Object type) {
				return false;
			}
			
		});
		
		if (papyrusTextInstance.getSelectedObject() instanceof NamedElement) {
			gotoElement((NamedElement) papyrusTextInstance.getSelectedObject());
		}
	}

	/**
	 * 
	 * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#createSourceViewer(org.eclipse.swt.widgets.Composite,
	 *      org.eclipse.jface.text.source.IVerticalRuler, int)
	 * 
	 * @param parent
	 * @param ruler
	 * @param styles
	 * @return
	 */
	@Override
	public ISourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler, int styles) {

		final ISourceViewer viewer = super.createSourceViewer(parent, ruler, styles);
		// ISourceViewer viewer = new DelegatingSourceViewer(origViewer, (Classifier)papyrusTextInstance.getEditedObject());
		focusListener = new FocusListener() {

			public void focusLost(FocusEvent e) {
				// potential problem for undo/redo!!
				if(isDirty()) {
					syncCpp.syncCDTtoModel();
					Classifier classifier = (Classifier)papyrusTextInstance.getEditedObject();
					doSave(new NullProgressMonitor());
					// regenerate code. TODO: raises update dialog
					SyncModelToCDT.syncModelToCDT(classifier);
				}
				// restore handlers
				IActionBars actionBars = getEditorSite().getActionBars();
				if(actionBars != null) {
					if((gmfUndo != null) && (gmfRedo != null)) {
						actionBars.setGlobalActionHandler(ITextEditorActionConstants.UNDO, gmfUndo);
						actionBars.setGlobalActionHandler(ITextEditorActionConstants.REDO, gmfRedo);
						actionBars.updateActionBars();
					}
				}
			}

			public void focusGained(FocusEvent e) {
				// restore handlers
				IActionBars actionBars = getEditorSite().getActionBars();
				if(actionBars != null) {
					if((textUndo != null) && (textRedo != null)) {
						actionBars.setGlobalActionHandler(ITextEditorActionConstants.UNDO, textUndo);
						actionBars.setGlobalActionHandler(ITextEditorActionConstants.REDO, textRedo);
						actionBars.updateActionBars();
					}
				}
			}
		};
		// register focus listener
		viewer.getTextWidget().addFocusListener(focusListener);
		SelectionListener selectionListener = new SelectionListener() {
			
			public void widgetSelected(SelectionEvent e) {
				IEditorPart ep = getEditorSite().getPage().getActiveEditor();
				ISelection selection = getSelectionProvider().getSelection();
				if ((ep instanceof IGotoMarker) && (selection instanceof ITextSelection)) {
					if (((ITextSelection) selection).getLength() > 0) {
						// don't try to navigate during selection of a chunk of text.
						return;
					}
					IMarker marker;
					/*
	                 * create a temporary validation marker on the
	                 * srcFile file, call the gotoMarker operation of the editor
	                 * and remove the marker afterwards.
	                 * TODO: operation activates the model-explorer
	                 */
					try {
						Element element = reveal.obtainSelectedElement((ITextSelection) selection);
						String modelURI = EcoreUtil.getURI(element).toString();
						marker = srcFile.createMarker(EValidator.MARKER);
						marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO);	// set severity before URI to avoid marker update without associated icon
						marker.setAttribute(EValidator.URI_ATTRIBUTE, modelURI);
						IWorkbenchPage wbpage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
						IWorkbenchPart activePart = wbpage.getActivePart();
						((IGotoMarker) ep).gotoMarker(marker);
						// reactivate this editor
						wbpage.activate(activePart);
						marker.delete();
					} catch (CoreException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
				}
			}
			
			public void widgetDefaultSelected(SelectionEvent e) {
			}
		};
		
		// TODO: don't add selection listener for the moment. It can cause deadlocks (setting a marker attribute while file is locked, reproducible with cut (^X) on selection)
		// viewer.getTextWidget().addSelectionListener(selectionListener);
		
		return viewer;
	}

	/**
	 * Goto a specific element within the text editor. Currently, only methods are supported.
	 * 
	 * @param element
	 * @throws CoreException
	 */
	public void gotoElement(NamedElement element) {
		// IFile srcFile = SyncModelToCDT.syncModelToCDT((Classifier) pe);
		// ITranslationUnit itu2 = (ITranslationUnit) CoreModel.getDefault().create(srcFile);

		ICElement ice = CDTUITools.getEditorInputCElement(m_input);

		if(ice instanceof ITranslationUnit) {
			ITranslationUnit itu = (ITranslationUnit) ice;
			ICElement icElement = ObtainICElement.getICElement(itu, element);
			if (icElement instanceof ISourceReference) {
				try {
					ISourceRange range = ((ISourceReference)icElement).getSourceRange();
					
					ISourceViewer viewer = getSourceViewer();
					viewer.revealRange(range.getStartPos(), 1);
					viewer.setSelectedRange(range.getStartPos(), range.getLength());
					return;
				}
				catch (CoreException e) {					
				}
			}
		}
	}
	
	@Override
	public boolean isDirty() {
		boolean isDirty = super.isDirty();
		if (isDirty != oldDirty) {
			oldDirty = isDirty;
			// hack. Normally, the saveAndDirtyService detects this itself (should find a solution how it can do that)
			// ((SaveAndDirtyService) saveAndDirtyService).fireIsDirtyChanged();
		}
		return isDirty;
	}
	
	/**
	 * We override this method because setInput can't be overriden for TextEditor.
	 * We replace the default Papyrus input with the CPP file
	 * 
	 * @see org.eclipse.ui.editors.text.TextEditor#doSetInput(org.eclipse.ui.IEditorInput)
	 * 
	 * @param input
	 * @throws CoreException
	 */
	@Override
	protected void doSetInput(IEditorInput input) throws CoreException {

		URI uri = papyrusTextInstance.eResource().getURI();
		Classifier classifier = (Classifier)papyrusTextInstance.getEditedObject();
		srcFile = SyncModelToCDT.syncModelToCDT(classifier);
		if (srcFile == null) {
			throw new PartInitException("Code generation before editing was not attempted. In most cases, this indicates that the projet (in which your model is stored) does not have a CDT nature"); //$NON-NLS-1$
		}
		if(!srcFile.exists()) {
			throw new PartInitException("Code generation before editing failed");
		}
		IEditorInput newInput = new FileEditorInput(srcFile);

		// IStorage storage = new TextStorage(string);
		super.doSetInput(newInput);

		syncCpp = new SyncCDTtoModel(newInput, classifier, uri.segment(1));
		m_input = newInput;
		reveal = new RevealCurrentOperation(newInput, classifier, uri.segment(1));
		
		// add the reconciler to get syntax check and completion. (still no full checks)
		addReconcileListener(new CodanCReconciler());
	}

	/**
	 * 
	 * @see org.eclipse.ui.editors.text.TextEditor#dispose()
	 * 
	 */
	@Override
	public void dispose() {

		saveAndDirtyService.removeIsaveablePart(this);
		
		//we remove the listener
		StyledText st = getSourceViewer().getTextWidget();
		st.removeFocusListener(focusListener);
		super.dispose();
		// assure that embedded text widget is disposed now. Otherwise, it will be closed
		// later, when the editorSite is already set to null by Papyrus (mainEditorSite attribute in MultiPageEditorSite4x)
		// resulting in a null pointer exception and resulting inconsistencies in the Papyrus editor.
		st.dispose();
	}

	protected IAction gmfUndo, gmfRedo;

	protected IAction textUndo, textRedo;

	// TODO: remove, unused
	protected IActionBars gmfActionBars, textActionBars;
	
	protected boolean oldDirty;
	
	protected ISaveAndDirtyService saveAndDirtyService;
	
	protected IFile srcFile;
	
	protected ISelectionProvider sp;
	
	protected IEditorInput m_input;
}

Back to the top