View Javadoc

1   /*
2    * Copyright 1999-2006 Faculty of Mathematics, Physics and Informatics, Comenius
3    * University, Bratislava. This file is protected by the Mozilla Public License
4    * version 1.1 (the License); you may not use this file except in compliance
5    * with the License. You may obtain a copy of the License at
6    * http://euromath2.sourceforge.net/license.html Unless required by applicable
7    * law or agreed to in writing, software distributed under the License is
8    * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
9    * KIND, either express or implied. See the License for the specific language
10   * governing permissions and limitations under the License.
11   */
12  package sk.uniba.euromath.editor.xmlEditor;
13  
14  import java.util.ArrayList;
15  import java.util.Collections;
16  import java.util.Comparator;
17  import java.util.HashMap;
18  import java.util.List;
19  import java.util.Map;
20  import java.util.Set;
21  
22  import javax.xml.transform.Source;
23  
24  import org.eclipse.draw2d.ColorConstants;
25  import org.eclipse.draw2d.geometry.Rectangle;
26  import org.eclipse.gef.EditDomain;
27  import org.eclipse.gef.EditPart;
28  import org.eclipse.gef.GraphicalViewer;
29  import org.eclipse.gef.KeyHandler;
30  import org.eclipse.gef.KeyStroke;
31  import org.eclipse.gef.RootEditPart;
32  import org.eclipse.gef.commands.CommandStack;
33  import org.eclipse.gef.editparts.ScalableRootEditPart;
34  import org.eclipse.gef.ui.actions.ActionRegistry;
35  import org.eclipse.gef.ui.parts.GraphicalViewerKeyHandler;
36  import org.eclipse.swt.SWT;
37  import org.eclipse.swt.events.FocusEvent;
38  import org.eclipse.swt.events.FocusListener;
39  import org.eclipse.swt.widgets.Composite;
40  import org.eclipse.ui.IEditorSite;
41  
42  import sk.baka.ikslibs.interval.DOMIntervalSet;
43  import sk.baka.ikslibs.modify.IChangeCollector;
44  import sk.uniba.euromath.document.XMLAccess;
45  import sk.uniba.euromath.editor.EditorException;
46  import sk.uniba.euromath.editor.EditorSite;
47  import sk.uniba.euromath.editor.IEditor;
48  import sk.uniba.euromath.editor.IFocusListener;
49  import sk.uniba.euromath.editor.IRenderer;
50  import sk.uniba.euromath.editor.actions.IActionContributor;
51  import sk.uniba.euromath.editor.figures.IEMFigure;
52  import sk.uniba.euromath.editor.selections.DOMSelectionChangedEvent;
53  import sk.uniba.euromath.editor.selections.IDOMSelectionChangedListener;
54  import sk.uniba.euromath.editor.selections.IDOMSelectionProvider;
55  import sk.uniba.euromath.editor.textEditor.viewers.ScrollingTextGraphicalViewer;
56  import sk.uniba.euromath.editor.textEditor.viewers.TextGraphicalViewerImpl;
57  import sk.uniba.euromath.editor.xmlEditor.actions.SelectParentAction;
58  import sk.uniba.euromath.editor.xmlEditor.actions.XMLActionContributor;
59  import sk.uniba.euromath.editor.xmlEditor.editParts.XMLEditPart;
60  import sk.uniba.euromath.editor.xmlEditor.tools.XMLStructureTool;
61  
62  /***
63   * TODO: Kollar change comment Common editor for any namespaces and any renderer
64   * used by default if no other is find for namespace examinig all registered
65   * editors for that namespace. Is implemented using GEF, providing selections,
66   * context menu, editing capability by that menu. Uses only one type EditPart -
67   * XMLEditPart. EditDomain provide one tool - XMLSelectionTool. GraphicalViewer
68   * is XMLScrollingGraphicalViewer if this is root editor in IEditor "hierarchy"
69   * otherwise GraphicalViewerImpl. Uses XMLActionContributor to create context
70   * menu and to contribute to global bars and context menu.
71   * 
72   * 
73   * @author Tomáš Studva 31.7.2005 Martin Kollar
74   */
75  
76  public class XMLEditor implements IEditor, IDOMSelectionProvider, FocusListener {
77  
78      /***
79       * Action contributor makes almost all action related contribution to bars.
80       */
81      private IActionContributor actionContributor;
82  
83      /***
84       * Factory for creating editparts. Special XMLEditPartFactory is used.
85       */
86      private XMLEditPartFactory editPartFactory;
87  
88      /***
89       * Graphical viewer instance. One from default implementation of GEF is
90       * used.
91       */
92      private GraphicalViewer graphicalViewer;
93  
94      /***
95       * Edit domain. Default implementation is used.
96       */
97      private EditDomain editDomain;
98  
99      /***
100      * XMLAccess instance.
101      */
102     private XMLAccess xmlAccess;
103 
104     /***
105      * Renderer associated/collaborating with this XMLEditor.
106      */
107     private IRenderer renderer;
108 
109     /***
110      * Key handler //TODO GUI pouvazovat kedy ouzivat keyhandler a naco, je to v
111      * popise classy KeyHandler (samozrejme je to na nas)
112      */
113     private KeyHandler keyHandler;
114 
115     /***
116      * EditorSite instance.
117      */
118     private EditorSite editorSite;
119 
120     /***
121      * List of listeners listening to XMLEditor's selection events.
122      */
123     private final ArrayList<IDOMSelectionChangedListener> selectionListeners;
124 
125     /***
126      * Composite from init, which host this editor.
127      */
128     private Composite composite;
129 
130     /***
131      * List of listeners listening to XMLEditor's focus events.
132      */
133     private final List<IFocusListener> focusListeners;
134 
135     /***
136      * Constructor.
137      */
138     public XMLEditor() {
139         this.selectionListeners = new ArrayList<IDOMSelectionChangedListener>();
140         this.focusListeners = new ArrayList<IFocusListener>();
141         setEditDomain(createEditDomain());
142     }
143 
144     /***
145      * Getter.
146      * 
147      * @return EditorSite instance
148      */
149     protected EditorSite getEditorSite() {
150         return this.editorSite;
151     }
152 
153     /***
154      * @return <code>true</code> if this is a rootEditor
155      */
156     public boolean isRootEditor() {
157         return getEditorSite().isRootEditor(this);
158     }
159 
160     /***
161      * Configures viewer for work. After this configuration viewer is prepared
162      * to display contents.
163      * 
164      * @param source
165      */
166     protected void configureGraphicalViewer() {
167         setEditPartFactory(createEditPartFactory());
168         GraphicalViewer viewer = getGraphicalViewer();
169         viewer.getControl().setBackground(ColorConstants.listBackground);
170         viewer.setEditDomain(getEditDomain());
171         viewer.setEditPartFactory(getEditPartFactory());
172         viewer.setRootEditPart(createRootEditPart());
173     }
174 
175     /***
176      * Creates XMLEditPartFactory.
177      * 
178      * @return created XMLEditPartFactory
179      */
180     protected XMLEditPartFactory createEditPartFactory() {
181         return new XMLEditPartFactory();
182     }
183 
184     /***
185      * Getter for global action registry. Global action registry are shared by
186      * all opened/used IEditors for actions. TODO STUDVA
187      * 
188      * @return global ActionRegistry
189      */
190     private ActionRegistry getGlobalActionRegistry() {
191         return getActionContributor().getGlobalActionRegistry();
192     }
193 
194     /***
195      * Creates and inits KeyHandler which handles key events, when no tool is
196      * active.
197      */
198     protected void createAndInitKeyHandler() {
199         setKeyHandler(new GraphicalViewerKeyHandler(getGraphicalViewer()));
200         // TODO KOLLAR I've deleted localregistry, were useless, but I don't
201         // know
202         // how it worked? you was asking registry for actions, which you haven't
203         // created
204         /*
205          * getKeyHandler().put( KeyStroke.getPressed(SWT.F2, 0),
206          * getLocalActionRegistry().getAction( GEFActionConstants.DIRECT_EDIT));
207          */
208         getKeyHandler().put(KeyStroke.getPressed(SWT.ARROW_UP, SWT.CTRL),
209                 new SelectParentAction(getEditorSite()));
210     }
211 
212     /***
213      * Hooks the GraphicalViewer to the rest of the EditorSite.
214      */
215     protected void hookGraphicalViewer() {
216         // getSelectionSynchronizer().addViewer(getGraphicalViewer());
217         // getSite().setSelectionProvider(getGraphicalViewer());
218         // createActionsVisual();
219 
220         getSelectionListeners().add(
221                 (IDOMSelectionChangedListener) getGraphicalViewer());
222         ((IDOMSelectionProvider) getGraphicalViewer())
223                 .addSelectionChangedListener(this);
224     }
225 
226     /***
227      * Returns graphical viewer hooked on this Editor's composite.
228      * 
229      * @return graphical viewer.
230      */
231     protected GraphicalViewer getGraphicalViewer() {
232         return this.graphicalViewer;
233     }
234 
235     /***
236      * Setter for GraphicalViewer.
237      * 
238      * @param viewer
239      */
240     protected void setGraphicalViewer(GraphicalViewer viewer) {
241         getEditDomain().addViewer(viewer);
242         this.graphicalViewer = viewer;
243     }
244 
245     /***
246      * Creates GraphicalViewer on parent Composite for transformed doc source.
247      * 
248      * @param parent
249      * @param source
250      */
251     public void createGraphicalViewer(Composite parent) {
252         GraphicalViewer viewer;
253         if (isRootEditor())
254             // viewer = new XMLScrollingGraphicalViewer();
255             viewer = new ScrollingTextGraphicalViewer(this);
256         else
257             // viewer = new GraphicalViewerImpl();
258             viewer = new TextGraphicalViewerImpl(this);
259 
260         viewer.createControl(parent);
261         setGraphicalViewer(viewer);
262         configureGraphicalViewer();
263         hookGraphicalViewer();
264     }
265 
266     /***
267      * Getter for key handler.
268      * 
269      * @return KeyHandler
270      */
271     private KeyHandler getKeyHandler() {
272         return this.keyHandler;
273     }
274 
275     /***
276      * Set the contents of the GraphicalViewer, when is ready.
277      * 
278      * @param rootFigure
279      *            root of tree of figures, to edit/display.
280      */
281     protected void initializeGraphicalViewer(IEMFigure rootFigure) {
282         getGraphicalViewer().setContents(createTopEditPart(rootFigure));
283 
284         createAndInitKeyHandler();
285         if (getKeyHandler() != null) {
286             getGraphicalViewer().setKeyHandler(getKeyHandler());
287         }
288     }
289 
290     /***
291      * Creates top most editpart which is editor dependend - manipulates with
292      * model, is used as editpart for editor dependent view - root figure.
293      * 
294      * @param rootFigure
295      *            root of figure tree to edit/display
296      * @return created top editpart
297      */
298     protected EditPart createTopEditPart(IEMFigure rootFigure) {
299         return getEditPartFactory().createEditPart(null, rootFigure);
300     }
301 
302     /*
303      * (non-Javadoc)
304      * 
305      * @see sk.uniba.euromath.api.editor.IEditor#init(javax.xml.transform.Source,
306      *      sk.uniba.euromath.api.editor.IRenderer,
307      *      org.eclipse.swt.widgets.Composite,
308      *      sk.uniba.euromath.document.XMLAccess)
309      */
310     public void init(Source transformedDoc, IRenderer renderer,
311             Composite parent, XMLAccess xmlAccess, EditorSite editorSite)
312             throws EditorException {
313         this.composite = parent;
314         this.xmlAccess = xmlAccess;
315         this.renderer = renderer;
316         this.editorSite = editorSite;
317 
318         createGraphicalViewer(parent);
319         setActionContributor(createActionContributor());
320         initializeGraphicalViewer(renderer.getRootFigure());
321         performPaintUpdate();
322     }
323 
324     /* (non-Javadoc)
325      * @see sk.uniba.euromath.editor.IEditor#reinit(javax.xml.transform.Source, sk.baka.ikslibs.modify.IChangeCollector)
326      */
327     public void reinit(Source transformedDoc, IChangeCollector changes) {
328         // clear up list of visualized ids
329         getVisualInfo().clear();
330         initializeGraphicalViewer(getRenderer().getRootFigure());
331         performPaintUpdate();
332     }
333 
334     /***
335      * Returns IdVisualInfo object. Holds which ids have been visualized and by
336      * which XMLEditPart
337      * 
338      * @return IdVisualInfo
339      */
340     public IdVisualInfo getVisualInfo() {
341         return ((XMLEditPartFactory) getEditPartFactory()).getVisualInfo();
342     }
343 
344     /***
345      * Creates action contributor.
346      * 
347      * @return created IActionContributor
348      */
349     protected IActionContributor createActionContributor() {
350         return new XMLActionContributor(getEditorSite(), getGraphicalViewer());
351     }
352 
353     /***
354      * Getter.
355      * 
356      * @return XMLAccess
357      */
358     public XMLAccess getXMLAccess() {
359         return this.xmlAccess;
360     }
361 
362     /***
363      * Returns EditorSite's PartSite.
364      * 
365      * @return IEditorSite of EditoSite.
366      */
367     protected IEditorSite getIEditorSite() {
368         return getEditorSite().getEditorSite();
369     }
370 
371     /***
372      * Returns renderer associated with this IEditor.
373      */
374     public IRenderer getRenderer() {
375         return this.renderer;
376     }
377 
378     /***
379      * Returns Composite where children IEditors will be created.
380      */
381     public Composite getClientComposite() {
382         return (Composite) getGraphicalViewer().getControl();
383     }
384 
385     /***
386      * Returns composite, which host this IEditor.
387      */
388     public Composite getHostingComposite() {
389         return this.composite;
390     }
391 
392     /***
393      * Sets this Editor active.
394      */
395     public void setFocus() {
396         getGraphicalViewer().getControl().setFocus();
397     }
398 
399     /*
400      * (non-Javadoc)
401      * 
402      * @see sk.uniba.euromath.api.editor.IEditor#performPaintUpdate()
403      */
404     public void performPaintUpdate() {
405         getGraphicalViewer().flush();
406     }
407 
408     /***
409      * Returns the command stack.
410      * 
411      * @return the command stack
412      */
413     protected CommandStack getCommandStack() {
414         return getEditDomain().getCommandStack();
415     }
416 
417     /***
418      * Returns EditDomain.
419      * 
420      * @return editomain
421      */
422     protected EditDomain getEditDomain() {
423         return this.editDomain;
424     }
425 
426     /***
427      * Setter.
428      * 
429      * @param editDomain
430      *            to set.
431      */
432     private void setEditDomain(EditDomain editDomain) {
433         this.editDomain = editDomain;
434     }
435 
436     /***
437      * Creates and configures EditDomain. Sets DefaultTool a ActiveTool
438      * 
439      * @return new EditDomain
440      */
441     protected EditDomain createEditDomain() {
442         EditDomain e = new EditDomain();
443         e.setDefaultTool(new XMLStructureTool());
444         e.setActiveTool(e.getDefaultTool());
445         return e;
446     }
447 
448     /***
449      * Creates root editpart
450      * 
451      * @return created root editpart
452      */
453     protected RootEditPart createRootEditPart() {
454         return new ScalableRootEditPart();
455     }
456 
457     /***
458      * Getter
459      * 
460      * @return used EditPartFactory
461      */
462     public XMLEditPartFactory getEditPartFactory() {
463         return this.editPartFactory;
464     }
465 
466     /***
467      * Setter.
468      * 
469      * @param editPartFactory
470      *            to set
471      */
472     public void setEditPartFactory(XMLEditPartFactory editPartFactory) {
473         this.editPartFactory = editPartFactory;
474     }
475 
476     public void dispose() {
477         // getCommandStack().removeCommandStackListener(this);
478         getEditDomain().setActiveTool(null);
479         getSelectionListeners().clear();
480         getFocusListeners().clear();
481     }
482 
483     /***
484      * @return Selection in that is stored, what is selected in whole document
485      */
486     public DOMIntervalSet getDOMSelection() {
487         if (getEditorSite() == null)
488             throw new IllegalStateException(
489                     "Editor have to be initialized at first");
490         return getEditorSite().getDOMSelection();
491 
492     }
493 
494     /***
495      * @param listener
496      *            IDOMSelectionChangedListener to be add to listeners
497      */
498     public void addSelectionChangedListener(
499             IDOMSelectionChangedListener listener) {
500         if (!(getSelectionListeners().contains(listener)))
501             getSelectionListeners().add(listener);
502     }
503 
504     /***
505      * @param listener
506      *            IDOMSelectionChangedListener to be removed from listeners
507      */
508     public void removeSelectionChangedListener(
509             IDOMSelectionChangedListener listener) {
510         getSelectionListeners().remove(listener);
511     }
512 
513     /***
514      * Unregisters this editor as selection listeners from this editor's
515      * listeners. Editors listeners are GraphicalViever and EditorSite.
516      */
517     protected void stopSelectionListening() {
518         for (IDOMSelectionChangedListener listener : getSelectionListeners())
519             if (listener instanceof IDOMSelectionProvider)
520                 ((IDOMSelectionProvider) listener)
521                         .removeSelectionChangedListener(this);
522     }
523 
524     /***
525      * Registers this editor as selection listeners to graphical viewer.
526      */
527     protected void startSelectionListening() {
528         for (IDOMSelectionChangedListener listener : getSelectionListeners())
529             if (listener instanceof IDOMSelectionProvider)
530                 ((IDOMSelectionProvider) listener)
531                         .addSelectionChangedListener(this);
532     }
533 
534     /***
535      * Fires selection change event.
536      * 
537      * @param selection
538      *            DOMIntervalSet instance which will be fired
539      */
540     protected void fireSelectionChangeEvent(DOMIntervalSet selection) {
541         DOMSelectionChangedEvent event = new DOMSelectionChangedEvent(this,
542                 selection);
543 
544         for (IDOMSelectionChangedListener listener : getSelectionListeners()) {
545             listener.selectionChanged(event);
546         }
547     }
548 
549     /***
550      * Routs information about this new IXMLSelection to SelectionListeners
551      * 
552      * @param selection
553      *            IXMLSelection to set
554      */
555     public void setSelection(DOMIntervalSet selection) {
556         fireSelectionChangeEvent(selection);
557     }
558 
559     /***
560      * If event is IXMLSelection than this selection is routed to
561      * SelectionListeners in other way do nothing
562      * 
563      * @param event
564      */
565     public void selectionChanged(DOMSelectionChangedEvent event) {
566         fireSelectionChangeEvent(event.getDOMSelection());
567     }
568 
569     /***
570      * @return Returns the selectionListeners.
571      */
572     public ArrayList<IDOMSelectionChangedListener> getSelectionListeners() {
573         return this.selectionListeners;
574     }
575 
576     /***
577      * Returns xml action contributor, can be called only after init.
578      * 
579      * @return action contributor to bars and menus
580      */
581     public IActionContributor getActionContributor() {
582         if (this.actionContributor == null)
583             throw new IllegalStateException();
584         return this.actionContributor;
585     }
586 
587     /***
588      * @param actionContributor
589      *            The ActionContributor to set.
590      */
591     public void setActionContributor(IActionContributor actionContributor) {
592         this.actionContributor = actionContributor;
593     }
594 
595     public void addFocusListener(IFocusListener listener) {
596         if (!(getFocusListeners().contains(listener)))
597             getFocusListeners().add(listener);
598     }
599 
600     public void removeFocusListener(IFocusListener listener) {
601         getFocusListeners().remove(listener);
602     }
603 
604     public void focusGained(FocusEvent e) {
605         for (IFocusListener listener : getFocusListeners())
606             listener.focusGained(this);
607     }
608 
609     public void focusLost(FocusEvent e) {
610         for (IFocusListener listener : getFocusListeners())
611             listener.focusGained(this);
612     }
613 
614     /***
615      * @return Returns the focusListeners.
616      */
617     public List<IFocusListener> getFocusListeners() {
618         return this.focusListeners;
619     }
620 
621     /*
622      * (non-Javadoc)
623      * 
624      * @see sk.uniba.euromath.api.editor.IEditor#getVisualizedIds()
625      */
626     public Set<String> getVisualizedIds() {
627         return getVisualInfo().getIds();
628     }
629 
630     /***
631      * @param keyHandler
632      *            The keyHandler to set.
633      */
634     protected void setKeyHandler(KeyHandler keyHandler) {
635         this.keyHandler = keyHandler;
636     }
637 
638     public void sortIds(List<String> ids) {
639         Map<Rectangle, String> map = new HashMap<Rectangle, String>();
640         List<Rectangle> visuals = new ArrayList<Rectangle>();
641         // for every id calculate bounding rectangle, store it with id in map
642         // and in visuals
643         for (String id : ids) {
644             if (getVisualInfo().getVisualParts(id).isEmpty())
645                 continue;
646             Rectangle rect = new Rectangle(getVisualInfo().getVisualParts(id)
647                     .get(0).getFigure().getBounds());
648             for (XMLEditPart editPart : getVisualInfo().getVisualParts(id)) {
649                 rect = rect.getUnion(editPart.getFigure().getBounds());
650             }
651             visuals.add(rect);
652             map.put(rect, id);
653         }
654         // sort visuals
655         Collections.sort(visuals, new Comparator<Rectangle>() {
656 
657             /***
658              * Compared are firstly Y-centers of rectangle with gap of 10% of
659              * height of smaller rectangle. If one has Y-center + 10% of height
660              * of smaller rectangle lesser than Y-center of second, is smaller.
661              * If not than their Y-positions are nearly same. So more are
662              * compared by X position. If stil not smaller was determined,
663              * compared are once more by Y-center but without gap. This results
664              * in one smaller or they are equals.
665              */
666             public int compare(Rectangle o1, Rectangle o2) {
667                 if ((o1.x < 0) || (o1.y < 0) || (o1.width < 0)
668                         || (o1.height < 0) || (o2.x < 0) || (o2.y < 0)
669                         || (o2.width < 0) || (o2.height < 0))
670                     throw new IllegalArgumentException();
671                 // 10% Y gap
672                 int yGap = (int) (0.1 * Math.min(o1.height, o2.height));
673 
674                 // comparison by Y-center with Ygap
675                 if (o1.y + o1.height * 0.5 + yGap < o2.y + o2.height * 0.5)
676                     return -1;
677                 if (o2.y + o2.height * 0.5 + yGap < o1.y + o1.height * 0.5)
678                     return 1;
679 
680                 // comparison by X position
681                 if (o1.x < o2.x)
682                     return -1;
683                 if (o2.x < o1.x)
684                     return 1;
685                 // x's are same
686                 // comparison by Y-center without Ygap
687                 if (o1.y + o1.height * 0.5 < o2.y + o2.height * 0.5)
688                     return -1;
689                 if (o2.y + o2.height * 0.5 < o1.y + o1.height * 0.5)
690                     return 1;
691 
692                 // they are equal, exactly this happens nearly never
693                 return 0;
694             }
695         });
696         ids.clear();
697         for (Rectangle rect : visuals) {
698             ids.add(map.get(rect));
699         }
700     }
701 
702 }