1 /******************************************************************************* 2 * Copyright (c) 2003, 2018 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Lars Vogel <Lars.Vogel@gmail.com> - Bug 430694 14 * Christian Georgi (SAP) - Bug 432480 15 * Patrik Suzzi <psuzzi@gmail.com> - Bug 490700, 502050 16 * Vasili Gulevich - Bug 501404 17 *******************************************************************************/ 18 package org.eclipse.ui.internal.ide.application; 19 20 import java.lang.reflect.InvocationTargetException; 21 import java.net.URL; 22 import java.text.Collator; 23 import java.util.ArrayList; 24 import java.util.Map; 25 import java.util.TreeMap; 26 27 import org.eclipse.core.internal.resources.Workspace; 28 import org.eclipse.core.net.proxy.IProxyService; 29 import org.eclipse.core.resources.IContainer; 30 import org.eclipse.core.resources.IResource; 31 import org.eclipse.core.resources.IWorkspace; 32 import org.eclipse.core.resources.ResourcesPlugin; 33 import org.eclipse.core.resources.WorkspaceJob; 34 import org.eclipse.core.runtime.CoreException; 35 import org.eclipse.core.runtime.FileLocator; 36 import org.eclipse.core.runtime.IAdaptable; 37 import org.eclipse.core.runtime.IBundleGroup; 38 import org.eclipse.core.runtime.IBundleGroupProvider; 39 import org.eclipse.core.runtime.IPath; 40 import org.eclipse.core.runtime.IProgressMonitor; 41 import org.eclipse.core.runtime.IStatus; 42 import org.eclipse.core.runtime.MultiStatus; 43 import org.eclipse.core.runtime.Path; 44 import org.eclipse.core.runtime.Platform; 45 import org.eclipse.core.runtime.ProgressMonitorWrapper; 46 import org.eclipse.core.runtime.Status; 47 import org.eclipse.core.runtime.jobs.ISchedulingRule; 48 import org.eclipse.core.runtime.jobs.Job; 49 import org.eclipse.e4.core.contexts.IEclipseContext; 50 import org.eclipse.e4.ui.internal.workbench.E4Workbench; 51 import org.eclipse.jface.dialogs.ErrorDialog; 52 import org.eclipse.jface.dialogs.IDialogSettings; 53 import org.eclipse.jface.dialogs.MessageDialog; 54 import org.eclipse.jface.dialogs.TrayDialog; 55 import org.eclipse.jface.operation.IRunnableWithProgress; 56 import org.eclipse.jface.preference.IPreferenceStore; 57 import org.eclipse.jface.resource.ImageDescriptor; 58 import org.eclipse.jface.util.Policy; 59 import org.eclipse.jface.window.Window; 60 import org.eclipse.swt.SWT; 61 import org.eclipse.swt.events.SelectionAdapter; 62 import org.eclipse.swt.events.SelectionEvent; 63 import org.eclipse.swt.widgets.Composite; 64 import org.eclipse.swt.widgets.Display; 65 import org.eclipse.swt.widgets.Listener; 66 import org.eclipse.swt.widgets.Shell; 67 import org.eclipse.ui.PlatformUI; 68 import org.eclipse.ui.application.IWorkbenchConfigurer; 69 import org.eclipse.ui.application.IWorkbenchWindowConfigurer; 70 import org.eclipse.ui.application.WorkbenchAdvisor; 71 import org.eclipse.ui.application.WorkbenchWindowAdvisor; 72 import org.eclipse.ui.ide.IDE; 73 import org.eclipse.ui.internal.ISelectionConversionService; 74 import org.eclipse.ui.internal.PluginActionBuilder; 75 import org.eclipse.ui.internal.Workbench; 76 import org.eclipse.ui.internal.ide.AboutInfo; 77 import org.eclipse.ui.internal.ide.IDEInternalPreferences; 78 import org.eclipse.ui.internal.ide.IDEInternalWorkbenchImages; 79 import org.eclipse.ui.internal.ide.IDESelectionConversionService; 80 import org.eclipse.ui.internal.ide.IDEWorkbenchActivityHelper; 81 import org.eclipse.ui.internal.ide.IDEWorkbenchErrorHandler; 82 import org.eclipse.ui.internal.ide.IDEWorkbenchMessages; 83 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin; 84 import org.eclipse.ui.internal.ide.undo.WorkspaceUndoMonitor; 85 import org.eclipse.ui.internal.progress.ProgressMonitorJobsDialog; 86 import org.eclipse.ui.progress.IProgressService; 87 import org.eclipse.ui.statushandlers.AbstractStatusHandler; 88 import org.eclipse.urischeme.AutoRegisterSchemeHandlersJob; 89 import org.osgi.framework.Bundle; 90 import org.osgi.framework.ServiceReference; 91 import org.osgi.framework.Version; 92 93 /** 94 * IDE-specified workbench advisor which configures the workbench for use as an 95 * IDE. 96 * <p> 97 * Note: This class replaces <code>org.eclipse.ui.internal.Workbench</code>. 98 * </p> 99 * 100 * @since 3.0 101 */ 102 public class IDEWorkbenchAdvisor extends WorkbenchAdvisor { 103 104 private static final String WORKBENCH_PREFERENCE_CATEGORY_ID = "org.eclipse.ui.preferencePages.Workbench"; //$NON-NLS-1$ 105 106 /** 107 * The dialog setting key to access the known installed features since the 108 * last time the workbench was run. 109 */ 110 private static final String INSTALLED_FEATURES = "installedFeatures"; //$NON-NLS-1$ 111 112 private static IDEWorkbenchAdvisor workbenchAdvisor = null; 113 114 /** 115 * Ordered map of versioned feature ids -gt; info that are new for this 116 * session; <code>null</code> if uninitialized. Key type: 117 * <code>String</code>, Value type: <code>AboutInfo</code>. 118 */ 119 private Map<String, AboutInfo> newlyAddedBundleGroups; 120 121 /** 122 * Array of <code>AboutInfo</code> for all new installed features that 123 * specify a welcome perspective. 124 */ 125 private AboutInfo[] welcomePerspectiveInfos = null; 126 127 /** 128 * Helper for managing activites in response to workspace changes. 129 */ 130 private IDEWorkbenchActivityHelper activityHelper = null; 131 132 private Listener settingsChangeListener; 133 134 /** 135 * Support class for monitoring workspace changes and periodically 136 * validating the undo history 137 */ 138 private WorkspaceUndoMonitor workspaceUndoMonitor; 139 140 /** 141 * The IDE workbench error handler. 142 */ 143 private AbstractStatusHandler ideWorkbenchErrorHandler; 144 145 /** 146 * Helper class used to process delayed events. 147 */ 148 private final DelayedEventsProcessor delayedEventsProcessor; 149 150 private static boolean jfaceComparatorIsSet = false; 151 152 /** 153 * Base wait time between workspace lock attempts 154 */ 155 private final int workspaceWaitDelay; 156 157 private final Listener closeListener = event -> { 158 boolean doExit = IDEWorkbenchWindowAdvisor.promptOnExit(null); 159 event.doit = doExit; 160 if (!doExit) 161 event.type = SWT.None; 162 }; 163 164 /** 165 * Creates a new workbench advisor instance. 166 */ IDEWorkbenchAdvisor()167 public IDEWorkbenchAdvisor() { 168 this(1000, null); 169 } 170 IDEWorkbenchAdvisor(int workspaceWaitDelay, DelayedEventsProcessor processor)171 IDEWorkbenchAdvisor(int workspaceWaitDelay, DelayedEventsProcessor processor) { 172 super(); 173 this.workspaceWaitDelay = workspaceWaitDelay; 174 if (workbenchAdvisor != null) { 175 throw new IllegalStateException(); 176 } 177 workbenchAdvisor = this; 178 179 this.delayedEventsProcessor = processor; 180 Display.getDefault().addListener(SWT.Close, closeListener); 181 } 182 183 /** 184 * Creates a new workbench advisor instance supporting delayed file open. 185 * @param processor helper class used to process delayed events 186 */ IDEWorkbenchAdvisor(DelayedEventsProcessor processor)187 public IDEWorkbenchAdvisor(DelayedEventsProcessor processor) { 188 this(1000, processor); 189 } 190 191 @Override initialize(IWorkbenchConfigurer configurer)192 public void initialize(IWorkbenchConfigurer configurer) { 193 194 PluginActionBuilder.setAllowIdeLogging(true); 195 196 // make sure we always save and restore workspace state 197 configurer.setSaveAndRestore(true); 198 199 // register workspace adapters 200 IDE.registerAdapters(); 201 202 // register shared images 203 declareWorkbenchImages(); 204 205 // initialize the activity helper 206 activityHelper = IDEWorkbenchActivityHelper.getInstance(); 207 208 // initialize the workspace undo monitor 209 workspaceUndoMonitor = WorkspaceUndoMonitor.getInstance(); 210 211 // show Help button in JFace dialogs 212 TrayDialog.setDialogHelpAvailable(true); 213 214 // Set the default value of the preference controlling the workspace 215 // name displayed in the window title. 216 setWorkspaceNameDefault(); 217 218 if (!jfaceComparatorIsSet) { 219 // Policy.setComparator can only be called once in Jface lifetime 220 Policy.setComparator(Collator.getInstance()); 221 jfaceComparatorIsSet = true; 222 } 223 224 new AutoRegisterSchemeHandlersJob().schedule(); 225 } 226 227 @Override preStartup()228 public void preStartup() { 229 // Register the build actions 230 IProgressService service = PlatformUI.getWorkbench() 231 .getProgressService(); 232 ImageDescriptor newImage = IDEInternalWorkbenchImages 233 .getImageDescriptor(IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC); 234 service.registerIconForFamily(newImage, 235 ResourcesPlugin.FAMILY_MANUAL_BUILD); 236 service.registerIconForFamily(newImage, 237 ResourcesPlugin.FAMILY_AUTO_BUILD); 238 } 239 240 @Override postStartup()241 public void postStartup() { 242 try { 243 refreshFromLocal(); 244 activateProxyService(); 245 ((Workbench) PlatformUI.getWorkbench()).registerService( 246 ISelectionConversionService.class, 247 new IDESelectionConversionService()); 248 249 initializeSettingsChangeListener(); 250 Display.getCurrent().addListener(SWT.Settings, 251 settingsChangeListener); 252 } finally { 253 // Resume the job manager to allow background jobs to run. 254 // The job manager was suspended by the IDEApplication.start method. 255 Job.getJobManager().resume(); 256 } 257 } 258 259 /** 260 * Activate the proxy service by obtaining it. 261 */ activateProxyService()262 private void activateProxyService() { 263 Bundle bundle = Platform.getBundle("org.eclipse.ui.ide"); //$NON-NLS-1$ 264 Object proxyService = null; 265 if (bundle != null) { 266 ServiceReference<IProxyService> ref = bundle.getBundleContext().getServiceReference(IProxyService.class); 267 if (ref != null) 268 proxyService = bundle.getBundleContext().getService(ref); 269 } 270 if (proxyService == null) { 271 IDEWorkbenchPlugin.log("Proxy service could not be found."); //$NON-NLS-1$ 272 } 273 } 274 275 /** 276 * Initialize the listener for settings changes. 277 */ initializeSettingsChangeListener()278 private void initializeSettingsChangeListener() { 279 settingsChangeListener = new Listener() { 280 281 boolean currentHighContrast = Display.getCurrent() 282 .getHighContrast(); 283 284 @Override 285 public void handleEvent(org.eclipse.swt.widgets.Event event) { 286 if (Display.getCurrent().getHighContrast() == currentHighContrast) 287 return; 288 289 currentHighContrast = !currentHighContrast; 290 291 // make sure they really want to do this 292 if (new MessageDialog(null, IDEWorkbenchMessages.SystemSettingsChange_title, null, 293 IDEWorkbenchMessages.SystemSettingsChange_message, MessageDialog.QUESTION, 1, 294 IDEWorkbenchMessages.SystemSettingsChange_yes, IDEWorkbenchMessages.SystemSettingsChange_no) 295 .open() == Window.OK) { 296 PlatformUI.getWorkbench().restart(); 297 } 298 } 299 }; 300 301 } 302 303 @Override postShutdown()304 public void postShutdown() { 305 Display.getDefault().removeListener(SWT.Close, closeListener); 306 if (activityHelper != null) { 307 activityHelper.shutdown(); 308 activityHelper = null; 309 } 310 311 if (workspaceUndoMonitor != null) { 312 workspaceUndoMonitor.shutdown(); 313 workspaceUndoMonitor = null; 314 } 315 316 IWorkspace workspace = IDEWorkbenchPlugin.getPluginWorkspace(); 317 318 final Runnable disconnectFromWorkspace = new Runnable() { 319 320 int attempts; 321 322 @Override 323 public void run() { 324 if (isWorkspaceLocked(workspace)) { 325 if (attempts < 3) { 326 attempts++; 327 IDEWorkbenchPlugin.log(null, createErrorStatus("Workspace is locked, waiting...")); //$NON-NLS-1$ 328 Display.getCurrent().timerExec(workspaceWaitDelay * attempts, this); 329 } else { 330 IDEWorkbenchPlugin.log(null, createErrorStatus("Workspace is locked and can't be saved.")); //$NON-NLS-1$ 331 } 332 return; 333 } 334 disconnectFromWorkspace(); 335 } 336 337 IStatus createErrorStatus(String exceptionMessage) { 338 return new Status(IStatus.ERROR, IDEWorkbenchPlugin.IDE_WORKBENCH, 1, 339 IDEWorkbenchMessages.ProblemsSavingWorkspace, new IllegalStateException(exceptionMessage)); 340 } 341 }; 342 343 // postShutdown may be called while workspace is locked, for example - 344 // during file save operation, see 345 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=501404. 346 // Disconnect is postponed until workspace is unlocked to prevent 347 // deadlock between background thread launched by 348 // disconnectFromWorkspace() and current thread 349 // WARNING: this condition makes code that relies on synchronous 350 // disconnect very hard to discover and test 351 if (workspace != null) { 352 if (isWorkspaceLocked(workspace)) { 353 Display.getCurrent().asyncExec(disconnectFromWorkspace); 354 } else { 355 disconnectFromWorkspace.run(); 356 } 357 } 358 // This completes workbench lifecycle. 359 // Another advisor can now be created for a new workbench instance. 360 workbenchAdvisor = null; 361 } 362 isWorkspaceLocked(IWorkspace workspace)363 private boolean isWorkspaceLocked(IWorkspace workspace) { 364 ISchedulingRule currentRule = Job.getJobManager().currentRule(); 365 return currentRule != null && currentRule.isConflicting(workspace.getRoot()); 366 } 367 368 @Override preShutdown()369 public boolean preShutdown() { 370 Display.getCurrent().removeListener(SWT.Settings, 371 settingsChangeListener); 372 return super.preShutdown(); 373 } 374 375 @Override createWorkbenchWindowAdvisor( IWorkbenchWindowConfigurer configurer)376 public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor( 377 IWorkbenchWindowConfigurer configurer) { 378 return new IDEWorkbenchWindowAdvisor(this, configurer); 379 } 380 381 /** 382 * Return true if the intro plugin is present and false otherwise. 383 * 384 * @return boolean 385 */ hasIntro()386 public boolean hasIntro() { 387 return getWorkbenchConfigurer().getWorkbench().getIntroManager() 388 .hasIntro(); 389 } 390 refreshFromLocal()391 private void refreshFromLocal() { 392 String[] commandLineArgs = Platform.getCommandLineArgs(); 393 IPreferenceStore store = IDEWorkbenchPlugin.getDefault() 394 .getPreferenceStore(); 395 boolean refresh = store 396 .getBoolean(IDEInternalPreferences.REFRESH_WORKSPACE_ON_STARTUP); 397 if (!refresh) { 398 return; 399 } 400 401 // Do not refresh if it was already done by core on startup. 402 for (String commandLineArg : commandLineArgs) { 403 if (commandLineArg.equalsIgnoreCase("-refresh")) { //$NON-NLS-1$ 404 return; 405 } 406 } 407 408 final IContainer root = ResourcesPlugin.getWorkspace().getRoot(); 409 Job job = new WorkspaceJob(IDEWorkbenchMessages.Workspace_refreshing) { 410 @Override 411 public IStatus runInWorkspace(IProgressMonitor monitor) 412 throws CoreException { 413 root.refreshLocal(IResource.DEPTH_INFINITE, monitor); 414 return Status.OK_STATUS; 415 } 416 }; 417 job.setRule(root); 418 job.schedule(); 419 } 420 421 private static class CancelableProgressMonitorWrapper extends 422 ProgressMonitorWrapper { 423 private double total = 0; 424 private ProgressMonitorJobsDialog dialog; 425 CancelableProgressMonitorWrapper(IProgressMonitor monitor, ProgressMonitorJobsDialog dialog)426 CancelableProgressMonitorWrapper(IProgressMonitor monitor, 427 ProgressMonitorJobsDialog dialog) { 428 super(monitor); 429 this.dialog = dialog; 430 } 431 432 @Override internalWorked(double work)433 public void internalWorked(double work) { 434 super.internalWorked(work); 435 total += work; 436 updateProgressDetails(); 437 } 438 439 @Override worked(int work)440 public void worked(int work) { 441 super.worked(work); 442 total += work; 443 updateProgressDetails(); 444 } 445 446 @Override beginTask(String name, int totalWork)447 public void beginTask(String name, int totalWork) { 448 super.beginTask(name, totalWork); 449 subTask(IDEWorkbenchMessages.IDEWorkbenchAdvisor_preHistoryCompaction); 450 } 451 updateProgressDetails()452 private void updateProgressDetails() { 453 if (!isCanceled() && Math.abs(total - 4.0) < 0.0001 /* right before history compacting */) { 454 subTask(IDEWorkbenchMessages.IDEWorkbenchAdvisor_cancelHistoryPruning); 455 dialog.setCancelable(true); 456 } 457 if (Math.abs(total - 5.0) < 0.0001 /* history compacting finished */) { 458 subTask(IDEWorkbenchMessages.IDEWorkbenchAdvisor_postHistoryCompaction); 459 dialog.setCancelable(false); 460 } 461 } 462 } 463 464 private static class CancelableProgressMonitorJobsDialog extends 465 ProgressMonitorJobsDialog { 466 CancelableProgressMonitorJobsDialog(Shell parent)467 public CancelableProgressMonitorJobsDialog(Shell parent) { 468 super(parent); 469 } 470 471 @Override createButtonsForButtonBar(Composite parent)472 protected void createButtonsForButtonBar(Composite parent) { 473 super.createButtonsForButtonBar(parent); 474 registerCancelButtonListener(); 475 } 476 registerCancelButtonListener()477 public void registerCancelButtonListener() { 478 cancel.addSelectionListener(new SelectionAdapter() { 479 @Override 480 public void widgetSelected(SelectionEvent e) { 481 subTaskLabel.setText(""); //$NON-NLS-1$ 482 } 483 }); 484 } 485 } 486 487 /** 488 * Disconnect from the core workspace. 489 * 490 * Locks workspace in a background thread, should not be called while 491 * holding any workspace locks. 492 */ disconnectFromWorkspace()493 private void disconnectFromWorkspace() { 494 // save the workspace 495 final MultiStatus status = new MultiStatus(IDEWorkbenchPlugin.IDE_WORKBENCH, 1, 496 IDEWorkbenchMessages.ProblemSavingWorkbench); 497 try { 498 final ProgressMonitorJobsDialog p = new CancelableProgressMonitorJobsDialog( 499 null); 500 501 final boolean applyPolicy = ResourcesPlugin.getWorkspace() 502 .getDescription().isApplyFileStatePolicy(); 503 504 IRunnableWithProgress runnable = monitor -> { 505 try { 506 if (applyPolicy) 507 monitor = new CancelableProgressMonitorWrapper(monitor, p); 508 509 status.merge(((Workspace) ResourcesPlugin.getWorkspace()).save(true, true, monitor)); 510 } catch (CoreException e) { 511 status.merge(e.getStatus()); 512 } 513 }; 514 515 p.run(true, false, runnable); 516 } catch (InvocationTargetException e) { 517 status 518 .merge(new Status(IStatus.ERROR, 519 IDEWorkbenchPlugin.IDE_WORKBENCH, 1, 520 IDEWorkbenchMessages.InternalError, e 521 .getTargetException())); 522 } catch (InterruptedException e) { 523 status.merge(new Status(IStatus.ERROR, 524 IDEWorkbenchPlugin.IDE_WORKBENCH, 1, 525 IDEWorkbenchMessages.InternalError, e)); 526 } 527 ErrorDialog.openError(null, 528 IDEWorkbenchMessages.ProblemsSavingWorkspace, null, status, 529 IStatus.ERROR | IStatus.WARNING); 530 if (!status.isOK()) { 531 IDEWorkbenchPlugin.log( 532 IDEWorkbenchMessages.ProblemsSavingWorkspace, status); 533 } 534 } 535 536 @Override getDefaultPageInput()537 public IAdaptable getDefaultPageInput() { 538 return ResourcesPlugin.getWorkspace().getRoot(); 539 } 540 541 @Override getInitialWindowPerspectiveId()542 public String getInitialWindowPerspectiveId() { 543 int index = PlatformUI.getWorkbench().getWorkbenchWindowCount() - 1; 544 545 String perspectiveId = null; 546 AboutInfo[] welcomeInfos = getWelcomePerspectiveInfos(); 547 if (index >= 0 && welcomeInfos != null && index < welcomeInfos.length) { 548 perspectiveId = welcomeInfos[index].getWelcomePerspectiveId(); 549 } 550 if (perspectiveId == null) { 551 perspectiveId = IDE.RESOURCE_PERSPECTIVE_ID; 552 } 553 return perspectiveId; 554 } 555 556 /** 557 * Returns the map of versioned feature ids -> info object for all installed 558 * features. The format of the versioned feature id (the key of the map) is 559 * featureId + ":" + versionId. 560 * 561 * @return map of versioned feature ids -> info object (key type: 562 * <code>String</code>, value type: <code>AboutInfo</code>) 563 * @since 3.0 564 */ computeBundleGroupMap()565 private Map<String, AboutInfo> computeBundleGroupMap() { 566 // use tree map to get predicable order 567 Map<String, AboutInfo> ids = new TreeMap<>(); 568 569 IBundleGroupProvider[] providers = Platform.getBundleGroupProviders(); 570 for (IBundleGroupProvider provider : providers) { 571 IBundleGroup[] groups = provider.getBundleGroups(); 572 for (IBundleGroup group : groups) { 573 AboutInfo info = new AboutInfo(group); 574 575 String version = info.getVersionId(); 576 version = version == null ? "0.0.0" //$NON-NLS-1$ 577 : new Version(version).toString(); 578 String versionedFeature = group.getIdentifier() + ":" + version; //$NON-NLS-1$ 579 580 ids.put(versionedFeature, info); 581 } 582 } 583 584 return ids; 585 } 586 587 /** 588 * Returns the ordered map of versioned feature ids -> AboutInfo that are 589 * new for this session. 590 * 591 * @return ordered map of versioned feature ids (key type: 592 * <code>String</code>) -> infos (value type: 593 * <code>AboutInfo</code>). 594 */ getNewlyAddedBundleGroups()595 public Map<String, AboutInfo> getNewlyAddedBundleGroups() { 596 if (newlyAddedBundleGroups == null) { 597 newlyAddedBundleGroups = createNewBundleGroupsMap(); 598 } 599 return newlyAddedBundleGroups; 600 } 601 602 /** 603 * Updates the old features setting and returns a map of new features. 604 */ createNewBundleGroupsMap()605 private Map<String, AboutInfo> createNewBundleGroupsMap() { 606 // retrieve list of installed bundle groups from last session 607 IDialogSettings settings = IDEWorkbenchPlugin.getDefault() 608 .getDialogSettings(); 609 String[] previousFeaturesArray = settings.getArray(INSTALLED_FEATURES); 610 611 // get a map of currently installed bundle groups and store it for next 612 // session 613 Map<String, AboutInfo> bundleGroups = computeBundleGroupMap(); 614 String[] currentFeaturesArray = new String[bundleGroups.size()]; 615 bundleGroups.keySet().toArray(currentFeaturesArray); 616 settings.put(INSTALLED_FEATURES, currentFeaturesArray); 617 618 // remove the previously known from the current set 619 if (previousFeaturesArray != null) { 620 for (String previousFeature : previousFeaturesArray) { 621 bundleGroups.remove(previousFeature); 622 } 623 } 624 625 return bundleGroups; 626 } 627 628 /** 629 * Sets the default value of the preference controlling the workspace name 630 * displayed in the window title to the name of the workspace directory. 631 * This preference cannot be set in the preference initializer because the 632 * workspace directory may not be known when the preference initializer is 633 * called. 634 */ setWorkspaceNameDefault()635 private static void setWorkspaceNameDefault() { 636 IPreferenceStore preferences = IDEWorkbenchPlugin.getDefault().getPreferenceStore(); 637 String workspaceNameDefault = preferences.getDefaultString(IDEInternalPreferences.WORKSPACE_NAME); 638 if (workspaceNameDefault != null && !workspaceNameDefault.isEmpty()) 639 return; // Default is set in a plugin customization file - don't change it. 640 IPath workspaceDir = Platform.getLocation(); 641 if (workspaceDir == null) 642 return; 643 String workspaceName = workspaceDir.lastSegment(); 644 if (workspaceName == null) 645 return; 646 preferences.setDefault(IDEInternalPreferences.WORKSPACE_NAME, workspaceName); 647 } 648 649 /** 650 * Declares all IDE-specific workbench images. This includes both "shared" 651 * images (named in {@link org.eclipse.ui.ide.IDE.SharedImages}) and internal images (named in 652 * {@link org.eclipse.ui.internal.ide.IDEInternalWorkbenchImages}). 653 * 654 * @see IWorkbenchConfigurer#declareImage 655 */ declareWorkbenchImages()656 private void declareWorkbenchImages() { 657 658 final String ICONS_PATH = "$nl$/icons/full/";//$NON-NLS-1$ 659 final String PATH_ELOCALTOOL = ICONS_PATH + "elcl16/"; // Enabled //$NON-NLS-1$ 660 661 // toolbar 662 // icons. 663 final String PATH_DLOCALTOOL = ICONS_PATH + "dlcl16/"; // Disabled //$NON-NLS-1$ 664 // //$NON-NLS-1$ 665 // toolbar 666 // icons. 667 final String PATH_ETOOL = ICONS_PATH + "etool16/"; // Enabled toolbar //$NON-NLS-1$ 668 // //$NON-NLS-1$ 669 // icons. 670 final String PATH_DTOOL = ICONS_PATH + "dtool16/"; // Disabled toolbar //$NON-NLS-1$ 671 // //$NON-NLS-1$ 672 // icons. 673 final String PATH_OBJECT = ICONS_PATH + "obj16/"; // Model object //$NON-NLS-1$ 674 // //$NON-NLS-1$ 675 // icons 676 final String PATH_WIZBAN = ICONS_PATH + "wizban/"; // Wizard //$NON-NLS-1$ 677 // //$NON-NLS-1$ 678 // icons 679 680 // View icons 681 final String PATH_EVIEW= ICONS_PATH + "eview16/"; //$NON-NLS-1$ 682 683 684 Bundle ideBundle = Platform.getBundle(IDEWorkbenchPlugin.IDE_WORKBENCH); 685 686 declareWorkbenchImage(ideBundle, 687 IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC, PATH_ETOOL 688 + "build_exec.png", false); //$NON-NLS-1$ 689 declareWorkbenchImage(ideBundle, 690 IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC_HOVER, 691 PATH_ETOOL + "build_exec.png", false); //$NON-NLS-1$ 692 declareWorkbenchImage(ideBundle, 693 IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC_DISABLED, 694 PATH_DTOOL + "build_exec.png", false); //$NON-NLS-1$ 695 696 declareWorkbenchImage(ideBundle, 697 IDEInternalWorkbenchImages.IMG_ETOOL_SEARCH_SRC, PATH_ETOOL 698 + "search_src.png", false); //$NON-NLS-1$ 699 declareWorkbenchImage(ideBundle, 700 IDEInternalWorkbenchImages.IMG_ETOOL_SEARCH_SRC_HOVER, 701 PATH_ETOOL + "search_src.png", false); //$NON-NLS-1$ 702 declareWorkbenchImage(ideBundle, 703 IDEInternalWorkbenchImages.IMG_ETOOL_SEARCH_SRC_DISABLED, 704 PATH_DTOOL + "search_src.png", false); //$NON-NLS-1$ 705 706 declareWorkbenchImage(ideBundle, 707 IDEInternalWorkbenchImages.IMG_ETOOL_NEXT_NAV, PATH_ETOOL 708 + "next_nav.png", false); //$NON-NLS-1$ 709 710 declareWorkbenchImage(ideBundle, 711 IDEInternalWorkbenchImages.IMG_ETOOL_PREVIOUS_NAV, PATH_ETOOL 712 + "prev_nav.png", false); //$NON-NLS-1$ 713 714 declareWorkbenchImage(ideBundle, 715 IDEInternalWorkbenchImages.IMG_WIZBAN_NEWPRJ_WIZ, PATH_WIZBAN 716 + "newprj_wiz.png", false); //$NON-NLS-1$ 717 declareWorkbenchImage(ideBundle, 718 IDEInternalWorkbenchImages.IMG_WIZBAN_NEWFOLDER_WIZ, 719 PATH_WIZBAN + "newfolder_wiz.png", false); //$NON-NLS-1$ 720 declareWorkbenchImage(ideBundle, 721 IDEInternalWorkbenchImages.IMG_WIZBAN_NEWFILE_WIZ, PATH_WIZBAN 722 + "newfile_wiz.png", false); //$NON-NLS-1$ 723 724 declareWorkbenchImage(ideBundle, 725 IDEInternalWorkbenchImages.IMG_WIZBAN_IMPORTDIR_WIZ, 726 PATH_WIZBAN + "importdir_wiz.png", false); //$NON-NLS-1$ 727 declareWorkbenchImage(ideBundle, 728 IDEInternalWorkbenchImages.IMG_WIZBAN_IMPORTZIP_WIZ, 729 PATH_WIZBAN + "importzip_wiz.png", false); //$NON-NLS-1$ 730 731 declareWorkbenchImage(ideBundle, 732 IDEInternalWorkbenchImages.IMG_WIZBAN_EXPORTDIR_WIZ, 733 PATH_WIZBAN + "exportdir_wiz.png", false); //$NON-NLS-1$ 734 declareWorkbenchImage(ideBundle, 735 IDEInternalWorkbenchImages.IMG_WIZBAN_EXPORTZIP_WIZ, 736 PATH_WIZBAN + "exportzip_wiz.png", false); //$NON-NLS-1$ 737 738 declareWorkbenchImage(ideBundle, 739 IDEInternalWorkbenchImages.IMG_WIZBAN_RESOURCEWORKINGSET_WIZ, 740 PATH_WIZBAN + "workset_wiz.png", false); //$NON-NLS-1$ 741 742 declareWorkbenchImage(ideBundle, 743 IDEInternalWorkbenchImages.IMG_DLGBAN_SAVEAS_DLG, PATH_WIZBAN 744 + "saveas_wiz.png", false); //$NON-NLS-1$ 745 746 declareWorkbenchImage(ideBundle, 747 IDEInternalWorkbenchImages.IMG_DLGBAN_QUICKFIX_DLG, PATH_WIZBAN 748 + "quick_fix.png", false); //$NON-NLS-1$ 749 750 declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OBJ_PROJECT, 751 PATH_OBJECT + "prj_obj.png", true); //$NON-NLS-1$ 752 declareWorkbenchImage(ideBundle, 753 IDE.SharedImages.IMG_OBJ_PROJECT_CLOSED, PATH_OBJECT 754 + "cprj_obj.png", true); //$NON-NLS-1$ 755 declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OPEN_MARKER, 756 PATH_ELOCALTOOL + "gotoobj_tsk.png", true); //$NON-NLS-1$ 757 758 759 // Quick fix icons 760 declareWorkbenchImage(ideBundle, 761 IDEInternalWorkbenchImages.IMG_ELCL_QUICK_FIX_ENABLED, 762 PATH_ELOCALTOOL + "smartmode_co.png", true); //$NON-NLS-1$ 763 764 declareWorkbenchImage(ideBundle, 765 IDEInternalWorkbenchImages.IMG_DLCL_QUICK_FIX_DISABLED, 766 PATH_DLOCALTOOL + "smartmode_co.png", true); //$NON-NLS-1$ 767 768 declareWorkbenchImage(ideBundle, 769 IDEInternalWorkbenchImages.IMG_OBJS_FIXABLE_WARNING, 770 PATH_OBJECT + "quickfix_warning_obj.png", true); //$NON-NLS-1$ 771 declareWorkbenchImage(ideBundle, 772 IDEInternalWorkbenchImages.IMG_OBJS_FIXABLE_ERROR, 773 PATH_OBJECT + "quickfix_error_obj.png", true); //$NON-NLS-1$ 774 declareWorkbenchImage(ideBundle, 775 IDEInternalWorkbenchImages.IMG_OBJS_FIXABLE_INFO, 776 PATH_OBJECT + "quickfix_info_obj.png", true); //$NON-NLS-1$ 777 778 779 // task objects 780 // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_HPRIO_TSK, 781 // PATH_OBJECT+"hprio_tsk.png"); 782 // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_MPRIO_TSK, 783 // PATH_OBJECT+"mprio_tsk.png"); 784 // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_LPRIO_TSK, 785 // PATH_OBJECT+"lprio_tsk.png"); 786 787 declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OBJS_TASK_TSK, 788 PATH_OBJECT + "taskmrk_tsk.png", true); //$NON-NLS-1$ 789 declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OBJS_BKMRK_TSK, 790 PATH_OBJECT + "bkmrk_tsk.png", true); //$NON-NLS-1$ 791 792 declareWorkbenchImage(ideBundle, 793 IDEInternalWorkbenchImages.IMG_OBJS_COMPLETE_TSK, PATH_OBJECT 794 + "complete_tsk.png", true); //$NON-NLS-1$ 795 declareWorkbenchImage(ideBundle, 796 IDEInternalWorkbenchImages.IMG_OBJS_INCOMPLETE_TSK, PATH_OBJECT 797 + "incomplete_tsk.png", true); //$NON-NLS-1$ 798 declareWorkbenchImage(ideBundle, 799 IDEInternalWorkbenchImages.IMG_OBJS_WELCOME_ITEM, PATH_OBJECT 800 + "welcome_item.png", true); //$NON-NLS-1$ 801 declareWorkbenchImage(ideBundle, 802 IDEInternalWorkbenchImages.IMG_OBJS_WELCOME_BANNER, PATH_OBJECT 803 + "welcome_banner.png", true); //$NON-NLS-1$ 804 declareWorkbenchImage(ideBundle, 805 IDEInternalWorkbenchImages.IMG_OBJS_ERROR_PATH, PATH_OBJECT 806 + "error_tsk.png", true); //$NON-NLS-1$ 807 declareWorkbenchImage(ideBundle, 808 IDEInternalWorkbenchImages.IMG_OBJS_WARNING_PATH, PATH_OBJECT 809 + "warn_tsk.png", true); //$NON-NLS-1$ 810 declareWorkbenchImage(ideBundle, 811 IDEInternalWorkbenchImages.IMG_OBJS_INFO_PATH, PATH_OBJECT 812 + "info_tsk.png", true); //$NON-NLS-1$ 813 814 declareWorkbenchImage(ideBundle, 815 IDEInternalWorkbenchImages.IMG_LCL_FLAT_LAYOUT, PATH_ELOCALTOOL 816 + "flatLayout.png", true); //$NON-NLS-1$ 817 declareWorkbenchImage(ideBundle, 818 IDEInternalWorkbenchImages.IMG_LCL_HIERARCHICAL_LAYOUT, 819 PATH_ELOCALTOOL + "hierarchicalLayout.png", true); //$NON-NLS-1$ 820 declareWorkbenchImage(ideBundle, 821 IDEInternalWorkbenchImages.IMG_ETOOL_PROBLEM_CATEGORY, 822 PATH_ETOOL + "problem_category.png", true); //$NON-NLS-1$ 823 824 declareWorkbenchImage(ideBundle, 825 IDEInternalWorkbenchImages.IMG_ETOOL_PROBLEMS_VIEW, 826 PATH_EVIEW + "problems_view.png", true); //$NON-NLS-1$ 827 declareWorkbenchImage(ideBundle, 828 IDEInternalWorkbenchImages.IMG_ETOOL_PROBLEMS_VIEW_ERROR, 829 PATH_EVIEW + "problems_view_error.png", true); //$NON-NLS-1$ 830 declareWorkbenchImage(ideBundle, 831 IDEInternalWorkbenchImages.IMG_ETOOL_PROBLEMS_VIEW_WARNING, 832 PATH_EVIEW + "problems_view_warning.png", true); //$NON-NLS-1$ 833 declareWorkbenchImage(ideBundle, 834 IDEInternalWorkbenchImages.IMG_ETOOL_PROBLEMS_VIEW_INFO, 835 PATH_EVIEW + "problems_view_info.png", true); //$NON-NLS-1$ 836 837 // synchronization indicator objects 838 // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_WBET_STAT, 839 // PATH_OVERLAY+"wbet_stat.png"); 840 // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_SBET_STAT, 841 // PATH_OVERLAY+"sbet_stat.png"); 842 // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_CONFLICT_STAT, 843 // PATH_OVERLAY+"conflict_stat.png"); 844 845 // content locality indicator objects 846 // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_NOTLOCAL_STAT, 847 // PATH_STAT+"notlocal_stat.png"); 848 // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_LOCAL_STAT, 849 // PATH_STAT+"local_stat.png"); 850 // declareRegistryImage(IDEInternalWorkbenchImages.IMG_OBJS_FILLLOCAL_STAT, 851 // PATH_STAT+"filllocal_stat.png"); 852 } 853 854 /** 855 * Declares an IDE-specific workbench image. 856 * 857 * @param symbolicName 858 * the symbolic name of the image 859 * @param path 860 * the path of the image file; this path is relative to the base 861 * of the IDE plug-in 862 * @param shared 863 * <code>true</code> if this is a shared image, and 864 * <code>false</code> if this is not a shared image 865 * @see IWorkbenchConfigurer#declareImage 866 */ declareWorkbenchImage(Bundle ideBundle, String symbolicName, String path, boolean shared)867 private void declareWorkbenchImage(Bundle ideBundle, String symbolicName, 868 String path, boolean shared) { 869 URL url = FileLocator.find(ideBundle, new Path(path), null); 870 ImageDescriptor desc = ImageDescriptor.createFromURL(url); 871 getWorkbenchConfigurer().declareImage(symbolicName, desc, shared); 872 } 873 874 @Override getMainPreferencePageId()875 public String getMainPreferencePageId() { 876 // indicate that we want the Workench preference page to be prominent 877 return WORKBENCH_PREFERENCE_CATEGORY_ID; 878 } 879 880 /** 881 * Returns the location specified in command line when -showlocation is 882 * defined. Otherwise returns null 883 * 884 * @return 885 */ getCommandLineLocation()886 public String getCommandLineLocation() { 887 IEclipseContext context = getWorkbenchConfigurer().getWorkbench().getService(IEclipseContext.class); 888 return context != null ? (String) context.get(E4Workbench.FORCED_SHOW_LOCATION) : null; 889 } 890 891 /** 892 * Returns the location to show in the window title, depending on a 893 * {@link IDEInternalPreferences#SHOW_LOCATION} user preference. Note that 894 * this may be overridden by the '-showlocation' command line argument. 895 * 896 * @return the location string, or <code>null</code> if the location is not 897 * being shown 898 */ getWorkspaceLocation()899 public String getWorkspaceLocation() { 900 String location = getCommandLineLocation(); 901 // read command line, which has priority 902 if (location != null) { 903 return location; 904 } 905 // read the preferences 906 if (IDEWorkbenchPlugin.getDefault().getPreferenceStore().getBoolean(IDEInternalPreferences.SHOW_LOCATION)) { 907 // show the full location 908 return Platform.getLocation().toOSString(); 909 } 910 return null; 911 } 912 913 /** 914 * @return the welcome perspective infos, or <code>null</code> if none or 915 * if they should be ignored due to the new intro being present 916 */ getWelcomePerspectiveInfos()917 public AboutInfo[] getWelcomePerspectiveInfos() { 918 if (welcomePerspectiveInfos == null) { 919 // support old welcome perspectives if intro plugin is not present 920 if (!hasIntro()) { 921 Map<String, AboutInfo> m = getNewlyAddedBundleGroups(); 922 ArrayList<AboutInfo> list = new ArrayList<>(m.size()); 923 for (AboutInfo info : m.values()) { 924 if (info != null && info.getWelcomePerspectiveId() != null 925 && info.getWelcomePageURL() != null) { 926 list.add(info); 927 } 928 } 929 welcomePerspectiveInfos = new AboutInfo[list.size()]; 930 list.toArray(welcomePerspectiveInfos); 931 } 932 } 933 return welcomePerspectiveInfos; 934 } 935 936 @Override getWorkbenchErrorHandler()937 public synchronized AbstractStatusHandler getWorkbenchErrorHandler() { 938 if (ideWorkbenchErrorHandler == null) { 939 ideWorkbenchErrorHandler = new IDEWorkbenchErrorHandler( 940 getWorkbenchConfigurer()); 941 } 942 return ideWorkbenchErrorHandler; 943 } 944 945 @Override eventLoopIdle(Display display)946 public void eventLoopIdle(Display display) { 947 if (delayedEventsProcessor != null) 948 delayedEventsProcessor.catchUp(display); 949 super.eventLoopIdle(display); 950 } 951 } 952