Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.babel.editor/src/org/eclipse/babel/editor/internal/AbstractMessagesEditor.java')
-rwxr-xr-xorg.eclipse.babel.editor/src/org/eclipse/babel/editor/internal/AbstractMessagesEditor.java614
1 files changed, 614 insertions, 0 deletions
diff --git a/org.eclipse.babel.editor/src/org/eclipse/babel/editor/internal/AbstractMessagesEditor.java b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/internal/AbstractMessagesEditor.java
new file mode 100755
index 0000000..75a72ea
--- /dev/null
+++ b/org.eclipse.babel.editor/src/org/eclipse/babel/editor/internal/AbstractMessagesEditor.java
@@ -0,0 +1,614 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Pascal Essiembre.
+ * 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:
+ * Pascal Essiembre - initial API and implementation
+ * Alexej Strelzow - TapJI integration, bug fixes & enhancements
+ * - issue 35, 36, 48, 73
+ ******************************************************************************/
+package org.eclipse.babel.editor.internal;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+
+import org.eclipse.babel.core.message.IMessagesBundle;
+import org.eclipse.babel.core.message.internal.IMessagesBundleGroupListener;
+import org.eclipse.babel.core.message.internal.IMessagesBundleListener;
+import org.eclipse.babel.core.message.internal.MessageException;
+import org.eclipse.babel.core.message.internal.MessagesBundle;
+import org.eclipse.babel.core.message.internal.MessagesBundleGroup;
+import org.eclipse.babel.core.message.internal.MessagesBundleGroupAdapter;
+import org.eclipse.babel.core.message.manager.RBManager;
+import org.eclipse.babel.core.message.resource.IMessagesResource;
+import org.eclipse.babel.core.message.tree.internal.AbstractKeyTreeModel;
+import org.eclipse.babel.editor.IMessagesEditor;
+import org.eclipse.babel.editor.IMessagesEditorChangeListener;
+import org.eclipse.babel.editor.builder.ToggleNatureAction;
+import org.eclipse.babel.editor.bundle.MessagesBundleGroupFactory;
+import org.eclipse.babel.editor.i18n.I18NPage;
+import org.eclipse.babel.editor.plugin.MessagesEditorPlugin;
+import org.eclipse.babel.editor.preferences.MsgEditorPreferences;
+import org.eclipse.babel.editor.resource.EclipsePropertiesEditorResource;
+import org.eclipse.babel.editor.util.UIUtils;
+import org.eclipse.babel.editor.views.MessagesBundleGroupOutline;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.dialogs.ErrorDialog;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorReference;
+import org.eclipse.ui.IEditorSite;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.editors.text.TextEditor;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.ide.IGotoMarker;
+import org.eclipse.ui.part.FileEditorInput;
+import org.eclipse.ui.part.MultiPageEditorPart;
+import org.eclipse.ui.texteditor.ITextEditor;
+import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
+
+/**
+ * Multi-page editor for editing resource bundles.
+ */
+public abstract class AbstractMessagesEditor extends MultiPageEditorPart
+ implements IGotoMarker, IMessagesEditor {
+
+ /** Editor ID, as defined in plugin.xml. */
+ public static final String EDITOR_ID = "org.eclilpse.babel.editor.editor.MessagesEditor"; //$NON-NLS-1$
+
+ protected String selectedKey;
+ protected List<IMessagesEditorChangeListener> changeListeners = new ArrayList<IMessagesEditorChangeListener>(
+ 2);
+
+ /** MessagesBundle group. */
+ protected MessagesBundleGroup messagesBundleGroup;
+
+ /** Page with key tree and text fields for all locales. */
+ protected I18NPage i18nPage;
+ protected final List<Locale> localesIndex = new ArrayList<Locale>();
+ protected final List<ITextEditor> textEditorsIndex = new ArrayList<ITextEditor>();
+
+ protected MessagesBundleGroupOutline outline;
+
+ protected MessagesEditorMarkers markers;
+
+ protected AbstractKeyTreeModel keyTreeModel;
+
+ protected IFile file; // init
+
+ protected boolean updateSelectedKey;
+
+ /**
+ * Creates a multi-page editor example.
+ */
+ public AbstractMessagesEditor() {
+ super();
+ outline = new MessagesBundleGroupOutline(this);
+ }
+
+ public MessagesEditorMarkers getMarkers() {
+ return markers;
+ }
+
+ private IPropertyChangeListener preferenceListener;
+
+ /**
+ * The <code>MultiPageEditorExample</code> implementation of this method
+ * checks that the input is an instance of <code>IFileEditorInput</code>.
+ */
+ @Override
+ public void init(IEditorSite site, IEditorInput editorInput)
+ throws PartInitException {
+
+ if (editorInput instanceof IFileEditorInput) {
+ file = ((IFileEditorInput) editorInput).getFile();
+ if (MsgEditorPreferences.getInstance()
+ .isBuilderSetupAutomatically()) {
+ IProject p = file.getProject();
+ if (p != null && p.isAccessible()) {
+ ToggleNatureAction
+ .addOrRemoveNatureOnProject(p, true, true);
+ }
+ }
+ try {
+ messagesBundleGroup = MessagesBundleGroupFactory
+ .createBundleGroup(site, file);
+ } catch (MessageException e) {
+ throw new PartInitException("Cannot create bundle group.", e); //$NON-NLS-1$
+ }
+ messagesBundleGroup
+ .addMessagesBundleGroupListener(getMsgBundleGroupListner());
+ markers = new MessagesEditorMarkers(messagesBundleGroup);
+ setPartName(messagesBundleGroup.getName());
+ setTitleImage(UIUtils.getImage(UIUtils.IMAGE_RESOURCE_BUNDLE));
+ closeIfAreadyOpen(site, file);
+ super.init(site, editorInput);
+ // TODO figure out model to use based on preferences
+ keyTreeModel = new AbstractKeyTreeModel(messagesBundleGroup);
+ // markerManager = new RBEMarkerManager(this);
+ } else {
+ throw new PartInitException(
+ "Invalid Input: Must be IFileEditorInput"); //$NON-NLS-1$
+ }
+ initRAP();
+ }
+
+ // public RBEMarkerManager getMarkerManager() {
+ // return markerManager;
+ // }
+
+ /**
+ * Creates the pages of the multi-page editor.
+ */
+ @Override
+ protected void createPages() {
+ // Create I18N page
+ i18nPage = new I18NPage(getContainer(), SWT.NONE, this);
+ int index = addPage(i18nPage);
+ setPageText(index, MessagesEditorPlugin.getString("editor.properties")); //$NON-NLS-1$
+ setPageImage(index, UIUtils.getImage(UIUtils.IMAGE_RESOURCE_BUNDLE));
+
+ // Create text editor pages for each locales
+ Locale[] locales = messagesBundleGroup.getLocales();
+ // first: sort the locales.
+ UIUtils.sortLocales(locales);
+ // second: filter+sort them according to the filter preferences.
+ locales = UIUtils.filterLocales(locales);
+ for (int i = 0; i < locales.length; i++) {
+ Locale locale = locales[i];
+ MessagesBundle messagesBundle = (MessagesBundle) messagesBundleGroup
+ .getMessagesBundle(locale);
+ createMessagesBundlePage(messagesBundle);
+ }
+ }
+
+ /**
+ * Creates a new text editor for the messages bundle, which gets added to a new page
+ */
+ protected void createMessagesBundlePage(MessagesBundle messagesBundle) {
+ try {
+ IMessagesResource resource = messagesBundle.getResource();
+ final TextEditor textEditor = (TextEditor) resource.getSource();
+ int index = addPage(textEditor, textEditor.getEditorInput());
+ setPageText(index,
+ UIUtils.getDisplayName(messagesBundle.getLocale()));
+ setPageImage(index, UIUtils.getImage(UIUtils.IMAGE_PROPERTIES_FILE));
+ localesIndex.add(messagesBundle.getLocale());
+ textEditorsIndex.add(textEditor);
+ } catch (PartInitException e) {
+ ErrorDialog.openError(getSite().getShell(),
+ "Error creating text editor page.", //$NON-NLS-1$
+ null, e.getStatus());
+ }
+ }
+
+ /**
+ * Adds a new messages bundle to an opened messages editor. Creates a new text edtor page
+ * and a new entry in the i18n page for the given locale and messages bundle.
+ */
+ protected void addMessagesBundle(MessagesBundle messagesBundle) {
+ createMessagesBundlePage(messagesBundle);
+ i18nPage.addI18NEntry(messagesBundle.getLocale());
+ }
+
+ /**
+ * Removes the text editor page + the entry from the i18n page of the given locale and messages bundle.
+ */
+ protected void removeMessagesBundle(MessagesBundle messagesBundle) {
+ IMessagesResource resource = messagesBundle.getResource();
+ final TextEditor textEditor = (TextEditor) resource.getSource();
+ // index + 1 because of i18n page
+ int pageIndex = textEditorsIndex.indexOf(textEditor) + 1;
+ removePage(pageIndex);
+
+ textEditorsIndex.remove(textEditor);
+ localesIndex.remove(messagesBundle.getLocale());
+
+ textEditor.dispose();
+
+ // remove entry from i18n page
+ i18nPage.removeI18NEntry(messagesBundle.getLocale());
+ }
+
+ /**
+ * Called when the editor's pages need to be reloaded. For example when the
+ * filters of locale is changed.
+ * <p>
+ * Currently this only reloads the index page. TODO: remove and add the new
+ * locales? it actually looks quite hard to do.
+ * </p>
+ */
+ public void reloadDisplayedContents() {
+ super.removePage(0);
+ int currentlyActivePage = super.getActivePage();
+ i18nPage.dispose();
+ i18nPage = new I18NPage(getContainer(), SWT.NONE, this);
+ super.addPage(0, i18nPage);
+ if (currentlyActivePage == 0) {
+ super.setActivePage(currentlyActivePage);
+ }
+ }
+
+ /**
+ * Saves the multi-page editor's document.
+ */
+ @Override
+ public void doSave(IProgressMonitor monitor) {
+ for (ITextEditor textEditor : textEditorsIndex) {
+ textEditor.doSave(monitor);
+ }
+
+ try { // [alst] remove in near future
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ updateSelectedKey = true;
+
+ RBManager instance = RBManager.getInstance(messagesBundleGroup
+ .getProjectName());
+
+ refreshKeyTreeModel(); // keeps editor and I18NPage in sync
+
+ instance.fireEditorSaved();
+
+ // // maybe new init?
+ }
+
+ protected void refreshKeyTreeModel() {
+ String selectedKey = getSelectedKey(); // memorize
+
+ if (messagesBundleGroup == null) {
+ messagesBundleGroup = MessagesBundleGroupFactory.createBundleGroup(
+ (IEditorSite) getSite(), file);
+ }
+
+ AbstractKeyTreeModel oldModel = this.keyTreeModel;
+ this.keyTreeModel = new AbstractKeyTreeModel(messagesBundleGroup);
+
+ for (IMessagesEditorChangeListener listener : changeListeners) {
+ listener.keyTreeModelChanged(oldModel, this.keyTreeModel);
+ }
+
+ i18nPage.getTreeViewer().expandAll();
+
+ if (selectedKey != null) {
+ setSelectedKey(selectedKey);
+ }
+ }
+
+ /**
+ * @see org.eclipse.ui.ISaveablePart#doSaveAs()
+ */
+ @Override
+ public void doSaveAs() {
+ // Save As not allowed.
+ }
+
+ /**
+ * @see org.eclipse.ui.ISaveablePart#isSaveAsAllowed()
+ */
+ @Override
+ public boolean isSaveAsAllowed() {
+ return false;
+ }
+
+ /**
+ * Change current page based on locale. If there is no editors associated
+ * with current locale, do nothing.
+ *
+ * @param locale
+ * locale used to identify the page to change to
+ */
+ public void setActivePage(Locale locale) {
+ int index = localesIndex.indexOf(locale);
+ if (index > -1) {
+ setActivePage(index + 1);
+ }
+ }
+
+ /**
+ * @see org.eclipse.ui.ide.IGotoMarker#gotoMarker(org.eclipse.core.resources.IMarker)
+ */
+ public void gotoMarker(IMarker marker) {
+ // String key = marker.getAttribute(RBEMarker.KEY, "");
+ // if (key != null && key.length() > 0) {
+ // setActivePage(0);
+ // setSelectedKey(key);
+ // getI18NPage().selectLocale(BabelUtils.parseLocale(
+ // marker.getAttribute(RBEMarker.LOCALE, "")));
+ // } else {
+ IResource resource = marker.getResource();
+ Locale[] locales = messagesBundleGroup.getLocales();
+ for (int i = 0; i < locales.length; i++) {
+ IMessagesResource messagesResource = ((MessagesBundle) messagesBundleGroup
+ .getMessagesBundle(locales[i])).getResource();
+ if (messagesResource instanceof EclipsePropertiesEditorResource) {
+ EclipsePropertiesEditorResource propFile = (EclipsePropertiesEditorResource) messagesResource;
+ if (resource.equals(propFile.getResource())) {
+ // ok we got the locale.
+ // try to open the master i18n page and select the
+ // corresponding key.
+ try {
+ String key = (String) marker
+ .getAttribute(IMarker.LOCATION);
+ if (key != null && key.length() > 0) {
+ getI18NPage().selectLocale(locales[i]);
+ setActivePage(0);
+ setSelectedKey(key);
+ return;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();// something better.s
+ }
+ // it did not work... fall back to the text editor.
+ setActivePage(locales[i]);
+ IDE.gotoMarker((IEditorPart) propFile.getSource(), marker);
+ break;
+ }
+ }
+ }
+ // }
+ }
+
+ /**
+ * Calculates the contents of page GUI page when it is activated.
+ */
+ @Override
+ protected void pageChange(int newPageIndex) {
+ super.pageChange(newPageIndex);
+ if (newPageIndex != 0) { // if we just want the default page -> == 1
+ setSelection(newPageIndex);
+ } else if (newPageIndex == 0 && updateSelectedKey) {
+ // TODO: find better way
+ for (IMessagesBundle bundle : messagesBundleGroup
+ .getMessagesBundles()) {
+ RBManager.getInstance(messagesBundleGroup.getProjectName())
+ .fireResourceChanged(bundle);
+ }
+ updateSelectedKey = false;
+ }
+
+ // if (newPageIndex == 0) {
+ // resourceMediator.reloadProperties();
+ // i18nPage.refreshTextBoxes();
+ // }
+ }
+
+ private void setSelection(int newPageIndex) {
+ ITextEditor editor = textEditorsIndex.get(--newPageIndex);
+ String selectedKey = getSelectedKey();
+ if (selectedKey != null) {
+ if (editor.getEditorInput() instanceof FileEditorInput) {
+ FileEditorInput input = (FileEditorInput) editor
+ .getEditorInput();
+ try {
+ IFile file = input.getFile();
+ file.refreshLocal(IResource.DEPTH_ZERO, null);
+ BufferedReader reader = new BufferedReader(
+ new InputStreamReader(file.getContents()));
+ String line = "";
+ int selectionIndex = 0;
+ boolean found = false;
+
+ while ((line = reader.readLine()) != null) {
+ int index = line.indexOf('=');
+ if (index != -1) {
+ if (selectedKey.equals(line.substring(0, index)
+ .trim())) {
+ found = true;
+ break;
+ }
+ }
+ selectionIndex += line.length() + 2; // + \r\n
+ }
+
+ if (found) {
+ editor.selectAndReveal(selectionIndex, 0);
+ }
+ } catch (CoreException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Is the given file a member of this resource bundle.
+ *
+ * @param file
+ * file to test
+ * @return <code>true</code> if file is part of bundle
+ */
+ public boolean isBundleMember(IFile file) {
+ // return resourceMediator.isResource(file);
+ return false;
+ }
+
+ protected void closeIfAreadyOpen(IEditorSite site, IFile file) {
+ IWorkbenchPage[] pages = site.getWorkbenchWindow().getPages();
+ for (int i = 0; i < pages.length; i++) {
+ IWorkbenchPage page = pages[i];
+ IEditorReference[] editors = page.getEditorReferences();
+ for (int j = 0; j < editors.length; j++) {
+ IEditorPart editor = editors[j].getEditor(false);
+ if (editor instanceof AbstractMessagesEditor) {
+ AbstractMessagesEditor rbe = (AbstractMessagesEditor) editor;
+ if (rbe.isBundleMember(file)) {
+ page.closeEditor(editor, true);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * @see org.eclipse.ui.IWorkbenchPart#dispose()
+ */
+ @Override
+ public void dispose() {
+ for (IMessagesEditorChangeListener listener : changeListeners) {
+ listener.editorDisposed();
+ }
+ i18nPage.dispose();
+ for (ITextEditor textEditor : textEditorsIndex) {
+ textEditor.dispose();
+ }
+
+ disposeRAP();
+ }
+
+ /**
+ * @return Returns the selectedKey.
+ */
+ public String getSelectedKey() {
+ return selectedKey;
+ }
+
+ /**
+ * @param selectedKey
+ * The selectedKey to set.
+ */
+ public void setSelectedKey(String activeKey) {
+ if ((selectedKey == null && activeKey != null)
+ || (selectedKey != null && activeKey == null)
+ || (selectedKey != null && !selectedKey.equals(activeKey))) {
+ String oldKey = this.selectedKey;
+ this.selectedKey = activeKey;
+ for (IMessagesEditorChangeListener listener : changeListeners) {
+ listener.selectedKeyChanged(oldKey, activeKey);
+ }
+ }
+ }
+
+ public void addChangeListener(IMessagesEditorChangeListener listener) {
+ changeListeners.add(0, listener);
+ }
+
+ public void removeChangeListener(IMessagesEditorChangeListener listener) {
+ changeListeners.remove(listener);
+ }
+
+ public Collection<IMessagesEditorChangeListener> getChangeListeners() {
+ return changeListeners;
+ }
+
+ /**
+ * @return Returns the messagesBundleGroup.
+ */
+ public MessagesBundleGroup getBundleGroup() {
+ return messagesBundleGroup;
+ }
+
+ /**
+ * @return Returns the keyTreeModel.
+ */
+ public AbstractKeyTreeModel getKeyTreeModel() {
+ return keyTreeModel;
+ }
+
+ /**
+ * @param keyTreeModel
+ * The keyTreeModel to set.
+ */
+ public void setKeyTreeModel(AbstractKeyTreeModel newKeyTreeModel) {
+ if ((this.keyTreeModel == null && newKeyTreeModel != null)
+ || (keyTreeModel != null && newKeyTreeModel == null)
+ || (!keyTreeModel.equals(newKeyTreeModel))) {
+ AbstractKeyTreeModel oldModel = this.keyTreeModel;
+ this.keyTreeModel = newKeyTreeModel;
+ for (IMessagesEditorChangeListener listener : changeListeners) {
+ listener.keyTreeModelChanged(oldModel, newKeyTreeModel);
+ }
+ }
+ }
+
+ public I18NPage getI18NPage() {
+ return i18nPage;
+ }
+
+ /**
+ * one of the SHOW_* constants defined in the
+ * {@link IMessagesEditorChangeListener}
+ */
+ private int showOnlyMissingAndUnusedKeys = IMessagesEditorChangeListener.SHOW_ALL;
+
+ /**
+ * @return true when only unused and missing keys should be displayed. flase
+ * by default.
+ */
+ public int isShowOnlyUnusedAndMissingKeys() {
+ return showOnlyMissingAndUnusedKeys;
+ }
+
+ public void setShowOnlyUnusedMissingKeys(int showFlag) {
+ showOnlyMissingAndUnusedKeys = showFlag;
+ for (IMessagesEditorChangeListener listener : getChangeListeners()) {
+ listener.showOnlyUnusedAndMissingChanged(showFlag);
+ }
+ }
+
+ @Override
+ public Object getAdapter(Class adapter) {
+ Object obj = super.getAdapter(adapter);
+ if (obj == null) {
+ if (IContentOutlinePage.class.equals(adapter)) {
+ return (outline);
+ }
+ }
+ return (obj);
+ }
+
+ public ITextEditor getTextEditor(Locale locale) {
+ int index = localesIndex.indexOf(locale);
+ return textEditorsIndex.get(index);
+ }
+
+ // Needed for RAP, otherwise super implementation of getTitleImage always
+ // returns
+ // same image with same device and same session context, and when this
+ // session ends
+ // -> NPE at org.eclipse.swt.graphics.Image.getImageData(Image.java:348)
+ @Override
+ public Image getTitleImage() {
+ // create new image with current display
+ return UIUtils.getImageDescriptor(UIUtils.IMAGE_RESOURCE_BUNDLE)
+ .createImage();
+ }
+
+ public void setTitleName(String name) {
+ setPartName(name);
+ }
+
+ abstract public void setEnabled(boolean enabled);
+
+ abstract protected void initRAP();
+
+ abstract protected void disposeRAP();
+
+ abstract protected IMessagesBundleGroupListener getMsgBundleGroupListner();
+}

Back to the top