Home | History | Annotate | Download | only in newxmlfile
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.ide.eclipse.adt.internal.wizards.newxmlfile;
     17 
     18 import static com.android.SdkConstants.FD_RES;
     19 import static com.android.SdkConstants.FD_RES_VALUES;
     20 import static com.android.SdkConstants.RES_QUALIFIER_SEP;
     21 
     22 import com.android.ide.common.rendering.api.ResourceValue;
     23 import com.android.ide.common.res2.ValueXmlHelper;
     24 import com.android.ide.common.resources.LocaleManager;
     25 import com.android.ide.common.resources.ResourceItem;
     26 import com.android.ide.common.resources.configuration.FolderConfiguration;
     27 import com.android.ide.eclipse.adt.AdtPlugin;
     28 import com.android.ide.eclipse.adt.AdtUtils;
     29 import com.android.ide.eclipse.adt.internal.editors.layout.configuration.FlagManager;
     30 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageControl;
     31 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.RenderPreviewManager;
     32 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
     33 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
     34 import com.android.resources.ResourceType;
     35 import com.google.common.base.Charsets;
     36 import com.google.common.collect.Maps;
     37 
     38 import org.eclipse.core.resources.IContainer;
     39 import org.eclipse.core.resources.IFile;
     40 import org.eclipse.core.resources.IFolder;
     41 import org.eclipse.core.resources.IProject;
     42 import org.eclipse.core.resources.IWorkspaceRoot;
     43 import org.eclipse.core.resources.ResourcesPlugin;
     44 import org.eclipse.core.runtime.CoreException;
     45 import org.eclipse.core.runtime.NullProgressMonitor;
     46 import org.eclipse.jface.dialogs.Dialog;
     47 import org.eclipse.jface.dialogs.IDialogConstants;
     48 import org.eclipse.jface.viewers.ArrayContentProvider;
     49 import org.eclipse.jface.viewers.CellEditor;
     50 import org.eclipse.jface.viewers.CellLabelProvider;
     51 import org.eclipse.jface.viewers.ColumnViewer;
     52 import org.eclipse.jface.viewers.EditingSupport;
     53 import org.eclipse.jface.viewers.IBaseLabelProvider;
     54 import org.eclipse.jface.viewers.TableViewer;
     55 import org.eclipse.jface.viewers.TableViewerColumn;
     56 import org.eclipse.jface.viewers.TextCellEditor;
     57 import org.eclipse.jface.viewers.ViewerCell;
     58 import org.eclipse.swt.SWT;
     59 import org.eclipse.swt.events.ControlEvent;
     60 import org.eclipse.swt.events.ControlListener;
     61 import org.eclipse.swt.events.SelectionEvent;
     62 import org.eclipse.swt.events.SelectionListener;
     63 import org.eclipse.swt.events.TraverseEvent;
     64 import org.eclipse.swt.events.TraverseListener;
     65 import org.eclipse.swt.graphics.Image;
     66 import org.eclipse.swt.graphics.Point;
     67 import org.eclipse.swt.graphics.Rectangle;
     68 import org.eclipse.swt.layout.GridData;
     69 import org.eclipse.swt.layout.GridLayout;
     70 import org.eclipse.swt.widgets.Button;
     71 import org.eclipse.swt.widgets.Combo;
     72 import org.eclipse.swt.widgets.Composite;
     73 import org.eclipse.swt.widgets.Control;
     74 import org.eclipse.swt.widgets.Label;
     75 import org.eclipse.swt.widgets.Shell;
     76 import org.eclipse.swt.widgets.Table;
     77 import org.eclipse.swt.widgets.TableColumn;
     78 import org.eclipse.ui.ISharedImages;
     79 import org.eclipse.ui.IWorkbench;
     80 import org.eclipse.ui.PlatformUI;
     81 
     82 import java.io.ByteArrayInputStream;
     83 import java.io.InputStream;
     84 import java.util.ArrayList;
     85 import java.util.Arrays;
     86 import java.util.Collection;
     87 import java.util.Collections;
     88 import java.util.List;
     89 import java.util.Map;
     90 import java.util.Set;
     91 import java.util.SortedSet;
     92 
     93 /**
     94  * Dialog which adds a new translation to the project
     95  */
     96 public class AddTranslationDialog extends Dialog implements ControlListener, SelectionListener,
     97         TraverseListener {
     98     private static final int KEY_COLUMN = 0;
     99     private static final int DEFAULT_TRANSLATION_COLUMN = 1;
    100     private static final int NEW_TRANSLATION_COLUMN = 2;
    101     private final FolderConfiguration mConfiguration = new FolderConfiguration();
    102     private final IProject mProject;
    103     private String mTarget;
    104     private boolean mIgnore;
    105     private Map<String, String> mTranslations;
    106     private Set<String> mExistingLanguages;
    107     private String mSelectedLanguage;
    108     private String mSelectedRegion;
    109 
    110     private Table mTable;
    111     private Combo mLanguageCombo;
    112     private Combo mRegionCombo;
    113     private ImageControl mFlag;
    114     private Label mFile;
    115     private Button mOkButton;
    116     private Composite mErrorPanel;
    117     private Label mErrorLabel;
    118     private MyTableViewer mTableViewer;
    119 
    120     /**
    121      * Creates the dialog.
    122      * @param parentShell the parent shell
    123      * @param project the project to add translations into
    124      */
    125     public AddTranslationDialog(Shell parentShell, IProject project) {
    126         super(parentShell);
    127         setShellStyle(SWT.CLOSE | SWT.RESIZE | SWT.TITLE);
    128         mProject = project;
    129     }
    130 
    131     @Override
    132     protected Control createDialogArea(Composite parent) {
    133         Composite container = (Composite) super.createDialogArea(parent);
    134         GridLayout gl_container = new GridLayout(6, false);
    135         gl_container.horizontalSpacing = 0;
    136         container.setLayout(gl_container);
    137 
    138         Label languageLabel = new Label(container, SWT.NONE);
    139         languageLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
    140         languageLabel.setText("Language:");
    141         mLanguageCombo = new Combo(container, SWT.READ_ONLY);
    142         GridData gd_mLanguageCombo = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
    143         gd_mLanguageCombo.widthHint = 150;
    144         mLanguageCombo.setLayoutData(gd_mLanguageCombo);
    145 
    146         Label regionLabel = new Label(container, SWT.NONE);
    147         GridData gd_regionLabel = new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1);
    148         gd_regionLabel.horizontalIndent = 10;
    149         regionLabel.setLayoutData(gd_regionLabel);
    150         regionLabel.setText("Region:");
    151         mRegionCombo = new Combo(container, SWT.READ_ONLY);
    152         GridData gd_mRegionCombo = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
    153         gd_mRegionCombo.widthHint = 150;
    154         mRegionCombo.setLayoutData(gd_mRegionCombo);
    155         mRegionCombo.setEnabled(false);
    156 
    157         mFlag = new ImageControl(container, SWT.NONE, null);
    158         mFlag.setDisposeImage(false);
    159         GridData gd_mFlag = new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1);
    160         gd_mFlag.exclude = true;
    161         gd_mFlag.widthHint = 32;
    162         gd_mFlag.horizontalIndent = 3;
    163         mFlag.setLayoutData(gd_mFlag);
    164 
    165         mFile = new Label(container, SWT.NONE);
    166         mFile.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1));
    167 
    168         mTableViewer = new MyTableViewer(container, SWT.BORDER | SWT.FULL_SELECTION);
    169         mTable = mTableViewer.getTable();
    170         mTable.setEnabled(false);
    171         mTable.setLinesVisible(true);
    172         mTable.setHeaderVisible(true);
    173         mTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 6, 2));
    174         mTable.addControlListener(this);
    175         mTable.addTraverseListener(this);
    176         // If you have difficulty opening up this form in WindowBuilder and it complains about
    177         // the next line, change the type of the mTableViewer field and the above
    178         // constructor call from MyTableViewer to TableViewer
    179         TableViewerColumn keyViewerColumn = new TableViewerColumn(mTableViewer, SWT.NONE);
    180         TableColumn keyColumn = keyViewerColumn.getColumn();
    181         keyColumn.setWidth(100);
    182         keyColumn.setText("Key");
    183         TableViewerColumn defaultViewerColumn = new TableViewerColumn(mTableViewer, SWT.NONE);
    184         TableColumn defaultColumn = defaultViewerColumn.getColumn();
    185         defaultColumn.setWidth(200);
    186         defaultColumn.setText("Default");
    187         TableViewerColumn translationViewerColumn = new TableViewerColumn(mTableViewer, SWT.NONE);
    188         TableColumn translationColumn = translationViewerColumn.getColumn();
    189         translationColumn.setWidth(200);
    190         translationColumn.setText("New Translation");
    191 
    192         mErrorPanel = new Composite(container, SWT.NONE);
    193         GridData gd_mErrorLabel = new GridData(SWT.FILL, SWT.CENTER, false, false, 6, 1);
    194         gd_mErrorLabel.exclude = true;
    195         mErrorPanel.setLayoutData(gd_mErrorLabel);
    196 
    197         translationViewerColumn.setEditingSupport(new TranslationEditingSupport(mTableViewer));
    198 
    199         fillLanguages();
    200         fillRegions();
    201         fillStrings();
    202         updateColumnWidths();
    203         validatePage();
    204 
    205         mLanguageCombo.addSelectionListener(this);
    206         mRegionCombo.addSelectionListener(this);
    207 
    208         return container;
    209     }
    210 
    211     /** Populates the table with keys and default strings */
    212     private void fillStrings() {
    213         ResourceManager manager = ResourceManager.getInstance();
    214         ProjectResources resources = manager.getProjectResources(mProject);
    215         mExistingLanguages = resources.getLanguages();
    216 
    217         Collection<ResourceItem> items = resources.getResourceItemsOfType(ResourceType.STRING);
    218 
    219         ResourceItem[] array = items.toArray(new ResourceItem[items.size()]);
    220         Arrays.sort(array);
    221 
    222         // TODO: Read in the actual XML files providing the default keys here
    223         // (they can be obtained via ResourceItem.getSourceFileList())
    224         // such that we can read all the attributes associated with each
    225         // item, and if it defines translatable=false, or the filename is
    226         // donottranslate.xml, we can ignore it, and in other cases just
    227         // duplicate all the attributes (such as "formatted=true", or other
    228         // local conventions such as "product=tablet", or "msgid="123123123",
    229         // etc.)
    230 
    231         mTranslations = Maps.newHashMapWithExpectedSize(items.size());
    232         IBaseLabelProvider labelProvider = new CellLabelProvider() {
    233             @Override
    234             public void update(ViewerCell cell) {
    235                 Object element = cell.getElement();
    236                 int index = cell.getColumnIndex();
    237                 ResourceItem item = (ResourceItem) element;
    238                 switch (index) {
    239                     case KEY_COLUMN: {
    240                         // Key
    241                         cell.setText(item.getName());
    242                         return;
    243                     }
    244                     case DEFAULT_TRANSLATION_COLUMN: {
    245                         // Default translation
    246                         ResourceValue value = item.getResourceValue(ResourceType.STRING,
    247                                 mConfiguration, false);
    248 
    249                         if (value != null) {
    250                             cell.setText(value.getValue());
    251                             return;
    252                         }
    253                         break;
    254                     }
    255                     case NEW_TRANSLATION_COLUMN: {
    256                         // New translation
    257                         String translation = mTranslations.get(item.getName());
    258                         if (translation != null) {
    259                             cell.setText(translation);
    260                             return;
    261                         }
    262                         break;
    263                     }
    264                     default:
    265                         assert false : index;
    266                 }
    267                 cell.setText("");
    268             }
    269         };
    270 
    271         mTableViewer.setLabelProvider(labelProvider);
    272         mTableViewer.setContentProvider(new ArrayContentProvider());
    273         mTableViewer.setInput(array);
    274     }
    275 
    276     /** Populate the languages dropdown */
    277     private void fillLanguages() {
    278         List<String> languageCodes = LocaleManager.getLanguageCodes();
    279         List<String> labels = new ArrayList<String>();
    280         for (String code : languageCodes) {
    281             labels.add(code + ": " + LocaleManager.getLanguageName(code)); //$NON-NLS-1$
    282         }
    283         Collections.sort(labels);
    284         labels.add(0, "(Select)");
    285         mLanguageCombo.setItems(labels.toArray(new String[labels.size()]));
    286         mLanguageCombo.select(0);
    287     }
    288 
    289     /** Populate the regions dropdown */
    290     private void fillRegions() {
    291         // TODO: When you switch languages, offer some "default" usable options. For example,
    292         // when you choose English, offer the countries that use English, and so on. Unfortunately
    293         // we don't have good data about this, we'd just need to hardcode a few common cases.
    294         List<String> regionCodes = LocaleManager.getRegionCodes();
    295         List<String> labels = new ArrayList<String>();
    296         for (String code : regionCodes) {
    297             labels.add(code + ": " + LocaleManager.getRegionName(code)); //$NON-NLS-1$
    298         }
    299         Collections.sort(labels);
    300         labels.add(0, "Any");
    301         mRegionCombo.setItems(labels.toArray(new String[labels.size()]));
    302         mRegionCombo.select(0);
    303     }
    304 
    305     /** React to resizing by distributing the space evenly between the last two columns */
    306     private void updateColumnWidths() {
    307         Rectangle r = mTable.getClientArea();
    308         int availableWidth = r.width;
    309         // Distribute all available space to the last two columns
    310         int columnCount = mTable.getColumnCount();
    311         for (int i = 0; i < columnCount; i++) {
    312             TableColumn column = mTable.getColumn(i);
    313             availableWidth -= column.getWidth();
    314         }
    315         if (availableWidth != 0) {
    316             TableColumn column = mTable.getColumn(DEFAULT_TRANSLATION_COLUMN);
    317             column.setWidth(column.getWidth() + availableWidth / 2);
    318             column = mTable.getColumn(NEW_TRANSLATION_COLUMN);
    319             column.setWidth(column.getWidth() + availableWidth / 2 + availableWidth % 2);
    320         }
    321     }
    322 
    323     @Override
    324     protected void createButtonsForButtonBar(Composite parent) {
    325         mOkButton = createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
    326                 // Don't make the OK button default as in most dialogs, since when you press
    327                 // Return thinking you might edit a value it dismisses the dialog instead
    328                 false);
    329         createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
    330         mOkButton.setEnabled(false);
    331 
    332         validatePage();
    333     }
    334 
    335     /**
    336      * Return the initial size of the dialog.
    337      */
    338     @Override
    339     protected Point getInitialSize() {
    340         return new Point(800, 600);
    341     }
    342 
    343     private void updateTarget() {
    344         if (mSelectedLanguage == null) {
    345             mTarget = null;
    346             mFile.setText("");
    347         } else {
    348             String folder = FD_RES + '/' + FD_RES_VALUES + RES_QUALIFIER_SEP + mSelectedLanguage;
    349             if (mSelectedRegion != null) {
    350                 folder = folder + RES_QUALIFIER_SEP + 'r' + mSelectedRegion;
    351             }
    352             mTarget = folder + "/strings.xml"; //$NON-NLS-1$
    353             mFile.setText(String.format("Creating %1$s", mTarget));
    354         }
    355     }
    356 
    357     private void updateFlag() {
    358         if (mSelectedLanguage == null) {
    359             // Nothing selected
    360             ((GridData) mFlag.getLayoutData()).exclude = true;
    361         } else {
    362             FlagManager manager = FlagManager.get();
    363             Image flag = manager.getFlag(mSelectedLanguage, mSelectedRegion);
    364             if (flag != null) {
    365                 ((GridData) mFlag.getLayoutData()).exclude = false;
    366                 mFlag.setImage(flag);
    367             }
    368         }
    369 
    370         mFlag.getParent().layout(true);
    371         mFlag.getParent().redraw();
    372     }
    373 
    374     /** Actually create the new translation file and write it to disk */
    375     private void createTranslation() {
    376         List<String> keys = new ArrayList<String>(mTranslations.keySet());
    377         Collections.sort(keys);
    378 
    379         StringBuilder sb = new StringBuilder(keys.size() * 120);
    380         sb.append("<resources>\n\n");          //$NON-NLS-1$
    381         for (String key : keys) {
    382             String value = mTranslations.get(key);
    383             if (value == null || value.trim().isEmpty()) {
    384                 continue;
    385             }
    386             sb.append("    <string name=\"");  //$NON-NLS-1$
    387             sb.append(key);
    388             sb.append("\">");                  //$NON-NLS-1$
    389             sb.append(ValueXmlHelper.escapeResourceString(value));
    390             sb.append("</string>\n");          //$NON-NLS-1$
    391         }
    392         sb.append("\n</resources>");           //$NON-NLS-1$
    393 
    394         IFile file = mProject.getFile(mTarget);
    395 
    396         try {
    397             IContainer parent = file.getParent();
    398             AdtUtils.ensureExists(parent);
    399             InputStream source = new ByteArrayInputStream(sb.toString().getBytes(Charsets.UTF_8));
    400             file.create(source, true, new NullProgressMonitor());
    401             AdtPlugin.openFile(file, null, true /*showEditorTab*/);
    402 
    403             // Ensure that the project resources updates itself to notice the new language.
    404             // In theory, this shouldn't be necessary.
    405             ResourceManager manager = ResourceManager.getInstance();
    406             IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
    407             IFolder folder = root.getFolder(parent.getFullPath());
    408             manager.getResourceFolder(folder);
    409             RenderPreviewManager.bumpRevision();
    410         } catch (CoreException e) {
    411             AdtPlugin.log(e, null);
    412         }
    413     }
    414 
    415     private void validatePage() {
    416         if (mOkButton == null) { // Early initialization
    417             return;
    418         }
    419 
    420         String message = null;
    421 
    422         if (mSelectedLanguage == null) {
    423             message = "Select a language";
    424         } else if (mExistingLanguages.contains(mSelectedLanguage)) {
    425             if (mSelectedRegion == null) {
    426                 message = String.format("%1$s is already translated in this project",
    427                         LocaleManager.getLanguageName(mSelectedLanguage));
    428             } else {
    429                 ResourceManager manager = ResourceManager.getInstance();
    430                 ProjectResources resources = manager.getProjectResources(mProject);
    431                 SortedSet<String> regions = resources.getRegions(mSelectedLanguage);
    432                 if (regions.contains(mSelectedRegion)) {
    433                     message = String.format("%1$s (%2$s) is already translated in this project",
    434                             LocaleManager.getLanguageName(mSelectedLanguage),
    435                             LocaleManager.getRegionName(mSelectedRegion));
    436                 }
    437             }
    438         } else {
    439             // Require all strings to be translated? No, some of these may not
    440             // be translatable (e.g. translatable=false, defined in donottranslate.xml, etc.)
    441             //int missing = mTable.getItemCount() - mTranslations.values().size();
    442             //if (missing > 0) {
    443             //    message = String.format("Missing %1$d translations", missing);
    444             //}
    445         }
    446 
    447         boolean valid = message == null;
    448         mTable.setEnabled(message == null);
    449         mOkButton.setEnabled(valid);
    450         showError(message);
    451     }
    452 
    453     private void showError(String error) {
    454         GridData data = (GridData) mErrorPanel.getLayoutData();
    455 
    456         boolean show = error != null;
    457         if (show == data.exclude) {
    458             if (show) {
    459                 if (mErrorLabel == null) {
    460                     mErrorPanel.setLayout(new GridLayout(2, false));
    461                     IWorkbench workbench = PlatformUI.getWorkbench();
    462                     ISharedImages sharedImages = workbench.getSharedImages();
    463                     String iconName = ISharedImages.IMG_OBJS_ERROR_TSK;
    464                     Image image = sharedImages.getImage(iconName);
    465                     @SuppressWarnings("unused")
    466                     ImageControl icon = new ImageControl(mErrorPanel, SWT.NONE, image);
    467 
    468                     mErrorLabel = new Label(mErrorPanel, SWT.NONE);
    469                     mErrorLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false,
    470                             1, 1));
    471                 }
    472                 mErrorLabel.setText(error);
    473             }
    474             data.exclude = !show;
    475             mErrorPanel.getParent().layout(true);
    476         }
    477     }
    478 
    479     @Override
    480     protected void okPressed() {
    481         mTableViewer.applyEditorValue();
    482 
    483         super.okPressed();
    484         createTranslation();
    485     }
    486 
    487     // ---- Implements ControlListener ----
    488 
    489     @Override
    490     public void controlMoved(ControlEvent e) {
    491     }
    492 
    493     @Override
    494     public void controlResized(ControlEvent e) {
    495         if (mIgnore) {
    496             return;
    497         }
    498 
    499         updateColumnWidths();
    500     }
    501 
    502     // ---- Implements SelectionListener ----
    503 
    504     @Override
    505     public void widgetSelected(SelectionEvent e) {
    506         if (mIgnore) {
    507             return;
    508         }
    509 
    510         Object source = e.getSource();
    511         if (source == mLanguageCombo) {
    512             try {
    513                 mIgnore = true;
    514                 mRegionCombo.select(0);
    515                 mSelectedRegion = null;
    516             } finally {
    517                 mIgnore = false;
    518             }
    519 
    520             int languageIndex = mLanguageCombo.getSelectionIndex();
    521             if (languageIndex == 0) {
    522                 mSelectedLanguage = null;
    523                 mRegionCombo.setEnabled(false);
    524             } else {
    525                 // This depends on the label format
    526                 mSelectedLanguage = mLanguageCombo.getItem(languageIndex).substring(0, 2);
    527                 mRegionCombo.setEnabled(true);
    528             }
    529 
    530             updateTarget();
    531             updateFlag();
    532         } else if (source == mRegionCombo) {
    533             int regionIndex = mRegionCombo.getSelectionIndex();
    534             if (regionIndex == 0) {
    535                 mSelectedRegion = null;
    536             } else {
    537                 mSelectedRegion = mRegionCombo.getItem(regionIndex).substring(0, 2);
    538             }
    539 
    540             updateTarget();
    541             updateFlag();
    542         }
    543 
    544         try {
    545             mIgnore = true;
    546             validatePage();
    547         } finally {
    548             mIgnore = false;
    549         }
    550     }
    551 
    552     @Override
    553     public void widgetDefaultSelected(SelectionEvent e) {
    554     }
    555 
    556     // ---- TraverseListener ----
    557 
    558     @Override
    559     public void keyTraversed(TraverseEvent e) {
    560         // If you press Return and we're not cell editing, start editing the current row
    561         if (e.detail == SWT.TRAVERSE_RETURN && !mTableViewer.isCellEditorActive()) {
    562             int index = mTable.getSelectionIndex();
    563             if (index != -1) {
    564                 Object next = mTable.getItem(index).getData();
    565                 mTableViewer.editElement(next, NEW_TRANSLATION_COLUMN);
    566             }
    567         }
    568     }
    569 
    570     /** Editing support for the translation column */
    571     private class TranslationEditingSupport extends EditingSupport {
    572         /**
    573          * When true, setValue is being called as part of a default action
    574          * (e.g. Return), not due to focus loss
    575          */
    576         private boolean mDefaultAction;
    577 
    578         private TranslationEditingSupport(ColumnViewer viewer) {
    579             super(viewer);
    580         }
    581 
    582         @Override
    583         protected void setValue(Object element, Object value) {
    584             ResourceItem item = (ResourceItem) element;
    585             mTranslations.put(item.getName(), value.toString());
    586             mTableViewer.update(element, null);
    587             validatePage();
    588 
    589             // If the user is pressing Return to finish editing a value (which is
    590             // not the only way this method can get called - for example, if you click
    591             // outside the cell while editing, the focus loss will also result in
    592             // this method getting called), then mDefaultAction is true, and we automatically
    593             // start editing the next row.
    594             if (mDefaultAction) {
    595                 mTable.getDisplay().asyncExec(new Runnable() {
    596                     @Override
    597                     public void run() {
    598                         if (!mTable.isDisposed() && !mTableViewer.isCellEditorActive()) {
    599                             int index = mTable.getSelectionIndex();
    600                             if (index != -1 && index < mTable.getItemCount() - 1) {
    601                                 Object next = mTable.getItem(index + 1).getData();
    602                                 mTableViewer.editElement(next, NEW_TRANSLATION_COLUMN);
    603                             }
    604                         }
    605                     }
    606                 });
    607             }
    608         }
    609 
    610         @Override
    611         protected Object getValue(Object element) {
    612             ResourceItem item = (ResourceItem) element;
    613             String value = mTranslations.get(item.getName());
    614             if (value == null) {
    615                 return "";
    616             }
    617             return value;
    618         }
    619 
    620         @Override
    621         protected CellEditor getCellEditor(Object element) {
    622             return new TextCellEditor(mTable) {
    623                 @Override
    624                 protected void handleDefaultSelection(SelectionEvent event) {
    625                     try {
    626                         mDefaultAction = true;
    627                         super.handleDefaultSelection(event);
    628                     } finally {
    629                         mDefaultAction = false;
    630                     }
    631                 }
    632             };
    633         }
    634 
    635         @Override
    636         protected boolean canEdit(Object element) {
    637             return true;
    638         }
    639     }
    640 
    641     private class MyTableViewer extends TableViewer {
    642         public MyTableViewer(Composite parent, int style) {
    643             super(parent, style);
    644         }
    645 
    646         // Make this public so we can call it to ensure values are applied before the dialog
    647         // is dismissed in {@link #okPressed}
    648         @Override
    649         public void applyEditorValue() {
    650             super.applyEditorValue();
    651         }
    652     }
    653 }
    654