/******************************************************************************* * Copyright (c) 2013 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 java.io.File; import java.util.List; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.linuxtools.internal.rpm.createrepo.Activator; import org.eclipse.linuxtools.internal.rpm.createrepo.CreaterepoProject; import org.eclipse.linuxtools.internal.rpm.createrepo.CreaterepoUtils; import org.eclipse.linuxtools.internal.rpm.createrepo.ICreaterepoConstants; import org.eclipse.linuxtools.internal.rpm.createrepo.Messages; import org.eclipse.linuxtools.internal.rpm.createrepo.dnd.ImportRPMDropListener; import org.eclipse.swt.SWT; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.FileTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; 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.FileDialog; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.console.MessageConsoleStream; 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; /** * This page will allow the user to import RPMs from either the * file system or the workspace. The RPMs imported will be the * RPMs used when executing the createrepo command. */ public class ImportRPMsPage extends FormPage implements IResourceChangeListener { private CreaterepoProject project; private Tree tree; private static final String MENU_URI = "toolbar:formsToolbar"; //$NON-NLS-1$ private static final String HEADER_ICON = "/icons/repository_rep.gif"; //$NON-NLS-1$ /** * Default constructor. * * @param editor The editor. * @param project The project. */ public ImportRPMsPage(FormEditor editor, CreaterepoProject project) { super(editor, Messages.ImportRPMsPage_title, Messages.ImportRPMsPage_title); this.project = project; } @Override public void init(IEditorSite site, IEditorInput input) { super.init(site, input); // add the resource change listener ResourcesPlugin.getWorkspace().addResourceChangeListener(this); } @Override public void dispose() { // remove the resource change listener ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); super.dispose(); } @Override protected void createFormContent(IManagedForm managedForm) { // setting up the form page super.createFormContent(managedForm); GridLayout layout = new GridLayout(); FormToolkit toolkit = managedForm.getToolkit(); ScrolledForm form = managedForm.getForm(); form.setText(Messages.ImportRPMsPage_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(2, true); layout.marginWidth = 6; layout.marginHeight = 12; form.getBody().setLayout(layout); // Section and its client area to manage importing the RPMs Section rpmSection = toolkit.createSection(form.getBody(), Section.DESCRIPTION | ExpandableComposite.TITLE_BAR); layout = new GridLayout(); rpmSection.setText(Messages.ImportRPMsPage_sectionTitle); rpmSection.setDescription(Messages.ImportRPMsPage_sectionInstruction); rpmSection.setLayoutData(expandComposite()); // the client area containing the tree + buttons Composite sectionClient = toolkit.createComposite(rpmSection); layout = new GridLayout(2, false); layout.marginWidth = 1; layout.marginHeight = 7; sectionClient.setLayout(layout); TreeViewer viewer = new TreeViewer(sectionClient, SWT.BORDER | SWT.MULTI | SWT.HORIZONTAL | SWT.VERTICAL | SWT.LEFT_TO_RIGHT | SWT.SMOOTH); viewer.addDropSupport(DND.DROP_COPY, new Transfer[] {FileTransfer.getInstance()}, new ImportRPMDropListener(viewer, project)); tree = viewer.getTree(); tree.setLayoutData(expandComposite()); Composite buttonList = toolkit.createComposite(sectionClient); layout = new GridLayout(); GridData data = new GridData(SWT.BEGINNING, SWT.FILL, false, true); layout.marginWidth = 0; layout.marginHeight = 0; buttonList.setLayout(layout); buttonList.setLayoutData(data); createPushButton(buttonList, Messages.ImportRPMsPage_buttonImportRPMs, toolkit).addSelectionListener(new ImportButtonListener()); createPushButton(buttonList, Messages.ImportRPMsPage_buttonRemoveRPMs, toolkit).addSelectionListener(new RemoveButtonListener()); new Label(buttonList, SWT.NONE).setLayoutData(new GridData(0,0)); createPushButton(buttonList, Messages.ImportRPMsPage_buttonCreateRepo, toolkit).addSelectionListener(new CreaterepoButtonListener()); refreshTree(); rpmSection.setClient(sectionClient); managedForm.refresh(); } /** * 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.grabExcessVerticalSpace = true; data.grabExcessHorizontalSpace = 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; } /** * On creating the form content the tree will be populated with * RPMs found in the root of the current project. * * @throws CoreException Thrown when getting rpms from project fails. */ private void refreshTree() { tree.removeAll(); try { for (IResource rpm : project.getRPMs()) { addItemToTree(rpm.getName()); } } catch (CoreException e) { Activator.logError(Messages.ImportRPMsPage_errorRefreshingTree, e); } tree.setFocus(); } /** * Add a new item to the tree if it does not yet exist. A null or empty * string will be ignored. * * @param itemName The name of the new item. * @return True if it does not exist and has been added, false otherwise. */ private boolean addItemToTree(String itemName) { boolean exists = false; if (itemName == null || itemName.isEmpty()) return false; // check to see if the tree item exists in the tree if (tree.getItemCount() > 0) { for (TreeItem item : tree.getItems()) { if (item.getText().equals(itemName)) { exists = true; } } } // if the tree item doesnt exists or the tree is empty if (!exists || tree.getItemCount() == 0) { TreeItem treeItem = new TreeItem(tree, SWT.NONE); treeItem.setText(itemName); return true; } return false; } /** * Handle the import button execution on the Import RPMs page. */ private class ImportButtonListener extends SelectionAdapter { private final String[] EXTENSION_FILTERS = {"*." + ICreaterepoConstants.RPM_FILE_EXTENSION}; //$NON-NLS-1$ @Override public void widgetSelected(SelectionEvent e) { IWorkbench workbench = PlatformUI.getWorkbench(); Shell shell = workbench.getModalDialogShellProvider().getShell(); FileDialog fileDialog = new FileDialog(shell, SWT.MULTI); fileDialog.setFilterExtensions(EXTENSION_FILTERS); if (fileDialog.open() != null) { String[] files = fileDialog.getFileNames(); if (files.length > 0) { String directoryPath = fileDialog.getFilterPath(); for (String file : files) { File externalFile = new File(directoryPath, file); try { project.importRPM(externalFile); } catch (CoreException e1) { Activator.logError(Messages.ImportButtonListener_error, e1); } } refreshTree(); } } } } /** * Handle the remove button execution on the Import RPMs page. */ private class RemoveButtonListener extends SelectionAdapter { @Override public void widgetSelected(SelectionEvent e) { TreeItem[] selection = tree.getSelection(); try { List<IResource> rpms = project.getRPMs(); if (selection.length > 0 && !rpms.isEmpty()) { for (IResource resource : rpms) { for (TreeItem treeItem : selection) { deleteIfEquals(resource, treeItem); } } } refreshTree(); } catch (CoreException e1) { Activator.logError(Messages.RemoveButtonListener_error, e1); } } /** * Delete the resource if the tree item has the same name as it. * * @param resource The resource in the project (the RPM). * @param treeItem The RPM in the tree. * @throws CoreException Thrown when deleting fails. */ private void deleteIfEquals(IResource resource, TreeItem treeItem) throws CoreException { if (resource.getName().equals(treeItem.getText())) { resource.delete(false, new NullProgressMonitor()); } } } /** * Handle the createrepo button execution on the Import RPMs page. */ private class CreaterepoButtonListener extends SelectionAdapter { @Override public void widgetSelected(SelectionEvent e) { Job executeCreaterepo = new Job(Messages.Createrepo_jobName) { @Override protected IStatus run(IProgressMonitor monitor) { try { monitor.beginTask(Messages.CreaterepoProject_executeCreaterepo, IProgressMonitor.UNKNOWN); MessageConsoleStream os = CreaterepoUtils.findConsole(Messages.CreaterepoProject_consoleName) .newMessageStream(); return project.createrepo(os); } catch (CoreException e) { Activator.logError(Messages.Createrepo_errorExecuting, e); } finally { monitor.done(); } return null; } }; executeCreaterepo.setUser(true); executeCreaterepo.schedule(); } } @Override public void resourceChanged(IResourceChangeEvent event) { // might have to place the delete/close events to RepoMetadataFormEditor to // occur for all the pages switch (event.getType()) { case IResourceChangeEvent.POST_CHANGE: try { IPath projectPath = project.getContentFolder().getFullPath(); IResourceDelta delta = event.getDelta().findMember(projectPath); // delta is only null when nothing changed within the project's // content folder if (delta != null) { delta.accept(new CreaterepoDeltaVisitor()); } } catch (CoreException e) { Activator.logError(Messages.ImportRPMsPage_errorResourceChanged, e); } break; } } /** * Class to control what to do if something happens in the workspace. */ private class CreaterepoDeltaVisitor implements IResourceDeltaVisitor { @Override public boolean visit(IResourceDelta delta) { // exit if the project is being removed or closed if (delta.getKind() == IResourceDelta.REMOVED || (delta.getFlags() | delta.getKind()) == (IResourceDelta.OPEN | IResourceDelta.CHANGED)) { return false; } // get the files that were removed/added and exit if nothing was removed/added IResourceDelta[] removedFiles = delta.getAffectedChildren(IResourceDelta.REMOVED); IResourceDelta[] addedFiles = delta.getAffectedChildren(IResourceDelta.ADDED); if (removedFiles.length <= 0 && addedFiles.length == 0) { return false; } // check if at least 1 of the files removed is an RPM and break out if so boolean rpmsDeleted = false; for (IResourceDelta resourceDelta : removedFiles) { String extension = resourceDelta.getResource().getFileExtension(); if (extension != null && extension.equals(ICreaterepoConstants.RPM_FILE_EXTENSION)) { rpmsDeleted = true; break; } } // check if at least 1 of the files added is an RPM and break out if so boolean rpmsAdded = false; for (IResourceDelta resourceDelta : addedFiles) { String extension = resourceDelta.getResource().getFileExtension(); if (extension != null && extension.equals(ICreaterepoConstants.RPM_FILE_EXTENSION)) { rpmsAdded = true; break; } } // exit if none of the removed/added files is an RPM; no need to update list if (!rpmsDeleted && !rpmsAdded) { return false; } // deals with updating the UI of the page PlatformUI.getWorkbench().getDisplay().asyncExec(() -> refreshTree()); return false; } } }