1 package org.simantics.export.ui;
3 import java.net.MalformedURLException;
5 import java.util.ArrayList;
6 import java.util.Collection;
7 import java.util.Collections;
8 import java.util.Comparator;
9 import java.util.HashMap;
10 import java.util.HashSet;
11 import java.util.List;
14 import java.util.TreeMap;
16 import org.eclipse.jface.resource.ImageDescriptor;
17 import org.eclipse.jface.resource.JFaceResources;
18 import org.eclipse.jface.resource.LocalResourceManager;
19 import org.eclipse.jface.wizard.WizardPage;
20 import org.eclipse.nebula.widgets.grid.Grid;
21 import org.eclipse.nebula.widgets.grid.GridColumn;
22 import org.eclipse.nebula.widgets.grid.GridItem;
23 import org.eclipse.swt.SWT;
24 import org.eclipse.swt.events.SelectionAdapter;
25 import org.eclipse.swt.events.SelectionEvent;
26 import org.eclipse.swt.events.SelectionListener;
27 import org.eclipse.swt.graphics.Color;
28 import org.eclipse.swt.graphics.Image;
29 import org.eclipse.swt.graphics.RGB;
30 import org.eclipse.swt.widgets.Composite;
31 import org.simantics.db.exception.DatabaseException;
32 import org.simantics.export.core.ExportContext;
33 import org.simantics.export.core.error.ExportException;
34 import org.simantics.export.core.intf.ContentType;
35 import org.simantics.export.core.intf.Discoverer;
36 import org.simantics.export.core.intf.Exporter;
37 import org.simantics.export.core.intf.Format;
38 import org.simantics.export.core.intf.Publisher;
39 import org.simantics.export.core.manager.Content;
40 import org.simantics.export.core.manager.ExportWizardResult;
41 import org.simantics.export.core.util.ExportQueries;
42 import org.simantics.export.ui.util.ExportUIQueries;
43 import org.simantics.utils.datastructures.MapList;
44 import org.simantics.utils.datastructures.ToStringComparator;
45 import org.simantics.utils.strings.AlphanumComparator;
46 import org.simantics.utils.ui.workbench.StringMemento;
49 * Show wizard page where content and export format is selected.
51 * @author toni.kalajainen@semantum.fi
53 public class ContentSelectionPage extends WizardPage {
55 /** Key for preference setting that contains sub-mementos for each content URI. */
56 public static final String KEY_FORMAT_SELECTIONS = "org.simantics.modeling.ui.export.wizard.formatSelections"; //$NON-NLS-1$
59 LocalResourceManager resourceManager;
64 // Hash-code for the initial data
65 String initialSelectionKey;
66 Set<Content> initialSelection = new HashSet<Content>();
68 // Previous selections
69 StringMemento formatSelections;
72 Collection<String> allModels, selectedModels;
73 ToStringComparator toStringComparator = new ToStringComparator();
74 MapList<ContentType, String> content; // ContentType -> Content[]
75 MapList<ContentType, Format> typeToFormatMap; // ContentType -> Format
76 MapList<String, ContentType> contentToTypeMap; // uri -> ContentType
77 Map<String, Map<String, String>> labels; // ContentType -> URI -> Label
78 MapList<String, String> modelContent; // ModelURI -> ContentURI
81 List<Content> contentSelection = new ArrayList<Content>();
83 public ContentSelectionPage(ExportContext ctx) throws ExportException {
84 super(Messages.ContentSelectionPage_SelectContent, Messages.ContentSelectionPage_SelectContentDescription, null);
90 void init() throws ExportException {
92 System.out.println("Found Content Types:"); //$NON-NLS-1$
93 for ( ContentType ct : ctx.eep.contentTypes() ) {
94 System.out.println(" "+ct); //$NON-NLS-1$
98 System.out.println("Exporters:"); //$NON-NLS-1$
99 for ( Exporter ex : ctx.eep.exporters() ) {
100 System.out.println(" "+ex); //$NON-NLS-1$
102 System.out.println();
104 System.out.println("Formats:"); //$NON-NLS-1$
105 for ( Format format : ctx.eep.formats() ) {
106 System.out.println(" "+format); //$NON-NLS-1$
108 System.out.println();
110 System.out.println("Discoverers:"); //$NON-NLS-1$
111 for ( Discoverer discoverer : ctx.eep.discoverers() ) {
112 System.out.println(" "+discoverer); //$NON-NLS-1$
114 System.out.println();
116 System.out.println("Publishers:"); //$NON-NLS-1$
117 for ( Publisher publisher : ctx.eep.publishers() ) {
118 System.out.println(" "+publisher.id()); //$NON-NLS-1$
120 System.out.println();
122 // Organize formats by content types - Filter out ContentTypes that don't have exporter and format.
123 System.out.println("Mapped ContentTypes to Exporters:"); //$NON-NLS-1$
124 typeToFormatMap = MapList.use( new TreeMap<ContentType, List<Format>>(toStringComparator) );
125 for ( ContentType ct : ctx.eep.contentTypes() ) {
126 for ( Exporter exp : ctx.eep.getExportersForContentType( ct.id() ) ) {
127 Format format = ctx.eep.getFormat( exp.formatId() );
128 if ( format==null ) continue;
129 System.out.println(" "+ct.id()+" -> "+format.fileext()); //$NON-NLS-1$ //$NON-NLS-2$
130 if (!typeToFormatMap.contains(ct, format)) typeToFormatMap.add(ct, format);
133 System.out.println();
135 // Discover the models in the project
136 allModels = ctx.session.syncRequest( ExportUIQueries.models(ctx.project) );
138 // Calculate hash for the initial selection
139 int initialContentHash = 0x52f3a45;
140 for ( String content : ctx.selection ) {
141 initialContentHash = 13*initialContentHash + content.hashCode();
143 initialSelectionKey = "InitialSelection-"+initialContentHash; //$NON-NLS-1$
144 String sel = ctx.store.get(initialSelectionKey, null);
146 initialSelection = ExportWizardResult.parse(sel);
148 // First time wizard was called with this selection.
150 for ( String contentUri : ctx.selection ) {
151 initialSelection.add( new Content(contentUri, null, "all", null, null, null) ); //$NON-NLS-1$
155 // Choose the models from user interface selection
156 selectedModels = new ArrayList<String>();
157 StringBuilder modelsStr = new StringBuilder();
158 for ( String content : ctx.selection ) {
159 for ( String model : allModels ) {
160 if ( content.equals(model) || content.startsWith(model + "/") ) { //$NON-NLS-1$
161 if ( !selectedModels.contains( model ) ) {
162 selectedModels.add( model );
163 if ( modelsStr.length()>0 ) modelsStr.append(", "); //$NON-NLS-1$
164 modelsStr.append( model );
169 // If user has nothing selected, choose active models
170 if ( selectedModels.isEmpty() ) selectedModels.addAll( ctx.activeModels );
171 // If there are no active models, choose all models
172 if ( selectedModels.isEmpty() ) selectedModels.addAll( allModels );
174 labels = new HashMap<String, Map<String, String>>();
175 labels.put( "model", ctx.session.syncRequest( ExportQueries.labels( selectedModels ) ) ); // Should Model CT be used for labeling? //$NON-NLS-1$
178 System.out.println("Discovering content: "+modelsStr); //$NON-NLS-1$
179 content = MapList.use( new TreeMap<ContentType, List<String>>(toStringComparator) );
180 contentToTypeMap = MapList.use( new TreeMap<String, List<ContentType>>(toStringComparator) );
181 modelContent = MapList.use( new TreeMap<String, List<String>>() );
183 for ( ContentType ct : typeToFormatMap.getKeys() ) {
184 System.out.println(" "+ct.label()); //$NON-NLS-1$
185 for ( Discoverer discoverer : ctx.eep.getDiscoverers( ct.id() )) {
186 System.out.println(" "+discoverer.toString()); //$NON-NLS-1$
189 Collection<String> contents = discoverer.discoverContent(ctx, selectedModels);
190 List<String> contentUris = new ArrayList<String>( contents );
193 Map<String, String> lbls = ct.getLabels(ctx, contentUris);
194 labels.put( ct.id(), lbls );
197 IndirectComparator comp = new IndirectComparator();
199 Collections.sort( contentUris, comp );
201 for ( String contentId : contentUris ) {
202 content.add( ct, contentId );
203 contentToTypeMap.add(contentId, ct);
204 //modelContent.add(key)
205 System.out.println(" "+contentId); //$NON-NLS-1$
210 System.out.println();
213 } catch (DatabaseException e) {
214 throw new ExportException(e);
221 public void createControl(Composite parent) {
222 grid = new Grid(parent, SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI);
223 grid.setHeaderVisible(true);
225 resourceManager = new LocalResourceManager(JFaceResources.getResources(), grid);
226 Color contentTypeColor = resourceManager.createColor( new RGB(245, 245, 252) );
227 GridColumn column = new GridColumn(grid,SWT.NONE);
228 column.setTree(true);
229 column.setText(Messages.ContentSelectionPage_Name);
230 column.setWidth(200);
233 assertColumnIndex(1);
235 Format pdfFormat = ctx.eep.getFormat("pdf"); //$NON-NLS-1$
238 ImageDescriptor folderID = null;
240 URL folderUrl = new URL("platform:/plugin/com.famfamfam.silk/icons/folder.png"); //$NON-NLS-1$
241 folderID = ImageDescriptor.createFromURL( folderUrl );
242 } catch (MalformedURLException e) {
246 List<GridItem> selectedNodes = new ArrayList<GridItem>();
248 // Iterate all models
249 for ( String modelUri : contentToTypeMap.getKeys() ) {
250 ContentType modelContentType = null;
251 for ( ContentType ct : contentToTypeMap.getValues(modelUri) ) {
252 if ( ct.isModel() ) {
253 modelContentType = ct;
257 if (modelContentType==null) continue;
260 String modelLabel = labels.get("model").get(modelUri); //$NON-NLS-1$
261 GridItem modelNode = newLine(grid, modelLabel, modelUri, modelContentType.id());
262 setIcon(modelNode, 0, modelContentType.icon(modelUri));
263 modelNode.setToolTipText(0, modelUri);
265 if ( ctx.selection.contains( modelUri ) ) selectedNodes.add( modelNode );
267 // Columns for formats
269 for ( ContentType ct : contentToTypeMap.getValues(modelUri) ) {
270 for ( Format format : typeToFormatMap.getValues(ct) ) {
272 assertColumnIndex( column1 );
273 modelNode.setText(column1, format.fileext());
274 modelNode.setGrayed(column1, false);
275 modelNode.setCheckable(column1, true);
276 modelNode.setData( Integer.toString(column1),
277 new Content(modelUri, ct.id(), format.id(), modelLabel, format.fileext(), modelUri ) );
278 modelNode.setChecked(column1, hasInitialSelection(modelUri, format.id()));
279 modelNode.setToolTipText(column1, format.label());
281 ImageDescriptor id = format.icon();
283 Image icon = resourceManager.createImage(id);
284 if ( icon!=null) modelNode.setImage(column1, icon);
289 for ( ContentType ct : content.getKeys() )
291 if ( ct.isModel() ) continue;
293 GridItem ctNode = newLine(modelNode, ct.label(), modelUri, ct.id());
294 ctNode.setExpanded( true );
295 ctNode.setBackground(0, contentTypeColor);
296 ctNode.setBackground(contentTypeColor);
297 setIcon(ctNode, 0, folderID);
298 int contentCount = 0;
299 ArrayList<Format> contentTypesFormats = new ArrayList<Format>();
301 for ( String contentUri : content.getValues(ct) ) {
302 // WORKAROUND: Should not be based on URI
303 if ( !contentUri.startsWith(modelUri) ) continue;
305 String nodeLabel = labels.get(ct.id()).get(contentUri);
306 GridItem contentNode = newLine(ctNode, nodeLabel, contentUri, ct.id());
308 contentNode.setToolTipText(0, contentUri);
309 setIcon(contentNode, 0, ct.icon(contentUri));
311 if ( ctx.selection.contains( contentUri) ) selectedNodes.add( contentNode );
313 int columnNumber = 0;
316 List<Format> formats = typeToFormatMap.getValues(ct);
317 if ( formats.contains( pdfFormat )) {
318 if ( !contentTypesFormats.contains(pdfFormat) ) contentTypesFormats.add(pdfFormat);
320 assertColumnIndex( columnNumber );
322 contentNode.setText(columnNumber, " "+pdfFormat.fileext()); //$NON-NLS-1$
323 contentNode.setGrayed(columnNumber, false);
324 contentNode.setCheckable(columnNumber, true);
325 contentNode.setChecked(columnNumber, hasInitialSelection(contentUri, pdfFormat.id()) );
326 contentNode.setToolTipText(columnNumber, pdfFormat.label());
327 setIcon(contentNode, columnNumber, pdfFormat.icon());
330 Integer.toString(columnNumber),
331 new Content(contentUri, ct.id(), pdfFormat.id(), nodeLabel, pdfFormat.fileext(), modelUri ) );
334 if ( !contentTypesFormats.contains(null) ) contentTypesFormats.add(null);
336 assertColumnIndex( columnNumber );
339 // Attachment Columns
340 for (Format format : formats ) {
341 if ( format==pdfFormat ) continue;
342 if ( !contentTypesFormats.contains(format) ) contentTypesFormats.add(format);
344 assertColumnIndex( columnNumber );
345 contentNode.setText(columnNumber, " "+format.fileext()); //$NON-NLS-1$
346 contentNode.setGrayed(columnNumber, false);
347 contentNode.setCheckable(columnNumber, true);
348 contentNode.setChecked(columnNumber, hasInitialSelection(contentUri, format.id()) );
349 contentNode.setToolTipText(columnNumber, format.label());
350 setIcon(contentNode, columnNumber, format.icon());
353 Integer.toString(columnNumber),
354 new Content(contentUri, ct.id(), format.id(), nodeLabel, format.fileext(), modelUri ) );
358 // Add the *.pdf buttons
359 int columnNumber = 0;
360 Set<GridItem> gis = new HashSet<GridItem>();
361 for ( Format format : contentTypesFormats ) {
363 ctNode.setBackground(columnNumber, contentTypeColor);
364 if ( format == null ) continue;
365 ctChecks.add( new CTCheck(ctNode, columnNumber, format) );
366 ctNode.setCheckable(columnNumber, true);
367 ctNode.setGrayed(columnNumber, true);
368 //setIcon(ctNode, columnNumber, format.icon());
370 //ctNode.setData(Integer.toString(columnNumber), format );
371 //ctNode.setText(columnNumber, "*"+format.fileext());
374 if ( contentCount == 0 ) {
379 modelNode.setExpanded( true );
381 grid.setSelection( selectedNodes.toArray( new GridItem[selectedNodes.size()] ) );
382 if ( selectedNodes.size()>0 ) {
383 GridItem first = selectedNodes.get(0);
384 grid.setFocusItem( first );
387 grid.addSelectionListener(ctChecksListener);
389 grid.addSelectionListener( new SelectionAdapter() {
390 public void widgetSelected(SelectionEvent e) {
391 if ( e.item == null || e.item instanceof GridItem==false ) return;
392 GridItem gi = (GridItem) e.item;
393 GridColumn column = grid.getColumn( new Point(e.x, e.y) );
394 if ( column == null ) return;
395 int columnIndex = -1;
396 int columnCount = grid.getColumnCount();
397 for ( int i=0; i<columnCount; i++) {
398 GridColumn gc = grid.getColumn(i);
399 if ( gc == column ) {
404 System.out.println(e.detail);
405 System.out.println(e);
406 System.out.println(columnIndex);
407 String text = gi.getText(columnIndex);
408 System.out.println(text);
413 setPageComplete(true);
416 void setIcon(GridItem node, int index, ImageDescriptor icon) {
417 if ( icon == null ) return;
418 Image image = resourceManager.createImage(icon);
419 if ( image == null ) return;
420 node.setImage(index, image);
424 * Creates column index if doesn't exist.
425 * Column=2, is "Pages"
426 * Column>=3, is "Attachement"
430 void assertColumnIndex(int columnIndex) {
431 while ( columnIndex >= grid.getColumnCount() ) {
432 int cc = grid.getColumnCount();
434 GridColumn column = new GridColumn(grid, SWT.CHECK);
435 column.setText( cc==1?Messages.ContentSelectionPage_Pages:Messages.ContentSelectionPage_Attachments);
436 column.setWidth( cc==1?150:200 );
438 for (GridItem gi : grid.getItems()) {
439 gi.setGrayed(cc, true);
440 gi.setCheckable(cc, false);
446 boolean hasInitialSelection(String uri, String formatId) {
447 for (Content c : initialSelection) {
448 if ( !c.url.equals( uri ) ) continue;
449 if ( c.formatId.equals("all") || c.formatId.equals(formatId) ) return true; //$NON-NLS-1$
454 GridItem newLine(Object parent, String label, String url, String contentTypeId) {
456 if (parent instanceof Grid) {
457 gi = new GridItem( (Grid)parent, SWT.NONE);
459 gi = new GridItem( (GridItem)parent, SWT.NONE);
463 if ( url!=null || contentTypeId!=null ) gi.setData( new Content(url, contentTypeId, null, label, null, null) );
464 for ( int columnIndex=0; columnIndex<grid.getColumnCount(); columnIndex++ ) {
465 gi.setGrayed(columnIndex, true);
466 gi.setCheckable(columnIndex, false);
472 public void validatePage() {
473 List<Content> newContentSelection = new ArrayList<Content>();
474 // Get list of content.. something must be checked
475 int columnWidth = grid.getColumnCount();
476 Set<String> checkedFormats = new HashSet<String>();
477 for (GridItem gi : grid.getItems()) {
479 checkedFormats.clear();
480 GridItem parentItem = gi.getParentItem();
481 if ( parentItem!=null ) {
482 for (int c=0; c<columnWidth; c++) {
483 if ( parentItem.getChecked(c) ) {
484 Object data = parentItem.getData( Integer.toString(c) );
485 if ( data==null || data instanceof Format == false ) continue;
486 Format format = (Format) data;
487 checkedFormats.add( format.id() );
492 for (int c=0; c<columnWidth; c++) {
493 Object data = gi.getData( Integer.toString(c) );
494 if ( data==null || data instanceof Content == false ) continue;
495 Content content = (Content) data;
496 if ( gi.getChecked(c) || checkedFormats.contains(content.formatId) ) {
497 newContentSelection.add( content );
503 contentSelection = newContentSelection;
506 public List<Content> getContentSelection() {
507 return contentSelection;
510 public void savePrefs() {
511 String str = ExportWizardResult.print( getContentSelection() );
512 ctx.store.put(initialSelectionKey, str);
515 static class IndirectComparator implements Comparator<String> {
516 Map<String, String> labels;
519 public int compare(String o1, String o2) {
520 String l1 = null, l2 = null;
521 if ( labels != null ) {
525 if ( l1 == null ) l1 = o1;
526 if ( l2 == null ) l2 = o2;
527 return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(l1, l2);
531 //// Code for following clicks on ContentType format specific checkboxes
532 List<CTCheck> ctChecks = new ArrayList<CTCheck>();
533 static class CTCheck {
537 boolean lastKnownCheckedStatus = false;
538 CTCheck(GridItem gi, int columnNumber, Format format) {
540 this.columnNumber = columnNumber;
541 this.format = format;
543 boolean previousSelection;
544 boolean checkStatus() {
545 return gi.getChecked(columnNumber);
547 void setCheck(boolean checked) {
548 gi.setChecked(columnNumber, checked);
552 SelectionListener ctChecksListener = new SelectionAdapter() {
553 public void widgetSelected(SelectionEvent e) {
554 if ( e.item == null || e.item instanceof GridItem==false ) return;
555 int columnWidth = grid.getColumnCount();
556 for ( CTCheck cc : ctChecks ) {
557 boolean checked = cc.checkStatus();
558 if ( checked == cc.lastKnownCheckedStatus ) continue;
559 cc.lastKnownCheckedStatus = checked;
561 for ( GridItem gi : cc.gi.getItems() ) {
562 for ( int columnNumber = 0; columnNumber<columnWidth; columnNumber++ ) {
563 Object data = gi.getData( Integer.toString( columnNumber ) );
564 if ( data==null || data instanceof Content == false ) continue;
565 Content content = (Content) data;
566 if ( !content.formatId.equals( cc.format.id() )) continue;
567 gi.setChecked(columnNumber, checked);