| /*=============================================================================# |
| # Copyright (c) 2017, 2021 Stephan Wahlbrink and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License 2.0 which is available at |
| # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 |
| # which is available at https://www.apache.org/licenses/LICENSE-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 |
| # |
| # Contributors: |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.ecommons.ui.mpbv; |
| |
| import java.io.File; |
| |
| import com.ibm.icu.lang.UCharacter; |
| import com.ibm.icu.text.UCharacterIterator; |
| |
| import org.eclipse.core.databinding.observable.list.IListChangeListener; |
| import org.eclipse.core.databinding.observable.list.IObservableList; |
| import org.eclipse.core.databinding.observable.list.ListChangeEvent; |
| import org.eclipse.jface.databinding.swt.typed.WidgetProperties; |
| import org.eclipse.jface.resource.ImageRegistry; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.dnd.DND; |
| import org.eclipse.swt.dnd.DropTargetAdapter; |
| import org.eclipse.swt.dnd.DropTargetEvent; |
| import org.eclipse.swt.dnd.FileTransfer; |
| import org.eclipse.swt.dnd.TextTransfer; |
| 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.Combo; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.ToolBar; |
| import org.eclipse.swt.widgets.ToolItem; |
| |
| import org.eclipse.statet.jcommons.lang.NonNullByDefault; |
| import org.eclipse.statet.jcommons.lang.Nullable; |
| |
| import org.eclipse.statet.ecommons.ui.util.DNDUtils; |
| import org.eclipse.statet.ecommons.ui.util.InputHistory; |
| import org.eclipse.statet.ecommons.ui.util.LayoutUtils; |
| import org.eclipse.statet.ecommons.ui.util.UIAccess; |
| import org.eclipse.statet.internal.ecommons.ui.Messages; |
| import org.eclipse.statet.internal.ecommons.ui.UIMiscellanyPlugin; |
| |
| |
| @NonNullByDefault |
| public class BrowserAddressBar extends Composite { |
| |
| |
| private final PageBookBrowserPage page; |
| |
| private final InputHistory<String> inputHistory; |
| private @Nullable IListChangeListener<String> urlHistoryListener; |
| |
| private ToolBar toolBar; |
| |
| private Combo textControl; |
| private ToolItem goControl; |
| |
| private boolean isEdited; |
| |
| |
| public BrowserAddressBar(final Composite parent, final PageBookBrowserPage page, |
| final InputHistory<String> urlHistory) { |
| super(parent, SWT.NONE); |
| |
| this.page= page; |
| this.inputHistory= urlHistory; |
| |
| final GridLayout layout= LayoutUtils.newCompositeGrid(2); |
| layout.marginBottom= 2; |
| layout.horizontalSpacing= 1; |
| setLayout(layout); |
| |
| create(); |
| |
| init(); |
| addListener(SWT.Dispose, this::onDispose); |
| } |
| |
| private void init() { |
| if (this.inputHistory != null) { |
| final IObservableList<String> textItemList= WidgetProperties.items() |
| .observe(this.textControl); |
| this.urlHistoryListener= new IListChangeListener<String>() { |
| @Override |
| public void handleListChange(final ListChangeEvent<? extends String> event) { |
| event.diff.applyTo(textItemList); |
| } |
| }; |
| this.inputHistory.addListChangeListener(this.urlHistoryListener); |
| textItemList.addAll(this.inputHistory); |
| } |
| } |
| |
| protected void onDispose(final Event e) { |
| if (this.urlHistoryListener != null) { |
| this.inputHistory.removeListChangeListener(this.urlHistoryListener); |
| this.urlHistoryListener= null; |
| } |
| } |
| |
| |
| protected void create() { |
| final ImageRegistry imageRegistry= UIMiscellanyPlugin.getInstance().getImageRegistry(); |
| |
| { final Combo text= new Combo(this, SWT.DROP_DOWN); |
| text.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(final SelectionEvent e) { |
| if (!text.getListVisible()) { |
| go(); |
| } |
| } |
| @Override |
| public void widgetDefaultSelected(final SelectionEvent e) { |
| go(); |
| } |
| }); |
| text.addListener(SWT.Modify, |
| (final Event event) -> { |
| this.isEdited= true; |
| }); |
| text.addListener(SWT.KeyDown, |
| (final Event event) -> { |
| if (!event.doit) { |
| return; |
| } |
| if (event.character == SWT.ESC) { |
| if (!BrowserAddressBar.this.textControl.getListVisible()) { |
| final BrowserSession session= BrowserAddressBar.this.page.getSession(); |
| setText(session.getViewUrl(), false); |
| } |
| } |
| }); |
| DNDUtils.addDropSupport(text, new DropTargetAdapter() { |
| private @Nullable String checkFileData(final @Nullable Object data) { |
| if (data instanceof String[] && ((String[]) data).length == 1) { |
| return ((String[]) data)[0]; |
| } |
| return null; |
| } |
| private @Nullable String checkTextData(final @Nullable Object data) { |
| if (data instanceof String) { |
| return (String) data; |
| } |
| return null; |
| } |
| @Override |
| public void dragEnter(final DropTargetEvent event) { |
| if (FileTransfer.getInstance().isSupportedType(event.currentDataType)) { |
| switch (event.detail) { |
| case DND.DROP_COPY: |
| case DND.DROP_LINK: |
| return; |
| case DND.DROP_DEFAULT: |
| if ((event.operations & DND.DROP_LINK) != 0) { |
| event.detail= DND.DROP_LINK; |
| return; |
| } |
| if ((event.operations & DND.DROP_COPY) != 0) { |
| event.detail= DND.DROP_COPY; |
| return; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| else if (TextTransfer.getInstance().isSupportedType(event.currentDataType)) { |
| switch (event.detail) { |
| case DND.DROP_COPY: |
| return; |
| case DND.DROP_DEFAULT: |
| if ((event.operations & DND.DROP_COPY) != 0) { |
| event.detail= DND.DROP_COPY; |
| return; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| event.detail= DND.DROP_NONE; |
| return; |
| } |
| @Override |
| public void dragOperationChanged(final DropTargetEvent event) { |
| dragEnter(event); |
| } |
| @Override |
| public void dragOver(final DropTargetEvent event) { |
| event.feedback= DND.FEEDBACK_NONE; |
| } |
| @Override |
| public void drop(final DropTargetEvent event) { |
| String text= null; |
| if (FileTransfer.getInstance().isSupportedType(event.currentDataType)) { |
| final String filePath= checkFileData(event.data); |
| try { |
| text= new File(filePath).toURI().toURL().toExternalForm(); |
| } |
| catch (final Exception e) {} |
| } |
| else if (TextTransfer.getInstance().isSupportedType(event.currentDataType)) { |
| text= checkTextData(event.data); |
| if (text != null) { |
| text= checkPasteText((String)event.data); |
| } |
| } |
| |
| if (text != null) { |
| setText(text, true); |
| } |
| else { |
| event.detail= DND.DROP_NONE; |
| } |
| } |
| }, new Transfer[] { |
| FileTransfer.getInstance(), |
| TextTransfer.getInstance() |
| }); |
| |
| text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); |
| this.textControl= text; |
| } |
| |
| this.toolBar= new ToolBar(this, SWT.FLAT); |
| this.toolBar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); |
| |
| { final ToolItem button= new ToolItem(this.toolBar, SWT.PUSH); |
| button.setToolTipText(Messages.Browser_Go_tooltip); |
| button.setImage(imageRegistry.get(UIMiscellanyPlugin.LOCTOOL_GO_IMAGE_ID)); |
| button.setDisabledImage(imageRegistry.get(UIMiscellanyPlugin.LOCTOOL_GO_D_IMAGE_ID)); |
| button.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(final SelectionEvent e) { |
| go(); |
| } |
| }); |
| this.goControl= button; |
| } |
| } |
| |
| protected void setText(final String text, final boolean edit) { |
| this.textControl.setText(text); |
| this.isEdited= edit; |
| } |
| |
| protected String checkPasteText(final String s) { |
| final StringBuilder sb= new StringBuilder(s.length()); |
| for (final UCharacterIterator iter= UCharacterIterator.getInstance(s); true; ) { |
| final int cp= iter.nextCodePoint(); |
| if (cp == UCharacterIterator.DONE) { |
| break; |
| } |
| if (UCharacter.isPrintable(cp)) { |
| sb.appendCodePoint(cp); |
| } |
| } |
| return sb.toString(); |
| } |
| |
| protected void go() { |
| final String orgUrl= this.textControl.getText(); |
| final @Nullable String checkedUrl= checkUrl(orgUrl); |
| if (checkedUrl == null) { |
| return; |
| } |
| setText(checkedUrl, false); |
| |
| this.page.setUrl(checkedUrl); |
| this.page.setFocusToBrowser(); |
| |
| if (this.inputHistory != null) { |
| this.inputHistory.append(checkedUrl); |
| } |
| } |
| |
| protected @Nullable String checkUrl(String url) { |
| if (url.indexOf(':') == -1) { |
| if (url.length() >= 1 && url.charAt(0) == '/') { |
| url= url.substring((url.length() >= 2 && url.charAt(1) == '/') ? 2 : 1); |
| } |
| url= "http://" + url; //$NON-NLS-1$ |
| } |
| if (url.equals("http://")) { //$NON-NLS-1$ |
| return null; |
| } |
| return url; |
| } |
| |
| protected void onPageChanged() { |
| if (UIAccess.isOkToUse(this.textControl) |
| && !this.textControl.isFocusControl() ) { |
| final BrowserSession session= this.page.getSession(); |
| setText(session.getViewUrl(), false); |
| } |
| } |
| |
| public boolean isEdited() { |
| return this.isEdited; |
| } |
| |
| } |