001 /* 002 * file FolderView.java 003 * 004 * Licensed Materials - Property of IBM 005 * Restricted Materials of IBM - you are allowed to copy, modify and 006 * redistribute this file as part of any program that interfaces with 007 * IBM Rational CM API. 008 * 009 * com.ibm.rational.teamapi.scout.FolderView 010 * 011 * © Copyright IBM Corporation 2004, 2008. All Rights Reserved. 012 * Note to U.S. Government Users Restricted Rights: Use, duplication or 013 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 014 */ 015 package com.ibm.rational.teamapi.scout; 016 017 import java.util.Iterator; 018 import java.util.List; 019 020 import javax.wvcm.PropertyRequestItem.PropertyRequest; 021 022 import org.eclipse.jface.action.Action; 023 import org.eclipse.jface.action.IMenuListener; 024 import org.eclipse.jface.action.IMenuManager; 025 import org.eclipse.jface.action.IToolBarManager; 026 import org.eclipse.jface.action.MenuManager; 027 import org.eclipse.jface.dialogs.InputDialog; 028 import org.eclipse.jface.dialogs.MessageDialog; 029 import org.eclipse.jface.viewers.ISelectionChangedListener; 030 import org.eclipse.jface.viewers.IStructuredSelection; 031 import org.eclipse.jface.viewers.SelectionChangedEvent; 032 import org.eclipse.jface.viewers.TreeViewer; 033 import org.eclipse.jface.viewers.ViewerFilter; 034 import org.eclipse.jface.viewers.ViewerSorter; 035 import org.eclipse.swt.SWT; 036 import org.eclipse.swt.layout.GridData; 037 import org.eclipse.swt.layout.GridLayout; 038 import org.eclipse.swt.widgets.Composite; 039 import org.eclipse.swt.widgets.Shell; 040 import org.eclipse.swt.widgets.Text; 041 import org.eclipse.ui.IWorkbenchPart; 042 import org.eclipse.ui.part.ViewPart; 043 044 import com.ibm.rational.wvcm.stp.StpProvider; 045 import com.ibm.rational.wvcm.stp.cc.CcRegistryRegion; 046 047 048 /** 049 * A CM API Folder hierarchy viewer based on the Eclipse TreeViewer control. The 050 * hierarchy is determined by the CHILD_BINDING_LIST property of each folder in 051 * the view. Each element of the tree view is a ProxyElement, which wraps a CM 052 * API proxy and represents the resource referenced by that proxy. Each element 053 * implements IPropertySource, so the properties of the CM API resource can be 054 * viewed in the Eclipse property viewer simply by selecting the element in the 055 * tree view. 056 */ 057 public class FolderView 058 extends ViewPart 059 { 060 /** 061 * The constructor. 062 */ 063 public FolderView() {} 064 065 /** 066 * @see IWorkbenchPart#createPartControl(Composite) 067 * 068 * @param parent The Composite that is the parent of this View. 069 */ 070 public void createPartControl(Composite parent) 071 { 072 /* 073 * Create a grid layout object so the text and tree viewer layout is 074 * under our control. 075 */ 076 GridLayout layout = new GridLayout(); 077 078 layout.numColumns = 1; 079 layout.verticalSpacing = 2; 080 layout.marginWidth = 0; 081 layout.marginHeight = 2; 082 parent.setLayout(layout); 083 084 /* 085 * Create a "label" in which to display information about the selected 086 * resource. It's a text field instead of a label so user can copy-paste 087 * out of it. 088 */ 089 m_text = new Text(parent, SWT.READ_ONLY | SWT.SINGLE | SWT.BORDER); 090 091 // layout the text field above the tree viewer 092 GridData layoutData = new GridData(); 093 094 layoutData.grabExcessHorizontalSpace = true; 095 layoutData.horizontalAlignment = GridData.FILL; 096 m_text.setLayoutData(layoutData); 097 098 // Create the tree viewer as a child of the composite parent 099 m_treeViewer = new TreeViewer(parent); 100 m_treeViewer.setContentProvider(new FolderContentProvider()); 101 m_treeViewer.setLabelProvider(new FolderLabelProvider()); 102 m_treeViewer.setUseHashlookup(true); 103 104 // layout the tree viewer below the text field 105 layoutData = new GridData(); 106 layoutData.grabExcessHorizontalSpace = true; 107 layoutData.grabExcessVerticalSpace = true; 108 layoutData.horizontalAlignment = GridData.FILL; 109 layoutData.verticalAlignment = GridData.FILL; 110 m_treeViewer.getControl().setLayoutData(layoutData); 111 112 // Create menu, tool bars, filters, sorters. 113 createFiltersAndSorters(); 114 createActions(); 115 createMenus(); 116 createToolbar(); 117 hookListeners(); 118 119 m_treeViewer.setInput(getInitalInput(parent.getShell())); 120 getSite().setSelectionProvider(m_treeViewer); 121 } 122 123 /** 124 * Creates the filtering and sort objects used to filter and sort the 125 * display 126 */ 127 protected void createFiltersAndSorters() 128 { 129 m_atLeastThreeFilter = new NonEmptyFolderFilter(); 130 m_onlyFoldersFilter = new FolderOnlyFilter(); 131 m_foldersFirstSorter = new FoldersFirstSorter(); 132 m_noTypeSorter = new NoTypeSorter(); 133 } 134 135 /** 136 * Creates selection-changed listener, which keeps the label field 137 * up-to-date with the selected elements of the tree view. 138 */ 139 protected void hookListeners() 140 { 141 m_treeViewer.addSelectionChangedListener(new ISelectionChangedListener() { 142 public void selectionChanged(SelectionChangedEvent event) 143 { 144 // if the selection is empty clear the label 145 if (event.getSelection().isEmpty()) { 146 m_text.setText(""); 147 148 return; 149 } 150 151 if (event.getSelection() instanceof IStructuredSelection) { 152 IStructuredSelection selection = 153 (IStructuredSelection)event.getSelection(); 154 StringBuffer toShow = new StringBuffer(); 155 156 for (Iterator iterator = selection.iterator(); 157 iterator.hasNext();) { 158 if (toShow.length() > 0) 159 toShow.append(", "); 160 161 toShow.append(((ProxyElement)iterator.next()) 162 .getSelector()); 163 } 164 165 m_text.setText(toShow.toString()); 166 } 167 } 168 }); 169 } 170 171 /** 172 * Creates the actions used by this view 173 */ 174 protected void createActions() 175 { 176 m_onlyFoldersAction = new Action("Only Folders") { 177 public void run() 178 { 179 updateFilter(m_onlyFoldersAction); 180 } 181 }; 182 m_onlyFoldersAction.setChecked(false); 183 184 m_nonEmptyFolders = new Action("No Empty Folders") { 185 public void run() 186 { 187 updateFilter(m_nonEmptyFolders); 188 } 189 }; 190 m_nonEmptyFolders.setChecked(false); 191 192 m_foldersFirstAction = new Action("Folders First") { 193 public void run() 194 { 195 updateSorter(m_foldersFirstAction); 196 } 197 }; 198 m_foldersFirstAction.setChecked(false); 199 200 m_noTypeAction = new Action("Ignore Types") { 201 public void run() 202 { 203 updateSorter(m_noTypeAction); 204 } 205 }; 206 m_noTypeAction.setChecked(false); 207 208 m_addRootAction = new Action("Add Root Folder") { 209 public void run() 210 { 211 addNewRoot(); 212 } 213 }; 214 m_addRootAction.setToolTipText("Add a new root folder to this view"); 215 m_addRootAction.setImageDescriptor(ScoutPlugin.getImageDescriptor("addRoot.gif")); 216 217 m_removeAction = new Action("Elide") { 218 public void run() 219 { 220 removeRoot(); 221 } 222 }; 223 m_removeAction.setToolTipText("Remove selected root folder(s) from this view"); 224 m_removeAction.setImageDescriptor(ScoutPlugin.getImageDescriptor("remove.gif")); 225 226 m_refreshAction = new Action("Refresh") { 227 public void run() 228 { 229 refresh(); 230 } 231 }; 232 m_refreshAction.setToolTipText("Recompute bound member list of selected folder(s)"); 233 m_refreshAction.setImageDescriptor(ScoutPlugin.getImageDescriptor("refresh.gif")); 234 } 235 236 /** 237 * Raises a dialog requesting the selector for a folder resource to be added 238 * to the tree view. Unless canceled, by the user, the new folder is added 239 * as a root of the display. 240 */ 241 protected void addNewRoot() 242 { 243 InputDialog dialog = 244 new InputDialog(null, "Add Root Folder", 245 "Enter the Location for a New Root folder." 246 + " Use 'views', 'vobs', or 'dbsets' " 247 + " to add repositories known to server", 248 "<domain>.<namespace>:<name>@<repository>", null); 249 250 if (dialog.open() == InputDialog.OK) { 251 String root = dialog.getValue(); 252 StpProvider provider = m_root.getProvider(); 253 254 try { 255 if (m_askForServerUrl) { 256 dialog = 257 new InputDialog(null, 258 "CM Server URL", 259 "Enter the URL for your CM Server.", 260 "http://qwin115:9082/TeamWeb/services/Team", 261 null); 262 263 if (dialog.open() == InputDialog.OK) 264 provider.setServerUrl(dialog.getValue()); 265 266 m_askForServerUrl = false; 267 } 268 269 if (root.equals("dbsets")) { 270 if (addRoots(provider.cqProvider().doGetDbSetList(null))) 271 m_treeViewer.refresh(m_root); 272 } else if (root.equals("vobs")) { 273 CcRegistryRegion rr = 274 provider.ccProvider().doGetDefaultCcRegistryRegion 275 (new PropertyRequest(CcRegistryRegion.VOB_TAG_LIST)); 276 277 if (addRoots(rr.getVobTagList())) 278 m_treeViewer.refresh(m_root); 279 } else if (root.equals("views")) { 280 CcRegistryRegion rr = 281 provider.ccProvider().doGetDefaultCcRegistryRegion 282 (new PropertyRequest(CcRegistryRegion.VIEW_TAG_LIST)); 283 284 if (addRoots(rr.getViewTagList())) 285 m_treeViewer.refresh(m_root); 286 } else if (addRoot(root)) 287 m_treeViewer.refresh(m_root); 288 } catch (Throwable e) { 289 MessageDialog.openError(null, 290 "Can't Add " + root, 291 e.getClass().getName() + ": " 292 + e.getLocalizedMessage()); 293 e.printStackTrace(); 294 } 295 } 296 } 297 298 /** 299 * Removes the selected object(s) from the tree view. Only root folders at 300 * the top level may be removed since all other tree view entries are 301 * derived objects. 302 */ 303 protected void removeRoot() 304 { 305 boolean removed = false; 306 307 try { 308 IStructuredSelection selection = 309 (IStructuredSelection)m_treeViewer.getSelection(); 310 311 for (Iterator iterator = selection.iterator(); iterator.hasNext();) 312 removed |= m_root.removeChild(iterator.next()); 313 } catch (Throwable ex) { 314 MessageDialog.openError(null, "Remove Root Failed", ex.getClass() 315 .getName() + ": " 316 + ex.getLocalizedMessage()); 317 } 318 319 if (removed) 320 m_treeViewer.refresh(m_root); 321 } 322 323 /** 324 * Refreshes the child set of each selected resource. The bound member list 325 * of each selected child is reread from the repository. 326 */ 327 protected void refresh() 328 { 329 try { 330 IStructuredSelection selection = 331 (IStructuredSelection)m_treeViewer.getSelection(); 332 333 for (Iterator iterator = selection.iterator(); 334 iterator.hasNext();) { 335 ProxyElement elem = (ProxyElement)iterator.next(); 336 337 m_treeViewer.collapseToLevel(elem, TreeViewer.ALL_LEVELS); 338 elem.refresh(); 339 m_treeViewer.expandToLevel(elem, 1); 340 } 341 } catch (Throwable ex) { 342 MessageDialog.openError(null, "Remove Root Failed", ex.getClass() 343 .getName() + ": " 344 + ex.getLocalizedMessage()); 345 } 346 } 347 348 /** 349 * Creates the menu structures needed for this view. 350 */ 351 protected void createMenus() 352 { 353 IMenuManager rootMenuManager = 354 getViewSite().getActionBars().getMenuManager(); 355 356 rootMenuManager.setRemoveAllWhenShown(true); 357 rootMenuManager.addMenuListener(new IMenuListener() { 358 public void menuAboutToShow(IMenuManager mgr) 359 { 360 fillMenu(mgr); 361 } 362 }); 363 364 fillMenu(rootMenuManager); 365 } 366 367 /** 368 * Builds the drop-down menu used for this tree view. 369 * 370 * @param rootMenuManager The menu manager that will contain the menu 371 * items. 372 */ 373 protected void fillMenu(IMenuManager rootMenuManager) 374 { 375 rootMenuManager.add(m_addRootAction); 376 rootMenuManager.add(m_removeAction); 377 rootMenuManager.add(m_refreshAction); 378 379 IMenuManager filterSubmenu = new MenuManager("Filters"); 380 rootMenuManager.add(filterSubmenu); 381 filterSubmenu.add(m_onlyFoldersAction); 382 filterSubmenu.add(m_nonEmptyFolders); 383 384 IMenuManager sortSubmenu = new MenuManager("Sort By"); 385 rootMenuManager.add(sortSubmenu); 386 sortSubmenu.add(m_foldersFirstAction); 387 sortSubmenu.add(m_noTypeAction); 388 } 389 390 /** 391 * Updates the TreeViewer's sorter to match the options selected by the user 392 * in the sort sub-menu. 393 * 394 * @param action The Action selected by the user. 395 */ 396 protected void updateSorter(Action action) 397 { 398 if (action == m_foldersFirstAction) { 399 m_noTypeAction.setChecked(!m_foldersFirstAction.isChecked()); 400 401 if (action.isChecked()) { 402 m_treeViewer.setSorter(m_foldersFirstSorter); 403 } else { 404 m_treeViewer.setSorter(null); 405 } 406 } else if (action == m_noTypeAction) { 407 m_foldersFirstAction.setChecked(!m_noTypeAction.isChecked()); 408 409 if (action.isChecked()) { 410 m_treeViewer.setSorter(m_noTypeSorter); 411 } else { 412 m_treeViewer.setSorter(null); 413 } 414 } 415 } 416 417 /** 418 * Updates the TreeViewer filters to reflect the filtering options selected 419 * by the user. 420 * 421 * @param action An Action identifying the filter option that has changed. 422 */ 423 protected void updateFilter(Action action) 424 { 425 if (action == m_nonEmptyFolders) { 426 if (action.isChecked()) { 427 m_treeViewer.addFilter(m_atLeastThreeFilter); 428 } else { 429 m_treeViewer.removeFilter(m_atLeastThreeFilter); 430 } 431 } else if (action == m_onlyFoldersAction) { 432 if (action.isChecked()) { 433 m_treeViewer.addFilter(m_onlyFoldersFilter); 434 } else { 435 m_treeViewer.removeFilter(m_onlyFoldersFilter); 436 } 437 } 438 } 439 440 /** 441 * Adds buttons to the tool bar for the actions defined for this view. 442 */ 443 protected void createToolbar() 444 { 445 IToolBarManager toolbarManager = 446 getViewSite().getActionBars().getToolBarManager(); 447 toolbarManager.add(m_addRootAction); 448 toolbarManager.add(m_removeAction); 449 toolbarManager.add(m_refreshAction); 450 } 451 452 /** 453 * Constructs a proxy for the resource named by a given selector and adds it 454 * to the tree view as a new top-level folder. The resource referenced by 455 * the selector is not accessed until the new entry is highlighted or 456 * expanded. 457 * 458 * @param selectorString A String containing a CM API object selector 459 * naming a resource. 460 * 461 * @return true if the resource was successfully added to the tree view. 462 */ 463 protected boolean addRoot(String selectorString) 464 { 465 try { 466 m_root.addChild(selectorString); 467 468 return true; 469 } catch (Throwable ex) { 470 MessageDialog.openError(null, "Can't Add " + selectorString, 471 ex.getClass().getName() + ": " 472 + ex.getLocalizedMessage()); 473 474 return false; 475 } 476 } 477 478 /** 479 * Adds the objects in a given list to the display. 480 * 481 * @param roots An array of object whose toString() value is a valid 482 * object selector 483 * 484 * @return true if at least one root was added to the display. 485 */ 486 protected boolean addRoots(List roots) 487 { 488 Iterator iter = roots.iterator(); 489 boolean added = false; 490 491 while (iter.hasNext()) 492 added |= addRoot(iter.next().toString()); 493 494 return added; 495 } 496 497 /** 498 * Makes initial entries into the tree viewer for the sample databases and 499 * projects usually installed with ClearQuest. Using a known server (which 500 * will have to be changed for your location), a ClearCase server is 501 * contacted and client workspaces known to the server are added as roots. 502 * 503 * @param shell The Shell used for display context when requesting 504 * credentials from the user. 505 * 506 * @return A ProxyElement object representing the (invisible) root of the 507 * tree to be viewed. 508 */ 509 public Object getInitalInput(Shell shell) 510 { 511 try { 512 m_root = new ProxyElement(shell); 513 StpProvider provider = m_root.getProvider(); 514 515 String url = System.getProperty("scout.cc.server"); 516 517 if (url != null) { 518 provider.setServerUrl(url); 519 m_askForServerUrl = false; 520 } 521 522 return m_root; 523 } catch (Throwable ex) { 524 ex.printStackTrace(); 525 526 return m_root = null; 527 } 528 } 529 530 /** 531 * @see IWorkbenchPart#setFocus() 532 */ 533 public void setFocus() {} 534 535 /** The TreeViewer control on which this view is based */ 536 protected TreeViewer m_treeViewer; 537 538 /** The Text for this view */ 539 protected Text m_text; 540 541 /** The Actions for implementing display options */ 542 protected Action m_onlyFoldersAction, m_nonEmptyFolders; 543 544 /** The Actions for implementing sort options */ 545 protected Action m_foldersFirstAction, m_noTypeAction; 546 547 /** The Actions for implementing list management operations */ 548 protected Action m_addRootAction, m_removeAction, m_refreshAction; 549 550 /** The ViewFilter for implementing display options */ 551 protected ViewerFilter m_onlyFoldersFilter, m_atLeastThreeFilter; 552 553 /** The ViewSorter for implementing sort options */ 554 protected ViewerSorter m_foldersFirstSorter, m_noTypeSorter; 555 556 /** The root of the tree view. */ 557 protected ProxyElement m_root; 558 559 private boolean m_askForServerUrl = true; 560 }