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