2 * Copyright (C) 2007-2012 Argeo GmbH
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org
.argeo
.slc
.client
.ui
.dist
.editors
;
18 import java
.util
.ArrayList
;
19 import java
.util
.List
;
21 import javax
.jcr
.Node
;
22 import javax
.jcr
.NodeIterator
;
23 import javax
.jcr
.PropertyType
;
24 import javax
.jcr
.RepositoryException
;
25 import javax
.jcr
.Session
;
26 import javax
.jcr
.query
.QueryManager
;
27 import javax
.jcr
.query
.QueryResult
;
28 import javax
.jcr
.query
.qom
.Constraint
;
29 import javax
.jcr
.query
.qom
.DynamicOperand
;
30 import javax
.jcr
.query
.qom
.QueryObjectModel
;
31 import javax
.jcr
.query
.qom
.QueryObjectModelFactory
;
32 import javax
.jcr
.query
.qom
.Selector
;
33 import javax
.jcr
.query
.qom
.StaticOperand
;
35 import org
.argeo
.ArgeoException
;
36 import org
.argeo
.eclipse
.ui
.utils
.CommandUtils
;
37 import org
.argeo
.jcr
.JcrUtils
;
38 import org
.argeo
.slc
.SlcException
;
39 import org
.argeo
.slc
.client
.ui
.dist
.DistImages
;
40 import org
.argeo
.slc
.client
.ui
.dist
.DistPlugin
;
41 import org
.argeo
.slc
.client
.ui
.dist
.commands
.DeleteArtifacts
;
42 import org
.argeo
.slc
.client
.ui
.dist
.utils
.NodeViewerComparator
;
43 import org
.argeo
.slc
.jcr
.SlcNames
;
44 import org
.argeo
.slc
.jcr
.SlcTypes
;
45 import org
.eclipse
.jface
.action
.IMenuListener
;
46 import org
.eclipse
.jface
.action
.IMenuManager
;
47 import org
.eclipse
.jface
.action
.MenuManager
;
48 import org
.eclipse
.jface
.viewers
.ColumnLabelProvider
;
49 import org
.eclipse
.jface
.viewers
.DoubleClickEvent
;
50 import org
.eclipse
.jface
.viewers
.IDoubleClickListener
;
51 import org
.eclipse
.jface
.viewers
.IStructuredContentProvider
;
52 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
53 import org
.eclipse
.jface
.viewers
.TableViewer
;
54 import org
.eclipse
.jface
.viewers
.TableViewerColumn
;
55 import org
.eclipse
.jface
.viewers
.Viewer
;
56 import org
.eclipse
.swt
.SWT
;
57 import org
.eclipse
.swt
.events
.ControlEvent
;
58 import org
.eclipse
.swt
.events
.ControlListener
;
59 import org
.eclipse
.swt
.events
.ModifyEvent
;
60 import org
.eclipse
.swt
.events
.ModifyListener
;
61 import org
.eclipse
.swt
.events
.SelectionAdapter
;
62 import org
.eclipse
.swt
.events
.SelectionEvent
;
63 import org
.eclipse
.swt
.events
.SelectionListener
;
64 import org
.eclipse
.swt
.layout
.GridData
;
65 import org
.eclipse
.swt
.layout
.GridLayout
;
66 import org
.eclipse
.swt
.widgets
.Button
;
67 import org
.eclipse
.swt
.widgets
.Composite
;
68 import org
.eclipse
.swt
.widgets
.Menu
;
69 import org
.eclipse
.swt
.widgets
.Table
;
70 import org
.eclipse
.swt
.widgets
.Text
;
71 import org
.eclipse
.ui
.IWorkbenchWindow
;
72 import org
.eclipse
.ui
.PartInitException
;
73 import org
.eclipse
.ui
.forms
.IManagedForm
;
74 import org
.eclipse
.ui
.forms
.editor
.FormEditor
;
75 import org
.eclipse
.ui
.forms
.editor
.FormPage
;
76 import org
.eclipse
.ui
.forms
.widgets
.FormToolkit
;
77 import org
.eclipse
.ui
.forms
.widgets
.ScrolledForm
;
80 * Show all modules contained in a given modular distribution as filter-able
83 public class ModuleListPage
extends FormPage
implements SlcNames
{
85 final static String PAGE_ID
= "ModuleListPage";
88 private Node modularDistribution
;
89 // private Node modularDistributionBase;
92 private NodeViewerComparator comparator
;
93 private TableViewer viewer
;
94 private FormToolkit tk
;
95 private Composite header
;
96 private Text filterTxt
;
97 private final static String FILTER_HELP_MSG
= "Enter filter criterion separated by a space";
99 public ModuleListPage(FormEditor formEditor
, String title
,
100 Node modularDistribution
) {
101 super(formEditor
, PAGE_ID
, title
);
102 this.modularDistribution
= modularDistribution
;
106 protected void createFormContent(IManagedForm managedForm
) {
107 ScrolledForm form
= managedForm
.getForm();
108 tk
= managedForm
.getToolkit();
111 GridLayout layout
= new GridLayout(1, false);
112 Composite body
= form
.getBody();
113 body
.setLayout(layout
);
114 // Add the filter section
115 createFilterPart(body
);
117 createTableViewer(body
);
119 // Add a listener to enable custom resize process
120 form
.addControlListener(new ControlListener() {
121 // form.addListener(SWT.RESIZE, new Listener() does not work
122 public void controlResized(ControlEvent e
) {
126 public void controlMoved(ControlEvent e
) {
132 private void refresh() {
133 final List
<Node
> result
= JcrUtils
134 .nodeIteratorToList(listBundleArtifacts());
135 viewer
.setInput(result
);
138 /** Build repository request */
139 private NodeIterator
listBundleArtifacts() {
141 Session session
= modularDistribution
.getSession();
142 QueryManager queryManager
= session
.getWorkspace()
144 QueryObjectModelFactory factory
= queryManager
.getQOMFactory();
146 Selector source
= factory
.selector(SlcTypes
.SLC_MODULE_COORDINATES
,
147 SlcTypes
.SLC_MODULE_COORDINATES
);
149 // Create a dynamic operand for each property on which we want to
151 DynamicOperand catDO
= factory
.propertyValue(
152 source
.getSelectorName(), SlcNames
.SLC_CATEGORY
);
153 DynamicOperand nameDO
= factory
.propertyValue(
154 source
.getSelectorName(), SlcNames
.SLC_NAME
);
155 DynamicOperand versionDO
= factory
.propertyValue(
156 source
.getSelectorName(), SlcNames
.SLC_VERSION
);
158 String path
= modularDistribution
.getPath() + "/"
159 + SlcNames
.SLC_MODULES
;
161 // Default Constraint: correct children
162 Constraint defaultC
= factory
.descendantNode(
163 source
.getSelectorName(), path
);
165 String filter
= filterTxt
.getText();
167 // Build constraints based the textArea content
168 if (filter
!= null && !"".equals(filter
.trim())) {
170 String
[] strs
= filter
.trim().split(" ");
171 for (String token
: strs
) {
172 token
= token
.replace('*', '%');
173 StaticOperand so
= factory
.literal(session
174 .getValueFactory().createValue("%" + token
+ "%"));
176 Constraint currC
= factory
.comparison(catDO
,
177 QueryObjectModelFactory
.JCR_OPERATOR_LIKE
, so
);
178 currC
= factory
.or(currC
, factory
.comparison(versionDO
,
179 QueryObjectModelFactory
.JCR_OPERATOR_LIKE
, so
));
180 currC
= factory
.or(currC
, factory
.comparison(nameDO
,
181 QueryObjectModelFactory
.JCR_OPERATOR_LIKE
, so
));
183 defaultC
= factory
.and(defaultC
, currC
);
187 QueryObjectModel query
= factory
.createQuery(source
, defaultC
,
189 QueryResult result
= query
.execute();
190 return result
.getNodes();
191 } catch (RepositoryException re
) {
192 throw new SlcException("Unable to refresh module list for node "
193 + modularDistribution
, re
);
197 private void createFilterPart(Composite parent
) {
198 header
= tk
.createComposite(parent
);
199 GridLayout layout
= new GridLayout(2, false);
200 layout
.marginWidth
= layout
.marginHeight
= layout
.verticalSpacing
= 0;
201 layout
.horizontalSpacing
= 5;
202 header
.setLayout(layout
);
203 GridData gd
= new GridData(SWT
.FILL
, SWT
.FILL
, false, false);
204 header
.setLayoutData(gd
);
206 // Text Area to filter
207 filterTxt
= tk
.createText(header
, "", SWT
.BORDER
| SWT
.SINGLE
208 | SWT
.SEARCH
| SWT
.CANCEL
);
209 filterTxt
.setMessage(FILTER_HELP_MSG
);
210 gd
= new GridData(SWT
.FILL
, SWT
.FILL
, false, false);
211 gd
.grabExcessHorizontalSpace
= true;
212 filterTxt
.setLayoutData(gd
);
213 filterTxt
.addModifyListener(new ModifyListener() {
214 public void modifyText(ModifyEvent event
) {
219 Button resetBtn
= tk
.createButton(header
, null, SWT
.PUSH
);
220 resetBtn
.setImage(DistImages
.IMG_REPO_READONLY
);
221 resetBtn
.addSelectionListener(new SelectionListener() {
223 public void widgetSelected(SelectionEvent e
) {
227 public void widgetDefaultSelected(SelectionEvent e
) {
233 private void resetFilter() {
234 filterTxt
.setText("");
235 filterTxt
.setMessage(FILTER_HELP_MSG
);
238 private void createTableViewer(Composite parent
) {
239 // helpers to enable sorting by column
240 List
<String
> propertiesList
= new ArrayList
<String
>();
241 List
<Integer
> propertyTypesList
= new ArrayList
<Integer
>();
243 // Define the TableViewer
244 viewer
= new TableViewer(parent
, SWT
.MULTI
| SWT
.H_SCROLL
245 | SWT
.V_SCROLL
| SWT
.FULL_SELECTION
| SWT
.BORDER
);
248 TableViewerColumn col
= new TableViewerColumn(viewer
, SWT
.NONE
);
249 col
.getColumn().setWidth(220);
250 col
.getColumn().setText("Category");
251 col
.setLabelProvider(new ColumnLabelProvider() {
253 public String
getText(Object element
) {
254 return JcrUtils
.get((Node
) element
, SlcNames
.SLC_CATEGORY
);
257 col
.getColumn().addSelectionListener(getSelectionAdapter(0));
258 propertiesList
.add(SlcNames
.SLC_CATEGORY
);
259 propertyTypesList
.add(PropertyType
.STRING
);
262 col
= new TableViewerColumn(viewer
, SWT
.NONE
);
263 col
.getColumn().setWidth(220);
264 col
.getColumn().setText("Name");
265 col
.setLabelProvider(new ColumnLabelProvider() {
267 public String
getText(Object element
) {
268 return JcrUtils
.get((Node
) element
, SLC_NAME
);
271 col
.getColumn().addSelectionListener(getSelectionAdapter(1));
272 propertiesList
.add(SLC_NAME
);
273 propertyTypesList
.add(PropertyType
.STRING
);
276 col
= new TableViewerColumn(viewer
, SWT
.NONE
);
277 col
.getColumn().setWidth(160);
278 col
.getColumn().setText("Version");
279 col
.setLabelProvider(new ColumnLabelProvider() {
281 public String
getText(Object element
) {
282 return JcrUtils
.get((Node
) element
, SLC_VERSION
);
285 col
.getColumn().addSelectionListener(getSelectionAdapter(2));
286 propertiesList
.add(SLC_VERSION
);
287 propertyTypesList
.add(PropertyType
.STRING
);
289 final Table table
= viewer
.getTable();
290 table
.setHeaderVisible(true);
291 table
.setLinesVisible(true);
293 viewer
.setContentProvider(new DistributionsContentProvider());
294 getSite().setSelectionProvider(viewer
);
296 comparator
= new NodeViewerComparator(2,
297 NodeViewerComparator
.ASCENDING
, propertiesList
,
299 viewer
.setComparator(comparator
);
302 MenuManager menuManager
= new MenuManager();
303 Menu menu
= menuManager
.createContextMenu(viewer
.getTable());
304 menuManager
.addMenuListener(new IMenuListener() {
305 public void menuAboutToShow(IMenuManager manager
) {
306 contextMenuAboutToShow(manager
);
309 viewer
.getTable().setMenu(menu
);
310 getSite().registerContextMenu(menuManager
, viewer
);
313 viewer
.addDoubleClickListener(new DoubleClickListener());
317 public void setFocus() {
318 viewer
.getTable().setFocus();
321 /** Programmatically configure the context menu */
322 protected void contextMenuAboutToShow(IMenuManager menuManager
) {
323 IWorkbenchWindow window
= DistPlugin
.getDefault().getWorkbench()
324 .getActiveWorkbenchWindow();
326 // Delete selected artifacts
327 CommandUtils
.refreshCommand(menuManager
, window
, DeleteArtifacts
.ID
,
328 DeleteArtifacts
.DEFAULT_LABEL
, DeleteArtifacts
.DEFAULT_ICON
,
332 private SelectionAdapter
getSelectionAdapter(final int index
) {
333 SelectionAdapter selectionAdapter
= new SelectionAdapter() {
335 public void widgetSelected(SelectionEvent e
) {
336 Table table
= viewer
.getTable();
337 comparator
.setColumn(index
);
338 int dir
= table
.getSortDirection();
339 if (table
.getSortColumn() == table
.getColumn(index
)) {
340 dir
= dir
== SWT
.UP ? SWT
.DOWN
: SWT
.UP
;
344 table
.setSortDirection(dir
);
345 table
.setSortColumn(table
.getColumn(index
));
349 return selectionAdapter
;
353 private class DistributionsContentProvider
implements
354 IStructuredContentProvider
{
355 // we keep a cache of the Nodes in the content provider to be able to
356 // manage long request
357 private List
<Node
> nodes
;
359 public void dispose() {
362 // We expect a list of nodes as a new input
363 @SuppressWarnings("unchecked")
364 public void inputChanged(Viewer viewer
, Object oldInput
, Object newInput
) {
365 nodes
= (List
<Node
>) newInput
;
368 public Object
[] getElements(Object arg0
) {
369 return nodes
.toArray();
373 private class DoubleClickListener
implements IDoubleClickListener
{
375 public void doubleClick(DoubleClickEvent event
) {
376 Object obj
= ((IStructuredSelection
) event
.getSelection())
379 if (obj
instanceof Node
) {
380 Node node
= (Node
) obj
;
381 if (node
.isNodeType(SlcTypes
.SLC_BUNDLE_ARTIFACT
)) {
382 GenericBundleEditorInput gaei
= new GenericBundleEditorInput(
384 DistPlugin
.getDefault().getWorkbench()
385 .getActiveWorkbenchWindow().getActivePage()
386 .openEditor(gaei
, GenericBundleEditor
.ID
);
389 } catch (RepositoryException re
) {
390 throw new ArgeoException(
391 "Repository error while getting node info", re
);
392 } catch (PartInitException pie
) {
393 throw new ArgeoException(
394 "Unexepected exception while opening artifact editor",
401 * UI Trick to put scroll bar on the table rather than on the scrollform
403 private void refreshLayout() {
404 // Compute desired table size
405 int maxH
= getManagedForm().getForm().getSize().y
;
406 int maxW
= getManagedForm().getForm().getParent().getSize().x
;
407 maxH
= maxH
- header
.getSize().y
;
408 final Table table
= viewer
.getTable();
409 GridData gd
= new GridData(SWT
.LEFT
, SWT
.TOP
, true, true);
411 // when table height is less than 200 px, we let the scroll bar on the
413 // FIXME substract some spare space. There is room here for optimization
414 gd
.heightHint
= Math
.max(maxH
- 35, 200);
415 gd
.widthHint
= Math
.max(maxW
- 35, 200);
417 table
.setLayoutData(gd
);
418 getManagedForm().reflow(true);
422 public void setActive(boolean active
) {
423 super.setActive(active
);