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 }