1 /*******************************************************************************
2  * Copyright (c) 2014, 2017 Rapicorp Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     Rapicorp Corporation - initial API and implementation
13  *     Martin Karpisek <martin.karpisek@gmail.com> - Bug 351356
14  *******************************************************************************/
15 package org.eclipse.pde.internal.ui.editor.product;
16 
17 import static org.eclipse.swt.events.SelectionListener.widgetSelectedAdapter;
18 
19 import java.net.MalformedURLException;
20 import java.net.URL;
21 import org.eclipse.core.runtime.IStatus;
22 import org.eclipse.core.runtime.Status;
23 import org.eclipse.equinox.internal.p2.ui.dialogs.TextURLDropAdapter;
24 import org.eclipse.jface.dialogs.IDialogConstants;
25 import org.eclipse.jface.dialogs.StatusDialog;
26 import org.eclipse.jface.viewers.*;
27 import org.eclipse.jface.window.Window;
28 import org.eclipse.pde.core.IModelChangedEvent;
29 import org.eclipse.pde.internal.core.iproduct.*;
30 import org.eclipse.pde.internal.ui.*;
31 import org.eclipse.pde.internal.ui.editor.*;
32 import org.eclipse.pde.internal.ui.parts.TablePart;
33 import org.eclipse.swt.SWT;
34 import org.eclipse.swt.custom.CCombo;
35 import org.eclipse.swt.custom.TableEditor;
36 import org.eclipse.swt.dnd.*;
37 import org.eclipse.swt.events.ControlEvent;
38 import org.eclipse.swt.events.ControlListener;
39 import org.eclipse.swt.graphics.Image;
40 import org.eclipse.swt.layout.GridData;
41 import org.eclipse.swt.layout.GridLayout;
42 import org.eclipse.swt.widgets.*;
43 import org.eclipse.ui.actions.ActionFactory;
44 import org.eclipse.ui.forms.widgets.FormToolkit;
45 import org.eclipse.ui.forms.widgets.Section;
46 
47 public class UpdatesSection extends TableSection {
48 
49 	private class RepositoryDialog extends StatusDialog {
50 		private Text fLocation;
51 		private IRepositoryInfo fEdit;
52 
RepositoryDialog(Shell shell, IRepositoryInfo repo)53 		public RepositoryDialog(Shell shell, IRepositoryInfo repo) {
54 			super(shell);
55 			fEdit = repo;
56 			setTitle(PDEUIMessages.UpdatesSection_RepositoryDialogTitle);
57 		}
58 
59 		@Override
createDialogArea(Composite parent)60 		protected Control createDialogArea(Composite parent) {
61 			Composite comp = (Composite) super.createDialogArea(parent);
62 			((GridLayout) comp.getLayout()).numColumns = 2;
63 			SWTFactory.createLabel(comp, PDEUIMessages.UpdatesSection_Location, 1);
64 			fLocation = SWTFactory.createSingleText(comp, 1);
65 			GridData data = new GridData(GridData.FILL_HORIZONTAL);
66 			data.widthHint = convertHorizontalDLUsToPixels(IDialogConstants.ENTRY_FIELD_WIDTH);
67 			fLocation.setLayoutData(data);
68 			DropTarget target = new DropTarget(fLocation, DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_LINK);
69 			target.setTransfer(new Transfer[] {URLTransfer.getInstance(), FileTransfer.getInstance()});
70 			target.addDropListener(new TextURLDropAdapter(fLocation, true));
71 			fLocation.addModifyListener(e -> validate());
72 
73 			if (fEdit != null) {
74 				if (fEdit.getURL() != null) {
75 					fLocation.setText(fEdit.getURL());
76 				}
77 			} else {
78 				String initialText = "http://"; //$NON-NLS-1$
79 				fLocation.setText(initialText);
80 				fLocation.setSelection(initialText.length());
81 			}
82 
83 			validate();
84 
85 			return comp;
86 		}
87 
validate()88 		protected void validate() {
89 			String location = fLocation.getText().trim();
90 			if (location.length() == 0) {
91 				updateStatus(new Status(IStatus.ERROR, IPDEUIConstants.PLUGIN_ID, PDEUIMessages.UpdatesSection_ErrorLocationNoName));
92 				return;
93 			}
94 			try {
95 				new URL(location);
96 			} catch (MalformedURLException e) {
97 				updateStatus(new Status(IStatus.ERROR, IPDEUIConstants.PLUGIN_ID, PDEUIMessages.UpdatesSection_ErrorInvalidURL));
98 				return;
99 			}
100 			updateStatus(Status.OK_STATUS);
101 		}
102 
103 		@Override
okPressed()104 		protected void okPressed() {
105 			if (fEdit != null) {
106 				// Remove the repository and add a new one
107 				getProduct().removeRepositories(new IRepositoryInfo[] {fEdit});
108 			}
109 
110 			IProductModelFactory factory = getModel().getFactory();
111 			fEdit = factory.createRepositoryInfo();
112 			String location = fLocation.getText().trim();
113 			if (!location.startsWith("http://") && !location.startsWith("file:")) //$NON-NLS-1$ //$NON-NLS-2$
114 				location = "http://" + location; //$NON-NLS-1$
115 			fEdit.setURL(location);
116 			fEdit.setEnabled(true);
117 			getProduct().addRepositories(new IRepositoryInfo[] {fEdit});
118 			super.okPressed();
119 		}
120 
121 		@Override
createHelpControl(Composite parent)122 		protected Control createHelpControl(Composite parent) {
123 			return parent;
124 		}
125 
126 		/**
127 		 * @return a repository info containing the values set in the dialog or <code>null</code>
128 		 */
getResult()129 		public IRepositoryInfo getResult() {
130 			return fEdit;
131 		}
132 
133 	}
134 
135 
136 	private class ContentProvider implements IStructuredContentProvider {
137 
ContentProvider()138 		ContentProvider() {
139 		}
140 
141 		@Override
getElements(Object inputElement)142 		public Object[] getElements(Object inputElement) {
143 			if (inputElement instanceof IProduct) {
144 				return ((IProduct) inputElement).getRepositories();
145 			}
146 			return new Object[0];
147 		}
148 
149 
150 	}
151 
152 	private class LabelProvider extends PDELabelProvider {
153 		@Override
getColumnImage(Object obj, int index)154 		public Image getColumnImage(Object obj, int index) {
155 			if (index == 0)
156 				return get(PDEPluginImages.DESC_REPOSITORY_OBJ);
157 			return null;
158 		}
159 
160 		@Override
getColumnText(Object obj, int index)161 		public String getColumnText(Object obj, int index) {
162 			IRepositoryInfo repo = (IRepositoryInfo) obj;
163 			switch (index) {
164 				case 0 :
165 					return repo.getURL();
166 				case 1 :
167 					return Boolean.toString(repo.getEnabled());
168 			}
169 			return null;
170 		}
171 
172 	}
173 
174 	private TableViewer fRepositoryTable;
175 	private TableEditor fEnabledColumnEditor;
176 
UpdatesSection(PDEFormPage formPage, Composite parent)177 	public UpdatesSection(PDEFormPage formPage, Composite parent) {
178 		super(formPage, parent, Section.DESCRIPTION, getButtonLabels());
179 	}
180 
getButtonLabels()181 	private static String[] getButtonLabels() {
182 		String[] labels = new String[4];
183 		labels[0] = PDEUIMessages.UpdatesSection_add;
184 		labels[1] = PDEUIMessages.UpdatesSection_edit;
185 		labels[2] = PDEUIMessages.UpdatesSection_remove;
186 		labels[3] = PDEUIMessages.UpdatesSection_removeAll;
187 		return labels;
188 	}
189 
190 	@Override
createClient(Section section, FormToolkit toolkit)191 	protected void createClient(Section section, FormToolkit toolkit) {
192 
193 		section.setLayout(FormLayoutFactory.createClearGridLayout(false, 1));
194 		GridData sectionData = new GridData(GridData.FILL_BOTH);
195 		sectionData.verticalSpan = 2;
196 		section.setLayoutData(sectionData);
197 
198 		Composite container = createClientContainer(section, 2, toolkit);
199 		createViewerPartControl(container, SWT.MULTI | SWT.FULL_SELECTION, 2, toolkit);
200 		container.setLayoutData(new GridData(GridData.FILL_BOTH));
201 
202 		TablePart tablePart = getTablePart();
203 		fRepositoryTable = tablePart.getTableViewer();
204 
205 		final Table table = fRepositoryTable.getTable();
206 
207 		final TableColumn locationColumn = new TableColumn(table, SWT.LEFT);
208 		locationColumn.setText(PDEUIMessages.UpdatesSection_LocationColumn);
209 		locationColumn.setWidth(240);
210 
211 		final TableColumn enabledColumn = new TableColumn(table, SWT.LEFT);
212 		enabledColumn.setText(PDEUIMessages.UpdatesSection_EnabledColumn);
213 		enabledColumn.setWidth(120);
214 
215 		GridData data = (GridData) tablePart.getControl().getLayoutData();
216 		data.minimumWidth = 200;
217 
218 		tablePart.setButtonEnabled(0, isEditable());
219 		tablePart.setButtonEnabled(1, isEditable());
220 
221 		table.setHeaderVisible(true);
222 		toolkit.paintBordersFor(container);
223 
224 		table.addControlListener(new ControlListener() {
225 
226 			@Override
227 			public void controlMoved(ControlEvent e) {
228 			}
229 
230 			@Override
231 			public void controlResized(ControlEvent e) {
232 				int size = table.getSize().x;
233 				locationColumn.setWidth(size / 6 * 5);
234 				enabledColumn.setWidth(size / 6 * 1);
235 			}
236 
237 		});
238 
239 
240 
241 		fRepositoryTable.setLabelProvider(new LabelProvider());
242 		fRepositoryTable.setContentProvider(new ContentProvider());
243 		fRepositoryTable.setComparator(new ViewerComparator() {
244 			@Override
245 			public int compare(Viewer viewer, Object e1, Object e2) {
246 				IRepositoryInfo r1 = (IRepositoryInfo) e1;
247 				IRepositoryInfo r2 = (IRepositoryInfo) e2;
248 				return super.compare(viewer, r1.getURL(), r2.getURL());
249 			}
250 		});
251 		fRepositoryTable.setInput(getProduct());
252 		createEditors();
253 
254 		section.setClient(container);
255 
256 		section.setText(PDEUIMessages.UpdatesSection_title);
257 		section.setDescription(PDEUIMessages.UpdatesSection_description);
258 	}
259 
260 
261 	@Override
buttonSelected(int index)262 	protected void buttonSelected(int index) {
263 		switch (index) {
264 			case 0 :
265 				handleAdd();
266 				break;
267 			case 1 :
268 			handleEdit(fRepositoryTable.getStructuredSelection());
269 				break;
270 			case 2 :
271 				handleDelete();
272 				break;
273 			case 3 :
274 				handleRemoveAll();
275 				break;
276 		}
277 	}
278 
279 
280 	@Override
handleDoubleClick(IStructuredSelection selection)281 	protected void handleDoubleClick(IStructuredSelection selection) {
282 		handleEdit(selection);
283 	}
284 
285 	@Override
doGlobalAction(String actionId)286 	public boolean doGlobalAction(String actionId) {
287 		if (actionId.equals(ActionFactory.DELETE.getId())) {
288 			handleDelete();
289 			return true;
290 		}
291 		if (actionId.equals(ActionFactory.CUT.getId())) {
292 			handleDelete();
293 			return false;
294 		}
295 		return super.doGlobalAction(actionId);
296 	}
297 
298 
handleEdit(IStructuredSelection selection)299 	private void handleEdit(IStructuredSelection selection) {
300 		clearEditors();
301 		if (!selection.isEmpty()) {
302 			Object[] objects = selection.toArray();
303 			RepositoryDialog dialog = new RepositoryDialog(PDEPlugin.getActiveWorkbenchShell(), (IRepositoryInfo) objects[0]);
304 			if (dialog.open() == Window.OK) {
305 				IRepositoryInfo result = dialog.getResult();
306 				if (result != null) {
307 					fRepositoryTable.refresh();
308 					fRepositoryTable.setSelection(new StructuredSelection(result));
309 					updateButtons();
310 				}
311 			}
312 		}
313 	}
314 
handleDelete()315 	private void handleDelete() {
316 		clearEditors();
317 		IStructuredSelection ssel = fRepositoryTable.getStructuredSelection();
318 		if (!ssel.isEmpty()) {
319 			Object[] objects = ssel.toArray();
320 			IRepositoryInfo[] repos = new IRepositoryInfo[objects.length];
321 			System.arraycopy(objects, 0, repos, 0, objects.length);
322 			getProduct().removeRepositories(repos);
323 			fRepositoryTable.refresh(false);
324 			updateButtons();
325 		}
326 	}
327 
handleRemoveAll()328 	private void handleRemoveAll() {
329 		clearEditors();
330 		getProduct().removeRepositories(getProduct().getRepositories());
331 		fRepositoryTable.refresh(false);
332 		updateButtons();
333 	}
334 
handleAdd()335 	private void handleAdd() {
336 		clearEditors();
337 		RepositoryDialog dialog = new RepositoryDialog(PDEPlugin.getActiveWorkbenchShell(), null);
338 		if (dialog.open() == Window.OK) {
339 			IRepositoryInfo result = dialog.getResult();
340 			if (result != null) {
341 				fRepositoryTable.refresh();
342 				fRepositoryTable.setSelection(new StructuredSelection(result));
343 				updateButtons();
344 			}
345 		}
346 	}
347 
getProduct()348 	private IProduct getProduct() {
349 		return getModel().getProduct();
350 	}
351 
getModel()352 	private IProductModel getModel() {
353 		return (IProductModel) getPage().getPDEEditor().getAggregateModel();
354 	}
355 
356 	@Override
refresh()357 	public void refresh() {
358 		fRepositoryTable.refresh();
359 		updateButtons();
360 		super.refresh();
361 	}
362 
363 
364 	@Override
selectionChanged(IStructuredSelection selection)365 	protected void selectionChanged(IStructuredSelection selection) {
366 		getPage().getPDEEditor().setSelection(selection);
367 		updateButtons();
368 	}
369 
370 	@Override
setFormInput(Object input)371 	public boolean setFormInput(Object input) {
372 		if (input instanceof IProductPlugin) {
373 			fRepositoryTable.setSelection(new StructuredSelection(input), true);
374 			return true;
375 		}
376 		return super.setFormInput(input);
377 	}
378 
379 	@Override
modelChanged(IModelChangedEvent e)380 	public void modelChanged(IModelChangedEvent e) {
381 		// No need to call super, handling world changed event here
382 		fRepositoryTable.setInput(getProduct());
383 		fRepositoryTable.refresh();
384 		updateButtons();
385 		clearEditors();
386 	}
387 
updateButtons()388 	private void updateButtons() {
389 		TablePart tablePart = getTablePart();
390 		ISelection selection = getViewerSelection();
391 		boolean enabled = isEditable() && !selection.isEmpty() && selection instanceof IStructuredSelection && ((IStructuredSelection) selection).getFirstElement() instanceof IRepositoryInfo;
392 		tablePart.setButtonEnabled(1, enabled);
393 		tablePart.setButtonEnabled(2, enabled);
394 		tablePart.setButtonEnabled(3, isEditable() && getProduct().getRepositories().length > 0);
395 	}
396 
clearEditors()397 	private void clearEditors() {
398 		Control oldEditor = fEnabledColumnEditor.getEditor();
399 		if (oldEditor != null && !oldEditor.isDisposed())
400 			oldEditor.dispose();
401 
402 	}
403 
createEditors()404 	private void createEditors() {
405 		final Table table = fRepositoryTable.getTable();
406 
407 		fEnabledColumnEditor = new TableEditor(table);
408 		fEnabledColumnEditor.horizontalAlignment = SWT.CENTER;
409 		fEnabledColumnEditor.grabHorizontal = true;
410 		fEnabledColumnEditor.minimumWidth = 50;
411 
412 		table.addSelectionListener(widgetSelectedAdapter(e -> showControls()));
413 	}
414 
showControls()415 	private void showControls() {
416 		// Clean up any previous editor control
417 		clearEditors();
418 
419 		// Identify the selected row
420 		Table table = fRepositoryTable.getTable();
421 		IStructuredSelection selection = fRepositoryTable.getStructuredSelection();
422 		if (selection.isEmpty())
423 			return;
424 		final TableItem item = table.getSelection()[0];
425 		if (item != null && !isEditable())
426 			return;
427 
428 		if (item != null) {
429 			final IRepositoryInfo repo = (IRepositoryInfo) selection.getFirstElement();
430 			final CCombo combo = new CCombo(table, SWT.BORDER | SWT.READ_ONLY);
431 			combo.setItems(new String[] {Boolean.toString(true), Boolean.toString(false)});
432 			combo.setText(item.getText(1));
433 			combo.pack();
434 			combo.addSelectionListener(widgetSelectedAdapter(e -> {
435 				item.setText(1, combo.getText());
436 				repo.setEnabled(Boolean.parseBoolean(combo.getText()));
437 			}));
438 			fEnabledColumnEditor.setEditor(combo, item, 1);
439 		}
440 	}
441 }
442