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