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