1 11 package org.eclipse.ui.internal.ide.application; 12 13 import java.lang.reflect.InvocationTargetException ; 14 import java.net.URL ; 15 import java.text.Collator ; 16 import java.util.ArrayList ; 17 import java.util.Iterator ; 18 import java.util.Map ; 19 import java.util.TreeMap ; 20 21 import org.eclipse.core.net.proxy.IProxyService; 22 import org.eclipse.core.resources.IContainer; 23 import org.eclipse.core.resources.IResource; 24 import org.eclipse.core.resources.ResourcesPlugin; 25 import org.eclipse.core.resources.WorkspaceJob; 26 import org.eclipse.core.runtime.CoreException; 27 import org.eclipse.core.runtime.FileLocator; 28 import org.eclipse.core.runtime.IAdaptable; 29 import org.eclipse.core.runtime.IBundleGroup; 30 import org.eclipse.core.runtime.IBundleGroupProvider; 31 import org.eclipse.core.runtime.IProgressMonitor; 32 import org.eclipse.core.runtime.IStatus; 33 import org.eclipse.core.runtime.MultiStatus; 34 import org.eclipse.core.runtime.Path; 35 import org.eclipse.core.runtime.Platform; 36 import org.eclipse.core.runtime.Status; 37 import org.eclipse.core.runtime.jobs.Job; 38 import org.eclipse.jface.dialogs.ErrorDialog; 39 import org.eclipse.jface.dialogs.IDialogSettings; 40 import org.eclipse.jface.dialogs.MessageDialog; 41 import org.eclipse.jface.dialogs.TrayDialog; 42 import org.eclipse.jface.operation.IRunnableWithProgress; 43 import org.eclipse.jface.preference.IPreferenceStore; 44 import org.eclipse.jface.resource.ImageDescriptor; 45 import org.eclipse.jface.util.Policy; 46 import org.eclipse.jface.window.Window; 47 import org.eclipse.swt.SWT; 48 import org.eclipse.swt.widgets.Display; 49 import org.eclipse.swt.widgets.Listener; 50 import org.eclipse.ui.PlatformUI; 51 import org.eclipse.ui.application.IWorkbenchConfigurer; 52 import org.eclipse.ui.application.IWorkbenchWindowConfigurer; 53 import org.eclipse.ui.application.WorkbenchAdvisor; 54 import org.eclipse.ui.application.WorkbenchWindowAdvisor; 55 import org.eclipse.ui.ide.IDE; 56 import org.eclipse.ui.internal.ISelectionConversionService; 57 import org.eclipse.ui.internal.PluginActionBuilder; 58 import org.eclipse.ui.internal.Workbench; 59 import org.eclipse.ui.internal.ide.AboutInfo; 60 import org.eclipse.ui.internal.ide.IDEInternalPreferences; 61 import org.eclipse.ui.internal.ide.IDEInternalWorkbenchImages; 62 import org.eclipse.ui.internal.ide.IDESelectionConversionService; 63 import org.eclipse.ui.internal.ide.IDEWorkbenchActivityHelper; 64 import org.eclipse.ui.internal.ide.IDEWorkbenchErrorHandler; 65 import org.eclipse.ui.internal.ide.IDEWorkbenchMessages; 66 import org.eclipse.ui.internal.ide.IDEWorkbenchPlugin; 67 import org.eclipse.ui.internal.ide.model.WorkbenchAdapterBuilder; 68 import org.eclipse.ui.internal.ide.undo.WorkspaceUndoMonitor; 69 import org.eclipse.ui.internal.progress.ProgressMonitorJobsDialog; 70 import org.eclipse.ui.progress.IProgressService; 71 import org.eclipse.ui.statushandlers.AbstractStatusHandler; 72 import org.eclipse.update.core.SiteManager; 73 import org.osgi.framework.Bundle; 74 import org.osgi.framework.ServiceReference; 75 import org.osgi.framework.Version; 76 77 86 public class IDEWorkbenchAdvisor extends WorkbenchAdvisor { 87 88 private static final String WORKBENCH_PREFERENCE_CATEGORY_ID = "org.eclipse.ui.preferencePages.Workbench"; 90 94 private static final String INSTALLED_FEATURES = "installedFeatures"; 96 private static IDEWorkbenchAdvisor workbenchAdvisor = null; 97 98 102 private String workspaceLocation = null; 103 104 109 private Map newlyAddedBundleGroups; 110 111 115 private AboutInfo[] welcomePerspectiveInfos = null; 116 117 120 private IDEWorkbenchActivityHelper activityHelper = null; 121 122 126 private IDEIdleHelper idleHelper; 127 128 private Listener settingsChangeListener; 129 130 134 private WorkspaceUndoMonitor workspaceUndoMonitor; 135 136 139 private AbstractStatusHandler ideWorkbenchErrorHandler; 140 141 144 public IDEWorkbenchAdvisor() { 145 super(); 146 if (workbenchAdvisor != null) { 147 throw new IllegalStateException (); 148 } 149 workbenchAdvisor = this; 150 } 151 152 157 public void initialize(IWorkbenchConfigurer configurer) { 158 159 PluginActionBuilder.setAllowIdeLogging(true); 160 161 configurer.setSaveAndRestore(true); 163 164 WorkbenchAdapterBuilder.registerAdapters(); 166 167 String [] cmdLineArgs = Platform.getCommandLineArgs(); 169 170 for (int i = 0; i < cmdLineArgs.length; i++) { 173 if ("-showlocation".equalsIgnoreCase(cmdLineArgs[i])) { String name = null; 175 if (cmdLineArgs.length > i + 1) { 176 name = cmdLineArgs[i + 1]; 177 } 178 if (name != null && name.indexOf("-") == -1) { workspaceLocation = name; 180 } else { 181 workspaceLocation = Platform.getLocation().toOSString(); 182 } 183 break; 184 } 185 } 186 187 declareWorkbenchImages(); 189 190 activityHelper = IDEWorkbenchActivityHelper.getInstance(); 192 193 idleHelper = new IDEIdleHelper(configurer); 195 196 workspaceUndoMonitor = WorkspaceUndoMonitor.getInstance(); 198 199 TrayDialog.setDialogHelpAvailable(true); 201 202 Policy.setComparator(Collator.getInstance()); 203 } 204 205 210 public void preStartup() { 211 212 Platform.getJobManager().suspend(); 214 215 IProgressService service = PlatformUI.getWorkbench() 217 .getProgressService(); 218 ImageDescriptor newImage = IDEInternalWorkbenchImages 219 .getImageDescriptor(IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC); 220 service.registerIconForFamily(newImage, 221 ResourcesPlugin.FAMILY_MANUAL_BUILD); 222 service.registerIconForFamily(newImage, 223 ResourcesPlugin.FAMILY_AUTO_BUILD); 224 } 225 226 231 public void postStartup() { 232 try { 233 refreshFromLocal(); 234 activateProxyService(); 235 checkUpdates(); 236 ((Workbench) PlatformUI.getWorkbench()).registerService( 237 ISelectionConversionService.class, 238 new IDESelectionConversionService()); 239 240 initializeSettingsChangeListener(); 241 Display.getCurrent().addListener(SWT.Settings, 242 settingsChangeListener); 243 } finally { Platform.getJobManager().resume(); 245 } 246 } 247 248 251 private void activateProxyService() { 252 Bundle bundle = Platform.getBundle("org.eclipse.ui.ide"); Object proxyService = null; 254 if (bundle != null) { 255 ServiceReference ref = bundle.getBundleContext().getServiceReference(IProxyService.class.getName()); 256 if (ref != null) 257 proxyService = bundle.getBundleContext().getService(ref); 258 } 259 if (proxyService == null) { 260 IDEWorkbenchPlugin.log("Proxy service could not be found."); } 262 } 263 264 267 private void initializeSettingsChangeListener() { 268 settingsChangeListener = new Listener() { 269 270 boolean currentHighContrast = Display.getCurrent() 271 .getHighContrast(); 272 273 public void handleEvent(org.eclipse.swt.widgets.Event event) { 274 if (Display.getCurrent().getHighContrast() == currentHighContrast) 275 return; 276 277 currentHighContrast = !currentHighContrast; 278 279 if (new MessageDialog(null, 281 IDEWorkbenchMessages.SystemSettingsChange_title, null, 282 IDEWorkbenchMessages.SystemSettingsChange_message, 283 MessageDialog.QUESTION, new String [] { 284 IDEWorkbenchMessages.SystemSettingsChange_yes, 285 IDEWorkbenchMessages.SystemSettingsChange_no }, 286 1).open() == Window.OK) { 287 PlatformUI.getWorkbench().restart(); 288 } 289 } 290 }; 291 292 } 293 294 299 public void postShutdown() { 300 if (activityHelper != null) { 301 activityHelper.shutdown(); 302 activityHelper = null; 303 } 304 if (idleHelper != null) { 305 idleHelper.shutdown(); 306 idleHelper = null; 307 } 308 if (workspaceUndoMonitor != null) { 309 workspaceUndoMonitor.shutdown(); 310 workspaceUndoMonitor = null; 311 } 312 if (IDEWorkbenchPlugin.getPluginWorkspace() != null) { 313 disconnectFromWorkspace(); 314 } 315 } 316 317 322 public boolean preShutdown() { 323 Display.getCurrent().removeListener(SWT.Settings, 324 settingsChangeListener); 325 return super.preShutdown(); 326 } 327 328 333 public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor( 334 IWorkbenchWindowConfigurer configurer) { 335 return new IDEWorkbenchWindowAdvisor(this, configurer); 336 } 337 338 343 public boolean hasIntro() { 344 return getWorkbenchConfigurer().getWorkbench().getIntroManager() 345 .hasIntro(); 346 } 347 348 private void refreshFromLocal() { 349 String [] commandLineArgs = Platform.getCommandLineArgs(); 350 IPreferenceStore store = IDEWorkbenchPlugin.getDefault() 351 .getPreferenceStore(); 352 boolean refresh = store 353 .getBoolean(IDEInternalPreferences.REFRESH_WORKSPACE_ON_STARTUP); 354 if (!refresh) { 355 return; 356 } 357 358 for (int i = 0; i < commandLineArgs.length; i++) { 360 if (commandLineArgs[i].equalsIgnoreCase("-refresh")) { return; 362 } 363 } 364 365 final IContainer root = ResourcesPlugin.getWorkspace().getRoot(); 366 Job job = new WorkspaceJob(IDEWorkbenchMessages.Workspace_refreshing) { 367 public IStatus runInWorkspace(IProgressMonitor monitor) 368 throws CoreException { 369 root.refreshLocal(IResource.DEPTH_INFINITE, monitor); 370 return Status.OK_STATUS; 371 } 372 }; 373 job.setRule(root); 374 job.schedule(); 375 } 376 377 380 private void disconnectFromWorkspace() { 381 final MultiStatus status = new MultiStatus( 383 IDEWorkbenchPlugin.IDE_WORKBENCH, 1, 384 IDEWorkbenchMessages.ProblemSavingWorkbench, null); 385 IRunnableWithProgress runnable = new IRunnableWithProgress() { 386 public void run(IProgressMonitor monitor) { 387 try { 388 status.merge(ResourcesPlugin.getWorkspace().save(true, 389 monitor)); 390 } catch (CoreException e) { 391 status.merge(e.getStatus()); 392 } 393 } 394 }; 395 try { 396 new ProgressMonitorJobsDialog(null).run(true, false, runnable); 397 } catch (InvocationTargetException e) { 398 status 399 .merge(new Status(IStatus.ERROR, 400 IDEWorkbenchPlugin.IDE_WORKBENCH, 1, 401 IDEWorkbenchMessages.InternalError, e 402 .getTargetException())); 403 } catch (InterruptedException e) { 404 status.merge(new Status(IStatus.ERROR, 405 IDEWorkbenchPlugin.IDE_WORKBENCH, 1, 406 IDEWorkbenchMessages.InternalError, e)); 407 } 408 ErrorDialog.openError(null, 409 IDEWorkbenchMessages.ProblemsSavingWorkspace, null, status, 410 IStatus.ERROR | IStatus.WARNING); 411 if (!status.isOK()) { 412 IDEWorkbenchPlugin.log( 413 IDEWorkbenchMessages.ProblemsSavingWorkspace, status); 414 } 415 } 416 417 421 private void checkUpdates() { 422 boolean newUpdates = false; 423 String [] commandLineArgs = Platform.getCommandLineArgs(); 424 for (int i = 0; i < commandLineArgs.length; i++) { 425 if (commandLineArgs[i].equalsIgnoreCase("-newUpdates")) { newUpdates = true; 427 break; 428 } 429 } 430 431 if (newUpdates) { 432 try { 433 SiteManager.handleNewChanges(); 434 } catch (CoreException e) { 435 IDEWorkbenchPlugin.log( 436 "Problem opening update manager", e.getStatus()); } 438 } 439 } 440 441 446 public IAdaptable getDefaultPageInput() { 447 return ResourcesPlugin.getWorkspace().getRoot(); 448 } 449 450 455 public String getInitialWindowPerspectiveId() { 456 int index = PlatformUI.getWorkbench().getWorkbenchWindowCount() - 1; 457 458 String perspectiveId = null; 459 AboutInfo[] welcomeInfos = getWelcomePerspectiveInfos(); 460 if (index >= 0 && welcomeInfos != null && index < welcomeInfos.length) { 461 perspectiveId = welcomeInfos[index].getWelcomePerspectiveId(); 462 } 463 if (perspectiveId == null) { 464 perspectiveId = IDE.RESOURCE_PERSPECTIVE_ID; 465 } 466 return perspectiveId; 467 } 468 469 478 private Map computeBundleGroupMap() { 479 Map ids = new TreeMap (); 481 482 IBundleGroupProvider[] providers = Platform.getBundleGroupProviders(); 483 for (int i = 0; i < providers.length; ++i) { 484 IBundleGroup[] groups = providers[i].getBundleGroups(); 485 for (int j = 0; j < groups.length; ++j) { 486 IBundleGroup group = groups[j]; 487 AboutInfo info = new AboutInfo(group); 488 489 String version = info.getVersionId(); 490 version = version == null ? "0.0.0" : new Version(version).toString(); 492 String versionedFeature = group.getIdentifier() + ":" + version; 494 ids.put(versionedFeature, info); 495 } 496 } 497 498 return ids; 499 } 500 501 509 public Map getNewlyAddedBundleGroups() { 510 if (newlyAddedBundleGroups == null) { 511 newlyAddedBundleGroups = createNewBundleGroupsMap(); 512 } 513 return newlyAddedBundleGroups; 514 } 515 516 519 private Map createNewBundleGroupsMap() { 520 IDialogSettings settings = IDEWorkbenchPlugin.getDefault() 522 .getDialogSettings(); 523 String [] previousFeaturesArray = settings.getArray(INSTALLED_FEATURES); 524 525 Map bundleGroups = computeBundleGroupMap(); 528 String [] currentFeaturesArray = new String [bundleGroups.size()]; 529 bundleGroups.keySet().toArray(currentFeaturesArray); 530 settings.put(INSTALLED_FEATURES, currentFeaturesArray); 531 532 if (previousFeaturesArray != null) { 534 for (int i = 0; i < previousFeaturesArray.length; ++i) { 535 bundleGroups.remove(previousFeaturesArray[i]); 536 } 537 } 538 539 return bundleGroups; 540 } 541 542 549 private void declareWorkbenchImages() { 550 551 final String ICONS_PATH = "$nl$/icons/full/"; final String PATH_ELOCALTOOL = ICONS_PATH + "elcl16/"; 554 final String PATH_DLOCALTOOL = ICONS_PATH + "dlcl16/"; final String PATH_ETOOL = ICONS_PATH + "etool16/"; final String PATH_DTOOL = ICONS_PATH + "dtool16/"; final String PATH_OBJECT = ICONS_PATH + "obj16/"; final String PATH_WIZBAN = ICONS_PATH + "wizban/"; 573 Bundle ideBundle = Platform.getBundle(IDEWorkbenchPlugin.IDE_WORKBENCH); 574 575 declareWorkbenchImage(ideBundle, 576 IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC, PATH_ETOOL 577 + "build_exec.gif", false); declareWorkbenchImage(ideBundle, 579 IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC_HOVER, 580 PATH_ETOOL + "build_exec.gif", false); declareWorkbenchImage(ideBundle, 582 IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC_DISABLED, 583 PATH_DTOOL + "build_exec.gif", false); 585 declareWorkbenchImage(ideBundle, 586 IDEInternalWorkbenchImages.IMG_ETOOL_SEARCH_SRC, PATH_ETOOL 587 + "search_src.gif", false); declareWorkbenchImage(ideBundle, 589 IDEInternalWorkbenchImages.IMG_ETOOL_SEARCH_SRC_HOVER, 590 PATH_ETOOL + "search_src.gif", false); declareWorkbenchImage(ideBundle, 592 IDEInternalWorkbenchImages.IMG_ETOOL_SEARCH_SRC_DISABLED, 593 PATH_DTOOL + "search_src.gif", false); 595 declareWorkbenchImage(ideBundle, 596 IDEInternalWorkbenchImages.IMG_ETOOL_NEXT_NAV, PATH_ETOOL 597 + "next_nav.gif", false); 599 declareWorkbenchImage(ideBundle, 600 IDEInternalWorkbenchImages.IMG_ETOOL_PREVIOUS_NAV, PATH_ETOOL 601 + "prev_nav.gif", false); 603 declareWorkbenchImage(ideBundle, 604 IDEInternalWorkbenchImages.IMG_WIZBAN_NEWPRJ_WIZ, PATH_WIZBAN 605 + "newprj_wiz.png", false); declareWorkbenchImage(ideBundle, 607 IDEInternalWorkbenchImages.IMG_WIZBAN_NEWFOLDER_WIZ, 608 PATH_WIZBAN + "newfolder_wiz.png", false); declareWorkbenchImage(ideBundle, 610 IDEInternalWorkbenchImages.IMG_WIZBAN_NEWFILE_WIZ, PATH_WIZBAN 611 + "newfile_wiz.png", false); 613 declareWorkbenchImage(ideBundle, 614 IDEInternalWorkbenchImages.IMG_WIZBAN_IMPORTDIR_WIZ, 615 PATH_WIZBAN + "importdir_wiz.png", false); declareWorkbenchImage(ideBundle, 617 IDEInternalWorkbenchImages.IMG_WIZBAN_IMPORTZIP_WIZ, 618 PATH_WIZBAN + "importzip_wiz.png", false); 620 declareWorkbenchImage(ideBundle, 621 IDEInternalWorkbenchImages.IMG_WIZBAN_EXPORTDIR_WIZ, 622 PATH_WIZBAN + "exportdir_wiz.png", false); declareWorkbenchImage(ideBundle, 624 IDEInternalWorkbenchImages.IMG_WIZBAN_EXPORTZIP_WIZ, 625 PATH_WIZBAN + "exportzip_wiz.png", false); 627 declareWorkbenchImage(ideBundle, 628 IDEInternalWorkbenchImages.IMG_WIZBAN_RESOURCEWORKINGSET_WIZ, 629 PATH_WIZBAN + "workset_wiz.png", false); 631 declareWorkbenchImage(ideBundle, 632 IDEInternalWorkbenchImages.IMG_DLGBAN_SAVEAS_DLG, PATH_WIZBAN 633 + "saveas_wiz.png", false); 635 declareWorkbenchImage(ideBundle, 636 IDEInternalWorkbenchImages.IMG_DLGBAN_QUICKFIX_DLG, PATH_WIZBAN 637 + "quick_fix.png", false); 639 declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OBJ_PROJECT, 640 PATH_OBJECT + "prj_obj.gif", true); declareWorkbenchImage(ideBundle, 642 IDE.SharedImages.IMG_OBJ_PROJECT_CLOSED, PATH_OBJECT 643 + "cprj_obj.gif", true); declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OPEN_MARKER, 645 PATH_ELOCALTOOL + "gotoobj_tsk.gif", true); 647 declareWorkbenchImage(ideBundle, 648 IDEInternalWorkbenchImages.IMG_ELCL_QUICK_FIX_ENABLED, 649 PATH_ELOCALTOOL + "smartmode_co.gif", true); 651 declareWorkbenchImage(ideBundle, 652 IDEInternalWorkbenchImages.IMG_DLCL_QUICK_FIX_DISABLED, 653 PATH_DLOCALTOOL + "smartmode_co.gif", true); 655 663 declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OBJS_TASK_TSK, 664 PATH_OBJECT + "taskmrk_tsk.gif", true); declareWorkbenchImage(ideBundle, IDE.SharedImages.IMG_OBJS_BKMRK_TSK, 666 PATH_OBJECT + "bkmrk_tsk.gif", true); 668 declareWorkbenchImage(ideBundle, 669 IDEInternalWorkbenchImages.IMG_OBJS_COMPLETE_TSK, PATH_OBJECT 670 + "complete_tsk.gif", true); declareWorkbenchImage(ideBundle, 672 IDEInternalWorkbenchImages.IMG_OBJS_INCOMPLETE_TSK, PATH_OBJECT 673 + "incomplete_tsk.gif", true); declareWorkbenchImage(ideBundle, 675 IDEInternalWorkbenchImages.IMG_OBJS_WELCOME_ITEM, PATH_OBJECT 676 + "welcome_item.gif", true); declareWorkbenchImage(ideBundle, 678 IDEInternalWorkbenchImages.IMG_OBJS_WELCOME_BANNER, PATH_OBJECT 679 + "welcome_banner.gif", true); declareWorkbenchImage(ideBundle, 681 IDEInternalWorkbenchImages.IMG_OBJS_ERROR_PATH, PATH_OBJECT 682 + "error_tsk.gif", true); declareWorkbenchImage(ideBundle, 684 IDEInternalWorkbenchImages.IMG_OBJS_WARNING_PATH, PATH_OBJECT 685 + "warn_tsk.gif", true); declareWorkbenchImage(ideBundle, 687 IDEInternalWorkbenchImages.IMG_OBJS_INFO_PATH, PATH_OBJECT 688 + "info_tsk.gif", true); 690 declareWorkbenchImage(ideBundle, 691 IDEInternalWorkbenchImages.IMG_LCL_FLAT_LAYOUT, PATH_ELOCALTOOL 692 + "flatLayout.gif", true); declareWorkbenchImage(ideBundle, 694 IDEInternalWorkbenchImages.IMG_LCL_HIERARCHICAL_LAYOUT, 695 PATH_ELOCALTOOL + "hierarchicalLayout.gif", true); declareWorkbenchImage(ideBundle, 697 IDEInternalWorkbenchImages.IMG_ETOOL_PROBLEM_CATEGORY, 698 PATH_ETOOL + "problem_category.gif", true); 700 708 } 716 717 730 private void declareWorkbenchImage(Bundle ideBundle, String symbolicName, 731 String path, boolean shared) { 732 URL url = FileLocator.find(ideBundle, new Path(path), null); 733 ImageDescriptor desc = ImageDescriptor.createFromURL(url); 734 getWorkbenchConfigurer().declareImage(symbolicName, desc, shared); 735 } 736 737 742 public String getMainPreferencePageId() { 743 return WORKBENCH_PREFERENCE_CATEGORY_ID; 745 } 746 747 751 public String getWorkspaceLocation() { 752 return workspaceLocation; 753 } 754 755 759 public AboutInfo[] getWelcomePerspectiveInfos() { 760 if (welcomePerspectiveInfos == null) { 761 if (!hasIntro()) { 763 Map m = getNewlyAddedBundleGroups(); 764 ArrayList list = new ArrayList (m.size()); 765 for (Iterator i = m.values().iterator(); i.hasNext();) { 766 AboutInfo info = (AboutInfo) i.next(); 767 if (info != null && info.getWelcomePerspectiveId() != null 768 && info.getWelcomePageURL() != null) { 769 list.add(info); 770 } 771 } 772 welcomePerspectiveInfos = new AboutInfo[list.size()]; 773 list.toArray(welcomePerspectiveInfos); 774 } 775 } 776 return welcomePerspectiveInfos; 777 } 778 779 784 public AbstractStatusHandler getWorkbenchErrorHandler() { 785 if (ideWorkbenchErrorHandler == null) { 786 ideWorkbenchErrorHandler = new IDEWorkbenchErrorHandler( 787 getWorkbenchConfigurer()); 788 } 789 return ideWorkbenchErrorHandler; 790 } 791 } 792 | Popular Tags |