/******************************************************************************* * Copyright (c) 2013, 2017 Red Hat Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Neil Guzman - initial API and implementation *******************************************************************************/ package org.eclipse.linuxtools.internal.rpm.createrepo.form; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.linuxtools.internal.rpm.createrepo.Activator; import org.eclipse.linuxtools.internal.rpm.createrepo.CreaterepoPreferenceConstants; import org.eclipse.linuxtools.internal.rpm.createrepo.CreaterepoProject; import org.eclipse.linuxtools.internal.rpm.createrepo.ICreaterepoConstants; import org.eclipse.linuxtools.internal.rpm.createrepo.Messages; import org.eclipse.linuxtools.internal.rpm.createrepo.tree.CreaterepoCategoryModel; import org.eclipse.linuxtools.internal.rpm.createrepo.tree.CreaterepoTreeCategory; import org.eclipse.linuxtools.internal.rpm.createrepo.tree.CreaterepoTreeContentProvider; import org.eclipse.linuxtools.internal.rpm.createrepo.tree.CreaterepoTreeLabelProvider; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.forms.IManagedForm; import org.eclipse.ui.forms.editor.FormEditor; import org.eclipse.ui.forms.editor.FormPage; import org.eclipse.ui.forms.widgets.ExpandableComposite; import org.eclipse.ui.forms.widgets.FormToolkit; import org.eclipse.ui.forms.widgets.ScrolledForm; import org.eclipse.ui.forms.widgets.Section; import org.eclipse.ui.menus.IMenuService; import org.osgi.service.prefs.BackingStoreException; /** * This page will allow the user to view/edit some of the repo * xml metadata (i.e., repo, revision, etc.). */ public class MetadataPage extends FormPage { private CreaterepoProject project; private IEclipsePreferences eclipsePreferences; private FormToolkit toolkit; private Text tagTxt; private Tree tagsTree; private TreeViewer tagsTreeViewer; private static final String MENU_URI = "toolbar:formsToolbar"; //$NON-NLS-1$ private static final String HEADER_ICON = "/icons/library_obj.gif"; //$NON-NLS-1$ /** Default constructor. */ public MetadataPage(FormEditor editor, CreaterepoProject project) { super(editor, Messages.MetadataPage_title, Messages.MetadataPage_title); this.project = project; eclipsePreferences = project.getEclipsePreferences(); } @Override protected void createFormContent(IManagedForm managedForm) { // setting up the form page super.createFormContent(managedForm); GridLayout layout = new GridLayout(); toolkit = managedForm.getToolkit(); ScrolledForm form = managedForm.getForm(); form.setText(Messages.MetadataPage_formHeaderText); form.setImage(Activator.getImageDescriptor(HEADER_ICON).createImage()); ToolBarManager toolbarManager = (ToolBarManager) form.getToolBarManager(); toolkit.decorateFormHeading(form.getForm()); // add the menuContribution from MANIFEST.MF to the form IMenuService menuService = getSite().getService(IMenuService.class); menuService.populateContributionManager(toolbarManager, MENU_URI); toolbarManager.update(true); layout = new GridLayout(); layout.marginWidth = 6; layout.marginHeight = 12; form.getBody().setLayout(layout); //--------------------------------- REVISION SECTION START ---------- // Section and its client area to manage updating revision info Section revSection = toolkit.createSection(form.getBody(), Section.DESCRIPTION | ExpandableComposite.TITLE_BAR); layout = new GridLayout(); GridData data = new GridData(); data.verticalAlignment = GridData.FILL; data.horizontalAlignment = GridData.FILL; data.grabExcessHorizontalSpace = true; revSection.setText(Messages.MetadataPage_sectionTitleRevision); revSection.setDescription(Messages.MetadataPage_sectionInstructionRevision); revSection.setLayoutData(data); // the client area containing the editing fields Composite sectionClient = toolkit.createComposite(revSection); layout = new GridLayout(2, false); layout.marginWidth = 1; layout.marginHeight = 7; sectionClient.setLayout(layout); Text revisionTxt = createTextFieldWithLabel(sectionClient, Messages.MetadataPage_labelRevision); String prefRevisionTxt = eclipsePreferences.get(CreaterepoPreferenceConstants.PREF_REVISION, ICreaterepoConstants.EMPTY_STRING); if (!prefRevisionTxt.isEmpty()) { revisionTxt.setText(prefRevisionTxt); } revisionTxt.addSelectionListener(SelectionListener.widgetDefaultSelectedAdapter(e -> { String revisionText = revisionTxt.getText().trim(); savePreferences(CreaterepoPreferenceConstants.PREF_REVISION, revisionText); } )); revSection.setClient(sectionClient); //---------- REVISION SECTION END //--------------------------------- TAGS SECTION START ---------- // Section and its client area to manage tags Section tagSection = toolkit.createSection(form.getBody(), Section.DESCRIPTION | ExpandableComposite.TITLE_BAR); layout = new GridLayout(); tagSection.setText(Messages.MetadataPage_sectionTitleTags); tagSection.setDescription(Messages.MetadataPage_sectionInstructionTags); tagSection.setLayoutData(expandComposite()); // the client area containing the tags Composite sectionClientTags = toolkit.createComposite(tagSection); layout = new GridLayout(2, false); layout.marginWidth = 1; layout.marginHeight = 7; sectionClientTags.setLayout(layout); tagTxt = createTextFieldWithLabel(sectionClientTags, Messages.MetadataPage_labelTags); tagTxt.addSelectionListener(new AddTagButtonListener()); tagsTreeViewer = new TreeViewer(sectionClientTags, SWT.BORDER | SWT.SINGLE | SWT.HORIZONTAL | SWT.VERTICAL | SWT.LEFT_TO_RIGHT | SWT.SMOOTH); tagsTreeViewer.setContentProvider(new CreaterepoTreeContentProvider()); tagsTreeViewer.setLabelProvider(new CreaterepoTreeLabelProvider()); CreaterepoCategoryModel model = new CreaterepoCategoryModel(project); tagsTreeViewer.setInput(model); // change the tag text field on change (make editing tag easier) tagsTreeViewer.addSelectionChangedListener(event -> { if (tagsTree.getSelectionCount() == 1) { TreeItem treeItem = tagsTree.getSelection()[0]; if (!(treeItem.getData() instanceof CreaterepoTreeCategory)) { String tag = (String) treeItem.getData(); tagTxt.setText(tag); } else { tagTxt.setText(ICreaterepoConstants.EMPTY_STRING); } } }); // expand or shrink a category tagsTreeViewer.addDoubleClickListener(event -> { IStructuredSelection selection = (IStructuredSelection) tagsTreeViewer.getSelection(); if (selection.getFirstElement() instanceof CreaterepoTreeCategory) { CreaterepoTreeCategory category = (CreaterepoTreeCategory) selection.getFirstElement(); tagsTreeViewer.setExpandedState(category, !tagsTreeViewer.getExpandedState(category)); } }); tagsTree = tagsTreeViewer.getTree(); tagsTree.setLayoutData(expandComposite()); // everything to do with the buttons Composite buttonList = toolkit.createComposite(sectionClientTags); layout = new GridLayout(); data = new GridData(SWT.BEGINNING, SWT.FILL, false, true); layout.marginWidth = 0; layout.marginHeight = 0; buttonList.setLayout(layout); buttonList.setLayoutData(data); createPushButton(buttonList, Messages.MetadataPage_buttonAddTag, toolkit).addSelectionListener(new AddTagButtonListener()); createPushButton(buttonList, Messages.MetadataPage_buttonEditTag, toolkit).addSelectionListener(new EditTagButtonListener()); createPushButton(buttonList, Messages.MetadataPage_buttonRemoveTag, toolkit).addSelectionListener(new RemoveTagButtonListener()); tagSection.setClient(sectionClientTags); //---------- TAGS SECTION END refreshTree(); managedForm.refresh(); } /** * Refresh the tree. This includes removing the expand button of a * category if there are no tags placed under it. */ private void refreshTree() { // expand categories with no tags under them to remove expand button for (TreeItem treeItem : tagsTree.getItems()) { if (treeItem.getData() instanceof CreaterepoTreeCategory) { CreaterepoTreeCategory category = (CreaterepoTreeCategory) treeItem.getData(); if (category.getTags().isEmpty()) { tagsTreeViewer.expandToLevel(category, 1); tagsTreeViewer.update(category, null); } } } } /** * Make a GridData that expands to fill both horizontally * and vertically. * * @return The created GridData. */ private static GridData expandComposite() { GridData data = new GridData(); data.verticalAlignment = GridData.FILL; data.horizontalAlignment = GridData.FILL; data.grabExcessHorizontalSpace = true; data.grabExcessVerticalSpace = true; return data; } /** * Create a push style button. * * @param parent The parent the button will belong to. * @param buttonText The text show on the button. * @param toolkit The form toolkit used in creating a button. * @return The button created. */ private static Button createPushButton(Composite parent, String buttonText, FormToolkit toolkit) { Button button = toolkit.createButton(parent, buttonText, SWT.PUSH | SWT.FLAT | SWT.CENTER | SWT.LEFT_TO_RIGHT); button.setFont(parent.getFont()); GridData gd = new GridData(SWT.FILL, SWT.BEGINNING, true, false); button.setLayoutData(gd); return button; } /** * Create a text field with a label. * * @param parent The parent of the text field and label. * @param labelName The name on the label. * @return The newly created text field. */ private Text createTextFieldWithLabel(Composite parent, String labelName) { // set up the area in which the label and text will reside Composite areaLabelText = new Composite(parent, SWT.NONE); GridData layoutData = new GridData(); GridLayout gridlayout = new GridLayout(2, false); layoutData.horizontalAlignment = GridData.FILL; layoutData.verticalAlignment = GridData.CENTER; layoutData.horizontalSpan = 2; layoutData.grabExcessHorizontalSpace = true; areaLabelText.setLayoutData(layoutData); areaLabelText.setLayout(gridlayout); // create the label Label respositoryBaseURLLbl = new Label(areaLabelText, SWT.NONE); respositoryBaseURLLbl.setText(labelName); layoutData = new GridData(); layoutData.widthHint = 100; layoutData.horizontalAlignment = GridData.BEGINNING; layoutData.verticalAlignment = GridData.CENTER; respositoryBaseURLLbl.setLayoutData(layoutData); // create the text field Text textField = new Text(areaLabelText, SWT.SINGLE); layoutData = new GridData(); layoutData.horizontalIndent = 50; layoutData.grabExcessHorizontalSpace = true; layoutData.horizontalAlignment = GridData.FILL; layoutData.verticalAlignment = GridData.CENTER; // achieve flat look (don't put SWT.BORDER) textField.setData(FormToolkit.KEY_DRAW_BORDER, FormToolkit.TEXT_BORDER); textField.setLayoutData(layoutData); toolkit.paintBordersFor(areaLabelText); return textField; } /** * Save the project preferences of some value to a key. * * @param key The preferences key. * @param val The value to save. */ private void savePreferences(String key, String val) { eclipsePreferences.put(key, val); try { eclipsePreferences.flush(); refreshTree(); } catch (BackingStoreException e) { Activator.logError(Messages.MetadataPage_errorSavingPreferences, e); } } /** * Prepare the tags to be saved to the preference. This class gets all the tags * from a category and transforms it into a semicolon-delimited string. * * @param category The category to prepare the tag string for. * @return A semicolon-delimited string of tags taken from the category. */ private static String preparePreferenceTag(CreaterepoTreeCategory category) { String preferenceToSave = ICreaterepoConstants.EMPTY_STRING; if (!category.getTags().isEmpty()) { for (String tag : category.getTags()){ preferenceToSave = preferenceToSave.concat(tag+ICreaterepoConstants.DELIMITER); } // remove the hanging delimiter preferenceToSave = preferenceToSave.substring(0, preferenceToSave.length()-1); } return preferenceToSave; } /** * Method to add the tag from the tag text field to the category in the tree. * Used by the "Add" button and the default operation when ENTER is pressed while * in the tag text field. */ private void addTag() { IStructuredSelection selection = (IStructuredSelection) tagsTreeViewer.getSelection(); if (selection.getFirstElement() instanceof CreaterepoTreeCategory) { CreaterepoTreeCategory category = (CreaterepoTreeCategory) selection.getFirstElement(); String text = tagTxt.getText().trim(); if (!text.isEmpty()) { category.addTag(text); tagsTreeViewer.refresh(category, false); tagsTreeViewer.setExpandedState(category, true); tagTxt.setText(ICreaterepoConstants.EMPTY_STRING); String preferenceToSave = preparePreferenceTag(category); savePreferences(category.getName(), preferenceToSave); } } } /** * Handle the add button execution on the Metadata page. */ private class AddTagButtonListener extends SelectionAdapter { @Override public void widgetSelected(SelectionEvent e) { addTag(); } @Override public void widgetDefaultSelected(SelectionEvent e) { addTag(); } } /** * Handle the edit button execution on the Metadata page. */ private class EditTagButtonListener extends SelectionAdapter { @Override public void widgetSelected(SelectionEvent e) { if (tagsTree.getSelectionCount() == 1) { TreeItem treeItem = tagsTree.getSelection()[0]; String newTag = tagTxt.getText().trim(); if (!(treeItem.getData() instanceof CreaterepoTreeCategory) && !newTag.isEmpty()) { CreaterepoTreeCategory parent = (CreaterepoTreeCategory) treeItem.getParentItem().getData(); String oldTag = (String) treeItem.getData(); int oldTagIndex = parent.getTags().indexOf(oldTag); if (parent.getTags().indexOf(newTag) == -1) { parent.getTags().set(oldTagIndex, newTag); tagsTreeViewer.refresh(parent, true); tagsTree.setSelection(treeItem); String preferenceToSave = preparePreferenceTag(parent); savePreferences(parent.getName(), preferenceToSave); } } } } } /** * Handle the remove button execution on the Metadata page. */ private class RemoveTagButtonListener extends SelectionAdapter { @Override public void widgetSelected(SelectionEvent e) { if (tagsTree.getSelectionCount() == 1) { TreeItem treeItem = tagsTree.getSelection()[0]; if (!(treeItem.getData() instanceof CreaterepoTreeCategory)) { CreaterepoTreeCategory parent = (CreaterepoTreeCategory) treeItem.getParentItem().getData(); String tag = (String) treeItem.getData(); parent.removeTag(tag); tagsTreeViewer.refresh(parent, true); tagTxt.setText(ICreaterepoConstants.EMPTY_STRING); String preferenceToSave = preparePreferenceTag(parent); savePreferences(parent.getName(), preferenceToSave); } } } } }