/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jkiss.dbeaver.ui.editors.binary;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.action.*;
import org.eclipse.jface.viewers.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.*;
import org.eclipse.ui.editors.text.IEncodingSupport;
import org.eclipse.ui.part.EditorPart;
import org.eclipse.ui.part.WorkbenchPart;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.core.CoreMessages;
import org.jkiss.dbeaver.core.DBeaverCore;
import org.jkiss.dbeaver.core.DBeaverUI;
import org.jkiss.dbeaver.model.preferences.DBPPreferenceListener;
import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
import org.jkiss.dbeaver.ui.editors.EditorUtils;
import org.jkiss.dbeaver.ui.editors.binary.pref.HexPreferencesPage;
import org.jkiss.dbeaver.utils.ContentUtils;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.dbeaver.utils.RuntimeUtils;
import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
public class BinaryEditor extends EditorPart implements ISelectionProvider, IMenuListener, IResourceChangeListener {
private static final Log log = Log.getLog(HexEditControl.class);
private HexManager manager;
private DBPPreferenceListener preferencesChangeListener = null;
private Set<ISelectionChangedListener> selectionListeners = null;
public BinaryEditor()
{
super();
}
protected HexManager getManager()
{
return manager;
}
@Override
public void resourceChanged(IResourceChangeEvent event)
{
IResourceDelta delta= event.getDelta();
if (delta == null) {
return;
}
IPath localPath = null;
IEditorInput input = getEditorInput();
if (input instanceof IPathEditorInput) {
localPath = ((IPathEditorInput)input).getPath();
}
if (localPath == null) {
return;
}
localPath = ContentUtils.convertPathToWorkspacePath(localPath);
delta = delta.findMember(localPath);
if (delta == null) {
return;
}
if (delta.getKind() == IResourceDelta.CHANGED) {
// Refresh editor
DBeaverUI.asyncExec(new Runnable() {
@Override
public void run() {
if (manager != null) {
loadBinaryContent();
}
}
});
}
}
@Override
public void addSelectionChangedListener(ISelectionChangedListener listener)
{
if (listener == null) return;
if (selectionListeners == null) {
selectionListeners = new HashSet<>();
}
selectionListeners.add(listener);
}
@Override
public void createPartControl(Composite parent)
{
IStorage storage = EditorUtils.getStorageFromInput(getEditorInput());
manager = new HexManager();
manager.setTextFont(HexPreferencesPage.getPrefFontData());
manager.setMenuListener(this);
int editorStyle = SWT.NONE;
if (storage != null && storage.isReadOnly()) {
editorStyle = SWT.READ_ONLY;
}
manager.createEditorPart(parent, editorStyle);
FillLayout fillLayout = new FillLayout();
parent.setLayout(fillLayout);
loadBinaryContent();
// Register any global actions with the site's IActionBars.
IActionBars bars = getEditorSite().getActionBars();
createEditorAction(bars, IWorkbenchCommandConstants.EDIT_UNDO);
createEditorAction(bars, IWorkbenchCommandConstants.EDIT_REDO);
createEditorAction(bars, IWorkbenchCommandConstants.EDIT_CUT);
createEditorAction(bars, IWorkbenchCommandConstants.EDIT_COPY);
createEditorAction(bars, IWorkbenchCommandConstants.EDIT_PASTE);
createEditorAction(bars, IWorkbenchCommandConstants.EDIT_DELETE);
createEditorAction(bars, IWorkbenchCommandConstants.EDIT_SELECT_ALL);
createEditorAction(bars, IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE);
createEditorAction(bars, ITextEditorActionConstants.GOTO_LINE);
manager.addListener(new Listener() {
@Override
public void handleEvent(Event event)
{
firePropertyChange(PROP_DIRTY);
updateActionsStatus();
}
});
bars.updateActionBars();
preferencesChangeListener = new DBPPreferenceListener() {
@Override
public void preferenceChange(PreferenceChangeEvent event)
{
if (HexPreferencesPage.PROP_FONT_DATA.equals(event.getProperty()))
manager.setTextFont((FontData) event.getNewValue());
}
};
DBPPreferenceStore store = DBeaverCore.getGlobalPreferenceStore();
store.addPropertyChangeListener(preferencesChangeListener);
manager.addLongSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e)
{
if (selectionListeners == null) return;
long[] longSelection = HexEditControl.getLongSelection(e);
SelectionChangedEvent event = new SelectionChangedEvent(
BinaryEditor.this,
new StructuredSelection(new Object[]{
longSelection[0], longSelection[1]}));
for (ISelectionChangedListener selectionListener : selectionListeners) {
selectionListener.selectionChanged(event);
}
}
});
}
private void createEditorAction(IActionBars bars, String id)
{
bars.setGlobalActionHandler(id, new EditorAction(id));
}
private void loadBinaryContent()
{
String charset = GeneralUtils.UTF8_ENCODING;
IEditorInput editorInput = getEditorInput();
if (editorInput instanceof IEncodingSupport) {
charset = ((IEncodingSupport) editorInput).getEncoding();
}
File systemFile = null;
if (editorInput instanceof IPathEditorInput) {
systemFile = ((IPathEditorInput) editorInput).getPath().toFile();
}
if (systemFile != null) {
// open file
try {
manager.openFile(systemFile, charset);
}
catch (IOException e) {
log.error("Can't open binary content", e);
}
setPartName(systemFile.getName());
}
}
/**
* Removes preferences-changed listener
*
* @see WorkbenchPart#dispose()
*/
@Override
public void dispose()
{
if (manager != null) {
manager.dispose();
manager = null;
}
DBPPreferenceStore store = DBeaverCore.getGlobalPreferenceStore();
store.removePropertyChangeListener(preferencesChangeListener);
ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
super.dispose();
}
/**
* @see org.eclipse.ui.part.EditorPart#doSave(org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
public void doSave(IProgressMonitor monitor)
{
IEditorInput editorInput = getEditorInput();
// Sync file changes
IFile file = EditorUtils.getFileFromInput(editorInput);
if (file != null) {
final IPath absolutePath = file.getLocation();
File systemFile = absolutePath.toFile();
// Save to file
try {
manager.getContent().get(systemFile);
}
catch (IOException e) {
log.error("Can't save binary content", e);
}
// Sync file changes
ContentUtils.syncFile(RuntimeUtils.makeMonitor(monitor), file);
}
}
/**
* @see org.eclipse.ui.part.EditorPart#doSaveAs()
*/
@Override
public void doSaveAs()
{
}
@Override
public <T> T getAdapter(Class<T> adapter)
{
if (BinaryContent.class.isAssignableFrom(adapter)) {
return adapter.cast(manager.getContent());
} else if (HexManager.class.isAssignableFrom(adapter)) {
return adapter.cast(manager);
} else {
return super.getAdapter(adapter);
}
}
@Override
public ISelection getSelection()
{
long[] longSelection = manager.getSelection();
return new StructuredSelection(new Object[]
{longSelection[0], longSelection[1]});
}
@Override
public void init(IEditorSite site, final IEditorInput input)
throws PartInitException
{
boolean reset = getEditorInput() != null;
setSite(site);
if (!(input instanceof IPathEditorInput)) {
throw new PartInitException("Editor Input is not a file");
}
setInput(input);
if (reset) {
loadBinaryContent();
} else {
// when opening an external file the workbench (Eclipse 3.1) calls HexEditorActionBarContributor.
// MyStatusLineContributionItem.fill() before HexEditorActionBarContributor.setActiveEditor()
// but we need an editor to fill the status bar.
site.setSelectionProvider(this);
ResourcesPlugin.getWorkspace().addResourceChangeListener(this);
}
}
@Override
public boolean isDirty()
{
return manager.isDirty();
}
@Override
public boolean isSaveAsAllowed()
{
return true;
}
@Override
public void removeSelectionChangedListener(ISelectionChangedListener listener)
{
if (selectionListeners != null) {
selectionListeners.remove(listener);
}
}
@Override
public void setFocus()
{
}
@Override
public void setSelection(ISelection selection)
{
if (selection.isEmpty()) return;
StructuredSelection aSelection = (StructuredSelection) selection;
long[] startEnd = (long[]) aSelection.getFirstElement();
long start = startEnd[0];
long end = start;
if (startEnd.length > 1) {
end = startEnd[1];
}
if (aSelection.size() > 1) {
startEnd = (long[]) aSelection.toArray()[1];
end = startEnd[0];
if (startEnd.length > 1) {
end = startEnd[1];
}
}
manager.setSelection(start, end);
}
/**
* Updates the status of actions: enables/disables them depending on whether there is text selected
* and whether inserting or overwriting. Undo/redo actions enabled/disabled as well.
*/
public void updateActionsStatus()
{
boolean textSelected = manager.isTextSelected();
boolean lengthModifiable = textSelected && !manager.isOverwriteMode();
IActionBars bars = getEditorSite().getActionBars();
IAction action = bars.getGlobalActionHandler(IWorkbenchCommandConstants.EDIT_UNDO);
if (action != null) action.setEnabled(manager.canUndo());
action = bars.getGlobalActionHandler(IWorkbenchCommandConstants.EDIT_REDO);
if (action != null) action.setEnabled(manager.canRedo());
action = bars.getGlobalActionHandler(IWorkbenchCommandConstants.EDIT_CUT);
if (action != null) action.setEnabled(lengthModifiable);
action = bars.getGlobalActionHandler(IWorkbenchCommandConstants.EDIT_COPY);
if (action != null) action.setEnabled(textSelected);
action = bars.getGlobalActionHandler(IWorkbenchCommandConstants.EDIT_DELETE);
if (action != null) action.setEnabled(lengthModifiable);
bars.updateActionBars();
}
@Override
public void menuAboutToShow(IMenuManager manager)
{
manager.add(new EditorAction(IWorkbenchCommandConstants.EDIT_COPY, CoreMessages.dialog_find_replace_copy));
manager.add(new EditorAction(IWorkbenchCommandConstants.EDIT_PASTE, CoreMessages.dialog_find_replace_paste));
manager.add(new EditorAction(IWorkbenchCommandConstants.EDIT_SELECT_ALL, CoreMessages.controls_querylog_action_select_all));
manager.add(new EditorAction(IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE, CoreMessages.dialog_find_replace_find_replace));
manager.add(new EditorAction(ITextEditorActionDefinitionIds.LINE_GOTO, CoreMessages.dialog_find_replace_goto_line));
manager.add(new Separator());
manager.add(new EditorAction(IWorkbenchCommandConstants.EDIT_UNDO, CoreMessages.dialog_find_replace_undo));
manager.add(new EditorAction(IWorkbenchCommandConstants.EDIT_REDO, CoreMessages.dialog_find_replace_redo));
}
class EditorAction extends Action {
String actionId = null;
EditorAction(String actionId, String text)
{
super(text);
this.actionId = actionId;
setActionDefinitionId(actionId);
}
EditorAction(String id)
{
actionId = id;
setActionDefinitionId(actionId);
}
@Override
public void run()
{
switch (actionId) {
case IWorkbenchCommandConstants.EDIT_UNDO:
manager.doUndo();
break;
case IWorkbenchCommandConstants.EDIT_REDO:
manager.doRedo();
break;
case IWorkbenchCommandConstants.EDIT_CUT:
manager.doCut();
break;
case IWorkbenchCommandConstants.EDIT_COPY:
manager.doCopy();
break;
case IWorkbenchCommandConstants.EDIT_PASTE:
manager.doPaste();
break;
case IWorkbenchCommandConstants.EDIT_DELETE:
manager.doDelete();
break;
case IWorkbenchCommandConstants.EDIT_SELECT_ALL:
manager.doSelectAll();
break;
case IWorkbenchCommandConstants.EDIT_FIND_AND_REPLACE:
manager.doFind();
break;
case ITextEditorActionDefinitionIds.LINE_GOTO:
manager.doGoTo();
break;
}
}
}
}