001/*****************************************************************************
002 * Copyright by The HDF Group.                                               *
003 * Copyright by the Board of Trustees of the University of Illinois.         *
004 * All rights reserved.                                                      *
005 *                                                                           *
006 * This file is part of the HDF Java Products distribution.                  *
007 * The full copyright notice, including terms governing use, modification,   *
008 * and redistribution, is contained in the COPYING file, which can be found  *
009 * at the root of the source code distribution tree,                         *
010 * or in https://www.hdfgroup.org/licenses.                                  *
011 * If you do not have access to either file, you may request a copy from     *
012 * help@hdfgroup.org.                                                        *
013 ****************************************************************************/
014
015package hdf.view.dialog;
016
017import java.util.ArrayList;
018import java.util.Iterator;
019import java.util.List;
020import org.eclipse.swt.SWT;
021import org.eclipse.swt.events.DisposeEvent;
022import org.eclipse.swt.events.DisposeListener;
023import org.eclipse.swt.events.SelectionAdapter;
024import org.eclipse.swt.events.SelectionEvent;
025import org.eclipse.swt.events.VerifyEvent;
026import org.eclipse.swt.events.VerifyListener;
027import org.eclipse.swt.graphics.Point;
028import org.eclipse.swt.graphics.Rectangle;
029import org.eclipse.swt.layout.GridData;
030import org.eclipse.swt.layout.GridLayout;
031import org.eclipse.swt.widgets.Button;
032import org.eclipse.swt.widgets.Combo;
033import org.eclipse.swt.widgets.Composite;
034import org.eclipse.swt.widgets.Display;
035import org.eclipse.swt.widgets.Label;
036import org.eclipse.swt.widgets.Shell;
037import org.eclipse.swt.widgets.Text;
038
039import hdf.object.Group;
040import hdf.object.HObject;
041import hdf.view.Tools;
042import hdf.view.ViewProperties;
043
044/**
045 * NewGroupDialog shows a message dialog requesting user input for creating a new HDF4/5 group.
046 *
047 * @author Jordan T. Henderson
048 * @version 2.4 12/30/2015
049 */
050public class NewGroupDialog extends NewDataObjectDialog {
051
052    /* Used to restore original size after click "less" button */
053    private Point       originalSize;
054
055    private Text        nameField;
056    private Text        compactField;
057    private Text        indexedField;
058
059    private Combo       parentChoice;
060    private Combo       orderFlags;
061
062    private Button      useCreationOrder;
063    private Button      setLinkStorage;
064    private Button      creationOrderHelpButton;
065    private Button      storageTypeHelpButton;
066    private Button      okButton;
067    private Button      cancelButton;
068    private Button      moreButton;
069
070    private Composite   moreOptionsComposite;
071    private Composite   creationOrderComposite;
072    private Composite   storageTypeComposite;
073    private Composite   dummyComposite;
074    private Composite   buttonComposite;
075
076    private List<Group> groupList;
077
078    private int         creationOrder;
079
080    private boolean     moreOptionsEnabled;
081
082    /**
083     * Constructs a NewGroupDialog with specified list of possible parent groups.
084     *
085     * @param parent
086     *            the parent shell of the dialog
087     * @param pGroup
088     *            the parent group which the new group is added to.
089     * @param objs
090     *            the list of all objects.
091     */
092    public NewGroupDialog(Shell parent, Group pGroup, List<?> objs) {
093        super(parent, pGroup, objs);
094
095        moreOptionsEnabled = false;
096    }
097
098    /**
099     * Open the NewGroupDialog for adding a new group.
100     */
101    public void open() {
102        Shell parent = getParent();
103        shell = new Shell(parent, SWT.SHELL_TRIM | SWT.APPLICATION_MODAL);
104        shell.setFont(curFont);
105        shell.setText("New Group...");
106        shell.setImages(ViewProperties.getHdfIcons());
107        GridLayout layout = new GridLayout(1, false);
108        layout.verticalSpacing = 0;
109        shell.setLayout(layout);
110
111        Composite fieldComposite = new Composite(shell, SWT.NONE);
112        fieldComposite.setLayout(new GridLayout(2, false));
113        fieldComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
114
115        Label groupNameLabel = new Label(fieldComposite, SWT.LEFT);
116        groupNameLabel.setFont(curFont);
117        groupNameLabel.setText("Group name:");
118
119        nameField = new Text(fieldComposite, SWT.SINGLE | SWT.BORDER);
120        nameField.setFont(curFont);
121        GridData fieldData = new GridData(SWT.FILL, SWT.FILL, true, false);
122        fieldData.minimumWidth = 250;
123        nameField.setLayoutData(fieldData);
124
125        Label parentGroupLabel = new Label(fieldComposite, SWT.LEFT);
126        parentGroupLabel.setFont(curFont);
127        parentGroupLabel.setText("Parent group:");
128        parentGroupLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false));
129
130        parentChoice = new Combo(fieldComposite, SWT.DROP_DOWN | SWT.BORDER | SWT.READ_ONLY);
131        parentChoice.setFont(curFont);
132        parentChoice.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
133        parentChoice.addSelectionListener(new SelectionAdapter() {
134            @Override
135            public void widgetSelected(SelectionEvent e) {
136                parentObj = groupList.get(parentChoice.getSelectionIndex());
137            }
138        });
139
140        groupList = new ArrayList<>();
141        Object obj = null;
142        Iterator<?> iterator = objList.iterator();
143        while (iterator.hasNext()) {
144            obj = iterator.next();
145            if (obj instanceof Group) {
146                Group g = (Group) obj;
147                groupList.add(g);
148                if (g.isRoot()) {
149                    parentChoice.add(HObject.SEPARATOR);
150                }
151                else {
152                    parentChoice.add(g.getPath() + g.getName() + HObject.SEPARATOR);
153                }
154            }
155        }
156
157        if (((Group) parentObj).isRoot()) {
158            parentChoice.select(parentChoice.indexOf(HObject.SEPARATOR));
159        }
160        else {
161            parentChoice.select(parentChoice.indexOf(parentObj.getPath() +
162                    parentObj.getName() + HObject.SEPARATOR));
163        }
164
165        // Only add "More" button if file is H5 type
166        if(isH5) {
167            moreOptionsComposite = new Composite(shell, SWT.NONE);
168            moreOptionsComposite.setLayout(new GridLayout(2, false));
169            moreOptionsComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
170
171            moreButton = new Button(moreOptionsComposite, SWT.PUSH);
172            moreButton.setFont(curFont);
173            moreButton.setText("   More   ");
174            moreButton.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
175            moreButton.addSelectionListener(new SelectionAdapter() {
176                @Override
177                public void widgetSelected(SelectionEvent e) {
178                    moreOptionsEnabled = !moreOptionsEnabled;
179
180                    if(moreOptionsEnabled) {
181                        addMoreOptions();
182                    }
183                    else {
184                        removeMoreOptions();
185                    }
186                }
187            });
188
189            dummyComposite = new Composite(moreOptionsComposite, SWT.NONE);
190            dummyComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
191        }
192        else {
193            // Add dummy label to take up space as dialog is resized
194            new Label(shell, SWT.NONE).setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
195        }
196
197        buttonComposite = new Composite(shell, SWT.NONE);
198        buttonComposite.setLayout(new GridLayout(2, true));
199        buttonComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
200
201        okButton = new Button(buttonComposite, SWT.PUSH);
202        okButton.setFont(curFont);
203        okButton.setText("   &OK   ");
204        okButton.setLayoutData(new GridData(SWT.END, SWT.FILL, true, false));
205        okButton.addSelectionListener(new SelectionAdapter() {
206            @Override
207            public void widgetSelected(SelectionEvent e) {
208                newObject = create();
209                if (newObject != null) {
210                    shell.dispose();
211                }
212            }
213        });
214
215        cancelButton = new Button(buttonComposite, SWT.PUSH);
216        cancelButton.setFont(curFont);
217        cancelButton.setText(" &Cancel ");
218        cancelButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, true, false));
219        cancelButton.addSelectionListener(new SelectionAdapter() {
220            @Override
221            public void widgetSelected(SelectionEvent e) {
222                newObject = null;
223                shell.dispose();
224            }
225        });
226
227        shell.pack();
228
229        shell.addDisposeListener(new DisposeListener() {
230            @Override
231            public void widgetDisposed(DisposeEvent e) {
232                if (curFont != null) curFont.dispose();
233            }
234        });
235
236        shell.setMinimumSize(shell.computeSize(SWT.DEFAULT, SWT.DEFAULT));
237
238        originalSize = shell.getSize();
239
240        Rectangle parentBounds = parent.getBounds();
241        Point shellSize = shell.getSize();
242        shell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
243                          (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
244
245        shell.open();
246
247        Display display = shell.getDisplay();
248        while (!shell.isDisposed())
249            if (!display.readAndDispatch())
250                display.sleep();
251    }
252
253    private HObject create() {
254        String name = null;
255        Group pgroup = null;
256        long gcpl = 0;
257
258        name = nameField.getText();
259        if (name == null || name.length() == 0) {
260            shell.getDisplay().beep();
261            Tools.showError(shell, "Create", "Group name is not specified.");
262            return null;
263        }
264
265        if (name.indexOf(HObject.SEPARATOR) >= 0) {
266            shell.getDisplay().beep();
267            Tools.showError(shell, "Create", "Group name cannot contain path.");
268            return null;
269        }
270
271        pgroup = groupList.get(parentChoice.getSelectionIndex());
272
273        if (pgroup == null) {
274            shell.getDisplay().beep();
275            Tools.showError(shell, "Create", "Parent group is null.");
276            return null;
277        }
278
279        Group obj = null;
280
281        if (orderFlags != null && orderFlags.isEnabled()) {
282            String order = orderFlags.getItem(orderFlags.getSelectionIndex());
283            if (order.equals("Tracked"))
284                creationOrder = Group.CRT_ORDER_TRACKED;
285            else if (order.equals("Tracked+Indexed"))
286                creationOrder = Group.CRT_ORDER_INDEXED;
287        }
288        else
289            creationOrder = 0;
290
291        if ((orderFlags != null) && ((orderFlags.isEnabled()) || (setLinkStorage.getSelection()))) {
292            int maxCompact = Integer.parseInt(compactField.getText());
293            int minDense = Integer.parseInt(indexedField.getText());
294
295            if ((maxCompact <= 0) || (maxCompact > 65536) || (minDense > 65536)) {
296                shell.getDisplay().beep();
297                Tools.showError(shell, "Create", "Max Compact and Min Indexed should be > 0 and < 65536.");
298                return null;
299            }
300
301            if (maxCompact < minDense) {
302                shell.getDisplay().beep();
303                Tools.showError(shell, "Create", "Min Indexed should be <= Max Compact");
304                return null;
305            }
306
307            try {
308                gcpl = fileFormat.createGcpl(creationOrder, maxCompact, minDense);
309            }
310            catch (Exception ex) {
311                ex.printStackTrace();
312            }
313        }
314
315        try {
316            if (isH5)
317                obj = fileFormat.createGroup(name, pgroup, 0, gcpl);
318            else
319                obj = fileFormat.createGroup(name, pgroup);
320        }
321        catch (Exception ex) {
322            shell.getDisplay().beep();
323            Tools.showError(shell, "Create", ex.getMessage());
324            return null;
325        }
326
327        return obj;
328    }
329
330    private void addMoreOptions() {
331        moreButton.setText("   Less   ");
332
333        creationOrderComposite = new Composite(moreOptionsComposite, SWT.BORDER);
334        creationOrderComposite.setLayout(new GridLayout(4, true));
335        creationOrderComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
336
337        creationOrderHelpButton = new Button(creationOrderComposite, SWT.PUSH);
338        creationOrderHelpButton.setImage(ViewProperties.getHelpIcon());
339        creationOrderHelpButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false));
340        creationOrderHelpButton.setToolTipText("Help on Creation Order");
341        creationOrderHelpButton.addSelectionListener(new SelectionAdapter() {
342            @Override
343            public void widgetSelected(SelectionEvent e) {
344                final String msg = "Use Creation Order allows the user to set the creation order \n"
345                        + "of links in a group, so that tracking, indexing, and iterating over links\n"
346                        + "in groups can be possible. \n\n"
347                        + "If the order flag Tracked is selected, links in a group can now \n"
348                        + "be explicitly tracked by the order that they were created. \n\n"
349                        + "If the order flag Tracked+Indexed is selected, links in a group can \n"
350                        + "now be explicitly tracked and indexed in the order that they were created. \n\n"
351                        + "The default order in which links in a group are listed is alphanumeric-by-name. \n\n\n";
352
353                Tools.showInformation(shell, "Create", msg);
354            }
355        });
356
357        useCreationOrder = new Button(creationOrderComposite, SWT.CHECK);
358        useCreationOrder.setFont(curFont);
359        useCreationOrder.setText("Use Creation Order");
360        useCreationOrder.addSelectionListener(new SelectionAdapter() {
361            @Override
362            public void widgetSelected(SelectionEvent e) {
363                boolean isOrder = useCreationOrder.getSelection();
364
365                if (isOrder)
366                    orderFlags.setEnabled(true);
367                else
368                    orderFlags.setEnabled(false);
369            }
370        });
371
372        Label label = new Label(creationOrderComposite, SWT.RIGHT);
373        label.setFont(curFont);
374        label.setText("Order Flags: ");
375        label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
376
377        orderFlags = new Combo(creationOrderComposite, SWT.DROP_DOWN | SWT.READ_ONLY);
378        orderFlags.setFont(curFont);
379        orderFlags.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
380        orderFlags.add("Tracked");
381        orderFlags.add("Tracked+Indexed");
382        orderFlags.select(orderFlags.indexOf("Tracked"));
383        orderFlags.setEnabled(false);
384
385
386        storageTypeComposite = new Composite(moreOptionsComposite, SWT.BORDER);
387        storageTypeComposite.setLayout(new GridLayout(3, true));
388        storageTypeComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 2, 1));
389
390        storageTypeHelpButton = new Button(storageTypeComposite, SWT.PUSH);
391        storageTypeHelpButton.setImage(ViewProperties.getHelpIcon());
392        storageTypeHelpButton.setToolTipText("Help on set Link Storage");
393        storageTypeHelpButton.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false));
394        storageTypeHelpButton.addSelectionListener(new SelectionAdapter() {
395            @Override
396            public void widgetSelected(SelectionEvent e) {
397                final String msg = "Set Link Storage allows the users to explicitly set the storage  \n"
398                        + "type of a group to be Compact or Indexed. \n\n"
399                        + "Compact Storage: For groups with only a few links, compact link storage\n"
400                        + "allows groups containing only a few links to take up much less space \n" + "in the file. \n\n"
401                        + "Indexed Storage: For groups with large number of links, indexed link storage  \n"
402                        + "provides a faster and more scalable method for storing and working with  \n"
403                        + "large groups containing many links. \n\n"
404                        + "The threshold for switching between the compact and indexed storage   \n"
405                        + "formats is either set to default values or can be set by the user. \n\n"
406                        + "<html><b>Max Compact</b></html> \n"
407                        + "Max Compact is the maximum number of links to store in the group in a  \n"
408                        + "compact format, before converting the group to the Indexed format. Groups \n"
409                        + "that are in compact format and in which the number of links rises above \n"
410                        + " this threshold are automatically converted to indexed format. \n\n"
411                        + "<html><b>Min Indexed</b></html> \n"
412                        + "Min Indexed is the minimum number of links to store in the Indexed format.   \n"
413                        + "Groups which are in indexed format and in which the number of links falls    \n"
414                        + "below this threshold are automatically converted to compact format. \n\n\n";
415
416                Tools.showInformation(shell, "Create", msg);
417            }
418        });
419
420        setLinkStorage = new Button(storageTypeComposite, SWT.CHECK);
421        setLinkStorage.setFont(curFont);
422        setLinkStorage.setText("Set Link Storage");
423        setLinkStorage.addSelectionListener(new SelectionAdapter() {
424            @Override
425            public void widgetSelected(SelectionEvent e) {
426                if (setLinkStorage.getSelection()) {
427                    compactField.setEnabled(true);
428                    indexedField.setEnabled(true);
429                }
430                else {
431                    compactField.setText("8");
432                    compactField.setEnabled(false);
433                    indexedField.setText("6");
434                    indexedField.setEnabled(false);
435                }
436            }
437        });
438
439        Composite indexedComposite = new Composite(storageTypeComposite, SWT.NONE);
440        indexedComposite.setLayout(new GridLayout(2, true));
441        indexedComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
442
443        Label minLabel = new Label(indexedComposite, SWT.LEFT);
444        minLabel.setFont(curFont);
445        minLabel.setText("Min Indexed: ");
446
447        Label maxLabel = new Label(indexedComposite, SWT.LEFT);
448        maxLabel.setFont(curFont);
449        maxLabel.setText("Max Compact: ");
450
451        indexedField = new Text(indexedComposite, SWT.SINGLE | SWT.BORDER);
452        indexedField.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
453        indexedField.setFont(curFont);
454        indexedField.setText("6");
455        indexedField.setTextLimit(5);
456        indexedField.setEnabled(false);
457        indexedField.addVerifyListener(new VerifyListener() {
458            @Override
459            public void verifyText(VerifyEvent e) {
460                String input = e.text;
461
462                char[] chars = new char[input.length()];
463                input.getChars(0, chars.length, chars, 0);
464                for (int i = 0; i < chars.length; i++) {
465                   if (!('0' <= chars[i] && chars[i] <= '9')) {
466                      e.doit = false;
467                      return;
468                   }
469                }
470            }
471        });
472
473        compactField = new Text(indexedComposite, SWT.SINGLE | SWT.BORDER);
474        compactField.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
475        compactField.setFont(curFont);
476        compactField.setText("8");
477        compactField.setTextLimit(5);
478        compactField.setEnabled(false);
479        compactField.addVerifyListener(new VerifyListener() {
480            @Override
481            public void verifyText(VerifyEvent e) {
482                String input = e.text;
483
484                char[] chars = new char[input.length()];
485                input.getChars(0, chars.length, chars, 0);
486                for (int i = 0; i < chars.length; i++) {
487                   if (!('0' <= chars[i] && chars[i] <= '9')) {
488                      e.doit = false;
489                      return;
490                   }
491                }
492            }
493        });
494
495        shell.pack();
496
497        shell.setMinimumSize(shell.computeSize(SWT.DEFAULT, SWT.DEFAULT));
498
499        Rectangle parentBounds = shell.getParent().getBounds();
500        Point shellSize = shell.getSize();
501        shell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
502                          (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
503    }
504
505    private void removeMoreOptions() {
506        moreButton.setText("   More   ");
507
508        creationOrderHelpButton.dispose();
509        storageTypeHelpButton.dispose();
510
511        creationOrderComposite.dispose();
512        storageTypeComposite.dispose();
513
514        shell.layout(true, true);
515        shell.pack();
516
517        shell.setMinimumSize(originalSize);
518        shell.setSize(originalSize);
519
520        Rectangle parentBounds = shell.getParent().getBounds();
521        Point shellSize = shell.getSize();
522        shell.setLocation((parentBounds.x + (parentBounds.width / 2)) - (shellSize.x / 2),
523                          (parentBounds.y + (parentBounds.height / 2)) - (shellSize.y / 2));
524    }
525}