|
1 /* |
|
2 * Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 package com.nokia.s60tools.crashanalyser.ui.viewers; |
|
19 |
|
20 import org.eclipse.jface.action.*; |
|
21 import org.eclipse.jface.dialogs.MessageDialog; |
|
22 import org.eclipse.jface.text.IDocument; |
|
23 import org.eclipse.jface.viewers.DoubleClickEvent; |
|
24 import org.eclipse.jface.viewers.IDoubleClickListener; |
|
25 import org.eclipse.jface.viewers.TableViewerColumn; |
|
26 import org.eclipse.swt.widgets.*; |
|
27 import org.eclipse.swt.SWT; |
|
28 import org.eclipse.core.resources.*; |
|
29 import org.eclipse.core.runtime.*; |
|
30 import org.eclipse.ui.*; |
|
31 import org.eclipse.ui.editors.text.*; |
|
32 import org.eclipse.ui.ide.IDE; |
|
33 import org.eclipse.ui.menus.*; |
|
34 import sun.awt.windows.*; |
|
35 import com.nokia.s60tools.crashanalyser.model.*; |
|
36 import com.nokia.s60tools.crashanalyser.resources.*; |
|
37 import com.nokia.s60tools.crashanalyser.containers.*; |
|
38 import com.nokia.s60tools.crashanalyser.ui.console.CrashAnalyserEditorConsole; |
|
39 import com.nokia.s60tools.util.sourcecode.*; |
|
40 import java.util.*; |
|
41 import java.net.URI; |
|
42 import java.io.*; |
|
43 import java.awt.datatransfer.*; |
|
44 import java.awt.Toolkit; |
|
45 |
|
46 /** |
|
47 * Table viewer for call stack table. Call stack table is used in |
|
48 * SummaryPage.java (Crash Data page in Crash Visualiser editor) |
|
49 * |
|
50 */ |
|
51 public class CallStackTableViewer extends CrashAnalyserTableViewer { |
|
52 Action actionCopySelectionToClipboardAsRichText; |
|
53 Action actionCopySelectionToClipboardAsPlainText; |
|
54 Action actionCopyStackToClipboardAsRichText; |
|
55 Action actionCopyStackToClipboardAsPlainText; |
|
56 Action actionOpenSourceFile; |
|
57 Action actionDoubleClick; |
|
58 Table tableControl; |
|
59 |
|
60 public final static int COLUMN_ADDRESS = 0; |
|
61 public final static int COLUMN_SYMBOL = 1; |
|
62 public final static int COLUMN_VALUE = 2; |
|
63 public final static int COLUMN_OFFSET = 3; |
|
64 public final static int COLUMN_OBJECT = 4; |
|
65 public final static int COLUMN_TEXT = 5; |
|
66 |
|
67 MenuManager subMenuSdk; |
|
68 |
|
69 /** |
|
70 * Constructor |
|
71 * @param table table where this viewer is used |
|
72 */ |
|
73 public CallStackTableViewer(Table table) { |
|
74 super(table); |
|
75 |
|
76 tableControl = table; |
|
77 |
|
78 doCreateActions(); |
|
79 doCreateContextMenu(); |
|
80 |
|
81 TableViewerColumn column = new TableViewerColumn(this, SWT.NONE); |
|
82 column.setLabelProvider(labelProvider); |
|
83 column.getColumn().setText(""); |
|
84 |
|
85 column = new TableViewerColumn(this, SWT.NONE); |
|
86 column.setLabelProvider(labelProvider); |
|
87 column.getColumn().setText("Symbol"); |
|
88 |
|
89 column = new TableViewerColumn(this, SWT.NONE); |
|
90 column.setLabelProvider(labelProvider); |
|
91 column.getColumn().setText("Address"); |
|
92 |
|
93 column = new TableViewerColumn(this, SWT.NONE); |
|
94 column.setLabelProvider(labelProvider); |
|
95 column.getColumn().setText("Offset"); |
|
96 |
|
97 column = new TableViewerColumn(this, SWT.NONE); |
|
98 column.setLabelProvider(labelProvider); |
|
99 column.getColumn().setText("Object"); |
|
100 |
|
101 column = new TableViewerColumn(this, SWT.NONE); |
|
102 column.setLabelProvider(labelProvider); |
|
103 column.getColumn().setText("Data"); |
|
104 |
|
105 addDoubleClickListener(new IDoubleClickListener() { |
|
106 public void doubleClick(DoubleClickEvent event) { |
|
107 actionDoubleClick.run(); |
|
108 } |
|
109 }); |
|
110 } |
|
111 |
|
112 /** |
|
113 * Creates pop-up menu actions |
|
114 */ |
|
115 private void doCreateActions() { |
|
116 actionCopySelectionToClipboardAsRichText = new Action("as Rich Text") { |
|
117 public void run() { |
|
118 copyToClipboard(tableControl, true, true); |
|
119 } |
|
120 }; |
|
121 |
|
122 actionCopyStackToClipboardAsRichText = new Action("as Rich Text") { |
|
123 public void run() { |
|
124 copyToClipboard(tableControl, false, true); |
|
125 } |
|
126 }; |
|
127 |
|
128 actionCopySelectionToClipboardAsPlainText = new Action("as Plain Text") { |
|
129 public void run() { |
|
130 copyToClipboard(tableControl, true, false); |
|
131 } |
|
132 }; |
|
133 |
|
134 actionCopyStackToClipboardAsPlainText = new Action("as Plain Text") { |
|
135 public void run() { |
|
136 copyToClipboard(tableControl, false, false); |
|
137 } |
|
138 }; |
|
139 |
|
140 actionOpenSourceFile = new Action("Open Source File") { |
|
141 public void run() { |
|
142 openSourceFile(tableControl); |
|
143 } |
|
144 }; |
|
145 |
|
146 actionDoubleClick = new Action() { |
|
147 public void run() { |
|
148 openSourceFile(tableControl); |
|
149 } |
|
150 }; |
|
151 } |
|
152 |
|
153 /** |
|
154 * Setting the focus in opened file there where the method name occurs, |
|
155 * must call after file is opened and only if opening was successful |
|
156 * @param location |
|
157 * @throws CoreException |
|
158 */ |
|
159 private static void setFocusToLineWhereMethodIs( |
|
160 final SourceFileLocation location) throws CoreException { |
|
161 //Runnable to open new file |
|
162 final IWorkspaceRunnable runSetFocus = new IWorkspaceRunnable() { |
|
163 public void run(IProgressMonitor monitor) throws CoreException { |
|
164 // do the actual work in here |
|
165 |
|
166 try { |
|
167 //Setting focus to correct line |
|
168 IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); |
|
169 IEditorPart activeEditor = activePage.getActiveEditor(); |
|
170 |
|
171 if(activeEditor != null && activeEditor instanceof TextEditor){ |
|
172 |
|
173 // This is actually an instance of 'org.eclipse.cdt.internal.ui.editor.CEditor' |
|
174 // that extends org.eclipse.ui.editors.text.TextEditor |
|
175 TextEditor editor = (TextEditor) activeEditor; |
|
176 IDocument doc = getDocument(editor); |
|
177 if(doc != null){ |
|
178 |
|
179 String text = doc.get(); |
|
180 int methodOffset = location.getMethodOffset(); |
|
181 if(methodOffset == SourceFileLocation.OFFSET_NOT_FOUND){ |
|
182 // Removing parameters and getting new offset. |
|
183 String methodNameWithOutParams = location.getMethodName(); |
|
184 methodNameWithOutParams = methodNameWithOutParams.substring(0, methodNameWithOutParams.indexOf("(")); //$NON-NLS-1$ |
|
185 methodOffset = text.indexOf(methodNameWithOutParams); |
|
186 |
|
187 if(methodOffset == SourceFileLocation.OFFSET_NOT_FOUND){ |
|
188 // Removing possible namespace and getting new offset. |
|
189 String separator = "::"; //$NON-NLS-1$ |
|
190 int separatorLocation = methodNameWithOutParams.lastIndexOf(separator); |
|
191 if(separatorLocation > 0){ |
|
192 methodNameWithOutParams = methodNameWithOutParams.substring(separatorLocation + separator.length()); |
|
193 methodOffset = text.indexOf(methodNameWithOutParams); |
|
194 } |
|
195 } |
|
196 } |
|
197 |
|
198 editor.setHighlightRange(methodOffset, 0, true); |
|
199 } |
|
200 } |
|
201 |
|
202 } catch (Exception e) { |
|
203 e.printStackTrace(); |
|
204 Status status = new Status(IStatus.ERROR, |
|
205 "com.nokia.s60tools.crashanalyser", 0, e //$NON-NLS-1$ |
|
206 .getMessage(), e); |
|
207 |
|
208 throw new CoreException(status); |
|
209 } |
|
210 |
|
211 } |
|
212 }; |
|
213 |
|
214 |
|
215 ResourcesPlugin.getWorkspace().run(runSetFocus, null, IWorkspace.AVOID_UPDATE, null); |
|
216 } |
|
217 |
|
218 /** |
|
219 * Returns the document interface for the currently active document |
|
220 * in the given editor. |
|
221 * @param editor Editor to ask currently active document from. |
|
222 * @return Document interface if found, otherwise <code>null</code>. |
|
223 */ |
|
224 private static IDocument getDocument(TextEditor editor) { |
|
225 |
|
226 TextFileDocumentProvider documentProvider = (TextFileDocumentProvider) editor.getDocumentProvider(); |
|
227 if(documentProvider != null){ |
|
228 return documentProvider.getDocument(editor.getEditorInput()); |
|
229 } |
|
230 return null; |
|
231 } |
|
232 |
|
233 /** |
|
234 * Shows a message box with given message |
|
235 * @param message |
|
236 */ |
|
237 private static void showMessage(String message, Shell shell) { |
|
238 MessageDialog.openInformation( |
|
239 shell, |
|
240 "Crash Analyser", |
|
241 message); |
|
242 } |
|
243 |
|
244 /** |
|
245 * Tries to open source file for the selected line in the call stack table |
|
246 * @param tableControl |
|
247 */ |
|
248 static void openSourceFile(Table tableControl) { |
|
249 try { |
|
250 if (tableControl.getSelection() != null && |
|
251 tableControl.getSelection().length == 1) { |
|
252 StackEntry stackEntry = |
|
253 (StackEntry)tableControl.getSelection()[0].getData(); |
|
254 |
|
255 // we need code segment in order to be able to open source code, |
|
256 // if we don't find code segment, notify user |
|
257 if (stackEntry == null || |
|
258 "".equals(stackEntry.getCodeSegmentName())) { |
|
259 showMessage("CODE SEGMENT NOT FOUND\n\nThe selected row does not contain information from which Code Segment (e.g. dll, exe) its address can be found. Source file cannot be opened.", tableControl.getShell()); |
|
260 return; |
|
261 } |
|
262 |
|
263 String currentSdkName = SourceSdkManager.getCurrentSkdName(); |
|
264 // user must first choose an active SDK. If it has not been chosen, notify user |
|
265 if (currentSdkName == null || "".equals(currentSdkName)) { |
|
266 showMessage("ACTIVE SDK NOT SELECTED.\n\n Please select Active SDK first by right-clicking in the Call Stack table.", tableControl.getShell()); |
|
267 return; |
|
268 } |
|
269 |
|
270 String epocroot = SourceSdkManager.getEpocroot(currentSdkName); |
|
271 // user might have e.g. selected long ago an sdk which is not available anymore |
|
272 if (epocroot == null) { |
|
273 showMessage("INVALID ACTIVE SDK.\n\n Please select a valid Active SDK by right-clicking in the Call Stack table.", tableControl.getShell()); |
|
274 return; |
|
275 } |
|
276 File epoc = new File(epocroot); |
|
277 |
|
278 // epocroot for the selected SDK if not valid, notify user |
|
279 if (!epoc.isDirectory() || !epoc.exists()) { |
|
280 String message = "INVALID EPOCROOT.\n\nThe EPOCROOT (" + |
|
281 epocroot + |
|
282 ") of selected Active SDK ("+ |
|
283 currentSdkName + |
|
284 ") is not valid. Please check your SDK settings from Window > Preferences > Carbide.c++ > SDK Preferences, or select a different Active SDK by right-clicking in the Call Stack table."; |
|
285 showMessage(message, tableControl.getShell()); |
|
286 return; |
|
287 } |
|
288 // currentSdkName is e.g. 'SDK_Name armv5 urel', we need to get out, build and variant |
|
289 int lastSpace = currentSdkName.lastIndexOf(" "); |
|
290 int secondToLastSpace = currentSdkName.lastIndexOf(" ", lastSpace-1); |
|
291 |
|
292 String build = currentSdkName.substring(lastSpace+1, currentSdkName.length()); //e.g. urel |
|
293 String variant = currentSdkName.substring(secondToLastSpace+1, lastSpace); // e.g. armv5 |
|
294 |
|
295 String methodNameAsItsInMapFile = tableControl.getSelection()[0].getText(COLUMN_SYMBOL); |
|
296 String dllName = stackEntry.getCodeSegmentName(); |
|
297 |
|
298 ISourceFinder finder = SourceFinderFactory.createMapSourceFinder(CrashAnalyserEditorConsole.getInstance()); |
|
299 |
|
300 SourceFileLocation location = |
|
301 finder.findSourceFileByMethodName(methodNameAsItsInMapFile, dllName, variant, build, epocroot); |
|
302 |
|
303 File file = new File(location.getSourceFileLocation()); |
|
304 // we could not find the source file, notify user |
|
305 if(file == null || !file.exists()){ |
|
306 showMessage("SOURCE FILE CANNOT BE FOUND\n\n" + |
|
307 file.getName() + |
|
308 " was not found from " + |
|
309 FileOperations.getFolder(location.getSourceFileLocation()) + |
|
310 ". Please make sure you have selected a correct Active SDK by right-clicking in the Call Stack table. It is also possible that you don't have all source files for your Active SDK present. Source file cannot be opened.", |
|
311 tableControl.getShell()); |
|
312 return; |
|
313 } |
|
314 |
|
315 //Create URI to open file |
|
316 String uriStr = location.getSourceFileLocation().replace("\\", "/"); //$NON-NLS-1$ //$NON-NLS-2$ |
|
317 uriStr = "file://" + uriStr; //$NON-NLS-1$ |
|
318 final URI srcURI = new URI(uriStr); |
|
319 |
|
320 //Find default editor for that file |
|
321 IEditorRegistry reg = PlatformUI.getWorkbench().getEditorRegistry(); |
|
322 |
|
323 IEditorDescriptor editor = reg.getDefaultEditor(file.getName()); |
|
324 //We open editor by it's ID |
|
325 final String editorId = editor.getId(); |
|
326 |
|
327 IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); |
|
328 try { |
|
329 IEditorPart part = IDE.openEditor(page, srcURI, editorId, true); |
|
330 if(part != null){ |
|
331 //Set focus to correct line |
|
332 setFocusToLineWhereMethodIs(location); |
|
333 } |
|
334 } catch (PartInitException e) { |
|
335 e.printStackTrace(); |
|
336 } |
|
337 } |
|
338 }catch (CannotFoundFileException ex) { |
|
339 String newLine = System.getProperty("line.separator"); |
|
340 showMessage(ex.getMessage() + newLine + newLine + "Please check that you have selected correct Active SDK, by right-clicking in the Call Stack table.", tableControl.getShell()); |
|
341 } catch (Exception e) { |
|
342 e.printStackTrace(); |
|
343 showMessage("Unable to find source file. Please check your SDK properties.", tableControl.getShell()); |
|
344 } |
|
345 } |
|
346 |
|
347 /** |
|
348 * Copies data from table into clipboard |
|
349 * @param table table from which data is copied from |
|
350 * @param selection if true, only the selected rows are copied, if false, all rows are copied |
|
351 * @param richText if true, data is copied in rich-text format, if false, data is copied in plain text format |
|
352 */ |
|
353 static void copyToClipboard(Table table, boolean selection, boolean richText) { |
|
354 TableItem[] items = null; |
|
355 // copy only selected rows |
|
356 if (selection) |
|
357 items = table.getSelection(); |
|
358 // copy all rows |
|
359 else |
|
360 items = table.getItems(); |
|
361 |
|
362 if (items != null && items.length > 0) { |
|
363 // rich-text format |
|
364 if (richText) { |
|
365 try { |
|
366 String data = HtmlFormatter.formatStackForClipboard(items, table.getColumnCount()); |
|
367 byte[] bytes = convertToHTMLFormat(data); |
|
368 PEBClip clip = new PEBClip(); |
|
369 clip.setData( WDataTransferer.CF_HTML, bytes); |
|
370 } catch (Exception e) { |
|
371 } |
|
372 // plain text format |
|
373 } else { |
|
374 String separator = System.getProperty("line.separator"); |
|
375 String data = ""; |
|
376 for (int i = 0; i < items.length; i++) { |
|
377 TableItem item = items[i]; |
|
378 data += String.format("%-10s %-10s %-6s %-6s %s %s", |
|
379 item.getText(COLUMN_ADDRESS), |
|
380 item.getText(COLUMN_VALUE), |
|
381 item.getText(COLUMN_TEXT), |
|
382 item.getText(COLUMN_OFFSET), |
|
383 item.getText(COLUMN_OBJECT), |
|
384 item.getText(COLUMN_SYMBOL)) + |
|
385 separator; |
|
386 } |
|
387 StringSelection stringSelection = new StringSelection(data); |
|
388 Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); |
|
389 clipboard.setContents(stringSelection, null); |
|
390 } |
|
391 } |
|
392 } |
|
393 |
|
394 /** |
|
395 * surrounds the html with this envelope, ready for |
|
396 * windows clipboard (jdk support can be made better) |
|
397 * <pre> |
|
398 * Version:1.0 |
|
399 * StartHTML:00000000000 |
|
400 * EndHTML:00000000000 |
|
401 * StartFragment:00000000000 |
|
402 * EndFragment:00000000000 |
|
403 * <!--StartFragment--> |
|
404 * ... |
|
405 * <!-- EndFragment-- > |
|
406 * </pre> |
|
407 * We have to return a byte array 'cause in Windows the html needs to be utf-8 |
|
408 * encoded. And because we have to calculate char-offsets, we encode it here. |
|
409 * @param htmlText |
|
410 * @return byte[] |
|
411 */ |
|
412 static byte[] convertToHTMLFormat(String htmlText) { |
|
413 try { |
|
414 String sep = "\r\n"; |
|
415 String header = "Version:1.0"+ sep + |
|
416 "StartHTML:00000000000"+ sep + |
|
417 "EndHTML:00000000000"+ sep + |
|
418 "StartFragment:00000000000"+ sep + |
|
419 "EndFragment:00000000000" + sep; |
|
420 |
|
421 String html = "<!--StartFragment-->\r\n" + htmlText + "<!--EndFragment-->\r\n"; |
|
422 |
|
423 byte[] bHtml = html.getBytes("UTF-8");// encode first 'cause it may grow |
|
424 |
|
425 int headerLen = header.length(); |
|
426 int htmlLen = bHtml.length; |
|
427 |
|
428 StringBuffer buf = new StringBuffer(header); |
|
429 setValue( buf, "StartHTML", headerLen-1); |
|
430 setValue( buf, "EndHTML", headerLen + htmlLen-1); |
|
431 setValue( buf, "StartFragment", headerLen-1); |
|
432 setValue( buf, "EndFragment", headerLen + htmlLen-1); |
|
433 byte[] bHeader = buf.toString().getBytes("UTF-8");// should stay the same (no nonASCII chars in header) |
|
434 |
|
435 byte result[] = new byte[headerLen + htmlLen ]; |
|
436 System.arraycopy(bHeader, 0, result, 0, bHeader.length); |
|
437 System.arraycopy(bHtml, 0, result, bHeader.length, bHtml.length); |
|
438 |
|
439 return result; |
|
440 } catch (Exception e) { |
|
441 return null; |
|
442 } |
|
443 } |
|
444 |
|
445 /** |
|
446 * Replaces name+":00000000000" with name+":xxxxxxxxxxx" where xxx... is the '0' padded value. |
|
447 * Value can't be to long, since maxint can be displayed with 11 digits. If value is below zero |
|
448 * there is enough place (10 for the digits 1 for sign).<br> |
|
449 * If the search is not found nothing is done. |
|
450 * @param src |
|
451 * @param name |
|
452 * @param value |
|
453 */ |
|
454 private static void setValue( StringBuffer src, String name, int value){ |
|
455 int val = value; |
|
456 String search = name+":00000000000"; |
|
457 int pos = src.indexOf(search); |
|
458 if (pos ==-1) return;// not found, do nothing |
|
459 |
|
460 boolean belowZero = val<0; |
|
461 if (belowZero) val = -val; |
|
462 |
|
463 src.replace(pos+search.length()-(val+"").length(), pos+search.length(), val+""); |
|
464 if (belowZero) src.setCharAt(pos+name.length()+1,'-'); // +1 'cause of ':' in "SearchMe:" |
|
465 } |
|
466 |
|
467 /** |
|
468 * creates context menu accordingly to what is selected in the table. |
|
469 */ |
|
470 private void doCreateContextMenu() { |
|
471 |
|
472 // Active SDK menu |
|
473 subMenuSdk = new MenuManager("Active SDK"); |
|
474 subMenuSdk.setRemoveAllWhenShown(true); |
|
475 subMenuSdk.addMenuListener(new IMenuListener() { |
|
476 public void menuAboutToShow(IMenuManager manager) { |
|
477 Map<String, String> sdks = SourceSdkManager.getAllSdks(); |
|
478 String currentSdk = SourceSdkManager.getCurrentSkdName(); |
|
479 // if there are sdks |
|
480 if (sdks != null && !sdks.isEmpty()) { |
|
481 String[] sdkNames = sdks.keySet().toArray(new String[sdks.size()]); |
|
482 java.util.Arrays.sort(sdkNames); |
|
483 // go through all found sdks and and them to pop-up menu |
|
484 for (int i = 0; i < sdkNames.length; i++) { |
|
485 String sdk = sdkNames[i]; |
|
486 CommandContributionItemParameter p = |
|
487 new CommandContributionItemParameter(PlatformUI.getWorkbench().getActiveWorkbenchWindow(), |
|
488 null, |
|
489 "com.nokia.s60tools.crashanalyser.commands.SdkSelection", |
|
490 CommandContributionItem.STYLE_PUSH); |
|
491 p.label = sdk; |
|
492 // if this sdk is selected as Active sdk, draw an circular image to |
|
493 // this menu item to indicate that this is the active sdk |
|
494 if (sdk.equalsIgnoreCase(currentSdk)) { |
|
495 p.icon = ImageResourceManager.getImageDescriptor((ImageKeys.SELECTED_SDK)); |
|
496 } |
|
497 CommandContributionItem item = new CommandContributionItem(p); |
|
498 subMenuSdk.add(item); |
|
499 } |
|
500 } |
|
501 } |
|
502 }); |
|
503 |
|
504 final MenuManager subMenuSelection = new MenuManager("Copy Selection to Clipboard"); |
|
505 subMenuSelection.add(actionCopySelectionToClipboardAsPlainText); |
|
506 subMenuSelection.add(actionCopySelectionToClipboardAsRichText); |
|
507 |
|
508 final MenuManager subMenuFull = new MenuManager("Copy Whole Stack to Clipboard"); |
|
509 subMenuFull.add(actionCopyStackToClipboardAsPlainText); |
|
510 subMenuFull.add(actionCopyStackToClipboardAsRichText); |
|
511 |
|
512 MenuManager manager = new MenuManager("#PopupMenu"); |
|
513 Menu menu = manager.createContextMenu(getControl()); |
|
514 getControl().setMenu(menu); |
|
515 |
|
516 manager.setRemoveAllWhenShown(true); |
|
517 manager.addMenuListener(new IMenuListener() { |
|
518 public void menuAboutToShow(IMenuManager manager) { |
|
519 Table table = (Table)getControl(); |
|
520 if (table.getSelection() != null) { |
|
521 // add 'Open source file' and 'Active SDK' menu items to pop-up menu |
|
522 // only if exactly one row is selected in table and if that |
|
523 // row's symbol column is not empty |
|
524 if (table.getSelection().length == 1 && |
|
525 !"".equals(table.getSelection()[0].getText(COLUMN_SYMBOL))) { |
|
526 manager.add(actionOpenSourceFile); |
|
527 manager.add(subMenuSdk); |
|
528 manager.add(new Separator()); |
|
529 } |
|
530 manager.add(subMenuSelection); |
|
531 manager.add(subMenuFull); |
|
532 } |
|
533 } |
|
534 }); |
|
535 } |
|
536 } |
|
537 |