Compare commits

...
16 Commits
Author SHA1 Message Date
hajdam 721c5f0cdc Release 0.2.2 2024-10-13 15:57:31 +02:00
hajdam 3722d5d28a Use of application context - 2 2024-10-13 14:06:34 +02:00
hajdam 39eca4ceea Use of application context 2024-10-13 13:59:00 +02:00
hajdam 6691b83558 Rearranging operations - 6 2024-10-12 23:29:11 +02:00
hajdam 341fb4ddad Minor color tweak 2024-10-12 18:37:34 +02:00
hajdam eec740a735 Updated about dialog 2024-10-12 14:23:09 +02:00
hajdam eb14750c3f Basic D-pad support 2024-10-11 23:32:00 +02:00
hajdam 924d69bdc9 Fixed mirror cursor 2024-10-11 20:34:04 +02:00
hajdam 4340d3d3d3 Update from library + support for nonprintable characters 2024-10-10 19:33:40 +02:00
hajdam ee07614fe7 Gradle version update 2024-10-04 21:14:51 +02:00
hajdam 713b2d37d6 Fix for Hindi translation 2024-10-01 20:45:39 +02:00
Miroslav HajdaandGitHub 1c1d890f65 Merge pull request #10 from G0v1ndD3v/master
Completed Hindi translation.
2024-10-01 20:33:38 +02:00
govindscode 950699102d Completed Hindi translation. 2024-10-01 23:41:26 +05:30
hajdam 6b79cdc3d0 Legacy permission request 2024-09-29 20:14:32 +02:00
hajdam 2569575340 Handle open with 2024-09-28 23:10:36 +02:00
hajdam a542cb3cb4 Version raised 2024-09-28 20:14:48 +02:00
112 changed files with 4117 additions and 1334 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules">
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
</set>
</option>
</component>
</project>
+8 -4
View File
@@ -6,10 +6,10 @@ android {
compileSdk 34
defaultConfig {
applicationId "org.exbin.bined.editor.android"
minSdk 27
minSdk 24
targetSdk 34
versionCode 5
versionName "0.2.1"
versionCode 8
versionName "0.2.2"
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}
buildTypes {
@@ -33,10 +33,14 @@ dependencies {
implementation 'androidx.appcompat:appcompat:1.7.0'
// implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
compileOnly group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2'
// implementation 'com.github.rustamg:file-dialogs:1.0'
implementation 'com.github.rustamg:file-dialogs:1.0'
implementation 'com.google.android.material:material:1.12.0'
implementation 'androidx.preference:preference:1.2.1'
testImplementation 'junit:junit:4.13.2'
testImplementation fileTree(include: ['*.jar'], dir: 'libs')
testImplementation 'androidx.test:core:1.6.1'
// testImplementation 'androidx.test.ext:junit:1.2.1'
testCompileOnly group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2'
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
// implementation 'org.exbin.bined:bined-core:0.3.0-SNAPSHOT'
@@ -24,7 +24,7 @@ import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.EditableBinaryData;
/**
* Binary editor component utilities.
* Binary editor utilities.
*
* @author ExBin Project (https://exbin.org)
*/
+2 -2
View File
@@ -15,7 +15,7 @@
*/
package org.exbin.bined;
import javax.annotation.Nullable;
import javax.annotation.Nonnull;
import org.exbin.auxiliary.binary_data.BinaryData;
/**
@@ -30,7 +30,7 @@ public interface DataProvider {
*
* @return binary data
*/
@Nullable
@Nonnull
BinaryData getContentData();
/**
@@ -15,14 +15,11 @@
*/
package org.exbin.bined;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Interface for selection change listener.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public interface SelectionChangedListener {
/**
@@ -598,7 +598,6 @@ public class BasicCodeAreaScrolling {
return Optional.of(targetScrollPosition);
}
@Nonnull
public void updateMaximumScrollPosition(long rowsPerDocument, int rowsPerPage, int charactersPerRow, int charactersPerPage, int lastCharOffset, int lastRowOffset) {
maximumScrollPosition.reset();
if (rowsPerDocument > rowsPerPage) {
@@ -15,7 +15,6 @@
*/
package org.exbin.bined.basic;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.annotation.concurrent.Immutable;
/**
@@ -23,7 +22,6 @@ import javax.annotation.concurrent.Immutable;
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
@Immutable
public class ScrollViewDimension {
@@ -26,7 +26,7 @@ public interface CodeAreaColorGroup {
/**
* Returns unique string identifier.
*
* <p>
* Custom implementations should start with full package name to avoid
* collisions.
*
@@ -25,12 +25,12 @@ import javax.annotation.Nonnull;
public interface BinaryDataCommand {
/**
* Returns command name.
* Returns type of the command.
*
* @return name
* @return command type
*/
@Nonnull
String getName();
BinaryDataCommandType getType();
/**
* Performs operation on given document.
@@ -15,20 +15,10 @@
*/
package org.exbin.bined.operation;
import javax.annotation.Nonnull;
/**
* Command type interface.
*
* @author ExBin Project (https://exbin.org)
*/
public interface BinaryDataCommandType {
/**
* Returns type name.
*
* @return type name
*/
@Nonnull
String getName();
}
@@ -21,7 +21,7 @@ import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Interface for compound XBUP editor operation.
* Interface for compound operation.
*
* @author ExBin Project (https://exbin.org)
*/
@@ -25,12 +25,12 @@ import javax.annotation.Nonnull;
public interface BinaryDataOperation {
/**
* Returns operation name.
* Returns type of the operation.
*
* @return operation name
* @return operation type
*/
@Nonnull
String getName();
BinaryDataOperationType getType();
/**
* Performs operation.
@@ -15,20 +15,10 @@
*/
package org.exbin.bined.operation;
import javax.annotation.Nonnull;
/**
* Operation type interface.
*
* @author ExBin Project (https://exbin.org)
*/
public interface BinaryDataOperationType {
/**
* Returns operation type name.
*
* @return operation type name
*/
@Nonnull
String getName();
}
@@ -15,14 +15,11 @@
*/
package org.exbin.bined.operation.undo;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Code area undo support handler.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public interface BinaryDataUndoRedoState {
/**
+24 -14
View File
@@ -2,35 +2,45 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<application
android:name=".ApplicationContext"
android:allowBackup="true"
android:icon="@mipmap/bined"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:targetApi="34">
<activity
android:name=".SettingsActivity"
android:exported="false"
android:label="@string/title_activity_settings" />
<activity
android:name=".MainActivity"
android:exported="true"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.EDIT" />
<category android:name="android.intent.category.OPENABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:host="*" />
<data android:mimeType="*/*" />
</intent-filter>
</activity>
<activity
android:name=".SettingsActivity"
android:exported="false"
android:label="@string/title_activity_settings" />
</application>
<uses-permission
android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
</manifest>
@@ -110,8 +110,11 @@ public class TwoDimensionScrollView extends FrameLayout {
// we rely on the fact the View.scrollBy calls scrollTo.
if (getChildCount() > 0) {
View child = getChildAt(0);
x = clamp(x, getWidth() - getPaddingRight() - getPaddingLeft(), child.getWidth());
y = clamp(y, getHeight() - getPaddingBottom() - getPaddingTop(), child.getHeight());
// TODO For some reason child size resets to zero, disabling this as workaround :-/
if (child.getWidth() > 0 && child.getHeight() > 0) {
x = clamp(x, getWidth() - getPaddingRight() - getPaddingLeft(), child.getWidth());
y = clamp(y, getHeight() - getPaddingBottom() - getPaddingTop(), child.getHeight());
}
if (x != getScrollX() || y != getScrollY()) {
super.scrollTo(x, y);
}
@@ -35,6 +35,8 @@ import org.exbin.bined.CharsetStreamTranslator;
import org.exbin.bined.CodeAreaUtils;
import org.exbin.bined.CodeCharactersCase;
import org.exbin.bined.CodeType;
import org.exbin.bined.android.capability.CharAssessorPainterCapable;
import org.exbin.bined.android.capability.ColorAssessorPainterCapable;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -119,6 +121,48 @@ public class CodeAreaAndroidUtils {
return new Rect(left, top, right, bottom);
}
/**
* Finds specific color assessor if present.
*
* @param <T> color assessor
* @param painter painter
* @param assessorClass color assessor class
* @return color assessor if present
*/
@Nullable
public static <T extends CodeAreaColorAssessor> T findColorAssessor(ColorAssessorPainterCapable painter, Class<T> assessorClass) {
CodeAreaColorAssessor colorAssessor = painter.getColorAssessor();
do {
if (assessorClass.isInstance(colorAssessor)) {
return assessorClass.cast(colorAssessor);
}
colorAssessor = colorAssessor.getParentColorAssessor().orElse(null);
} while (colorAssessor != null);
return null;
}
/**
* Finds specific character assessor if present.
*
* @param <T> character assessor
* @param painter painter
* @param assessorClass character assessor class
* @return character assessor if present
*/
@Nullable
public static <T extends CodeAreaCharAssessor> T findCharAssessor(CharAssessorPainterCapable painter, Class<T> assessorClass) {
CodeAreaCharAssessor charAssessor = painter.getCharAssessor();
do {
if (assessorClass.isInstance(charAssessor)) {
return assessorClass.cast(charAssessor);
}
charAssessor = charAssessor.getParentCharAssessor().orElse(null);
} while (charAssessor != null);
return null;
}
public static int getMetaMaskDown() {
return KeyEvent.META_CTRL_MASK;
}
@@ -0,0 +1,62 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.android;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.CodeAreaSection;
/**
* Code area character assessor.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public interface CodeAreaCharAssessor extends CodeAreaPaintAssessor {
/**
* Returns preview character for particular position.
*
* @param rowDataPosition row data position
* @param byteOnRow byte on current row
* @param charOnRow character on current row
* @param section current section
* @return color or null for default color
*/
char getPreviewCharacter(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section);
/**
* Returns preview character for cursor position.
*
* @param rowDataPosition row data position
* @param byteOnRow byte on current row
* @param charOnRow character on current row
* @param cursorData cursor data
* @param cursorDataLength cursor data length
* @param section current section
* @return color or null for default color
*/
char getPreviewCursorCharacter(long rowDataPosition, int byteOnRow, int charOnRow, byte[] cursorData, int cursorDataLength, CodeAreaSection section);
/**
* Returns parent character assessor if present.
*
* @return character assessor
*/
@Nonnull
Optional<CodeAreaCharAssessor> getParentCharAssessor();
}
@@ -0,0 +1,65 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.android;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.CodeAreaSection;
/**
* Code area color assessor.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public interface CodeAreaColorAssessor extends CodeAreaPaintAssessor {
/**
* Returns background color for particular code.
*
* @param rowDataPosition row data position
* @param byteOnRow byte on current row
* @param charOnRow character on current row
* @param section current section
* @param inSelection true if in selection
* @return color or null for default color
*/
@Nullable
Integer getPositionTextColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section, boolean inSelection);
/**
* Returns background color for particular code.
*
* @param rowDataPosition row data position
* @param byteOnRow byte on current row
* @param charOnRow character on current row
* @param section current section
* @param inSelection true if in selection
* @return color or null for default color
*/
@Nullable
Integer getPositionBackgroundColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section, boolean inSelection);
/**
* Returns parent color assessor if present.
*
* @return color assessor
*/
@Nonnull
Optional<CodeAreaColorAssessor> getParentColorAssessor();
}
@@ -31,7 +31,7 @@ public interface CodeAreaCommandHandler {
/**
* Notifies command handler about end of sequence of append-able commands.
*/
void undoSequenceBreak();
void sequenceBreak();
/**
* Keyboard key was pressed.
@@ -93,16 +93,6 @@ public interface CodeAreaCommandHandler {
*/
void paste();
/**
* Expands selection to all data.
*/
void selectAll();
/**
* Clears data selection.
*/
void clearSelection();
/**
* Pastes content of clipboard to cursor area analyzing string code.
*/
@@ -115,6 +105,16 @@ public interface CodeAreaCommandHandler {
*/
boolean canPaste();
/**
* Expands selection to all data.
*/
void selectAll();
/**
* Clears data selection.
*/
void clearSelection();
/**
* Moves caret with mouse event.
*
@@ -50,18 +50,18 @@ import javax.annotation.ParametersAreNonnullByDefault;
public abstract class CodeAreaCore extends ViewGroup implements CodeAreaControl {
@Nonnull
private BinaryData contentData = EmptyBinaryData.INSTANCE;
protected BinaryData contentData = EmptyBinaryData.INSTANCE;
@Nonnull
protected final PrimaryView primaryView;
@Nonnull
private CodeAreaCommandHandler commandHandler;
protected CodeAreaCommandHandler commandHandler;
private final List<DataChangedListener> dataChangedListeners = new ArrayList<>();
protected final List<DataChangedListener> dataChangedListeners = new ArrayList<>();
private float touchPositionX;
private float touchPositionY;
protected float touchPositionX;
protected float touchPositionY;
/**
* Creates new instance with default command handler and painter.
@@ -88,6 +88,7 @@ public abstract class CodeAreaCore extends ViewGroup implements CodeAreaControl
}
private void init() {
setContentDescription("Code area");
// TODO: Use swing color instead
// setBackgroundColor(ColorUtils.WHITE);
// setFocusable(true);
@@ -0,0 +1,34 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.android;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Code area paint assessor.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public interface CodeAreaPaintAssessor {
/**
* Reports start of the paint operation.
*
* @param codeAreaPaintState paint state
*/
void startPaint(CodeAreaPaintState codeAreaPaintState);
}
@@ -0,0 +1,63 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.android;
import java.nio.charset.Charset;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.BinaryData;
import org.exbin.bined.CodeAreaSection;
import org.exbin.bined.CodeAreaSelection;
import org.exbin.bined.android.basic.color.CodeAreaColorsProfile;
/**
* Code area paint state.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public interface CodeAreaPaintState {
@Nonnull
CodeAreaSection getActiveSection();
@Nonnull
CodeAreaColorsProfile getColorsProfile();
@Nonnull
Charset getCharset();
@Nonnull
byte[] getRowData();
int getMaxBytesPerChar();
int getCodeLastCharPos();
int getCharactersPerRow();
int getBytesPerRow();
long getDataSize();
// TODO: Replace with row data only?
@Nonnull
BinaryData getContentData();
@Nullable
CodeAreaSelection getSelectionHandler();
}
@@ -30,38 +30,31 @@ import org.exbin.bined.basic.BasicCodeAreaZone;
@ParametersAreNonnullByDefault
public class BasicCodeAreaDimensions {
private int scrollPanelX;
private int scrollPanelY;
private int scrollPanelWidth;
private int scrollPanelHeight;
private int verticalScrollBarSize;
private int horizontalScrollBarSize;
private int dataViewWidth;
private int dataViewHeight;
private int lastCharOffset;
private int lastRowOffset;
protected int scrollPanelX;
protected int scrollPanelY;
protected int scrollPanelWidth;
protected int scrollPanelHeight;
protected int verticalScrollBarSize;
protected int horizontalScrollBarSize;
protected int dataViewWidth;
protected int dataViewHeight;
protected int lastCharOffset;
protected int lastRowOffset;
private int headerAreaHeight;
private int rowPositionAreaWidth;
private int rowsPerRect;
private int rowsPerPage;
private int charactersPerPage;
private int charactersPerRect;
protected int headerAreaHeight;
protected int rowPositionAreaWidth;
protected int rowsPerRect;
protected int rowsPerPage;
protected int charactersPerPage;
protected int charactersPerRect;
@Nonnull
private final Rect componentRectangle = new Rect();
@Nonnull
private final Rect mainAreaRectangle = new Rect();
@Nonnull
private final Rect headerAreaRectangle = new Rect();
@Nonnull
private final Rect rowPositionAreaRectangle = new Rect();
@Nonnull
private final Rect scrollPanelRectangle = new Rect();
@Nonnull
private final Rect dataViewRectangle = new Rect();
@Nonnull
private final Rect dataViewInnerRectangle = new Rect();
protected final Rect componentRectangle = new Rect();
protected final Rect mainAreaRectangle = new Rect();
protected final Rect headerAreaRectangle = new Rect();
protected final Rect rowPositionAreaRectangle = new Rect();
protected final Rect scrollPanelRectangle = new Rect();
protected final Rect dataViewRectangle = new Rect();
protected final Rect dataViewInnerRectangle = new Rect();
public void recomputeSizes(BasicCodeAreaMetrics metrics, int componentX, int componentY, int componentWidth, int componentHeight, int rowPositionLength, int verticalScrollBarSize, int horizontalScrollBarSize) {
modifyRect(componentRectangle, componentX, componentY, componentWidth, componentHeight);
@@ -34,14 +34,14 @@ import javax.annotation.ParametersAreNonnullByDefault;
public class BasicCodeAreaMetrics {
@Nullable
private Paint fontMetrics;
private boolean monospaceFont;
protected Paint fontMetrics;
protected boolean monospaceFont;
private int rowHeight;
private int characterWidth;
private int fontHeight;
private int maxBytesPerChar;
private int subFontSpace = 0;
protected int rowHeight;
protected int characterWidth;
protected int fontHeight;
protected int maxBytesPerChar;
protected int subFontSpace = 0;
public void recomputeMetrics(@Nullable Paint fontMetrics, Charset charset) {
this.fontMetrics = fontMetrics;
@@ -57,7 +57,7 @@ public class BasicCodeAreaMetrics {
*/
characterWidth = (int) fontMetrics.measureText("w");
int fontSize = (int) fontMetrics.getTextSize();
subFontSpace = rowHeight - fontSize + 4;
subFontSpace = (rowHeight / 6);
/*
* Compare it to small 'i' to detect if font is monospaced.
@@ -30,22 +30,22 @@ import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
public class BasicCodeAreaVisibility {
private int splitLinePos;
protected int splitLinePos;
private int skipToCode;
private int skipToChar;
private int skipToPreview;
private int skipRestFromCode;
private int skipRestFromChar;
private int skipRestFromPreview;
protected int skipToCode;
protected int skipToChar;
protected int skipToPreview;
protected int skipRestFromCode;
protected int skipRestFromChar;
protected int skipRestFromPreview;
private boolean codeSectionVisible;
private boolean previewSectionVisible;
protected boolean codeSectionVisible;
protected boolean previewSectionVisible;
private int charactersPerCodeSection;
private int codeLastCharPos;
private int previewCharPos;
private int previewRelativeX;
protected int charactersPerCodeSection;
protected int codeLastCharPos;
protected int previewCharPos;
protected int previewRelativeX;
public void recomputeCharPositions(BasicCodeAreaMetrics metrics, BasicCodeAreaStructure structure, BasicCodeAreaDimensions dimensions, BasicCodeAreaLayout layout, BasicCodeAreaScrolling scrolling) {
int bytesPerRow = structure.getBytesPerRow();
@@ -74,55 +74,53 @@ import javax.annotation.ParametersAreNonnullByDefault;
public class CodeArea extends CodeAreaCore implements DefaultCodeArea, CodeAreaAndroidControl {
@Nonnull
private CodeAreaPainter painter;
protected CodeAreaPainter painter;
@Nonnull
private final DefaultCodeAreaCaret codeAreaCaret;
@Nonnull
private final CodeAreaSelection selection = new CodeAreaSelection();
@Nonnull
private final CodeAreaScrollPosition scrollPosition = new CodeAreaScrollPosition();
protected final DefaultCodeAreaCaret codeAreaCaret;
protected final CodeAreaSelection selection = new CodeAreaSelection();
protected final CodeAreaScrollPosition scrollPosition = new CodeAreaScrollPosition();
@Nonnull
private Charset charset = Charset.forName(CodeAreaAndroidUtils.DEFAULT_ENCODING);
protected Charset charset = Charset.forName(CodeAreaAndroidUtils.DEFAULT_ENCODING);
@Nonnull
private ClipboardHandlingMode clipboardHandlingMode = ClipboardHandlingMode.PROCESS;
protected ClipboardHandlingMode clipboardHandlingMode = ClipboardHandlingMode.PROCESS;
@Nonnull
private EditMode editMode = EditMode.EXPANDING;
protected EditMode editMode = EditMode.EXPANDING;
@Nonnull
private EditOperation editOperation = EditOperation.OVERWRITE;
protected EditOperation editOperation = EditOperation.OVERWRITE;
@Nonnull
private CodeAreaViewMode viewMode = CodeAreaViewMode.DUAL;
protected CodeAreaViewMode viewMode = CodeAreaViewMode.DUAL;
@Nullable
private Font codeFont;
protected Font codeFont;
@Nonnull
private BasicBackgroundPaintMode backgroundPaintMode = BasicBackgroundPaintMode.STRIPED;
protected BasicBackgroundPaintMode backgroundPaintMode = BasicBackgroundPaintMode.STRIPED;
@Nonnull
private CodeType codeType = CodeType.HEXADECIMAL;
protected CodeType codeType = CodeType.HEXADECIMAL;
@Nonnull
private CodeCharactersCase codeCharactersCase = CodeCharactersCase.UPPER;
private boolean showMirrorCursor = true;
protected CodeCharactersCase codeCharactersCase = CodeCharactersCase.UPPER;
protected boolean showMirrorCursor = true;
@Nonnull
private RowWrappingMode rowWrapping = RowWrappingMode.NO_WRAPPING;
private int minRowPositionLength = 0;
private int maxRowPositionLength = 0;
private int wrappingBytesGroupSize = 0;
private int maxBytesPerRow = 16;
protected RowWrappingMode rowWrapping = RowWrappingMode.NO_WRAPPING;
protected int minRowPositionLength = 0;
protected int maxRowPositionLength = 0;
protected int wrappingBytesGroupSize = 0;
protected int maxBytesPerRow = 16;
@Nonnull
private ScrollBarVisibility verticalScrollBarVisibility = ScrollBarVisibility.IF_NEEDED;
protected ScrollBarVisibility verticalScrollBarVisibility = ScrollBarVisibility.IF_NEEDED;
@Nonnull
private VerticalScrollUnit verticalScrollUnit = VerticalScrollUnit.PIXEL;
protected VerticalScrollUnit verticalScrollUnit = VerticalScrollUnit.PIXEL;
@Nonnull
private ScrollBarVisibility horizontalScrollBarVisibility = ScrollBarVisibility.IF_NEEDED;
protected ScrollBarVisibility horizontalScrollBarVisibility = ScrollBarVisibility.IF_NEEDED;
@Nonnull
private HorizontalScrollUnit horizontalScrollUnit = HorizontalScrollUnit.PIXEL;
protected HorizontalScrollUnit horizontalScrollUnit = HorizontalScrollUnit.PIXEL;
private final List<CodeAreaCaretListener> caretMovedListeners = new ArrayList<>();
private final List<ScrollingListener> scrollingListeners = new ArrayList<>();
private final List<SelectionChangedListener> selectionChangedListeners = new ArrayList<>();
private final List<EditModeChangedListener> editModeChangedListeners = new ArrayList<>();
protected final List<CodeAreaCaretListener> caretMovedListeners = new ArrayList<>();
protected final List<ScrollingListener> scrollingListeners = new ArrayList<>();
protected final List<SelectionChangedListener> selectionChangedListeners = new ArrayList<>();
protected final List<EditModeChangedListener> editModeChangedListeners = new ArrayList<>();
/**
* Creates new instance with default command handler and painter.
@@ -37,19 +37,19 @@ import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
public class DefaultCodeAreaCaret implements CodeAreaCaret {
private static final int DOUBLE_CURSOR_WIDTH = 2;
private static final int DEFAULT_BLINK_RATE = 450;
protected static final int DOUBLE_CURSOR_WIDTH = 2;
protected static final int DEFAULT_BLINK_RATE = 450;
@Nonnull
private final CaretChangeListener changeListener;
private final DefaultCodeAreaCaretPosition caretPosition = new DefaultCodeAreaCaretPosition();
protected final CaretChangeListener changeListener;
protected final DefaultCodeAreaCaretPosition caretPosition = new DefaultCodeAreaCaretPosition();
private int blinkRate = 0;
private Timer blinkTimer = null;
private boolean cursorVisible = true;
protected int blinkRate = 0;
protected Timer blinkTimer = null;
protected boolean cursorVisible = true;
@Nonnull
private CursorRenderingMode renderingMode = CursorRenderingMode.NEGATIVE;
protected CursorRenderingMode renderingMode = CursorRenderingMode.NEGATIVE;
public DefaultCodeAreaCaret(CaretChangeListener changeListener) {
CodeAreaUtils.requireNonNull(changeListener, "Change listener cannot be null");
@@ -0,0 +1,142 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.android.basic;
import java.nio.charset.Charset;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.CodeAreaSection;
import org.exbin.bined.android.CodeAreaCharAssessor;
import org.exbin.bined.android.CodeAreaPaintState;
/**
* Default code area character assessor.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class DefaultCodeAreaCharAssessor implements CodeAreaCharAssessor {
protected final CodeAreaCharAssessor parentCharAssessor;
@Nullable
protected Charset charMappingCharset = null;
@Nonnull
protected final char[] charMapping = new char[256];
protected long dataSize;
protected int maxBytesPerChar;
protected byte[] rowData;
protected Charset charset;
public DefaultCodeAreaCharAssessor() {
parentCharAssessor = null;
}
public DefaultCodeAreaCharAssessor(@Nullable CodeAreaCharAssessor parentCharAssessor) {
this.parentCharAssessor = parentCharAssessor;
}
@Override
public void startPaint(CodeAreaPaintState codeAreaPaintState) {
dataSize = codeAreaPaintState.getDataSize();
charset = codeAreaPaintState.getCharset();
rowData = codeAreaPaintState.getRowData();
maxBytesPerChar = codeAreaPaintState.getMaxBytesPerChar();
if (parentCharAssessor != null) {
parentCharAssessor.startPaint(codeAreaPaintState);
}
}
@Override
public char getPreviewCharacter(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section) {
if (maxBytesPerChar > 1) {
if (rowDataPosition + maxBytesPerChar > dataSize) {
maxBytesPerChar = (int) (dataSize - rowDataPosition);
}
int charDataLength = maxBytesPerChar;
if (byteOnRow + charDataLength > rowData.length) {
charDataLength = rowData.length - byteOnRow;
}
String displayString = new String(rowData, byteOnRow, charDataLength, charset);
if (!displayString.isEmpty()) {
return displayString.charAt(0);
}
} else {
if (charMappingCharset == null || charMappingCharset != charset) {
buildCharMapping(charset);
}
return charMapping[rowData[byteOnRow] & 0xFF];
}
if (parentCharAssessor != null) {
return parentCharAssessor.getPreviewCharacter(rowDataPosition, byteOnRow, charOnRow, section);
}
return ' ';
}
@Override
public char getPreviewCursorCharacter(long rowDataPosition, int byteOnRow, int charOnRow, byte[] cursorData, int cursorDataLength, CodeAreaSection section) {
if (cursorDataLength == 0) {
return ' ';
}
if (maxBytesPerChar > 1) {
String displayString = new String(cursorData, 0, cursorDataLength, charset);
if (!displayString.isEmpty()) {
return displayString.charAt(0);
}
} else {
if (charMappingCharset == null || charMappingCharset != charset) {
buildCharMapping(charset);
}
return charMapping[cursorData[0] & 0xFF];
}
if (parentCharAssessor != null) {
return parentCharAssessor.getPreviewCursorCharacter(rowDataPosition, byteOnRow, charOnRow, cursorData, cursorDataLength, section);
}
return ' ';
}
@Nonnull
@Override
public Optional<CodeAreaCharAssessor> getParentCharAssessor() {
return Optional.ofNullable(parentCharAssessor);
}
/**
* Precomputes widths for basic ascii characters.
*
* @param charset character set
*/
private void buildCharMapping(Charset charset) {
for (int i = 0; i < 256; i++) {
charMapping[i] = new String(new byte[]{(byte) i}, charset).charAt(0);
}
charMappingCharset = charset;
}
}
@@ -0,0 +1,108 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.android.basic;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.CodeAreaSection;
import org.exbin.bined.basic.BasicCodeAreaSection;
import org.exbin.bined.color.CodeAreaBasicColors;
import org.exbin.bined.android.CodeAreaPaintState;
import org.exbin.bined.android.CodeAreaColorAssessor;
import org.exbin.bined.android.basic.color.CodeAreaColorsProfile;
/**
* Default code area color assessor.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class DefaultCodeAreaColorAssessor implements CodeAreaColorAssessor {
protected final CodeAreaColorAssessor parentColorAssessor;
protected CodeAreaSection activeSection = null;
protected int codeLastCharPos;
protected Integer selectionColor;
protected Integer selectionMirrorColor;
protected Integer selectionBackground;
protected Integer selectionMirrorBackground;
public DefaultCodeAreaColorAssessor() {
parentColorAssessor = null;
}
public DefaultCodeAreaColorAssessor(@Nullable CodeAreaColorAssessor parentColorAssessor) {
this.parentColorAssessor = parentColorAssessor;
}
@Override
public void startPaint(CodeAreaPaintState codeAreaPainterState) {
activeSection = codeAreaPainterState.getActiveSection();
codeLastCharPos = codeAreaPainterState.getCodeLastCharPos();
CodeAreaColorsProfile colorsProfile = codeAreaPainterState.getColorsProfile();
selectionColor = colorsProfile.getColor(CodeAreaBasicColors.SELECTION_COLOR);
selectionMirrorColor = colorsProfile.getColor(CodeAreaBasicColors.SELECTION_MIRROR_COLOR);
selectionBackground = colorsProfile.getColor(CodeAreaBasicColors.SELECTION_BACKGROUND);
selectionMirrorBackground = colorsProfile.getColor(CodeAreaBasicColors.SELECTION_MIRROR_BACKGROUND);
if (parentColorAssessor != null) {
parentColorAssessor.startPaint(codeAreaPainterState);
}
}
@Nullable
@Override
public Integer getPositionTextColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section, boolean inSelection) {
if (inSelection) {
return section == activeSection ? selectionColor : selectionMirrorColor;
}
if (parentColorAssessor != null) {
return parentColorAssessor.getPositionTextColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);
}
return null;
}
@Nullable
@Override
public Integer getPositionBackgroundColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section, boolean inSelection) {
if (inSelection && (section == BasicCodeAreaSection.CODE_MATRIX)) {
if (charOnRow == codeLastCharPos) {
inSelection = false;
}
}
if (inSelection) {
return section == activeSection ? selectionBackground : selectionMirrorBackground;
}
if (parentColorAssessor != null) {
return parentColorAssessor.getPositionBackgroundColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);
}
return null;
}
@Nonnull
@Override
public Optional<CodeAreaColorAssessor> getParentColorAssessor() {
return Optional.ofNullable(parentColorAssessor);
}
}
@@ -20,10 +20,7 @@ import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import androidx.annotation.Nullable;
import android.view.KeyEvent;
import org.exbin.auxiliary.binary_data.ByteArrayData;
@@ -141,7 +138,7 @@ public class DefaultCodeAreaCommandHandler implements CodeAreaCommandHandler {
}
@Override
public void undoSequenceBreak() {
public void sequenceBreak() {
// Do nothing
}
@@ -154,28 +151,28 @@ public class DefaultCodeAreaCommandHandler implements CodeAreaCommandHandler {
switch (keyEvent.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_LEFT: {
move(isSelectingMode(keyEvent), MovementDirection.LEFT);
undoSequenceBreak();
sequenceBreak();
revealCursor();
// keyEvent.consume();
break;
}
case KeyEvent.KEYCODE_DPAD_RIGHT: {
move(isSelectingMode(keyEvent), MovementDirection.RIGHT);
undoSequenceBreak();
sequenceBreak();
revealCursor();
// keyEvent.consume();
break;
}
case KeyEvent.KEYCODE_DPAD_UP: {
move(isSelectingMode(keyEvent), MovementDirection.UP);
undoSequenceBreak();
sequenceBreak();
revealCursor();
// keyEvent.consume();
break;
}
case KeyEvent.KEYCODE_DPAD_DOWN: {
move(isSelectingMode(keyEvent), MovementDirection.DOWN);
undoSequenceBreak();
sequenceBreak();
revealCursor();
// keyEvent.consume();
break;
@@ -186,7 +183,7 @@ public class DefaultCodeAreaCommandHandler implements CodeAreaCommandHandler {
} else {
move(isSelectingMode(keyEvent), MovementDirection.ROW_START);
}
undoSequenceBreak();
sequenceBreak();
revealCursor();
// keyEvent.consume();
break;
@@ -197,7 +194,7 @@ public class DefaultCodeAreaCommandHandler implements CodeAreaCommandHandler {
} else {
move(isSelectingMode(keyEvent), MovementDirection.ROW_END);
}
undoSequenceBreak();
sequenceBreak();
revealCursor();
// keyEvent.consume();
break;
@@ -205,7 +202,7 @@ public class DefaultCodeAreaCommandHandler implements CodeAreaCommandHandler {
case KeyEvent.KEYCODE_PAGE_UP: {
scroll(ScrollingDirection.PAGE_UP);
move(isSelectingMode(keyEvent), MovementDirection.PAGE_UP);
undoSequenceBreak();
sequenceBreak();
revealCursor();
// keyEvent.consume();
break;
@@ -213,7 +210,7 @@ public class DefaultCodeAreaCommandHandler implements CodeAreaCommandHandler {
case KeyEvent.KEYCODE_PAGE_DOWN: {
scroll(ScrollingDirection.PAGE_DOWN);
move(isSelectingMode(keyEvent), MovementDirection.PAGE_DOWN);
undoSequenceBreak();
sequenceBreak();
// keyEvent.consume();
break;
}
@@ -320,7 +317,7 @@ public class DefaultCodeAreaCommandHandler implements CodeAreaCommandHandler {
EditMode editMode = ((EditModeCapable) codeArea).getEditMode();
if (codeArea.hasSelection() && editMode != EditMode.INPLACE) {
deleteSelection();
undoSequenceBreak();
sequenceBreak();
}
int value;
@@ -392,7 +389,7 @@ public class DefaultCodeAreaCommandHandler implements CodeAreaCommandHandler {
}
}
if (codeArea.hasSelection() && editMode != EditMode.INPLACE) {
undoSequenceBreak();
sequenceBreak();
deleteSelection();
}
@@ -456,7 +453,7 @@ public class DefaultCodeAreaCommandHandler implements CodeAreaCommandHandler {
if (tabKeyHandlingMode == TabKeyHandlingMode.PLATFORM_SPECIFIC || tabKeyHandlingMode == TabKeyHandlingMode.CYCLE_TO_NEXT_SECTION || tabKeyHandlingMode == TabKeyHandlingMode.CYCLE_TO_PREVIOUS_SECTION) {
if (viewModeSupported && ((ViewModeCapable) codeArea).getViewMode() == CodeAreaViewMode.DUAL) {
move(selectingMode, MovementDirection.SWITCH_SECTION);
undoSequenceBreak();
sequenceBreak();
revealCursor();
}
} else if (((CaretCapable) codeArea).getActiveSection() == BasicCodeAreaSection.TEXT_PREVIEW) {
@@ -715,7 +712,7 @@ public class DefaultCodeAreaCommandHandler implements CodeAreaCommandHandler {
caret.setCodeOffset(0);
((CaretCapable) codeArea).setActiveCaretPosition(caret.getCaretPosition());
undoSequenceBreak();
sequenceBreak();
codeArea.notifyDataChanged();
revealCursor();
clearSelection();
@@ -785,7 +782,7 @@ public class DefaultCodeAreaCommandHandler implements CodeAreaCommandHandler {
caret.setCodeOffset(0);
((CaretCapable) codeArea).setActiveCaretPosition(caret.getCaretPosition());
undoSequenceBreak();
sequenceBreak();
codeArea.notifyDataChanged();
revealCursor();
clearSelection();
@@ -853,7 +850,7 @@ public class DefaultCodeAreaCommandHandler implements CodeAreaCommandHandler {
((CaretCapable) codeArea).getCodeAreaCaret().setCaretPosition(caretPosition);
updateSelection(selecting, caretPosition);
undoSequenceBreak();
sequenceBreak();
codeArea.repaint();
}
@@ -34,13 +34,13 @@ public class DefaultCodeAreaMouseListener implements View.OnTouchListener, View.
public static final int MOUSE_SCROLL_LINES = 3;
private final CodeAreaCore codeArea;
private final DefaultCodeAreaScrollPane view;
protected final CodeAreaCore codeArea;
protected final DefaultCodeAreaScrollPane view;
private final int defaultCursor = 0;
private final int textCursor = 1;
private int currentCursor;
private boolean menuShown = false;
protected final int defaultCursor = 0;
protected final int textCursor = 1;
protected int currentCursor;
protected boolean menuShown = false;
public DefaultCodeAreaMouseListener(CodeAreaCore codeArea, DefaultCodeAreaScrollPane view) {
this.codeArea = codeArea;
@@ -18,6 +18,7 @@ package org.exbin.bined.android.basic;
import android.app.Activity;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
@@ -42,11 +43,17 @@ import org.exbin.bined.DefaultCodeAreaCaretPosition;
import org.exbin.bined.EditOperation;
import org.exbin.bined.PositionCodeType;
import org.exbin.bined.android.CodeAreaAndroidUtils;
import org.exbin.bined.android.CodeAreaCharAssessor;
import org.exbin.bined.android.CodeAreaColorAssessor;
import org.exbin.bined.android.CodeAreaCore;
import org.exbin.bined.android.CodeAreaPaintState;
import org.exbin.bined.android.CodeAreaPainter;
import org.exbin.bined.android.Font;
import org.exbin.bined.android.basic.color.BasicCodeAreaColorsProfile;
import org.exbin.bined.android.basic.color.BasicColorsCapableCodeAreaPainter;
import org.exbin.bined.android.basic.color.CodeAreaColorsProfile;
import org.exbin.bined.android.capability.CharAssessorPainterCapable;
import org.exbin.bined.android.capability.ColorAssessorPainterCapable;
import org.exbin.bined.android.capability.FontCapable;
import org.exbin.bined.basic.BasicBackgroundPaintMode;
import org.exbin.bined.basic.BasicCodeAreaLayout;
@@ -84,80 +91,77 @@ import javax.annotation.ParametersAreNonnullByDefault;
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapableCodeAreaPainter {
public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapableCodeAreaPainter, CodeAreaPaintState, ColorAssessorPainterCapable, CharAssessorPainterCapable {
@Nonnull
protected final CodeAreaCore codeArea;
private volatile boolean initialized = false;
private volatile boolean adjusting = false;
protected volatile boolean initialized = false;
protected volatile boolean adjusting = false;
private volatile boolean fontChanged = false;
private volatile boolean layoutChanged = true;
private volatile boolean resetColors = true;
private volatile boolean caretChanged = true;
protected volatile boolean fontChanged = false;
protected volatile boolean layoutChanged = true;
protected volatile boolean resetColors = true;
protected volatile boolean caretChanged = true;
@Nullable
private Paint paint = null;
protected Paint paint = null;
@Nonnull
private final View dataView;
protected final View dataView;
@Nonnull
private final DefaultCodeAreaScrollPane scrollPanel;
protected final DefaultCodeAreaScrollPane scrollPanel;
@Nonnull
private final DefaultCodeAreaMouseListener codeAreaMouseListener;
protected final DefaultCodeAreaMouseListener codeAreaMouseListener;
private int dataViewOffsetX = 0;
private int dataViewOffsetY = 0;
private int scrollOffsetX = 0;
private int scrollOffsetY = 0;
protected int dataViewOffsetX = 0;
protected int dataViewOffsetY = 0;
protected int scrollOffsetX = 0;
protected int scrollOffsetY = 0;
@Nonnull
private final DataChangedListener codeAreaDataChangeListener;
protected final DataChangedListener codeAreaDataChangeListener;
@Nonnull
private final BasicCodeAreaMetrics metrics = new BasicCodeAreaMetrics();
@Nonnull
private final BasicCodeAreaStructure structure = new BasicCodeAreaStructure();
@Nonnull
private final BasicCodeAreaScrolling scrolling = new BasicCodeAreaScrolling();
@Nonnull
private final BasicCodeAreaDimensions dimensions = new BasicCodeAreaDimensions();
@Nonnull
private final BasicCodeAreaVisibility visibility = new BasicCodeAreaVisibility();
protected final BasicCodeAreaMetrics metrics = new BasicCodeAreaMetrics();
protected final BasicCodeAreaStructure structure = new BasicCodeAreaStructure();
protected final BasicCodeAreaScrolling scrolling = new BasicCodeAreaScrolling();
protected final BasicCodeAreaDimensions dimensions = new BasicCodeAreaDimensions();
protected final BasicCodeAreaVisibility visibility = new BasicCodeAreaVisibility();
protected final BasicCodeAreaLayout layout = new BasicCodeAreaLayout();
@Nonnull
private final BasicCodeAreaLayout layout = new BasicCodeAreaLayout();
@Nonnull
private BasicCodeAreaColorsProfile colorsProfile = new BasicCodeAreaColorsProfile();
protected BasicCodeAreaColorsProfile colorsProfile = new BasicCodeAreaColorsProfile();
@Nullable
private CodeCharactersCase codeCharactersCase;
protected CodeCharactersCase codeCharactersCase;
@Nullable
private EditOperation editOperation;
protected EditOperation editOperation;
@Nullable
private BasicBackgroundPaintMode backgroundPaintMode;
private boolean showMirrorCursor;
protected BasicBackgroundPaintMode backgroundPaintMode;
protected boolean showMirrorCursor;
private int rowPositionLength;
private int minRowPositionLength;
private int maxRowPositionLength;
protected int rowPositionLength;
protected int minRowPositionLength;
protected int maxRowPositionLength;
@Nullable
private Font font;
protected Font font;
@Nonnull
private Charset charset;
protected Charset charset;
@Nonnull
protected CodeAreaColorAssessor colorAssessor = null;
@Nonnull
protected CodeAreaCharAssessor charAssessor = null;
@Nullable
private RowDataCache rowDataCache = null;
protected RowDataCache rowDataCache = null;
@Nullable
private CursorDataCache cursorDataCache = null;
@Nullable
private Charset charMappingCharset = null;
private final char[] charMapping = new char[256];
protected CursorDataCache cursorDataCache = null;
public DefaultCodeAreaPainter(final CodeAreaCore codeArea) {
this.codeArea = codeArea;
colorAssessor = new DefaultCodeAreaColorAssessor();
charAssessor = new DefaultCodeAreaCharAssessor();
scrollPanel = new DefaultCodeAreaScrollPane(codeArea.getContext()) {
@Override
protected void onScrollChanged(int horizontal, int vertical, int oldHorizontal, int oldVertical) {
@@ -186,6 +190,7 @@ public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapab
paintMainArea(canvas);
}
};
dataView.setContentDescription("Code area");
dataView.setLongClickable(true);
scrollPanel.addView(dataView);
@@ -606,6 +611,9 @@ public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapab
// Rect clipBounds = g.getClipBounds();
// Rect mainArea = dimensions.getMainAreaRectangle();
// g.clipRect(clipBounds != null ? CodeAreaAndroidUtils.computeIntersection(mainArea, clipBounds) : mainArea);
colorAssessor.startPaint(this);
charAssessor.startPaint(this);
paintBackground(g);
Rect dataViewRectangle = dimensions.getDataViewRectangle();
@@ -764,28 +772,7 @@ public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapab
int skipRestFromPreview = visibility.getSkipRestFromPreview();
int endPreview = Math.min(skipRestFromPreview, rowBytesLimit);
for (int byteOnRow = skipToPreview; byteOnRow < endPreview; byteOnRow++) {
byte dataByte = rowDataCache.rowData[byteOnRow];
if (maxBytesPerChar > 1) {
if (dataPosition + maxBytesPerChar > dataSize) {
maxBytesPerChar = (int) (dataSize - dataPosition);
}
int charDataLength = maxBytesPerChar;
if (byteOnRow + charDataLength > rowDataCache.rowData.length) {
charDataLength = rowDataCache.rowData.length - byteOnRow;
}
String displayString = new String(rowDataCache.rowData, byteOnRow, charDataLength, charset);
if (!displayString.isEmpty()) {
rowDataCache.rowCharacters[previewCharPos + byteOnRow] = displayString.charAt(0);
}
} else {
if (charMappingCharset == null || charMappingCharset != charset) {
buildCharMapping(charset);
}
rowDataCache.rowCharacters[previewCharPos + byteOnRow] = charMapping[dataByte & 0xFF];
}
rowDataCache.rowCharacters[previewCharPos + byteOnRow] = charAssessor.getPreviewCharacter(dataPosition, byteOnRow, previewCharPos, BasicCodeAreaSection.TEXT_PREVIEW);
}
if (bytesPerRow > rowBytesLimit) {
Arrays.fill(rowDataCache.rowCharacters, previewCharPos + rowBytesLimit, previewCharPos + bytesPerRow, ' ');
@@ -808,9 +795,9 @@ public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapab
int skipToChar = visibility.getSkipToChar();
int skipRestFromChar = visibility.getSkipRestFromChar();
int rowHeight = metrics.getRowHeight();
int subFontSpace = metrics.getSubFontSpace();
CodeAreaSelection selectionHandler = ((SelectionCapable) codeArea).getSelectionHandler();
int positionY = rowPositionY + rowHeight - subFontSpace;
int positionY = rowPositionY + rowHeight;
int renderOffset = skipToChar;
Integer renderColor = null;
@@ -826,7 +813,8 @@ public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapab
}
boolean sequenceBreak = false;
Integer color = getPositionBackgroundColor(rowDataPosition, byteOnRow, charOnRow, section);
boolean inSelection = selectionHandler.isInSelection(rowDataPosition + byteOnRow);
Integer color = colorAssessor.getPositionBackgroundColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);
if (!CodeAreaAndroidUtils.areSameColors(color, renderColor)) {
sequenceBreak = true;
}
@@ -855,33 +843,6 @@ public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapab
}
}
/**
* Returns background color for particular code.
*
* @param rowDataPosition row data position
* @param byteOnRow byte on current row
* @param charOnRow character on current row
* @param section current section
* @return color or null for default color
*/
@Nullable
public Integer getPositionBackgroundColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section) {
CodeAreaSelection selectionHandler = ((SelectionCapable) codeArea).getSelectionHandler();
int codeLastCharPos = visibility.getCodeLastCharPos();
boolean inSelection = selectionHandler.isInSelection(rowDataPosition + byteOnRow);
if (inSelection && (section == BasicCodeAreaSection.CODE_MATRIX)) {
if (charOnRow == codeLastCharPos) {
inSelection = false;
}
}
if (inSelection) {
return section == ((CaretCapable) codeArea).getActiveSection() ? colorsProfile.getSelectionBackground() : colorsProfile.getSelectionMirrorBackground();
}
return null;
}
@Nonnull
@Override
public PositionScrollVisibility computePositionScrollVisibility(CodeAreaCaretPosition caretPosition) {
@@ -974,6 +935,7 @@ public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapab
int rowHeight = metrics.getRowHeight();
int characterWidth = metrics.getCharacterWidth();
int subFontSpace = metrics.getSubFontSpace();
CodeAreaSelection selectionHandler = ((SelectionCapable) codeArea).getSelectionHandler();
int positionY = rowPositionY + rowHeight - subFontSpace;
@@ -994,7 +956,8 @@ public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapab
section = BasicCodeAreaSection.CODE_MATRIX;
}
Integer color = getPositionTextColor(rowDataPosition, byteOnRow, charOnRow, section);
boolean inSelection = selectionHandler.isInSelection(rowDataPosition + byteOnRow);
Integer color = colorAssessor.getPositionTextColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);
if (color == null) {
color = colorsProfile.getTextColor();
}
@@ -1037,24 +1000,26 @@ public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapab
}
}
/**
* Returns background color for particular code.
*
* @param rowDataPosition row data position
* @param byteOnRow byte on current row
* @param charOnRow character on current row
* @param section current section
* @return color or null for default color
*/
@Nullable
public Integer getPositionTextColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section) {
CodeAreaSelection selectionHandler = ((SelectionCapable) codeArea).getSelectionHandler();
boolean inSelection = selectionHandler.isInSelection(rowDataPosition + byteOnRow);
if (inSelection) {
return section == ((CaretCapable) codeArea).getActiveSection() ? colorsProfile.getSelectionColor() : colorsProfile.getSelectionMirrorColor();
}
@Nonnull
@Override
public CodeAreaColorAssessor getColorAssessor() {
return colorAssessor;
}
return null;
@Override
public void setColorAssessor(CodeAreaColorAssessor colorAssessor) {
this.colorAssessor = CodeAreaUtils.requireNonNull(colorAssessor);
}
@Nonnull
@Override
public CodeAreaCharAssessor getCharAssessor() {
return charAssessor;
}
@Override
public void setCharAssessor(CodeAreaCharAssessor charAssessor) {
this.charAssessor = charAssessor;
}
@Override
@@ -1095,12 +1060,9 @@ public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapab
g.save();
Rect clipBounds = g.getClipBounds();
Rect mainAreaRect = dimensions.getMainAreaRectangle();
mainAreaRect.left += dataViewOffsetX;
mainAreaRect.right += dataViewOffsetX;
mainAreaRect.bottom += dataViewOffsetY;
mainAreaRect.top += dataViewOffsetY;
Rect mainAreaRectAdj = new Rect(mainAreaRect.left + dataViewOffsetX, mainAreaRect.top + dataViewOffsetY, mainAreaRect.right + dataViewOffsetX, mainAreaRect.bottom + dataViewOffsetY);
Rect intersection = CodeAreaAndroidUtils.computeIntersection(mainAreaRect, scrolledCursorRect);
Rect intersection = CodeAreaAndroidUtils.computeIntersection(mainAreaRectAdj, scrolledCursorRect);
boolean cursorVisible = caret.isCursorVisible() && (intersection == null || !intersection.isEmpty());
if (cursorVisible) {
@@ -1113,22 +1075,27 @@ public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapab
paintCursorRect(g, intersection, scrolledCursorRect, cursorRect, renderingMode, caret);
}
// // Paint mirror cursor
// if (viewMode == CodeAreaViewMode.DUAL && showMirrorCursor) {
// Rect mirrorCursorRect = getMirrorCursorRect(caret.getDataPosition(), caret.getSection());
// if (mirrorCursorRect != null) {
// intersection = CodeAreaAndroidUtils.computeIntersection(mainAreaRect, mirrorCursorRect);
// boolean mirrorCursorVisible = !intersection.isEmpty();
// if (mirrorCursorVisible) {
// g.clipRect(intersection);
// paint.setColor(colorsProfile.getCursor();
//// Graphics2D g2d = (Graphics2D) g.create();
//// Stroke dashed = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[]{2}, 0);
//// g2d.setStroke(dashed);
// g.drawRect(mirrorCursorRect.left, mirrorCursorRect.top, mirrorCursorRect.right - 1, mirrorCursorRect.bottom - 1, paint);
// }
// }
// }
// Paint mirror cursor
if (viewMode == CodeAreaViewMode.DUAL && showMirrorCursor) {
updateMirrorCursorRect(caret.getDataPosition(), caret.getSection());
Rect mirrorCursorRect = cursorDataCache.mirrorCursorRect;
if (mirrorCursorRect != null) {
Rect mirrorCursorRectAdj = new Rect(mirrorCursorRect.left + dataViewOffsetX, mirrorCursorRect.top + dataViewOffsetY, mirrorCursorRect.right + dataViewOffsetX, mirrorCursorRect.bottom + dataViewOffsetY);
intersection = CodeAreaAndroidUtils.computeIntersection(mainAreaRectAdj, mirrorCursorRectAdj);
boolean mirrorCursorVisible = intersection != null && !intersection.isEmpty();
if (mirrorCursorVisible) {
g.restore();
g.save();
g.clipRect(intersection);
paint.setStyle(Paint.Style.STROKE);
paint.setColor(colorsProfile.getCursorColor());
paint.setPathEffect(cursorDataCache.dashedStroke);
g.drawRect(mirrorCursorRectAdj.left, mirrorCursorRectAdj.top, mirrorCursorRectAdj.right - 1, mirrorCursorRectAdj.bottom - 1, paint);
paint.setPathEffect(null);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
}
}
}
g.restore();
}
@@ -1160,7 +1127,7 @@ public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapab
g.drawRect(scrolledCursorRect, paint);
paint.setColor(colorsProfile.getCursorNegativeColor());
BinaryData codeAreaData = codeArea.getContentData();
BinaryData contentData = codeArea.getContentData();
int row = (cursorRect.top + scrollPosition.getRowOffset() - dataViewY) / rowHeight;
int scrolledX = cursorRect.left + scrollPosition.getCharPosition() * characterWidth + scrollPosition.getCharOffset();
int posY = dataViewY + (row + 1) * rowHeight - subFontSpace - scrollPosition.getRowOffset();
@@ -1171,30 +1138,23 @@ public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapab
break;
}
if (maxBytesPerChar > 1) {
int charDataLength = maxBytesPerChar;
if (dataPosition + maxBytesPerChar > dataSize) {
charDataLength = (int) (dataSize - dataPosition);
}
int byteOnRow = (int) (dataPosition % structure.getBytesPerRow());
int previewCharPos = visibility.getPreviewCharPos();
if (codeAreaData == null) {
cursorDataCache.cursorChars[0] = ' ';
} else {
codeAreaData.copyToArray(dataPosition, cursorDataCache.cursorData, 0, charDataLength);
String displayString = new String(cursorDataCache.cursorData, 0, charDataLength, charset);
if (!displayString.isEmpty()) {
cursorDataCache.cursorChars[0] = displayString.charAt(0);
}
}
if (contentData.isEmpty()) {
cursorDataCache.cursorChars[0] = charAssessor.getPreviewCursorCharacter(dataPosition, byteOnRow, previewCharPos, cursorDataCache.cursorData, 0, BasicCodeAreaSection.TEXT_PREVIEW);
} else {
if (charMappingCharset == null || charMappingCharset != charset) {
buildCharMapping(charset);
}
if (maxBytesPerChar > 1) {
int charDataLength = maxBytesPerChar;
if (dataPosition + maxBytesPerChar > dataSize) {
charDataLength = (int) (dataSize - dataPosition);
}
if (codeAreaData == null) {
cursorDataCache.cursorChars[0] = ' ';
contentData.copyToArray(dataPosition, cursorDataCache.cursorData, 0, charDataLength);
cursorDataCache.cursorChars[0] = charAssessor.getPreviewCursorCharacter(dataPosition, byteOnRow, previewCharPos, cursorDataCache.cursorData, charDataLength, BasicCodeAreaSection.TEXT_PREVIEW);
} else {
cursorDataCache.cursorChars[0] = charMapping[codeAreaData.getByte(dataPosition) & 0xFF];
cursorDataCache.cursorData[0] = contentData.getByte(dataPosition);
cursorDataCache.cursorChars[0] = charAssessor.getPreviewCursorCharacter(dataPosition, byteOnRow, previewCharPos, cursorDataCache.cursorData, 1, BasicCodeAreaSection.TEXT_PREVIEW);
}
}
int posX = previewRelativeX + charPos * characterWidth - scrollPosition.getCharPosition() * characterWidth - scrollPosition.getCharOffset();
@@ -1208,8 +1168,8 @@ public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapab
int byteOffset = structure.computePositionByte(charPos);
int codeCharPos = structure.computeFirstCodeCharacterPos(byteOffset);
if (codeAreaData != null && dataPosition < dataSize) {
byte dataByte = codeAreaData.getByte(dataPosition);
if (dataPosition < dataSize) {
byte dataByte = contentData.getByte(dataPosition);
CodeAreaUtils.byteToCharsCode(dataByte, codeType, cursorDataCache.cursorChars, 0, codeCharactersCase);
} else {
Arrays.fill(cursorDataCache.cursorChars, ' ');
@@ -1357,16 +1317,14 @@ public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapab
return new Point(caretX, caretY);
}
@Nullable
private Rect getMirrorCursorRect(long dataPosition, CodeAreaSection section) {
private void updateMirrorCursorRect(long dataPosition, CodeAreaSection section) {
CodeType codeType = structure.getCodeType();
Point mirrorCursorPoint = getPositionPoint(dataPosition, 0, section == BasicCodeAreaSection.CODE_MATRIX ? BasicCodeAreaSection.TEXT_PREVIEW : BasicCodeAreaSection.CODE_MATRIX);
if (mirrorCursorPoint == null) {
return null;
cursorDataCache.mirrorCursorRect.setEmpty();
} else {
cursorDataCache.mirrorCursorRect = new Rect(mirrorCursorPoint.x, mirrorCursorPoint.y, mirrorCursorPoint.x + metrics.getCharacterWidth() * (section == BasicCodeAreaSection.TEXT_PREVIEW ? codeType.getMaxDigitsForByte() : 1), mirrorCursorPoint.y + metrics.getRowHeight());
}
Rect mirrorCursorRect = new Rect(mirrorCursorPoint.x, mirrorCursorPoint.y, mirrorCursorPoint.x + metrics.getCharacterWidth() * (section == BasicCodeAreaSection.TEXT_PREVIEW ? codeType.getMaxDigitsForByte() : 1), mirrorCursorPoint.y + metrics.getRowHeight());
return mirrorCursorRect;
}
@Override
@@ -1458,18 +1416,6 @@ public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapab
g.drawText(drawnChars, charOffset, length, positionX, positionY, paint);
}
/**
* Precomputes widths for basic ascii characters.
*
* @param charset character set
*/
private void buildCharMapping(Charset charset) {
for (int i = 0; i < 256; i++) {
charMapping[i] = new String(new byte[]{(byte) i}, charset).charAt(0);
}
charMappingCharset = charset;
}
private int getRowPositionLength() {
if (minRowPositionLength > 0 && minRowPositionLength == maxRowPositionLength) {
return minRowPositionLength;
@@ -1628,10 +1574,67 @@ public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapab
recomputeLayout();
}
protected int getCharactersPerRow() {
@Override
public int getCharactersPerRow() {
return structure.getCharactersPerRow();
}
@Override
public int getBytesPerRow() {
return structure.getBytesPerRow();
}
@Nonnull
@Override
public CodeAreaSection getActiveSection() {
return ((CaretCapable) codeArea).getActiveSection();
}
@Nonnull
@Override
public Charset getCharset() {
return charset;
}
@Override
public int getMaxBytesPerChar() {
return metrics.getMaxBytesPerChar();
}
@Nonnull
@Override
public CodeAreaColorsProfile getColorsProfile() {
return colorsProfile;
}
@Nonnull
@Override
public byte[] getRowData() {
return rowDataCache.rowData;
}
@Override
public int getCodeLastCharPos() {
return visibility.getCodeLastCharPos();
}
@Override
public long getDataSize() {
return structure.getDataSize();
}
@Nonnull
@Override
public BinaryData getContentData() {
return codeArea.getContentData();
}
@Nullable
@Override
public CodeAreaSelection getSelectionHandler() {
return ((SelectionCapable) codeArea).getSelectionHandler();
}
private int getHorizontalScrollBarSize() {
// JScrollBar horizontalScrollBar = scrollPanel.getHorizontalScrollBar();
// int size;
@@ -1668,9 +1671,9 @@ public class DefaultCodeAreaPainter implements CodeAreaPainter, BasicColorsCapab
private static class CursorDataCache {
Rectangle caretRect = new Rectangle();
Rectangle mirrorCursorRect = new Rectangle();
// final Stroke dashedStroke = new BasicStroke(1, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[]{2}, 0);
Rect caretRect = new Rect();
Rect mirrorCursorRect = new Rect();
final DashPathEffect dashedStroke = new DashPathEffect(new float[] {4f, 4f}, 0f);
int cursorCharsLength;
char[] cursorChars;
int cursorDataLength;
@@ -30,8 +30,8 @@ import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
public class DefaultCodeAreaScrollPane extends TwoDimensionScrollView {
private volatile boolean scrollingByUser = false;
private volatile boolean scrollingUpdate = false;
protected volatile boolean scrollingByUser = false;
protected volatile boolean scrollingUpdate = false;
public DefaultCodeAreaScrollPane(Context context) {
super(context);
@@ -50,11 +50,15 @@ public class DefaultCodeAreaScrollPane extends TwoDimensionScrollView {
private void init() {
setScrollContainer(true);
// setHorizontalScrollBarEnabled(true);
// setVerticalScrollBarEnabled(true);
// setHorizontalFadingEdgeEnabled(false);
}
public void updateScrollBars(int verticalScrollValue, int horizontalScrollValue) {
scrollingUpdate = true;
scrollTo(horizontalScrollValue, verticalScrollValue);
// awakenScrollBars();
scrollingUpdate = false;
}
}
@@ -46,29 +46,29 @@ import javax.annotation.ParametersAreNonnullByDefault;
public class BasicCodeAreaColorsProfile implements CodeAreaColorsProfile {
@Nullable
private Integer textColor;
protected Integer textColor;
@Nullable
private Integer textBackground;
protected Integer textBackground;
@Nullable
private Integer selectionColor;
protected Integer selectionColor;
@Nullable
private Integer selectionBackground;
protected Integer selectionBackground;
@Nullable
private Integer selectionMirrorColor;
protected Integer selectionMirrorColor;
@Nullable
private Integer selectionMirrorBackground;
protected Integer selectionMirrorBackground;
@Nullable
private Integer alternateColor;
protected Integer alternateColor;
@Nullable
private Integer alternateBackground;
protected Integer alternateBackground;
@Nullable
private Integer cursorColor;
protected Integer cursorColor;
@Nullable
private Integer cursorNegativeColor;
protected Integer cursorNegativeColor;
@Nullable
private Integer decorationLine;
protected Integer decorationLine;
@Nullable
private Context context;
protected Context context;
public BasicCodeAreaColorsProfile() {
}
@@ -174,7 +174,7 @@ public class BasicCodeAreaColorsProfile implements CodeAreaColorsProfile {
if (textBackground == null) {
textBackground = CodeAreaAndroidUtils.createNegativeColor(textColor);
}
selectionColor = getThemeColor(androidx.appcompat.R.attr.editTextColor);
selectionColor = textColor; // getThemeColor(androidx.appcompat.R.attr.editTextColor);
if (selectionColor == null) {
selectionColor = isDarkMode() ? Color.BLACK : Color.WHITE;
}
@@ -15,12 +15,10 @@
*/
package org.exbin.bined.android.capability;
import org.exbin.bined.android.basic.color.BasicCodeAreaColorsProfile;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.android.basic.color.BasicCodeAreaColorsProfile;
/**
* Support for basic set of colors.
@@ -30,8 +28,18 @@ import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
public interface BasicColorsCapable {
/**
* Returns basic profile for colors.
*
* @return colors profile
*/
@Nonnull
Optional<BasicCodeAreaColorsProfile> getBasicColors();
void setBasicColors(BasicCodeAreaColorsProfile colors);
/**
* Sets basic profile for colors.
*
* @param colorsProfile colors profile
*/
void setBasicColors(BasicCodeAreaColorsProfile colorsProfile);
}
@@ -0,0 +1,44 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.android.capability;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.android.CodeAreaCharAssessor;
/**
* Painter support for character assessor capability.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public interface CharAssessorPainterCapable {
/**
* Returns character assessor.
*
* @return character assessor
*/
@Nonnull
CodeAreaCharAssessor getCharAssessor();
/**
* Sets character assessor.
*
* @param charAssessor character assessor
*/
void setCharAssessor(CodeAreaCharAssessor charAssessor);
}
@@ -0,0 +1,44 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.android.capability;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.android.CodeAreaColorAssessor;
/**
* Painter support for color assessor capability.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public interface ColorAssessorPainterCapable {
/**
* Returns color assessor.
*
* @return color assessor
*/
@Nonnull
CodeAreaColorAssessor getColorAssessor();
/**
* Sets color assessor.
*
* @param colorAssessor color assessor
*/
void setColorAssessor(CodeAreaColorAssessor colorAssessor);
}
@@ -28,8 +28,18 @@ import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
public interface FontCapable {
/**
* Returns font used for text painting.
*
* @return font
*/
@Nonnull
Font getCodeFont();
void setCodeFont(Font font);
/**
* Sets font used for text painting.
*
* @param codeFont font
*/
void setCodeFont(Font codeFont);
}
@@ -17,7 +17,10 @@ package org.exbin.bined.editor.android;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
@@ -29,9 +32,22 @@ import androidx.fragment.app.FragmentActivity;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* About application dialog.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class AboutDialog extends AppCompatDialogFragment {
private static final String DONATE_LINK = "https://bined.exbin.org/?p=donate";
private String appVersion;
public void setAppVersion(String appVersion) {
this.appVersion = appVersion;
}
@Nonnull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
@@ -44,11 +60,17 @@ public class AboutDialog extends AppCompatDialogFragment {
// Pass null as the parent view because its going in the
// dialog layout
View aboutView = inflater.inflate(R.layout.about_view, null);
TextView textView = (TextView) aboutView.findViewById(R.id.textView);
TextView textView = aboutView.findViewById(R.id.textView);
String htmlString = String.format(getResources().getString(R.string.app_about), appVersion).replace("\n", "<br/>");
textView.setText(Html.fromHtml(htmlString, Html.FROM_HTML_MODE_COMPACT));
textView.setMovementMethod(LinkMovementMethod.getInstance());
builder.setView(aboutView);
builder.setPositiveButton(getResources().getString(R.string.button_close), (dialog, which) -> {
});
builder.setNeutralButton(getResources().getString(R.string.button_donate), (dialog, which) -> {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(DONATE_LINK));
startActivity(browserIntent);
});
return builder.create();
}
}
@@ -0,0 +1,59 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.editor.android;
import android.app.Application;
import org.exbin.bined.editor.android.preference.BinaryEditorPreferences;
import org.exbin.bined.editor.android.preference.PreferencesWrapper;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Application context.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class ApplicationContext extends Application {
private BinaryEditorPreferences appPreferences;
private BinEdFileHandler fileHandler = null;
@Override
public void onCreate() {
super.onCreate();
appPreferences = new BinaryEditorPreferences(new PreferencesWrapper(getApplicationContext()));
}
@Nonnull
public BinaryEditorPreferences getAppPreferences() {
return appPreferences;
}
@Nullable
public BinEdFileHandler getFileHandler() {
return fileHandler;
}
public void setFileHandler(BinEdFileHandler fileHandler) {
this.fileHandler = fileHandler;
}
}
@@ -0,0 +1,151 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.editor.android;
import android.content.ContentResolver;
import android.content.res.XmlResourceParser;
import android.net.Uri;
import org.exbin.auxiliary.binary_data.BinaryData;
import org.exbin.auxiliary.binary_data.ByteArrayEditableData;
import org.exbin.bined.EditOperation;
import org.exbin.bined.android.CodeAreaPainter;
import org.exbin.bined.android.basic.CodeArea;
import org.exbin.bined.android.capability.CharAssessorPainterCapable;
import org.exbin.bined.android.capability.ColorAssessorPainterCapable;
import org.exbin.bined.operation.android.CodeAreaOperationCommandHandler;
import org.exbin.bined.operation.android.CodeAreaUndoRedo;
import org.exbin.framework.bined.BinEdCodeAreaAssessor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* File handler for binary editor.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class BinEdFileHandler {
private CodeArea codeArea;
private CodeAreaUndoRedo undoRedo;
private long documentOriginalSize = 0;
private Uri currentFileUri = null;
private Uri pickerInitialUri = null;
public BinEdFileHandler(CodeArea codeArea) {
// ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
this.codeArea = codeArea;
codeArea.setContentData(new ByteArrayEditableData());
codeArea.setEditOperation(EditOperation.INSERT);
undoRedo = new CodeAreaUndoRedo(codeArea);
CodeAreaOperationCommandHandler commandHandler = new CodeAreaOperationCommandHandler(codeArea.getContext(), codeArea, undoRedo);
codeArea.setCommandHandler(commandHandler);
CodeAreaPainter painter = codeArea.getPainter();
BinEdCodeAreaAssessor codeAreaAssessor = new BinEdCodeAreaAssessor(((ColorAssessorPainterCapable) painter).getColorAssessor(), ((CharAssessorPainterCapable) painter).getCharAssessor());
((ColorAssessorPainterCapable) painter).setColorAssessor(codeAreaAssessor);
((CharAssessorPainterCapable) painter).setCharAssessor(codeAreaAssessor);
codeArea.setPainter(painter);
}
public void newFile() {
codeArea.setContentData(new ByteArrayEditableData());
undoRedo.clear();
currentFileUri = null;
documentOriginalSize = 0;
}
public void openFile(ContentResolver contentResolver, Uri fileUri) {
ByteArrayEditableData fileData = new ByteArrayEditableData();
try {
InputStream inputStream = contentResolver.openInputStream(fileUri);
if (inputStream == null) {
return;
}
fileData.loadFromStream(inputStream);
inputStream.close();
documentOriginalSize = fileData.getDataSize();
undoRedo.clear();
codeArea.setContentData(fileData);
currentFileUri = fileUri;
pickerInitialUri = fileUri;
} catch (IOException ex) {
Logger.getLogger(BinEdFileHandler.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void saveFile(ContentResolver contentResolver, Uri fileUri) {
BinaryData contentData = codeArea.getContentData();
try {
OutputStream outputStream = contentResolver.openOutputStream(fileUri);
if (outputStream == null) {
return;
}
codeArea.getContentData().saveToStream(outputStream);
outputStream.close();
documentOriginalSize = contentData.getDataSize();
undoRedo.setSyncPosition();
currentFileUri = fileUri;
pickerInitialUri = fileUri;
} catch (IOException ex) {
Logger.getLogger(BinEdFileHandler.class.getName()).log(Level.SEVERE, null, ex);
}
}
@Nonnull
public CodeArea getCodeArea() {
return codeArea;
}
@Nonnull
public CodeAreaUndoRedo getUndoRedo() {
return undoRedo;
}
public long getDocumentOriginalSize() {
return documentOriginalSize;
}
@Nullable
public Uri getCurrentFileUri() {
return currentFileUri;
}
@Nullable
public Uri getPickerInitialUri() {
return pickerInitialUri;
}
public void clearFileUri() {
currentFileUri = null;
pickerInitialUri = null;
}
public boolean isModified() {
return undoRedo.isModified();
}
}
@@ -15,8 +15,12 @@
*/
package org.exbin.bined.editor.android;
import android.Manifest;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -31,6 +35,7 @@ import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.Toast;
@@ -43,46 +48,54 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.widget.SearchView;
import androidx.appcompat.widget.Toolbar;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.os.LocaleListCompat;
import androidx.fragment.app.DialogFragment;
import com.rustamg.filedialogs.FileDialog;
import com.rustamg.filedialogs.OpenFileDialog;
import com.rustamg.filedialogs.SaveFileDialog;
import org.exbin.auxiliary.binary_data.BinaryData;
import org.exbin.auxiliary.binary_data.ByteArrayEditableData;
import org.exbin.bined.CaretOverlapMode;
import org.exbin.bined.CodeAreaCaretListener;
import org.exbin.bined.CodeAreaCaretPosition;
import org.exbin.bined.CodeCharactersCase;
import org.exbin.bined.CodeType;
import org.exbin.bined.DataChangedListener;
import org.exbin.bined.DefaultCodeAreaCaretPosition;
import org.exbin.bined.EditMode;
import org.exbin.bined.EditOperation;
import org.exbin.bined.EditModeChangedListener;
import org.exbin.bined.RowWrappingMode;
import org.exbin.bined.SelectionChangedListener;
import org.exbin.bined.SelectionRange;
import org.exbin.bined.android.CodeAreaAndroidUtils;
import org.exbin.bined.android.Font;
import org.exbin.bined.android.basic.CodeArea;
import org.exbin.bined.android.basic.color.BasicCodeAreaColorsProfile;
import org.exbin.bined.android.capability.ColorAssessorPainterCapable;
import org.exbin.bined.basic.BasicCodeAreaSection;
import org.exbin.bined.basic.CodeAreaScrollPosition;
import org.exbin.bined.basic.CodeAreaViewMode;
import org.exbin.bined.capability.EditModeCapable;
import org.exbin.bined.editor.android.options.Theme;
import org.exbin.bined.editor.android.preference.BinaryEditorPreferences;
import org.exbin.bined.editor.android.preference.EncodingPreference;
import org.exbin.bined.editor.android.preference.FontPreference;
import org.exbin.bined.editor.android.preference.MainPreferences;
import org.exbin.bined.editor.android.preference.PreferencesWrapper;
import org.exbin.bined.editor.android.search.BinarySearchService;
import org.exbin.bined.editor.android.search.BinarySearchServiceImpl;
import org.exbin.bined.editor.android.search.SearchCondition;
import org.exbin.bined.editor.android.search.SearchParameters;
import org.exbin.bined.highlight.android.HighlightNonAsciiCodeAreaPainter;
import org.exbin.bined.operation.android.CodeAreaOperationCommandHandler;
import org.exbin.bined.operation.android.CodeAreaUndoRedo;
import org.exbin.bined.highlight.android.NonAsciiCodeAreaColorAssessor;
import org.exbin.bined.highlight.android.NonprintablesCodeAreaAssessor;
import org.exbin.bined.operation.undo.BinaryDataUndoRedoChangeListener;
import org.exbin.framework.bined.BinaryStatusApi;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.File;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -90,8 +103,13 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Main activity.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class MainActivity extends AppCompatActivity {
public class MainActivity extends AppCompatActivity implements FileDialog.OnFileSelectedListener {
private static final int SELECTION_START_POPUP_ID = 1;
private static final int SELECTION_END_POPUP_ID = 2;
@@ -104,27 +122,20 @@ public class MainActivity extends AppCompatActivity {
private static final int COPY_AS_CODE_ACTION_POPUP_ID = 9;
private static final int PASTE_FROM_CODE_ACTION_POPUP_ID = 10;
private static final String CURSOR_POSITION_TAG = "bined.cursorPosition";
private static final String CURSOR_OFFSET_TAG = "bined.cursorOffset";
private static final String CURSOR_SECTION_TAG = "bined.cursorSection";
private static final String SCROLL_ROW_POSITION_TAG = "bined.scrollRowPosition";
private static final String SCROLL_ROW_OFFSET_TAG = "bined.scrollRowOffset";
private static final String SCROLL_CHAR_POSITION_TAG = "bined.scrollCharPosition";
private static final String SCROLL_CHAR_OFFSET_TAG = "bined.scrollOffsetPosition";
private static final String SELECTION_START_TAG = "bined.selectionStart";
private static final String SELECTION_END_TAG = "bined.selectionEnd";
private static final int STORAGE_PERMISSION_CODE = 1;
private BinEdFileHandler fileHandler;
private CodeArea codeArea;
private CodeAreaUndoRedo undoRedo;
private BinaryEditorPreferences appPreferences;
private long documentOriginalSize = 0;
private Toolbar toolbar;
private Menu menu;
private static ByteArrayEditableData fileData = null;
private Uri currentFileUri = null;
private Uri pickerInitialUri = null;
private SearchView searchView;
private final BinaryStatusHandler binaryStatus = new BinaryStatusHandler(this);
private BinarySearchService searchService;
private Runnable postSaveAsAction = null;
private boolean keyboardShown = false;
private final BinarySearchService.SearchStatusListener searchStatusListener = new BinarySearchService.SearchStatusListener() {
@Override
public void setStatus(BinarySearchService.FoundMatches foundMatches, SearchParameters.MatchMode matchMode) {
@@ -136,9 +147,90 @@ public class MainActivity extends AppCompatActivity {
}
};
private final BinaryDataUndoRedoChangeListener codeAreaChangeListener = () -> {
if (menu != null) {
updateUndoState();
}
};
private final DataChangedListener codeAreaDataChangedListener = () -> {
// activeFile.getComponent().notifyDataChanged();
// if (editorModificationListener != null) {
// editorModificationListener.modified();
// }
updateCurrentDocumentSize();
};
private final SelectionChangedListener codeAreaSelectionChangedListener = () -> {
binaryStatus.setSelectionRange(codeArea.getSelection());
// updateClipboardActionsStatus();
};
private final CodeAreaCaretListener codeAreaCodeAreaCaretListener = caretPosition -> {
binaryStatus.setCursorPosition(caretPosition);
private boolean keyboardShown = false;
private BinaryEditorPreferences appPreferences;
boolean showKeyboard = codeArea.getActiveSection() == BasicCodeAreaSection.TEXT_PREVIEW;
if (showKeyboard != keyboardShown) {
keyboardShown = showKeyboard;
InputMethodManager im = (InputMethodManager) codeArea.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
codeArea.requestFocus();
if (showKeyboard) {
im.showSoftInput(codeArea, InputMethodManager.SHOW_IMPLICIT);
} else {
im.hideSoftInputFromWindow(codeArea.getWindowToken(), 0);
}
}
};
private final View.OnKeyListener codeAreaOnKeyListener = new View.OnKeyListener() {
private final KeyListener keyListener = new TextKeyListener(TextKeyListener.Capitalize.NONE, false);
private final Editable editable = Editable.Factory.getInstance().newEditable("");
@Override
public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
try {
if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DEL || keyEvent.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL) {
editable.clear();
codeArea.getCommandHandler().keyPressed(keyEvent);
} else {
keyListener.onKeyDown(view, editable, keyCode, keyEvent);
processKeys(keyEvent);
}
} else if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
editable.clear();
if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER) {
if (keyEvent.getEventTime() - keyEvent.getDownTime() > TimeUnit.SECONDS.toMillis(1)) {
codeArea.showContextMenu();
} else {
View keyStripeView = findViewById(R.id.controlPanel);
keyStripeView.requestFocus();
}
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_BACK && isGoogleTV(codeArea.getContext())) {
toolbar.showOverflowMenu();
} else {
codeArea.getCommandHandler().keyPressed(keyEvent);
}
} else {
keyListener.onKeyOther(view, editable, keyEvent);
processKeys(keyEvent);
}
return true;
} catch (Exception ex) {
// ignore
}
return false;
}
private void processKeys(KeyEvent keyEvent) {
int outputCharsLength = editable.length();
if (outputCharsLength > 0) {
for (int i = 0; i < outputCharsLength; i++) {
codeArea.getCommandHandler().keyTyped(editable.charAt(i), keyEvent);
}
editable.clear();
}
}
};
private final EditModeChangedListener codeAreaEditModeChangedListener = binaryStatus::setEditMode;
private final ActivityResultLauncher<Intent> openFileLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), this::openFileResultCallback);
private final ActivityResultLauncher<Intent> saveFileLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), this::saveFileResultCallback);
@@ -147,131 +239,106 @@ public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
appPreferences = new BinaryEditorPreferences(new PreferencesWrapper(getApplicationContext()));
ApplicationContext application = (ApplicationContext) getApplication();
appPreferences = application.getAppPreferences();
setContentView(R.layout.activity_main);
MainPreferences mainPreferences = appPreferences.getMainPreferences();
String theme = mainPreferences.getTheme();
if ("dark".equals(theme)) {
if (Theme.DARK.name().equalsIgnoreCase(theme)) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
} else if ("light".equals(theme)) {
} else if (Theme.LIGHT.name().equalsIgnoreCase(theme)) {
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
}
toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
// For now steal code area and keep it in application context
fileHandler = application.getFileHandler();
if (fileHandler == null) {
codeArea = findViewById(R.id.codeArea);
fileHandler = new BinEdFileHandler(codeArea);
application.setFileHandler(fileHandler);
} else {
codeArea = fileHandler.getCodeArea();
ViewGroup parentView = (ViewGroup) codeArea.getParent();
parentView.removeView(codeArea);
ViewGroup contentView = findViewById(R.id.contentMain);
contentView.removeView(findViewById(R.id.codeArea));
contentView.addView(codeArea);
}
AppCompatDelegate.setApplicationLocales(LocaleListCompat.wrap(getLanguageLocaleList()));
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
codeArea = findViewById(R.id.codeArea);
codeArea.setEditOperation(EditOperation.INSERT);
searchService = new BinarySearchServiceImpl(codeArea);
undoRedo = new CodeAreaUndoRedo(codeArea);
undoRedo.addChangeListener(() -> {
if (menu != null) {
updateUndoState();
}
});
CodeAreaOperationCommandHandler commandHandler = new CodeAreaOperationCommandHandler(codeArea.getContext(), codeArea, undoRedo);
codeArea.setCommandHandler(commandHandler);
HighlightNonAsciiCodeAreaPainter painter = new HighlightNonAsciiCodeAreaPainter(codeArea);
codeArea.setPainter(painter);
BasicCodeAreaColorsProfile basicColors = painter.getBasicColors();
BasicCodeAreaColorsProfile basicColors = codeArea.getBasicColors().orElse(null);
if (basicColors == null) {
throw new IllegalStateException("Missing colors profile");
}
basicColors.setContext(this);
basicColors.reinitialize();
painter.resetColors();
codeArea.resetColors();
searchService = new BinarySearchServiceImpl(codeArea);
registerForContextMenu(codeArea);
if (fileData != null) {
codeArea.setContentData(fileData);
} else {
codeArea.setContentData(new ByteArrayEditableData());
}
codeArea.addDataChangedListener(() -> {
// activeFile.getComponent().notifyDataChanged();
// if (editorModificationListener != null) {
// editorModificationListener.modified();
// }
updateCurrentDocumentSize();
});
codeArea.addSelectionChangedListener(() -> {
binaryStatus.setSelectionRange(codeArea.getSelection());
// updateClipboardActionsStatus();
});
codeArea.addCaretMovedListener((CodeAreaCaretPosition caretPosition) -> {
binaryStatus.setCursorPosition(caretPosition);
boolean showKeyboard = codeArea.getActiveSection() == BasicCodeAreaSection.TEXT_PREVIEW;
if (showKeyboard != keyboardShown) {
keyboardShown = showKeyboard;
InputMethodManager im = (InputMethodManager)codeArea.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
codeArea.requestFocus();
if (showKeyboard) {
im.showSoftInput(codeArea, InputMethodManager.SHOW_IMPLICIT);
} else {
im.hideSoftInputFromWindow(codeArea.getWindowToken(), 0);
}
}
});
codeArea.setOnKeyListener(new View.OnKeyListener() {
private final KeyListener keyListener = new TextKeyListener(TextKeyListener.Capitalize.NONE, false);
private final Editable editable = Editable.Factory.getInstance().newEditable("");
@Override
public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
try {
if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_DEL || keyEvent.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL) {
commandHandler.keyPressed(keyEvent);
} else {
keyListener.onKeyDown(view, editable, keyCode, keyEvent);
processKeys(keyEvent);
}
} else if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
commandHandler.keyPressed(keyEvent);
} else {
keyListener.onKeyOther(view, editable, keyEvent);
processKeys(keyEvent);
}
return true;
} catch (Exception ex) {
// ignore
}
return false;
}
private void processKeys(KeyEvent keyEvent) {
int outputCharsLength = editable.length();
if (outputCharsLength > 0) {
for (int i = 0; i < outputCharsLength; i++) {
commandHandler.keyTyped(editable.charAt(i), keyEvent);
}
editable.clear();
}
}
});
codeArea.addEditModeChangedListener(binaryStatus::setEditMode);
fileHandler.getUndoRedo().addChangeListener(codeAreaChangeListener);
codeArea.addDataChangedListener(codeAreaDataChangedListener);
codeArea.addSelectionChangedListener(codeAreaSelectionChangedListener);
codeArea.addCaretMovedListener(codeAreaCodeAreaCaretListener);
codeArea.addEditModeChangedListener(codeAreaEditModeChangedListener);
codeArea.setOnKeyListener(codeAreaOnKeyListener);
applySettings();
processIntent(getIntent());
}
@Override
protected void onDestroy() {
super.onDestroy();
codeArea.setOnKeyListener(null);
codeArea.removeEditModeChangedListener(codeAreaEditModeChangedListener);
codeArea.removeCaretMovedListener(codeAreaCodeAreaCaretListener);
codeArea.removeSelectionChangedListener(codeAreaSelectionChangedListener);
codeArea.removeDataChangedListener(codeAreaDataChangedListener);
fileHandler.getUndoRedo().removeChangeListener(codeAreaChangeListener);
}
private void processIntent(Intent intent) {
String action = intent.getAction();
if (action == null) {
return;
}
if (Intent.ACTION_VIEW.equals(action) || Intent.ACTION_EDIT.equals(action)) {
String scheme = intent.getScheme();
if ("file".equals(scheme) || "content".equals(scheme)) {
Uri fileUri = intent.getData();
if (fileUri != null) {
releaseFile(() -> {
fileHandler.openFile(getContentResolver(), fileUri);
// Content should be opened as unspecified file
if ("content".equals(scheme)) {
fileHandler.clearFileUri();
}
});
}
}
}
}
@Nonnull
private LocaleList getLanguageLocaleList() {
String language = appPreferences.getMainPreferences().getLocaleTag();
if (language.isEmpty()) {
return LocaleList.getEmptyLocaleList();
} else if ("en".equals(language)) {
return LocaleList.getDefault();
return LocaleList.forLanguageTags("en_US");
}
return LocaleList.forLanguageTags(language);
@@ -318,15 +385,22 @@ public class MainActivity extends AppCompatActivity {
}
this.menu = menu;
updateUndoState();
// Currently on Google TV access to app bar icons doesn't seem to work
if (isGoogleTV(this)) {
for (int i = 0; i < menu.size(); i++) {
MenuItem menuItem = menu.getItem(i);
menuItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
}
}
codeArea.addSelectionChangedListener(this::updateEditActionsState);
updateUndoState();
updateEditActionsState();
updateViewActionsState();
MenuItem searchMenuItem = menu.findItem(R.id.action_search);
SearchView searchView = Objects.requireNonNull((SearchView) searchMenuItem.getActionView());
searchView = Objects.requireNonNull((SearchView) searchMenuItem.getActionView());
searchView.setSubmitButtonEnabled(true);
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
@@ -441,37 +515,20 @@ public class MainActivity extends AppCompatActivity {
if (id == R.id.action_new) {
releaseFile(() -> {
codeArea.setContentData(new ByteArrayEditableData());
undoRedo.clear();
currentFileUri = null;
documentOriginalSize = 0;
fileHandler.newFile();
});
return true;
} else if (id == R.id.action_open) {
releaseFile(() -> {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.putExtra(Intent.EXTRA_LOCALE_LIST, getLanguageLocaleList());
}
if (pickerInitialUri != null) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);
}
// startActivityForResult(intent, 1);
openFileLauncher.launch(Intent.createChooser(intent, getResources().getString(R.string.select_file)));
});
releaseFile(this::openFile);
return true;
} else if (id == R.id.action_save) {
Uri currentFileUri = fileHandler.getCurrentFileUri();
if (currentFileUri == null) {
saveAs();
} else {
saveFile(currentFileUri);
fileHandler.saveFile(getContentResolver(), currentFileUri);
}
return true;
@@ -486,7 +543,17 @@ public class MainActivity extends AppCompatActivity {
return true;
} else if (id == R.id.action_about) {
PackageManager manager = this.getPackageManager();
String appVersion = "";
try {
PackageInfo info = manager.getPackageInfo(this.getPackageName(), PackageManager.GET_ACTIVITIES);
appVersion = info.versionName;
} catch (PackageManager.NameNotFoundException e) {
// ignore
}
AboutDialog aboutDialog = new AboutDialog();
aboutDialog.setAppVersion(appVersion);
aboutDialog.show(getSupportFragmentManager(), "aboutDialog");
return true;
@@ -584,17 +651,32 @@ public class MainActivity extends AppCompatActivity {
appPreferences.getCodeAreaPreferences().setMaxBytesPerRow(codeArea.getMaxBytesPerRow());
menu.findItem(R.id.bytes_per_row_16).setChecked(true);
return true;
} else if (id == R.id.non_printable_characters) {
boolean checked = item.isChecked();
item.setChecked(!checked);
NonprintablesCodeAreaAssessor nonprintablesCodeAreaAssessor = CodeAreaAndroidUtils.findColorAssessor((ColorAssessorPainterCapable) codeArea.getPainter(), NonprintablesCodeAreaAssessor.class);
if (nonprintablesCodeAreaAssessor != null) {
nonprintablesCodeAreaAssessor.setShowNonprintables(!checked);
}
appPreferences.getCodeAreaPreferences().setShowNonprintables(!checked);
return true;
} else if (id == R.id.code_colorization) {
boolean checked = item.isChecked();
item.setChecked(!checked);
((HighlightNonAsciiCodeAreaPainter) codeArea.getPainter()).setNonAsciiHighlightingEnabled(!checked);
NonAsciiCodeAreaColorAssessor nonAsciiColorAssessor = CodeAreaAndroidUtils.findColorAssessor((ColorAssessorPainterCapable) codeArea.getPainter(), NonAsciiCodeAreaColorAssessor.class);
if (nonAsciiColorAssessor != null) {
nonAsciiColorAssessor.setNonAsciiHighlightingEnabled(!checked);
}
appPreferences.getCodeAreaPreferences().setCodeColorization(!checked);
return true;
} else if (id == R.id.action_undo) {
undoRedo.performUndo();
fileHandler.getUndoRedo().performUndo();
// TODO fix operations instead of validating
codeArea.validateCaret();
return true;
} else if (id == R.id.action_redo) {
undoRedo.performRedo();
fileHandler.getUndoRedo().performRedo();
codeArea.validateCaret();
return true;
} else if (id == R.id.action_cut) {
codeArea.cut();
@@ -604,6 +686,19 @@ public class MainActivity extends AppCompatActivity {
return true;
} else if (id == R.id.action_paste) {
codeArea.paste();
return true;
} else if (id == R.id.action_search) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.action_search);
searchView.setIconifiedByDefault(false);
builder.setNegativeButton(R.string.button_cancel, (dialog, which) -> {
searchService.clearMatches();
});
builder.setView(searchView);
AlertDialog alertDialog = builder.create();
alertDialog.show();
searchView.requestFocus();
return true;
} else if (id == R.id.action_delete) {
codeArea.delete();
@@ -622,8 +717,10 @@ public class MainActivity extends AppCompatActivity {
DefaultCodeAreaCaretPosition caretPosition = new DefaultCodeAreaCaretPosition();
caretPosition.setCodeOffset(0);
caretPosition.setPosition(codeArea.getActiveCaretPosition());
caretPosition.setDataPosition(Long.parseLong(inputNumber.getText().toString()));
long targetPosition = Long.parseLong(inputNumber.getText().toString());
caretPosition.setDataPosition(Math.min(targetPosition, codeArea.getDataSize()));
codeArea.setActiveCaretPosition(caretPosition);
codeArea.validateCaret();
codeArea.centerOnCursor();
});
builder.setNegativeButton(R.string.button_cancel, null);
@@ -637,7 +734,7 @@ public class MainActivity extends AppCompatActivity {
}
public void releaseFile(Runnable postReleaseAction) {
if (!undoRedo.isModified()) {
if (!fileHandler.isModified()) {
postReleaseAction.run();
return;
}
@@ -645,10 +742,11 @@ public class MainActivity extends AppCompatActivity {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.file_modified);
builder.setPositiveButton(R.string.button_save, (dialog, which) -> {
Uri currentFileUri = fileHandler.getCurrentFileUri();
if (currentFileUri == null) {
saveAs();
} else {
saveFile(currentFileUri);
fileHandler.saveFile(getContentResolver(), currentFileUri);
}
postReleaseAction.run();
});
@@ -660,6 +758,61 @@ public class MainActivity extends AppCompatActivity {
alertDialog.show();
}
public void openFile() {
if (MainActivity.isGoogleTV(this)) {
fallBackOpenFile();
return;
}
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.putExtra(Intent.EXTRA_LOCALE_LIST, getLanguageLocaleList());
}
Uri pickerInitialUri = fileHandler.getPickerInitialUri();
if (pickerInitialUri != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);
}
try {
openFileLauncher.launch(Intent.createChooser(intent, getResources().getString(R.string.select_file)));
} catch (ActivityNotFoundException ex) {
fallBackOpenFile();
}
}
private void fallBackOpenFile() {
boolean permissionGranted = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
if (!permissionGranted) {
requestWriteExternalStoragePermission();
return;
}
OpenFileDialog dialog = new OpenFileDialog();
dialog.setStyle(DialogFragment.STYLE_NO_TITLE, R.style.AppTheme);
dialog.show(getSupportFragmentManager(), OpenFileDialog.class.getName());
}
private void requestWriteExternalStoragePermission() {
final String[] permissions = new String[]{
Manifest.permission.READ_EXTERNAL_STORAGE
, Manifest.permission.WRITE_EXTERNAL_STORAGE
};
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(R.string.storage_permission_request);
builder.setPositiveButton(R.string.button_request, (dialog, which) -> {
ActivityCompat.requestPermissions(MainActivity.this, permissions, STORAGE_PERMISSION_CODE);
});
builder.setNegativeButton(R.string.button_cancel, null);
builder.show();
} else {
ActivityCompat.requestPermissions(MainActivity.this, permissions, STORAGE_PERMISSION_CODE);
}
}
public void saveAs() {
saveAs(null);
}
@@ -667,6 +820,11 @@ public class MainActivity extends AppCompatActivity {
public void saveAs(@Nullable Runnable postSaveAsAction) {
this.postSaveAsAction = postSaveAsAction;
if (MainActivity.isGoogleTV(this)) {
fallBackSaveAs();
return;
}
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
@@ -674,92 +832,43 @@ public class MainActivity extends AppCompatActivity {
intent.putExtra(Intent.EXTRA_LOCALE_LIST, getLanguageLocaleList());
}
if (pickerInitialUri != null) {
Uri pickerInitialUri = fileHandler.getPickerInitialUri();
if (pickerInitialUri != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri);
}
saveFileLauncher.launch(Intent.createChooser(intent, getResources().getString(R.string.save_as_file)));
}
public void openFile(Uri fileUri) {
fileData = new ByteArrayEditableData();
try {
InputStream inputStream = getContentResolver().openInputStream(fileUri);
if (inputStream == null) {
return;
}
fileData.loadFromStream(inputStream);
inputStream.close();
documentOriginalSize = fileData.getDataSize();
undoRedo.clear();
codeArea.setContentData(fileData);
currentFileUri = fileUri;
pickerInitialUri = fileUri;
} catch (IOException ex) {
Logger.getLogger(MainActivity.class.getName()).log(Level.SEVERE, null, ex);
saveFileLauncher.launch(Intent.createChooser(intent, getResources().getString(R.string.save_as_file)));
} catch (ActivityNotFoundException ex) {
fallBackSaveAs();
}
}
public void saveFile(Uri fileUri) {
BinaryData contentData = codeArea.getContentData();
try {
OutputStream outputStream = getContentResolver().openOutputStream(fileUri);
if (outputStream == null) {
return;
}
fileData.saveToStream(outputStream);
outputStream.close();
documentOriginalSize = contentData.getDataSize();
undoRedo.setSyncPosition();
currentFileUri = fileUri;
pickerInitialUri = fileUri;
} catch (IOException ex) {
Logger.getLogger(MainActivity.class.getName()).log(Level.SEVERE, null, ex);
private void fallBackSaveAs() {
boolean permissionGranted = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
if (permissionGranted) {
SaveFileDialog dialog = new SaveFileDialog();
dialog.setStyle(DialogFragment.STYLE_NO_TITLE, R.style.AppTheme);
dialog.show(getSupportFragmentManager(), SaveFileDialog.class.getName());
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, STORAGE_PERMISSION_CODE);
}
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
long cursorDataPosition = savedInstanceState.getLong(CURSOR_POSITION_TAG);
int cursorOffset = savedInstanceState.getInt(CURSOR_OFFSET_TAG);
int cursorSection = savedInstanceState.getInt(CURSOR_SECTION_TAG);
codeArea.setActiveCaretPosition(new DefaultCodeAreaCaretPosition(cursorDataPosition, cursorOffset, cursorSection == 0 ? BasicCodeAreaSection.CODE_MATRIX : BasicCodeAreaSection.TEXT_PREVIEW));
long scrollRowPosition = savedInstanceState.getLong(SCROLL_ROW_POSITION_TAG);
int scrollRowOffset = savedInstanceState.getInt(SCROLL_ROW_OFFSET_TAG);
int scrollCharPosition = savedInstanceState.getInt(SCROLL_CHAR_POSITION_TAG);
int scrollCharOffset = savedInstanceState.getInt(SCROLL_CHAR_OFFSET_TAG);
codeArea.setScrollPosition(new CodeAreaScrollPosition(scrollRowPosition, scrollRowOffset, scrollCharPosition, scrollCharOffset));
long selectionStart = savedInstanceState.getLong(SELECTION_START_TAG);
long selectionEnd = savedInstanceState.getLong(SELECTION_END_TAG);
codeArea.setSelection(new SelectionRange(selectionStart, selectionEnd));
boolean permissionGranted = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
if (!permissionGranted) {
Toast.makeText(this, R.string.storage_permission_is_not_granted, Toast.LENGTH_LONG).show();
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
SelectionRange selection = codeArea.getSelection();
outState.putLong(SELECTION_START_TAG, selection.getStart());
outState.putLong(SELECTION_END_TAG, selection.getEnd());
CodeAreaScrollPosition scrollPosition = codeArea.getScrollPosition();
outState.putLong(SCROLL_ROW_POSITION_TAG, scrollPosition.getRowPosition());
outState.putInt(SCROLL_ROW_OFFSET_TAG, scrollPosition.getRowOffset());
outState.putInt(SCROLL_CHAR_POSITION_TAG, scrollPosition.getCharPosition());
outState.putInt(SCROLL_CHAR_OFFSET_TAG, scrollPosition.getCharOffset());
CodeAreaCaretPosition caretPosition = codeArea.getActiveCaretPosition();
outState.putLong(CURSOR_POSITION_TAG, caretPosition.getDataPosition());
outState.putInt(CURSOR_OFFSET_TAG, caretPosition.getCodeOffset());
outState.putInt(CURSOR_SECTION_TAG, ((BasicCodeAreaSection) caretPosition.getSection().orElse(BasicCodeAreaSection.CODE_MATRIX)).ordinal());
}
private void showPermissionError() {
Toast.makeText(this, "Storage permission is not granted", Toast.LENGTH_LONG).show();
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
processIntent(intent);
}
public void updateStatus() {
@@ -776,7 +885,7 @@ public class MainActivity extends AppCompatActivity {
}
long dataSize = codeArea.getDataSize();
binaryStatus.setCurrentDocumentSize(dataSize, documentOriginalSize);
binaryStatus.setCurrentDocumentSize(dataSize, fileHandler.getDocumentOriginalSize());
}
private void updateCurrentCaretPosition() {
@@ -834,6 +943,7 @@ public class MainActivity extends AppCompatActivity {
private void updateViewActionsState() {
menu.findItem(R.id.code_colorization).setChecked(appPreferences.getCodeAreaPreferences().isCodeColorization());
menu.findItem(R.id.non_printable_characters).setChecked(appPreferences.getCodeAreaPreferences().isShowNonprintables());
int bytesPerRow = appPreferences.getCodeAreaPreferences().getMaxBytesPerRow();
switch (bytesPerRow) {
case 0: {
@@ -861,14 +971,15 @@ public class MainActivity extends AppCompatActivity {
private void updateUndoState() {
MenuItem saveMenuItem = menu.findItem(R.id.action_save);
saveMenuItem.setEnabled(currentFileUri == null || undoRedo.isModified());
Uri currentFileUri = fileHandler.getCurrentFileUri();
saveMenuItem.setEnabled(currentFileUri == null || fileHandler.getUndoRedo().isModified());
boolean canUndo = undoRedo.canUndo();
boolean canUndo = fileHandler.getUndoRedo().canUndo();
MenuItem undoMenuItem = menu.findItem(R.id.action_undo);
undoMenuItem.setEnabled(canUndo);
// TODO undoMenuItem.setIconTintList(new ColorStateList(ColorStateList.));
boolean canRedo = undoRedo.canRedo();
boolean canRedo = fileHandler.getUndoRedo().canRedo();
MenuItem redoMenuItem = menu.findItem(R.id.action_redo);
redoMenuItem.setEnabled(canRedo);
}
@@ -880,7 +991,7 @@ public class MainActivity extends AppCompatActivity {
return;
}
openFile(data.getData());
fileHandler.openFile(getContentResolver(), data.getData());
}
private void saveFileResultCallback(ActivityResult activityResult) {
@@ -890,16 +1001,37 @@ public class MainActivity extends AppCompatActivity {
return;
}
saveFile(data.getData());
fileHandler.saveFile(getContentResolver(), data.getData());
if (postSaveAsAction != null) {
postSaveAsAction.run();
postSaveAsAction = null;
}
}
/**
* Legacy support for file dialog using external library.
*
* @param dialog file dialog
* @param file selected file
*/
@Override
public void onFileSelected(FileDialog dialog, File file) {
if (dialog instanceof OpenFileDialog) {
fileHandler.openFile(getContentResolver(), Uri.fromFile(file));
} else {
fileHandler.saveFile(getContentResolver(), Uri.fromFile(file));
if (postSaveAsAction != null) {
postSaveAsAction.run();
postSaveAsAction = null;
}
}
}
private void settingsResultCallback(ActivityResult activityResult) {
applySettings();
updateViewActionsState();
if (menu != null) {
updateViewActionsState();
}
}
public void buttonAction0(View view) {
@@ -1005,4 +1137,9 @@ public class MainActivity extends AppCompatActivity {
public void buttonActionTab(View view) {
codeArea.getCommandHandler().keyPressed(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TAB));
}
public static boolean isGoogleTV(Context context) {
final PackageManager pm = context.getPackageManager();
return pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
}
}
@@ -47,6 +47,11 @@ import java.util.Locale;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Settings activity.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class SettingsActivity extends AppCompatActivity implements
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
@@ -62,6 +67,7 @@ public class SettingsActivity extends AppCompatActivity implements
public static final String CODE_TYPE_KEY = "code_type";
public static final String HEX_CHARACTERS_CASE = "hex_characters_case";
public static final String CODE_COLORIZATION = "code_colorization";
public static final String NONPRINTABLE_CHARACTERS = "nonprintable_characters";
private BinaryEditorPreferences appPreferences;
@@ -224,6 +230,7 @@ public class SettingsActivity extends AppCompatActivity implements
((ListPreference) findPreference(CODE_TYPE_KEY)).setValue(codeAreaPreferences.getCodeType().name());
((ListPreference) findPreference(HEX_CHARACTERS_CASE)).setValue(codeAreaPreferences.getCodeCharactersCase().name());
((TwoStatePreference) findPreference(CODE_COLORIZATION)).setChecked(codeAreaPreferences.isCodeColorization());
((TwoStatePreference) findPreference(NONPRINTABLE_CHARACTERS)).setChecked(codeAreaPreferences.isShowNonprintables());
}
@Override
@@ -240,6 +247,7 @@ public class SettingsActivity extends AppCompatActivity implements
codeAreaPreferences.setCodeType(CodeType.valueOf(((ListPreference) findPreference(CODE_TYPE_KEY)).getValue()));
codeAreaPreferences.setCodeCharactersCase(CodeCharactersCase.valueOf(((ListPreference) findPreference(HEX_CHARACTERS_CASE)).getValue()));
codeAreaPreferences.setCodeColorization(((TwoStatePreference) findPreference(CODE_COLORIZATION)).isChecked());
codeAreaPreferences.setShowNonprintables(((TwoStatePreference) findPreference(NONPRINTABLE_CHARACTERS)).isChecked());
super.onDestroy();
}
@@ -55,7 +55,7 @@ public interface CodeAreaOptions {
boolean isCodeColorization();
boolean isShowUnprintables();
boolean isShowNonprintables();
void setCodeCharactersCase(CodeCharactersCase codeCharactersCase);
@@ -73,7 +73,7 @@ public interface CodeAreaOptions {
void setRowWrappingMode(RowWrappingMode rowWrappingMode);
void setShowUnprintables(boolean showUnprintables);
void setShowNonprintables(boolean showUnprintables);
void setViewMode(CodeAreaViewMode viewMode);
}
@@ -19,10 +19,13 @@ import org.exbin.bined.CodeCharactersCase;
import org.exbin.bined.CodeType;
import org.exbin.bined.PositionCodeType;
import org.exbin.bined.RowWrappingMode;
import org.exbin.bined.android.CodeAreaAndroidUtils;
import org.exbin.bined.android.basic.CodeArea;
import org.exbin.bined.android.capability.ColorAssessorPainterCapable;
import org.exbin.bined.basic.CodeAreaViewMode;
import org.exbin.bined.editor.android.options.CodeAreaOptions;
import org.exbin.bined.highlight.android.HighlightNonAsciiCodeAreaPainter;
import org.exbin.bined.highlight.android.NonAsciiCodeAreaColorAssessor;
import org.exbin.bined.highlight.android.NonprintablesCodeAreaAssessor;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -39,7 +42,7 @@ import javax.annotation.ParametersAreNonnullByDefault;
public class CodeAreaPreferences implements CodeAreaOptions {
public static final String PREFERENCES_CODE_TYPE = "codeType";
public static final String PREFERENCES_SHOW_UNPRINTABLES = "showNonpritables";
public static final String PREFERENCES_SHOW_NONPRINTABLES = "showNonpritables";
public static final String PREFERENCES_BYTES_PER_LINE = "bytesPerLine";
public static final String PREFERENCES_LINE_NUMBERS_LENGTH_TYPE = "lineNumbersLengthType";
public static final String PREFERENCES_LINE_NUMBERS_LENGTH = "lineNumbersLength";
@@ -77,13 +80,13 @@ public class CodeAreaPreferences implements CodeAreaOptions {
}
@Override
public boolean isShowUnprintables() {
return preferences.getBoolean(PREFERENCES_SHOW_UNPRINTABLES, false);
public boolean isShowNonprintables() {
return preferences.getBoolean(PREFERENCES_SHOW_NONPRINTABLES, true);
}
@Override
public void setShowUnprintables(boolean showUnprintables) {
preferences.putBoolean(PREFERENCES_SHOW_UNPRINTABLES, showUnprintables);
public void setShowNonprintables(boolean showNonprintables) {
preferences.putBoolean(PREFERENCES_SHOW_NONPRINTABLES, showNonprintables);
}
@Nonnull
@@ -208,6 +211,13 @@ public class CodeAreaPreferences implements CodeAreaOptions {
codeArea.setCodeCharactersCase(getCodeCharactersCase());
codeArea.setRowWrapping(getRowWrappingMode());
codeArea.setMaxBytesPerRow(getMaxBytesPerRow());
((HighlightNonAsciiCodeAreaPainter) codeArea.getPainter()).setNonAsciiHighlightingEnabled(isCodeColorization());
NonprintablesCodeAreaAssessor nonprintablesCodeAreaAssessor = CodeAreaAndroidUtils.findColorAssessor((ColorAssessorPainterCapable) codeArea.getPainter(), NonprintablesCodeAreaAssessor.class);
if (nonprintablesCodeAreaAssessor != null) {
nonprintablesCodeAreaAssessor.setShowNonprintables(isShowNonprintables());
}
NonAsciiCodeAreaColorAssessor nonAsciiColorAssessor = CodeAreaAndroidUtils.findColorAssessor((ColorAssessorPainterCapable) codeArea.getPainter(), NonAsciiCodeAreaColorAssessor.class);
if (nonAsciiColorAssessor != null) {
nonAsciiColorAssessor.setNonAsciiHighlightingEnabled(isCodeColorization());
}
}
}
@@ -31,6 +31,11 @@ import java.util.SortedMap;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Application encoding preferences.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class EncodingPreference extends EditTextPreference {
@@ -28,6 +28,11 @@ import org.exbin.bined.editor.android.R;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Application font preferences.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class FontPreference extends EditTextPreference {
@@ -19,9 +19,11 @@ import org.exbin.auxiliary.binary_data.BinaryData;
import org.exbin.auxiliary.binary_data.EditableBinaryData;
import org.exbin.bined.CharsetStreamTranslator;
import org.exbin.bined.CodeAreaUtils;
import org.exbin.bined.android.CodeAreaAndroidUtils;
import org.exbin.bined.android.basic.CodeArea;
import org.exbin.bined.highlight.android.HighlightCodeAreaPainter;
import org.exbin.bined.highlight.android.HighlightNonAsciiCodeAreaPainter;
import org.exbin.bined.android.capability.ColorAssessorPainterCapable;
import org.exbin.bined.highlight.android.SearchCodeAreaColorAssessor;
import org.exbin.bined.highlight.android.SearchMatch;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
@@ -50,11 +52,11 @@ public class BinarySearchServiceImpl implements BinarySearchService {
@Override
public void performFind(SearchParameters searchParameters, SearchStatusListener searchStatusListener) {
HighlightNonAsciiCodeAreaPainter painter = (HighlightNonAsciiCodeAreaPainter) codeArea.getPainter();
SearchCodeAreaColorAssessor searchAssessor = CodeAreaAndroidUtils.findColorAssessor((ColorAssessorPainterCapable) codeArea.getPainter(), SearchCodeAreaColorAssessor.class);
SearchCondition condition = searchParameters.getCondition();
searchStatusListener.clearStatus();
if (condition.isEmpty()) {
painter.clearMatches();
searchAssessor.clearMatches();
codeArea.repaint();
return;
}
@@ -113,7 +115,7 @@ public class BinarySearchServiceImpl implements BinarySearchService {
* Performs search by binary data.
*/
private void searchForBinaryData(SearchParameters searchParameters, SearchStatusListener searchStatusListener) {
HighlightNonAsciiCodeAreaPainter painter = (HighlightNonAsciiCodeAreaPainter) codeArea.getPainter();
SearchCodeAreaColorAssessor searchAssessor = CodeAreaAndroidUtils.findColorAssessor((ColorAssessorPainterCapable) codeArea.getPainter(), SearchCodeAreaColorAssessor.class);
SearchCondition condition = searchParameters.getCondition();
long position = searchParameters.getStartPosition();
@@ -121,7 +123,7 @@ public class BinarySearchServiceImpl implements BinarySearchService {
long searchDataSize = searchData.getDataSize();
BinaryData data = codeArea.getContentData();
List<HighlightNonAsciiCodeAreaPainter.SearchMatch> foundMatches = new ArrayList<>();
List<SearchMatch> foundMatches = new ArrayList<>();
long dataSize = data.getDataSize();
while (position >= 0 && position <= dataSize - searchDataSize) {
@@ -134,7 +136,7 @@ public class BinarySearchServiceImpl implements BinarySearchService {
}
if (matchLength == searchDataSize) {
HighlightNonAsciiCodeAreaPainter.SearchMatch match = new HighlightNonAsciiCodeAreaPainter.SearchMatch();
SearchMatch match = new SearchMatch();
match.setPosition(position);
match.setLength(searchDataSize);
if (searchParameters.getSearchDirection() == SearchParameters.SearchDirection.BACKWARD) {
@@ -151,18 +153,18 @@ public class BinarySearchServiceImpl implements BinarySearchService {
position++;
}
painter.setMatches(foundMatches);
searchAssessor.setMatches(foundMatches);
if (!foundMatches.isEmpty()) {
if (searchParameters.getSearchDirection() == SearchParameters.SearchDirection.BACKWARD) {
painter.setCurrentMatchIndex(foundMatches.size() - 1);
searchAssessor.setCurrentMatchIndex(foundMatches.size() - 1);
} else {
painter.setCurrentMatchIndex(0);
searchAssessor.setCurrentMatchIndex(0);
}
HighlightNonAsciiCodeAreaPainter.SearchMatch firstMatch = Objects.requireNonNull(painter.getCurrentMatch());
SearchMatch firstMatch = Objects.requireNonNull(searchAssessor.getCurrentMatch());
codeArea.revealPosition(firstMatch.getPosition(), 0, codeArea.getActiveSection());
}
lastSearchParameters.setFromParameters(searchParameters);
searchStatusListener.setStatus(new FoundMatches(foundMatches.size(), foundMatches.isEmpty() ? -1 : painter.getCurrentMatchIndex()), searchParameters.getMatchMode());
searchStatusListener.setStatus(new FoundMatches(foundMatches.size(), foundMatches.isEmpty() ? -1 : searchAssessor.getCurrentMatchIndex()), searchParameters.getMatchMode());
codeArea.repaint();
}
@@ -170,7 +172,7 @@ public class BinarySearchServiceImpl implements BinarySearchService {
* Performs search by text/characters.
*/
private void searchForText(SearchParameters searchParameters, SearchStatusListener searchStatusListener) {
HighlightNonAsciiCodeAreaPainter painter = (HighlightNonAsciiCodeAreaPainter) codeArea.getPainter();
SearchCodeAreaColorAssessor searchAssessor = CodeAreaAndroidUtils.findColorAssessor((ColorAssessorPainterCapable) codeArea.getPainter(), SearchCodeAreaColorAssessor.class);
SearchCondition condition = searchParameters.getCondition();
long position = searchParameters.getStartPosition();
@@ -183,7 +185,7 @@ public class BinarySearchServiceImpl implements BinarySearchService {
long searchDataSize = findText.length();
BinaryData data = codeArea.getContentData();
List<HighlightNonAsciiCodeAreaPainter.SearchMatch> foundMatches = new ArrayList<>();
List<SearchMatch> foundMatches = new ArrayList<>();
Charset charset = codeArea.getCharset();
int maxBytesPerChar;
@@ -221,7 +223,7 @@ public class BinarySearchServiceImpl implements BinarySearchService {
}
if (matchCharLength == findText.length()) {
HighlightNonAsciiCodeAreaPainter.SearchMatch match = new HighlightNonAsciiCodeAreaPainter.SearchMatch();
SearchMatch match = new SearchMatch();
match.setPosition(position);
match.setLength(matchLength);
if (searchParameters.getSearchDirection() == SearchParameters.SearchDirection.BACKWARD) {
@@ -249,41 +251,41 @@ public class BinarySearchServiceImpl implements BinarySearchService {
}
}
painter.setMatches(foundMatches);
searchAssessor.setMatches(foundMatches);
if (!foundMatches.isEmpty()) {
if (searchParameters.getSearchDirection() == SearchParameters.SearchDirection.BACKWARD) {
painter.setCurrentMatchIndex(foundMatches.size() - 1);
searchAssessor.setCurrentMatchIndex(foundMatches.size() - 1);
} else {
painter.setCurrentMatchIndex(0);
searchAssessor.setCurrentMatchIndex(0);
}
HighlightNonAsciiCodeAreaPainter.SearchMatch firstMatch = painter.getCurrentMatch();
SearchMatch firstMatch = searchAssessor.getCurrentMatch();
codeArea.revealPosition(firstMatch.getPosition(), 0, codeArea.getActiveSection());
}
lastSearchParameters.setFromParameters(searchParameters);
searchStatusListener.setStatus(new FoundMatches(foundMatches.size(), foundMatches.isEmpty() ? -1 : painter.getCurrentMatchIndex()), searchParameters.getMatchMode());
searchStatusListener.setStatus(new FoundMatches(foundMatches.size(), foundMatches.isEmpty() ? -1 : searchAssessor.getCurrentMatchIndex()), searchParameters.getMatchMode());
codeArea.repaint();
}
public void setMatchPosition(int matchPosition) {
HighlightNonAsciiCodeAreaPainter painter = (HighlightNonAsciiCodeAreaPainter) codeArea.getPainter();
painter.setCurrentMatchIndex(matchPosition);
HighlightNonAsciiCodeAreaPainter.SearchMatch currentMatch = painter.getCurrentMatch();
SearchCodeAreaColorAssessor searchAssessor = CodeAreaAndroidUtils.findColorAssessor((ColorAssessorPainterCapable) codeArea.getPainter(), SearchCodeAreaColorAssessor.class);
searchAssessor.setCurrentMatchIndex(matchPosition);
SearchMatch currentMatch = searchAssessor.getCurrentMatch();
codeArea.revealPosition(currentMatch.getPosition(), 0, codeArea.getActiveSection());
codeArea.repaint();
}
@Override
public void performFindAgain(SearchStatusListener searchStatusListener) {
HighlightNonAsciiCodeAreaPainter painter = (HighlightNonAsciiCodeAreaPainter) codeArea.getPainter();
List<HighlightCodeAreaPainter.SearchMatch> foundMatches = painter.getMatches();
SearchCodeAreaColorAssessor searchAssessor = CodeAreaAndroidUtils.findColorAssessor((ColorAssessorPainterCapable) codeArea.getPainter(), SearchCodeAreaColorAssessor.class);
List<SearchMatch> foundMatches = searchAssessor.getMatches();
int matchesCount = foundMatches.size();
if (matchesCount > 0) {
switch (lastSearchParameters.getMatchMode()) {
case MULTIPLE:
if (matchesCount > 1) {
int currentMatchIndex = painter.getCurrentMatchIndex();
int currentMatchIndex = searchAssessor.getCurrentMatchIndex();
setMatchPosition(currentMatchIndex < matchesCount - 1 ? currentMatchIndex + 1 : 0);
searchStatusListener.setStatus(new FoundMatches(foundMatches.size(), painter.getCurrentMatchIndex()), lastSearchParameters.getMatchMode());
searchStatusListener.setStatus(new FoundMatches(foundMatches.size(), searchAssessor.getCurrentMatchIndex()), lastSearchParameters.getMatchMode());
}
break;
@@ -293,7 +295,7 @@ public class BinarySearchServiceImpl implements BinarySearchService {
lastSearchParameters.setStartPosition(foundMatches.get(0).getPosition() + 1);
break;
case BACKWARD:
HighlightCodeAreaPainter.SearchMatch match = foundMatches.get(0);
SearchMatch match = foundMatches.get(0);
lastSearchParameters.setStartPosition(match.getPosition() - 1);
break;
}
@@ -319,8 +321,8 @@ public class BinarySearchServiceImpl implements BinarySearchService {
@Override
public void performReplace(SearchParameters searchParameters, ReplaceParameters replaceParameters) {
SearchCondition replaceCondition = replaceParameters.getCondition();
HighlightNonAsciiCodeAreaPainter painter = (HighlightNonAsciiCodeAreaPainter) codeArea.getPainter();
HighlightNonAsciiCodeAreaPainter.SearchMatch currentMatch = painter.getCurrentMatch();
SearchCodeAreaColorAssessor searchAssessor = CodeAreaAndroidUtils.findColorAssessor((ColorAssessorPainterCapable) codeArea.getPainter(), SearchCodeAreaColorAssessor.class);
SearchMatch currentMatch = searchAssessor.getCurrentMatch();
if (currentMatch != null) {
EditableBinaryData editableData = ((EditableBinaryData) codeArea.getContentData());
editableData.remove(currentMatch.getPosition(), currentMatch.getLength());
@@ -329,7 +331,7 @@ public class BinarySearchServiceImpl implements BinarySearchService {
} else {
editableData.insert(currentMatch.getPosition(), replaceCondition.getSearchText().getBytes(codeArea.getCharset()));
}
painter.getMatches().remove(currentMatch);
searchAssessor.getMatches().remove(currentMatch);
codeArea.repaint();
}
}
@@ -342,7 +344,7 @@ public class BinarySearchServiceImpl implements BinarySearchService {
@Override
public void clearMatches() {
HighlightNonAsciiCodeAreaPainter painter = (HighlightNonAsciiCodeAreaPainter) codeArea.getPainter();
painter.clearMatches();
SearchCodeAreaColorAssessor searchAssessor = CodeAreaAndroidUtils.findColorAssessor((ColorAssessorPainterCapable) codeArea.getPainter(), SearchCodeAreaColorAssessor.class);
searchAssessor.clearMatches();
}
}
@@ -1,195 +0,0 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.highlight.android;
import android.graphics.Canvas;
import android.graphics.Color;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.basic.BasicCodeAreaSection;
import org.exbin.bined.CodeAreaSection;
import org.exbin.bined.android.CodeAreaCore;
import org.exbin.bined.android.basic.DefaultCodeAreaPainter;
/**
* Hexadecimal component painter supporting search matches highlighting.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class HighlightCodeAreaPainter extends DefaultCodeAreaPainter {
/**
* Matches must be ordered by position.
*/
private final List<SearchMatch> matches = new ArrayList<>();
private int currentMatchIndex = -1;
private int matchIndex = 0;
private long matchPosition = -1;
private Integer foundMatchesColor;
private Integer currentMatchColor;
public HighlightCodeAreaPainter(CodeAreaCore codeArea) {
super(codeArea);
foundMatchesColor = Color.rgb(180, 255, 180);
currentMatchColor = Color.rgb(255, 210, 180);
}
@Override
public void paintMainArea(Canvas g) {
matchIndex = 0;
super.paintMainArea(g);
}
@Nullable
@Override
public Integer getPositionTextColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section) {
return super.getPositionTextColor(rowDataPosition, byteOnRow, charOnRow, section);
}
@Nullable
@Override
public Integer getPositionBackgroundColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section) {
if (!matches.isEmpty() && charOnRow < getCharactersPerRow() - 1) {
long dataPosition = rowDataPosition + byteOnRow;
if (currentMatchIndex >= 0) {
SearchMatch currentMatch = matches.get(currentMatchIndex);
if (dataPosition >= currentMatch.position && dataPosition < currentMatch.position + currentMatch.length
&& (section == BasicCodeAreaSection.TEXT_PREVIEW || charOnRow != ((currentMatch.position + currentMatch.length) - rowDataPosition) * getCharactersPerRow() - 1)) {
return currentMatchColor;
}
}
if (matchPosition < rowDataPosition) {
matchIndex = 0;
}
int lineMatchIndex = matchIndex;
while (lineMatchIndex < matches.size()) {
SearchMatch match = matches.get(lineMatchIndex);
if (dataPosition >= match.position && dataPosition < match.position + match.length
&& (section == BasicCodeAreaSection.TEXT_PREVIEW || charOnRow != ((match.position + match.length) - rowDataPosition) * getCharactersPerRow() - 1)) {
if (byteOnRow == 0) {
matchIndex = lineMatchIndex;
matchPosition = match.position;
}
return foundMatchesColor;
}
if (match.position > dataPosition) {
break;
}
if (byteOnRow == 0) {
matchIndex = lineMatchIndex;
matchPosition = match.position;
}
lineMatchIndex++;
}
}
return super.getPositionBackgroundColor(rowDataPosition, byteOnRow, charOnRow, section);
}
@Nonnull
public List<SearchMatch> getMatches() {
return matches;
}
public void setMatches(List<SearchMatch> matches) {
this.matches.clear();
this.matches.addAll(matches);
currentMatchIndex = -1;
}
public void clearMatches() {
this.matches.clear();
currentMatchIndex = -1;
}
@Nullable
public SearchMatch getCurrentMatch() {
if (currentMatchIndex >= 0) {
return matches.get(currentMatchIndex);
}
return null;
}
public int getCurrentMatchIndex() {
return currentMatchIndex;
}
public void setCurrentMatchIndex(int currentMatchIndex) {
this.currentMatchIndex = currentMatchIndex;
}
@Nonnull
public Integer getFoundMatchesBackgroundColor() {
return foundMatchesColor;
}
public void setFoundMatchesBackgroundColor(Integer foundMatchesBackgroundColor) {
this.foundMatchesColor = foundMatchesBackgroundColor;
}
@Nonnull
public Integer getCurrentMatchBackgroundColor() {
return currentMatchColor;
}
public void setCurrentMatchBackgroundColor(Integer currentMatchBackgroundColor) {
this.currentMatchColor = currentMatchBackgroundColor;
}
/**
* Simple POJO class for search match.
*/
public static class SearchMatch {
private long position;
private long length;
public SearchMatch() {
}
public SearchMatch(long position, long length) {
this.position = position;
this.length = length;
}
public long getPosition() {
return position;
}
public void setPosition(long position) {
this.position = position;
}
public long getLength() {
return length;
}
public void setLength(long length) {
this.length = length;
}
}
}
@@ -1,158 +0,0 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.highlight.android;
import android.graphics.Color;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.basic.BasicCodeAreaSection;
import org.exbin.bined.CodeAreaSection;
import org.exbin.bined.android.CodeAreaCore;
/**
* Experimental support for highlighting of non-ascii characters.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class HighlightNonAsciiCodeAreaPainter extends HighlightCodeAreaPainter {
private Integer controlCodes;
private Integer upperCodes;
private Integer textColor;
private boolean nonAsciiHighlightingEnabled = true;
public HighlightNonAsciiCodeAreaPainter(CodeAreaCore codeArea) {
super(codeArea);
textColor = Color.BLACK; // TODO codeArea.getForeground(); //MainColors().getTextColor();
if (textColor == null) {
textColor = Color.BLACK;
}
int controlCodesRed = Color.red(textColor);
int controlCodesRedDiff = 0;
if (controlCodesRed > 32) {
if (controlCodesRed > 192) {
controlCodesRedDiff = controlCodesRed - 192;
}
controlCodesRed = 255;
} else {
controlCodesRed += 224;
}
int controlCodesBlue = Color.blue(textColor);
int controlCodesBlueDiff = 0;
if (controlCodesBlue > 32) {
if (controlCodesBlue > 192) {
controlCodesBlueDiff = controlCodesBlue - 192;
}
controlCodesBlue = 255;
} else {
controlCodesBlue += 224;
}
controlCodes = Color.rgb(
controlCodesRed,
downShift(Color.green(textColor), controlCodesBlueDiff + controlCodesRedDiff),
controlCodesBlue);
int upperCodesGreen = Color.green(textColor);
int upperCodesGreenDiff = 0;
if (upperCodesGreen > 64) {
if (upperCodesGreen > 192) {
upperCodesGreenDiff = upperCodesGreen - 192;
}
upperCodesGreen = 255;
} else {
upperCodesGreen += 192;
}
int upperCodesBlue = Color.blue(textColor);
int upperCodesBlueDiff = 0;
if (upperCodesBlue > 64) {
if (upperCodesBlue > 192) {
upperCodesBlueDiff = upperCodesBlue - 192;
}
upperCodesBlue = 255;
} else {
upperCodesBlue += 192;
}
upperCodes = Color.rgb(
downShift(Color.red(textColor), upperCodesGreenDiff + upperCodesBlueDiff),
upperCodesGreen, upperCodesBlue);
}
private int downShift(int color, int diff) {
if (color < diff) {
return 0;
}
return color - diff;
}
@Nullable
@Override
public Integer getPositionTextColor(long rowDataPosition, int byteOnRow, int charOnRow, @Nonnull CodeAreaSection section) {
Integer color = super.getPositionTextColor(rowDataPosition, byteOnRow, charOnRow, section);
if (nonAsciiHighlightingEnabled && section == BasicCodeAreaSection.CODE_MATRIX) {
if (color == null || textColor.equals(color)) {
long dataPosition = rowDataPosition + byteOnRow;
if (dataPosition < codeArea.getDataSize()) {
byte value = codeArea.getContentData().getByte(dataPosition);
if (value < 0) {
color = upperCodes;
} else if (value < 0x20) {
color = controlCodes;
}
}
}
}
return color;
}
@Nonnull
public Integer getControlCodes() {
return controlCodes;
}
public void setControlCodes(Integer controlCodes) {
this.controlCodes = controlCodes;
}
@Nonnull
public Integer getUpperCodes() {
return upperCodes;
}
public void setUpperCodes(Integer upperCodes) {
this.upperCodes = upperCodes;
}
public boolean isNonAsciiHighlightingEnabled() {
return nonAsciiHighlightingEnabled;
}
public void setNonAsciiHighlightingEnabled(boolean nonAsciiHighlightingEnabled) {
this.nonAsciiHighlightingEnabled = nonAsciiHighlightingEnabled;
}
}
@@ -0,0 +1,205 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.highlight.android;
import android.graphics.Color;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.BinaryData;
import org.exbin.bined.basic.BasicCodeAreaSection;
import org.exbin.bined.CodeAreaSection;
import org.exbin.bined.color.CodeAreaBasicColors;
import org.exbin.bined.highlight.android.color.CodeAreaColorizationColorType;
import org.exbin.bined.android.CodeAreaPaintState;
import org.exbin.bined.android.basic.color.CodeAreaColorsProfile;
import org.exbin.bined.android.CodeAreaColorAssessor;
/**
* Support for highlighting of non-ascii characters.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class NonAsciiCodeAreaColorAssessor implements CodeAreaColorAssessor {
protected final CodeAreaColorAssessor parentAssessor;
@Nullable
protected Integer controlCodesColor;
@Nullable
protected Integer controlCodesBackground;
@Nullable
protected Integer upperCodesColor;
@Nullable
protected Integer upperCodesBackground;
@Nullable
protected Integer textColor;
protected boolean nonAsciiHighlightingEnabled = true;
protected long dataSize;
protected BinaryData contentData;
public NonAsciiCodeAreaColorAssessor(@Nullable CodeAreaColorAssessor parentAssessor) {
this.parentAssessor = parentAssessor;
}
@Override
public void startPaint(CodeAreaPaintState codeAreaPaintState) {
CodeAreaColorsProfile colorsProfile = codeAreaPaintState.getColorsProfile();
dataSize = codeAreaPaintState.getDataSize();
contentData = codeAreaPaintState.getContentData();
textColor = colorsProfile.getColor(CodeAreaBasicColors.TEXT_COLOR);
if (textColor == null) {
textColor = Color.BLACK;
}
controlCodesColor = colorsProfile.getColor(CodeAreaColorizationColorType.CONTROL_CODES_COLOR);
if (controlCodesColor == null) {
int controlCodesRed = Color.red(textColor);
int controlCodesRedDiff = 0;
if (controlCodesRed > 32) {
if (controlCodesRed > 192) {
controlCodesRedDiff = controlCodesRed - 192;
}
controlCodesRed = 255;
} else {
controlCodesRed += 224;
}
int controlCodesBlue = Color.blue(textColor);
int controlCodesBlueDiff = 0;
if (controlCodesBlue > 32) {
if (controlCodesBlue > 192) {
controlCodesBlueDiff = controlCodesBlue - 192;
}
controlCodesBlue = 255;
} else {
controlCodesBlue += 224;
}
controlCodesColor = Color.rgb(
controlCodesRed,
downShift(Color.green(textColor), controlCodesBlueDiff + controlCodesRedDiff),
controlCodesBlue);
}
controlCodesBackground = colorsProfile.getColor(CodeAreaColorizationColorType.CONTROL_CODES_BACKGROUND);
upperCodesColor = colorsProfile.getColor(CodeAreaColorizationColorType.UPPER_CODES_COLOR);
if (upperCodesColor == null) {
int upperCodesGreen = Color.green(textColor);
int upperCodesGreenDiff = 0;
if (upperCodesGreen > 64) {
if (upperCodesGreen > 192) {
upperCodesGreenDiff = upperCodesGreen - 192;
}
upperCodesGreen = 255;
} else {
upperCodesGreen += 192;
}
int upperCodesBlue = Color.blue(textColor);
int upperCodesBlueDiff = 0;
if (upperCodesBlue > 64) {
if (upperCodesBlue > 192) {
upperCodesBlueDiff = upperCodesBlue - 192;
}
upperCodesBlue = 255;
} else {
upperCodesBlue += 192;
}
upperCodesColor = Color.rgb(
downShift(Color.red(textColor), upperCodesGreenDiff + upperCodesBlueDiff),
upperCodesGreen, upperCodesBlue);
}
upperCodesBackground = colorsProfile.getColor(CodeAreaColorizationColorType.UPPER_CODES_BACKGROUND);
if (parentAssessor != null) {
parentAssessor.startPaint(codeAreaPaintState);
}
}
@Nullable
@Override
public Integer getPositionTextColor(long rowDataPosition, int byteOnRow, int charOnRow, @Nonnull CodeAreaSection section, boolean inSelection) {
Integer color = parentAssessor != null ? parentAssessor.getPositionTextColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection) : null;
if (nonAsciiHighlightingEnabled && section == BasicCodeAreaSection.CODE_MATRIX) {
if (color == null || textColor.equals(color)) {
long dataPosition = rowDataPosition + byteOnRow;
if (dataPosition < dataSize) {
byte value = contentData.getByte(dataPosition);
if (value < 0) {
color = upperCodesColor;
} else if (value < 0x20) {
color = controlCodesColor;
}
}
}
}
return color;
}
@Nullable
@Override
public Integer getPositionBackgroundColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section, boolean inSelection) {
Integer color = parentAssessor != null ? parentAssessor.getPositionBackgroundColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection) : null;
if ((upperCodesBackground != null || controlCodesBackground != null) && nonAsciiHighlightingEnabled && section == BasicCodeAreaSection.CODE_MATRIX) {
if (color == null || textColor.equals(color)) {
long dataPosition = rowDataPosition + byteOnRow;
if (dataPosition < dataSize) {
byte value = contentData.getByte(dataPosition);
if (upperCodesBackground != null && value < 0) {
color = upperCodesBackground;
} else if (controlCodesBackground != null && value < 0x20) {
color = controlCodesBackground;
}
}
}
}
return color;
}
@Nonnull
@Override
public Optional<CodeAreaColorAssessor> getParentColorAssessor() {
return Optional.ofNullable(parentAssessor);
}
public boolean isNonAsciiHighlightingEnabled() {
return nonAsciiHighlightingEnabled;
}
public void setNonAsciiHighlightingEnabled(boolean nonAsciiHighlightingEnabled) {
this.nonAsciiHighlightingEnabled = nonAsciiHighlightingEnabled;
}
private static int downShift(int color, int diff) {
if (color < diff) {
return 0;
}
return color - diff;
}
}
@@ -0,0 +1,180 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.highlight.android;
import android.graphics.Color;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.CodeAreaSection;
import org.exbin.bined.basic.BasicCodeAreaSection;
import org.exbin.bined.color.CodeAreaBasicColors;
import org.exbin.bined.highlight.android.color.CodeAreaNonprintablesColorType;
import org.exbin.bined.android.CodeAreaCharAssessor;
import org.exbin.bined.android.CodeAreaPaintState;
import org.exbin.bined.android.CodeAreaColorAssessor;
import org.exbin.bined.android.basic.color.CodeAreaColorsProfile;
/**
* Code area non-printable characters highlighting.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class NonprintablesCodeAreaAssessor implements CodeAreaColorAssessor, CodeAreaCharAssessor {
protected final CodeAreaColorAssessor parentColorAssessor;
protected final CodeAreaCharAssessor parentCharAssessor;
@Nullable
protected Map<Character, Character> nonprintableCharactersMapping = null;
protected boolean showNonprintables = true;
@Nullable
protected Integer nonprintablesColor;
@Nullable
protected Integer nonprintablesBackground;
public NonprintablesCodeAreaAssessor(@Nullable CodeAreaColorAssessor parentColorAssessor, @Nullable CodeAreaCharAssessor parentCharAssessor) {
this.parentColorAssessor = parentColorAssessor;
this.parentCharAssessor = parentCharAssessor;
}
public boolean isShowNonprintables() {
return showNonprintables;
}
public void setShowNonprintables(boolean showNonprintables) {
this.showNonprintables = showNonprintables;
}
@Override
public void startPaint(CodeAreaPaintState codeAreaPaintState) {
if (nonprintableCharactersMapping == null) {
buildNonprintableCharactersMapping();
}
CodeAreaColorsProfile colorsProfile = codeAreaPaintState.getColorsProfile();
nonprintablesColor = colorsProfile.getColor(CodeAreaNonprintablesColorType.NONPRINTABLES_COLOR);
if (nonprintablesColor == null) {
Integer textColor = colorsProfile.getColor(CodeAreaBasicColors.TEXT_COLOR);
nonprintablesColor = Color.rgb(Color.red(textColor), (Color.green(textColor) + 128) % 256, (Color.blue(textColor) + 96) % 256);
}
nonprintablesBackground = colorsProfile.getColor(CodeAreaNonprintablesColorType.NONPRINTABLES_BACKGROUND);
if (parentColorAssessor != null) {
parentColorAssessor.startPaint(codeAreaPaintState);
}
if (parentCharAssessor != null) {
parentCharAssessor.startPaint(codeAreaPaintState);
}
}
@Nullable
@Override
public Integer getPositionTextColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section, boolean inSelection) {
if (showNonprintables && section == BasicCodeAreaSection.TEXT_PREVIEW) {
// Cache results to speed up?
Character character = parentCharAssessor != null ? parentCharAssessor.getPreviewCharacter(rowDataPosition, byteOnRow, charOnRow, section) : null;
if (character != null && nonprintableCharactersMapping.containsKey(character)) {
return nonprintablesColor;
}
}
if (parentColorAssessor != null) {
return parentColorAssessor.getPositionTextColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);
}
return null;
}
@Nullable
@Override
public Integer getPositionBackgroundColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section, boolean inSelection) {
if (nonprintablesBackground != null && showNonprintables && section == BasicCodeAreaSection.TEXT_PREVIEW) {
// Cache results to speed up?
Character character = parentCharAssessor != null ? parentCharAssessor.getPreviewCharacter(rowDataPosition, byteOnRow, charOnRow, section) : null;
if (character != null && nonprintableCharactersMapping.containsKey(character)) {
return nonprintablesBackground;
}
}
if (parentColorAssessor != null) {
return parentColorAssessor.getPositionBackgroundColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);
}
return null;
}
@Nonnull
@Override
public char getPreviewCharacter(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section) {
Character character = parentCharAssessor != null ? parentCharAssessor.getPreviewCharacter(rowDataPosition, byteOnRow, charOnRow, section) : null;
if (showNonprintables && section == BasicCodeAreaSection.TEXT_PREVIEW && character != null) {
Character altChar = nonprintableCharactersMapping.get(character);
return altChar == null ? character : altChar;
}
return character == null ? ' ' : character;
}
@Nonnull
@Override
public char getPreviewCursorCharacter(long rowDataPosition, int byteOnRow, int charOnRow, byte[] cursorData, int cursorDataLength, CodeAreaSection section) {
Character character = parentCharAssessor != null ? parentCharAssessor.getPreviewCursorCharacter(rowDataPosition, byteOnRow, charOnRow, cursorData, cursorDataLength, section) : null;
if (showNonprintables && section == BasicCodeAreaSection.TEXT_PREVIEW && character != null) {
Character altChar = nonprintableCharactersMapping.get(character);
return altChar == null ? character : altChar;
}
return character == null ? ' ' : character;
}
@Nonnull
@Override
public Optional<CodeAreaCharAssessor> getParentCharAssessor() {
return Optional.ofNullable(parentCharAssessor);
}
@Nonnull
@Override
public Optional<CodeAreaColorAssessor> getParentColorAssessor() {
return Optional.ofNullable(parentColorAssessor);
}
private void buildNonprintableCharactersMapping() {
nonprintableCharactersMapping = new HashMap<>();
// Unicode control characters, might not be supported by font
for (int i = 0; i < 32; i++) {
nonprintableCharactersMapping.put((char) i, Character.toChars(9216 + i)[0]);
}
// Space -> Middle Dot
nonprintableCharactersMapping.put(' ', Character.toChars(183)[0]);
// Tab -> Right-Pointing Double Angle Quotation Mark
nonprintableCharactersMapping.put('\t', Character.toChars(187)[0]);
// Line Feed -> Currency Sign
nonprintableCharactersMapping.put('\r', Character.toChars(164)[0]);
// Carriage Return -> Pilcrow Sign
nonprintableCharactersMapping.put('\n', Character.toChars(182)[0]);
// Ideographic Space -> Degree Sign
nonprintableCharactersMapping.put(Character.toChars(127)[0], Character.toChars(176)[0]);
}
}
@@ -0,0 +1,222 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.highlight.android;
import android.graphics.Color;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.basic.BasicCodeAreaSection;
import org.exbin.bined.CodeAreaSection;
import org.exbin.bined.highlight.android.color.CodeAreaMatchColorType;
import org.exbin.bined.android.CodeAreaPaintState;
import org.exbin.bined.android.CodeAreaColorAssessor;
import org.exbin.bined.android.basic.color.CodeAreaColorsProfile;
/**
* Code area search matches highlighting.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class SearchCodeAreaColorAssessor implements CodeAreaColorAssessor {
private final CodeAreaColorAssessor parentAssessor;
/**
* Matches must be ordered by position.
*/
private final List<SearchMatch> matches = new ArrayList<>();
private int currentMatchIndex = -1;
private int matchIndex = 0;
private long matchPosition = -1;
@Nullable
private Integer foundMatchesColor;
@Nullable
private Integer foundMatchesBackground;
@Nullable
private Integer currentMatchColor;
@Nullable
private Integer currentMatchBackground;
private int charactersPerRow = 1;
public SearchCodeAreaColorAssessor(@Nullable CodeAreaColorAssessor parentAssessor) {
this.parentAssessor = parentAssessor;
}
@Override
public void startPaint(CodeAreaPaintState codeAreaPaintState) {
matchIndex = 0;
charactersPerRow = codeAreaPaintState.getCharactersPerRow();
CodeAreaColorsProfile colorsProfile = codeAreaPaintState.getColorsProfile();
foundMatchesColor = colorsProfile.getColor(CodeAreaMatchColorType.MATCH_COLOR);
foundMatchesBackground = colorsProfile.getColor(CodeAreaMatchColorType.MATCH_BACKGROUND);
if (foundMatchesBackground == null) {
foundMatchesBackground = Color.rgb(180, 255, 180);
}
currentMatchColor = colorsProfile.getColor(CodeAreaMatchColorType.CURRENT_MATCH_COLOR);
currentMatchBackground = colorsProfile.getColor(CodeAreaMatchColorType.CURRENT_MATCH_BACKGROUND);
if (currentMatchBackground == null) {
currentMatchBackground = Color.rgb(255, 210, 180);
}
if (parentAssessor != null) {
parentAssessor.startPaint(codeAreaPaintState);
}
}
@Nullable
@Override
public Integer getPositionTextColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section, boolean inSelection) {
if ((currentMatchColor != null || foundMatchesColor != null) && !matches.isEmpty() && charOnRow < charactersPerRow - 1) {
long dataPosition = rowDataPosition + byteOnRow;
if (currentMatchIndex >= 0) {
SearchMatch currentMatch = matches.get(currentMatchIndex);
if (currentMatchColor != null && dataPosition >= currentMatch.position && dataPosition < currentMatch.position + currentMatch.length
&& (section == BasicCodeAreaSection.TEXT_PREVIEW || charOnRow != ((currentMatch.position + currentMatch.length) - rowDataPosition) * charactersPerRow - 1)) {
return currentMatchColor;
}
}
if (matchPosition < rowDataPosition) {
matchIndex = 0;
}
int lineMatchIndex = matchIndex;
while (lineMatchIndex < matches.size()) {
SearchMatch match = matches.get(lineMatchIndex);
if (dataPosition >= match.position && dataPosition < match.position + match.length
&& (section == BasicCodeAreaSection.TEXT_PREVIEW || charOnRow != ((match.position + match.length) - rowDataPosition) * charactersPerRow - 1)) {
if (byteOnRow == 0) {
matchIndex = lineMatchIndex;
matchPosition = match.position;
}
if (foundMatchesColor != null) {
return foundMatchesColor;
}
break;
}
if (match.position > dataPosition) {
break;
}
if (byteOnRow == 0) {
matchIndex = lineMatchIndex;
matchPosition = match.position;
}
lineMatchIndex++;
}
}
if (parentAssessor != null) {
return parentAssessor.getPositionTextColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);
}
return null;
}
@Nullable
@Override
public Integer getPositionBackgroundColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section, boolean inSelection) {
if (!matches.isEmpty() && charOnRow < charactersPerRow - 1) {
long dataPosition = rowDataPosition + byteOnRow;
if (currentMatchIndex >= 0) {
SearchMatch currentMatch = matches.get(currentMatchIndex);
if (dataPosition >= currentMatch.position && dataPosition < currentMatch.position + currentMatch.length
&& (section == BasicCodeAreaSection.TEXT_PREVIEW || charOnRow != ((currentMatch.position + currentMatch.length) - rowDataPosition) * charactersPerRow - 1)) {
return currentMatchBackground;
}
}
if (matchPosition < rowDataPosition) {
matchIndex = 0;
}
int lineMatchIndex = matchIndex;
while (lineMatchIndex < matches.size()) {
SearchMatch match = matches.get(lineMatchIndex);
if (dataPosition >= match.position && dataPosition < match.position + match.length
&& (section == BasicCodeAreaSection.TEXT_PREVIEW || charOnRow != ((match.position + match.length) - rowDataPosition) * charactersPerRow - 1)) {
if (byteOnRow == 0) {
matchIndex = lineMatchIndex;
matchPosition = match.position;
}
return foundMatchesBackground;
}
if (match.position > dataPosition) {
break;
}
if (byteOnRow == 0) {
matchIndex = lineMatchIndex;
matchPosition = match.position;
}
lineMatchIndex++;
}
}
if (parentAssessor != null) {
return parentAssessor.getPositionBackgroundColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);
}
return null;
}
@Override
public Optional<CodeAreaColorAssessor> getParentColorAssessor() {
return Optional.ofNullable(parentAssessor);
}
@Nonnull
public List<SearchMatch> getMatches() {
return matches;
}
public void setMatches(List<SearchMatch> matches) {
this.matches.clear();
this.matches.addAll(matches);
currentMatchIndex = -1;
}
public void clearMatches() {
this.matches.clear();
currentMatchIndex = -1;
}
@Nullable
public SearchMatch getCurrentMatch() {
if (currentMatchIndex >= 0) {
return matches.get(currentMatchIndex);
}
return null;
}
public int getCurrentMatchIndex() {
return currentMatchIndex;
}
public void setCurrentMatchIndex(int currentMatchIndex) {
this.currentMatchIndex = currentMatchIndex;
}
}
@@ -0,0 +1,51 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.highlight.android;
/**
* Simple POJO class for search match.
*
* @author ExBin Project (https://exbin.org)
*/
public class SearchMatch {
long position;
long length;
public SearchMatch() {
}
public SearchMatch(long position, long length) {
this.position = position;
this.length = length;
}
public long getPosition() {
return position;
}
public void setPosition(long position) {
this.position = position;
}
public long getLength() {
return length;
}
public void setLength(long length) {
this.length = length;
}
}
@@ -19,7 +19,6 @@ import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.color.CodeAreaBasicColors;
import org.exbin.bined.color.CodeAreaColorGroup;
import org.exbin.bined.color.CodeAreaColorType;
@@ -31,10 +30,10 @@ import org.exbin.bined.color.CodeAreaColorType;
@ParametersAreNonnullByDefault
public enum CodeAreaColorizationColorType implements CodeAreaColorType {
CONTROL_CODES_COLOR(CodeAreaBasicColors.TEXT_COLOR.getId(), ColorizationCodeAreaColorsGroup.COLORIZATION),
CONTROL_CODES_BACKGROUND(CodeAreaBasicColors.TEXT_BACKGROUND.getId(), ColorizationCodeAreaColorsGroup.COLORIZATION),
UPPER_CODES_COLOR(CodeAreaBasicColors.TEXT_COLOR.getId(), ColorizationCodeAreaColorsGroup.COLORIZATION),
UPPER_CODES_BACKGROUND(CodeAreaBasicColors.TEXT_BACKGROUND.getId(), ColorizationCodeAreaColorsGroup.COLORIZATION);
CONTROL_CODES_COLOR("controlCodesColor", ColorizationCodeAreaColorsGroup.COLORIZATION),
CONTROL_CODES_BACKGROUND("controlCodesBackground", ColorizationCodeAreaColorsGroup.COLORIZATION),
UPPER_CODES_COLOR("upperCodesColor", ColorizationCodeAreaColorsGroup.COLORIZATION),
UPPER_CODES_BACKGROUND("upperCodesBackground", ColorizationCodeAreaColorsGroup.COLORIZATION);
@Nonnull
private final String typeId;
@@ -19,7 +19,6 @@ import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.color.CodeAreaBasicColors;
import org.exbin.bined.color.CodeAreaColorGroup;
import org.exbin.bined.color.CodeAreaColorType;
@@ -31,10 +30,10 @@ import org.exbin.bined.color.CodeAreaColorType;
@ParametersAreNonnullByDefault
public enum CodeAreaMatchColorType implements CodeAreaColorType {
MATCH_COLOR(CodeAreaBasicColors.TEXT_COLOR.getId(), MatchCodeAreaColorsGroup.MATCHING),
MATCH_BACKGROUND(CodeAreaBasicColors.TEXT_BACKGROUND.getId(), MatchCodeAreaColorsGroup.MATCHING),
ACTIVE_MATCH_COLOR(CodeAreaBasicColors.TEXT_COLOR.getId(), MatchCodeAreaColorsGroup.MATCHING),
ACTIVE_MATCH_BACKGROUND(CodeAreaBasicColors.TEXT_BACKGROUND.getId(), MatchCodeAreaColorsGroup.MATCHING);
MATCH_COLOR("matchColor", MatchCodeAreaColorsGroup.MATCHING),
MATCH_BACKGROUND("matchBackground", MatchCodeAreaColorsGroup.MATCHING),
CURRENT_MATCH_COLOR("currentMatchColor", MatchCodeAreaColorsGroup.MATCHING),
CURRENT_MATCH_BACKGROUND("currentMatchBackground", MatchCodeAreaColorsGroup.MATCHING);
@Nonnull
private final String typeId;
@@ -0,0 +1,58 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.highlight.android.color;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.color.CodeAreaBasicColors;
import org.exbin.bined.color.CodeAreaColorGroup;
import org.exbin.bined.color.CodeAreaColorType;
/**
* Enumeration of non-printable color types.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public enum CodeAreaNonprintablesColorType implements CodeAreaColorType {
NONPRINTABLES_COLOR(CodeAreaBasicColors.TEXT_COLOR.getId(), NonprintableCodeAreaColorsGroup.NONPRINTABLES),
NONPRINTABLES_BACKGROUND(CodeAreaBasicColors.TEXT_BACKGROUND.getId(), NonprintableCodeAreaColorsGroup.NONPRINTABLES);
@Nonnull
private final String typeId;
@Nullable
private final CodeAreaColorGroup group;
private CodeAreaNonprintablesColorType(String typeId, @Nullable CodeAreaColorGroup group) {
this.typeId = typeId;
this.group = group;
}
@Nonnull
@Override
public String getId() {
return typeId;
}
@Nonnull
@Override
public Optional<CodeAreaColorGroup> getGroup() {
return Optional.ofNullable(group);
}
}
@@ -0,0 +1,44 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.highlight.android.color;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.color.CodeAreaColorGroup;
/**
* Enumeration of color groups for nonprintable colors.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public enum NonprintableCodeAreaColorsGroup implements CodeAreaColorGroup {
NONPRINTABLES("nonprintables");
@Nonnull
private final String groupId;
NonprintableCodeAreaColorsGroup(String groupId) {
this.groupId = groupId;
}
@Nonnull
@Override
public String getId() {
return groupId;
}
}
@@ -35,7 +35,7 @@ import org.exbin.bined.android.CodeAreaCore;
@ParametersAreNonnullByDefault
public class CodeAreaCompoundOperation extends CodeAreaOperation implements BinaryDataCompoundOperation {
private final List<BinaryDataOperation> operations = new ArrayList<>();
protected final List<BinaryDataOperation> operations = new ArrayList<>();
public CodeAreaCompoundOperation(CodeAreaCore codeArea) {
super(codeArea);
@@ -92,5 +92,4 @@ public class CodeAreaCompoundOperation extends CodeAreaOperation implements Bina
public boolean isEmpty() {
return operations.isEmpty();
}
}
@@ -33,7 +33,6 @@ public abstract class CodeAreaOperation implements BinaryDataUndoableOperation {
@Nonnull
protected final CodeAreaCore codeArea;
@Nonnull
protected final DefaultCodeAreaCaretPosition backPosition = new DefaultCodeAreaCaretPosition();
public CodeAreaOperation(CodeAreaCore codeArea) {
@@ -47,30 +46,11 @@ public abstract class CodeAreaOperation implements BinaryDataUndoableOperation {
}
}
/**
* Returns type of the operation.
*
* @return operation type
*/
@Nonnull
public abstract CodeAreaOperationType getType();
@Nonnull
public CodeAreaCore getCodeArea() {
return codeArea;
}
/**
* Returns caption as text.
*
* @return text caption
*/
@Nonnull
@Override
public String getName() {
return getType().getName();
}
@Nonnull
public CodeAreaCaretPosition getBackPosition() {
return backPosition;
@@ -55,14 +55,13 @@ import org.exbin.bined.capability.ScrollingCapable;
import org.exbin.bined.capability.SelectionCapable;
import org.exbin.bined.capability.ViewModeCapable;
import org.exbin.bined.operation.android.command.CodeAreaCommand;
import org.exbin.bined.operation.android.command.CodeAreaCommandType;
import org.exbin.bined.operation.android.command.CodeAreaCompoundCommand;
import org.exbin.bined.operation.android.command.DeleteSelectionCommand;
import org.exbin.bined.operation.android.command.EditCharDataCommand;
import org.exbin.bined.operation.android.command.EditCodeDataCommand;
import org.exbin.bined.operation.android.command.EditDataCommand;
import org.exbin.bined.operation.android.command.InsertDataCommand;
import org.exbin.bined.operation.android.command.ModifyDataCommand;
import org.exbin.bined.operation.android.command.RemoveDataCommand;
import org.exbin.bined.operation.undo.BinaryDataAppendableUndoRedo;
import org.exbin.bined.operation.undo.BinaryDataUndoRedo;
@@ -92,15 +91,15 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
@Nonnull
protected final CodeAreaCore codeArea;
@Nonnull
private EnterKeyHandlingMode enterKeyHandlingMode = EnterKeyHandlingMode.PLATFORM_SPECIFIC;
protected EnterKeyHandlingMode enterKeyHandlingMode = EnterKeyHandlingMode.PLATFORM_SPECIFIC;
@Nonnull
private TabKeyHandlingMode tabKeyHandlingMode = TabKeyHandlingMode.PLATFORM_SPECIFIC;
private final boolean codeTypeSupported;
private final boolean viewModeSupported;
protected TabKeyHandlingMode tabKeyHandlingMode = TabKeyHandlingMode.PLATFORM_SPECIFIC;
protected final boolean codeTypeSupported;
protected final boolean viewModeSupported;
private Context context;
protected Context context;
protected ClipboardManager clipboard;
private boolean canPaste = false;
protected boolean canPaste = false;
private ClipDescription binedDataFlavor;
private ClipDescription binaryDataFlavor;
private ClipData currentClipboardData = null;
@@ -161,7 +160,7 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
}
@Override
public void undoSequenceBreak() {
public void sequenceBreak() {
editCommand = null;
}
@@ -174,28 +173,28 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
switch (keyEvent.getKeyCode()) {
case KeyEvent.KEYCODE_DPAD_LEFT: {
move(isSelectingMode(keyEvent), MovementDirection.LEFT);
undoSequenceBreak();
sequenceBreak();
revealCursor();
// keyEvent.consume();
break;
}
case KeyEvent.KEYCODE_DPAD_RIGHT: {
move(isSelectingMode(keyEvent), MovementDirection.RIGHT);
undoSequenceBreak();
sequenceBreak();
revealCursor();
// keyEvent.consume();
break;
}
case KeyEvent.KEYCODE_DPAD_UP: {
move(isSelectingMode(keyEvent), MovementDirection.UP);
undoSequenceBreak();
sequenceBreak();
revealCursor();
// keyEvent.consume();
break;
}
case KeyEvent.KEYCODE_DPAD_DOWN: {
move(isSelectingMode(keyEvent), MovementDirection.DOWN);
undoSequenceBreak();
sequenceBreak();
revealCursor();
// keyEvent.consume();
break;
@@ -206,7 +205,7 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
} else {
move(isSelectingMode(keyEvent), MovementDirection.ROW_START);
}
undoSequenceBreak();
sequenceBreak();
revealCursor();
// keyEvent.consume();
break;
@@ -217,7 +216,7 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
} else {
move(isSelectingMode(keyEvent), MovementDirection.ROW_END);
}
undoSequenceBreak();
sequenceBreak();
revealCursor();
// keyEvent.consume();
break;
@@ -225,7 +224,7 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
case KeyEvent.KEYCODE_PAGE_UP: {
scroll(ScrollingDirection.PAGE_UP);
move(isSelectingMode(keyEvent), MovementDirection.PAGE_UP);
undoSequenceBreak();
sequenceBreak();
revealCursor();
// keyEvent.consume();
break;
@@ -233,7 +232,7 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
case KeyEvent.KEYCODE_PAGE_DOWN: {
scroll(ScrollingDirection.PAGE_DOWN);
move(isSelectingMode(keyEvent), MovementDirection.PAGE_DOWN);
undoSequenceBreak();
sequenceBreak();
// keyEvent.consume();
break;
}
@@ -323,11 +322,14 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
EditMode editMode = ((EditModeCapable) codeArea).getEditMode();
EditOperation editOperation = ((EditModeCapable) codeArea).getActiveOperation();
DeleteSelectionCommand deleteSelectionCommand = null;
long dataPosition = ((CaretCapable) codeArea).getDataPosition();
int codeOffset = ((CaretCapable) codeArea).getCodeOffset();
if (codeArea.hasSelection()) {
long selectionStart = ((SelectionCapable) codeArea).getSelection().getFirst();
dataPosition = ((SelectionCapable) codeArea).getSelection().getFirst();
codeOffset = 0;
deleteSelectionCommand = new DeleteSelectionCommand(codeArea);
((CaretCapable) codeArea).setActiveCaretPosition(selectionStart);
undoSequenceBreak();
((CaretCapable) codeArea).setActiveCaretPosition(dataPosition);
sequenceBreak();
}
int value;
@@ -348,15 +350,11 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
if (deleteSelectionCommand != null) {
CodeAreaCompoundCommand compoundCommand = new CodeAreaCompoundCommand(codeArea);
compoundCommand.addCommand(deleteSelectionCommand);
long dataPosition = ((CaretCapable) codeArea).getDataPosition();
int codeOffset = ((CaretCapable) codeArea).getCodeOffset();
editCommand = new EditCodeDataCommand(codeArea, EditCodeDataCommand.EditCommandType.OVERWRITE, dataPosition, codeOffset, (byte) value);
editCommand = new EditCodeDataCommand(codeArea, EditDataCommand.EditOperationType.OVERWRITE, dataPosition, codeOffset, (byte) value);
compoundCommand.addCommand(editCommand);
undoRedo.execute(compoundCommand);
} else {
long dataPosition = ((CaretCapable) codeArea).getDataPosition();
int codeOffset = ((CaretCapable) codeArea).getCodeOffset();
EditCodeDataCommand command = new EditCodeDataCommand(codeArea, EditCodeDataCommand.EditCommandType.OVERWRITE, dataPosition, codeOffset, (byte) value);
EditCodeDataCommand command = new EditCodeDataCommand(codeArea, EditDataCommand.EditOperationType.OVERWRITE, dataPosition, codeOffset, (byte) value);
if (editCommand != null && isAppendAllowed() && undoRedo instanceof BinaryDataAppendableUndoRedo) {
if (!((BinaryDataAppendableUndoRedo) undoRedo).appendExecute(command)) {
editCommand = command;
@@ -370,15 +368,11 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
if (deleteSelectionCommand != null) {
CodeAreaCompoundCommand compoundCommand = new CodeAreaCompoundCommand(codeArea);
compoundCommand.addCommand(deleteSelectionCommand);
long dataPosition = ((CaretCapable) codeArea).getDataPosition();
int codeOffset = ((CaretCapable) codeArea).getCodeOffset();
editCommand = new EditCodeDataCommand(codeArea, EditCharDataCommand.EditCommandType.INSERT, dataPosition, codeOffset, (byte) value);
editCommand = new EditCodeDataCommand(codeArea, EditDataCommand.EditOperationType.INSERT, dataPosition, codeOffset, (byte) value);
compoundCommand.addCommand(editCommand);
undoRedo.execute(compoundCommand);
} else {
long dataPosition = ((CaretCapable) codeArea).getDataPosition();
int codeOffset = ((CaretCapable) codeArea).getCodeOffset();
EditCodeDataCommand command = new EditCodeDataCommand(codeArea, EditCharDataCommand.EditCommandType.INSERT, dataPosition, codeOffset, (byte) value);
EditCodeDataCommand command = new EditCodeDataCommand(codeArea, EditDataCommand.EditOperationType.INSERT, dataPosition, codeOffset, (byte) value);
if (editCommand != null && isAppendAllowed() && undoRedo instanceof BinaryDataAppendableUndoRedo) {
if (!((BinaryDataAppendableUndoRedo) undoRedo).appendExecute(command)) {
editCommand = command;
@@ -403,23 +397,25 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
// if (editCommand != null && editCommand.wasReverted()) {
// editCommand = null;
// }
long dataPosition = ((CaretCapable) codeArea).getDataPosition();
DeleteSelectionCommand deleteCommand = null;
if (codeArea.hasSelection()) {
undoSequenceBreak();
sequenceBreak();
dataPosition = ((SelectionCapable) codeArea).getSelection().getFirst();
deleteCommand = new DeleteSelectionCommand(codeArea);
((CaretCapable) codeArea).setActiveCaretPosition(dataPosition);
}
if (editMode == EditMode.EXPANDING && editOperation == EditOperation.OVERWRITE) {
if (deleteCommand != null) {
CodeAreaCompoundCommand compoundCommand = new CodeAreaCompoundCommand(codeArea);
compoundCommand.addCommand(deleteCommand);
long dataPosition = ((CaretCapable) codeArea).getDataPosition();
editCommand = new EditCharDataCommand(codeArea, EditCodeDataCommand.EditCommandType.OVERWRITE, dataPosition, keyChar);
editCommand = new EditCharDataCommand(codeArea, EditDataCommand.EditOperationType.OVERWRITE, dataPosition, keyChar);
compoundCommand.addCommand(editCommand);
undoRedo.execute(compoundCommand);
} else {
long dataPosition = ((CaretCapable) codeArea).getDataPosition();
EditCharDataCommand command = new EditCharDataCommand(codeArea, EditCodeDataCommand.EditCommandType.OVERWRITE, dataPosition, keyChar);
EditCharDataCommand command = new EditCharDataCommand(codeArea, EditDataCommand.EditOperationType.OVERWRITE, dataPosition, keyChar);
if (editCommand != null && isAppendAllowed() && undoRedo instanceof BinaryDataAppendableUndoRedo) {
if (!((BinaryDataAppendableUndoRedo) undoRedo).appendExecute(command)) {
editCommand = command;
@@ -433,13 +429,11 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
if (deleteCommand != null) {
CodeAreaCompoundCommand compoundCommand = new CodeAreaCompoundCommand(codeArea);
compoundCommand.addCommand(deleteCommand);
long dataPosition = ((CaretCapable) codeArea).getDataPosition();
editCommand = new EditCharDataCommand(codeArea, EditCodeDataCommand.EditCommandType.INSERT, dataPosition, keyChar);
editCommand = new EditCharDataCommand(codeArea, EditDataCommand.EditOperationType.INSERT, dataPosition, keyChar);
compoundCommand.addCommand(editCommand);
undoRedo.execute(compoundCommand);
} else {
long dataPosition = ((CaretCapable) codeArea).getDataPosition();
EditCharDataCommand command = new EditCharDataCommand(codeArea, EditCodeDataCommand.EditCommandType.INSERT, dataPosition, keyChar);
EditCharDataCommand command = new EditCharDataCommand(codeArea, EditDataCommand.EditOperationType.INSERT, dataPosition, keyChar);
if (editCommand != null && isAppendAllowed() && undoRedo instanceof BinaryDataAppendableUndoRedo) {
if (!((BinaryDataAppendableUndoRedo) undoRedo).appendExecute(command)) {
editCommand = command;
@@ -462,8 +456,7 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
return;
}
CodeAreaSection section = ((CaretCapable) codeArea).getActiveSection();
if (section == BasicCodeAreaSection.TEXT_PREVIEW) {
if (((CaretCapable) codeArea).getActiveSection() == BasicCodeAreaSection.TEXT_PREVIEW) {
String sequence = enterKeyHandlingMode.getSequence();
if (!sequence.isEmpty()) {
pressedCharInPreview(sequence.charAt(0));
@@ -487,7 +480,7 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
if (tabKeyHandlingMode == TabKeyHandlingMode.PLATFORM_SPECIFIC || tabKeyHandlingMode == TabKeyHandlingMode.CYCLE_TO_NEXT_SECTION || tabKeyHandlingMode == TabKeyHandlingMode.CYCLE_TO_PREVIOUS_SECTION) {
if (viewModeSupported && ((ViewModeCapable) codeArea).getViewMode() == CodeAreaViewMode.DUAL) {
move(selectingMode, MovementDirection.SWITCH_SECTION);
undoSequenceBreak();
sequenceBreak();
revealCursor();
}
} else if (((CaretCapable) codeArea).getActiveSection() == BasicCodeAreaSection.TEXT_PREVIEW) {
@@ -521,7 +514,7 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
if (codeArea.hasSelection()) {
DeleteSelectionCommand deleteSelectionCommand = new DeleteSelectionCommand(codeArea);
undoRedo.execute(deleteSelectionCommand);
undoSequenceBreak();
sequenceBreak();
codeArea.notifyDataChanged();
} else {
// if (editCommand != null && editCommand.wasReverted()) {
@@ -530,8 +523,13 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
CodeAreaSection section = ((CaretCapable) codeArea).getActiveSection();
long dataPosition = ((CaretCapable) codeArea).getDataPosition();
if ((DELETE_CHAR == keyChar && dataPosition >= codeArea.getDataSize())
|| (DELETE_CHAR != keyChar && (dataPosition == 0 || dataPosition > codeArea.getDataSize()))) {
return;
}
if (section == BasicCodeAreaSection.CODE_MATRIX) {
EditCodeDataCommand command = new EditCodeDataCommand(codeArea, EditCodeDataCommand.EditCommandType.DELETE, dataPosition, 0, (byte) keyChar);
EditCodeDataCommand command = new EditCodeDataCommand(codeArea, EditDataCommand.EditOperationType.DELETE, dataPosition, 0, (byte) keyChar);
if (editCommand != null && isAppendAllowed() && undoRedo instanceof BinaryDataAppendableUndoRedo) {
if (!((BinaryDataAppendableUndoRedo) undoRedo).appendExecute(command)) {
editCommand = command;
@@ -541,7 +539,7 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
undoRedo.execute(editCommand);
}
} else {
EditCharDataCommand command = new EditCharDataCommand(codeArea, EditCharDataCommand.EditCommandType.DELETE, dataPosition, keyChar);
EditCharDataCommand command = new EditCharDataCommand(codeArea, EditDataCommand.EditOperationType.DELETE, dataPosition, keyChar);
if (editCommand != null && isAppendAllowed() && undoRedo instanceof BinaryDataAppendableUndoRedo) {
if (!((BinaryDataAppendableUndoRedo) undoRedo).appendExecute(command)) {
editCommand = command;
@@ -562,7 +560,7 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
}
undoRedo.execute(new DeleteSelectionCommand(codeArea));
undoSequenceBreak();
sequenceBreak();
codeArea.notifyDataChanged();
}
@@ -629,7 +627,7 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
copy();
if (editMode == EditMode.EXPANDING) {
undoRedo.execute(new DeleteSelectionCommand(codeArea));
undoSequenceBreak();
sequenceBreak();
codeArea.notifyDataChanged();
}
}
@@ -680,7 +678,7 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
}
}
} catch (IllegalStateException ex) {
Logger.getLogger(CodeAreaCommandHandler.class.getName()).log(Level.SEVERE, null, ex);
Logger.getLogger(CodeAreaOperationCommandHandler.class.getName()).log(Level.SEVERE, null, ex);
// Clipboard not available - ignore
}
}
@@ -690,7 +688,7 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
if (codeArea.hasSelection()) {
deleteSelectionCommand = new DeleteSelectionCommand(codeArea);
deleteSelectionCommand.execute();
undoSequenceBreak();
sequenceBreak();
}
EditMode editMode = ((EditModeCapable) codeArea).getEditMode();
@@ -701,22 +699,33 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
CodeAreaCommand modifyCommand = null;
long clipDataSize = pastedData.getDataSize();
long insertionPosition = dataPosition;
if ((editMode == EditMode.EXPANDING && editOperation == EditOperation.OVERWRITE) || editMode == EditMode.INPLACE) {
BinaryData modifiedData;
long replacedPartSize = clipDataSize;
if (insertionPosition + replacedPartSize > dataSize) {
replacedPartSize = dataSize - insertionPosition;
modifiedData = pastedData.copy(0, replacedPartSize);
} else {
modifiedData = pastedData.copy();
if (editMode == EditMode.INPLACE) {
long toReplace = clipDataSize;
if (dataPosition + toReplace > codeArea.getDataSize()) {
toReplace = codeArea.getDataSize() - dataPosition;
}
if (replacedPartSize > 0) {
modifyCommand = new ModifyDataCommand(codeArea, dataPosition, modifiedData);
if (clipDataSize > replacedPartSize) {
pastedData = pastedData.copy(replacedPartSize, clipDataSize - replacedPartSize);
insertionPosition += replacedPartSize;
if (toReplace > 0) {
modifyCommand = new ModifyDataCommand(codeArea, dataPosition, pastedData.copy(0, toReplace));
}
pastedData = new ByteArrayData();
} else {
if (editMode == EditMode.EXPANDING && editOperation == EditOperation.OVERWRITE) {
BinaryData modifiedData;
long replacedPartSize = clipDataSize;
if (insertionPosition + replacedPartSize > dataSize) {
replacedPartSize = dataSize - insertionPosition;
modifiedData = pastedData.copy(0, replacedPartSize);
} else {
pastedData = new ByteArrayData();
modifiedData = pastedData.copy();
}
if (replacedPartSize > 0) {
modifyCommand = new ModifyDataCommand(codeArea, dataPosition, modifiedData);
if (clipDataSize > replacedPartSize) {
pastedData = pastedData.copy(replacedPartSize, clipDataSize - replacedPartSize);
insertionPosition += replacedPartSize;
} else {
pastedData = new ByteArrayData();
}
}
}
}
@@ -733,7 +742,7 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
undoRedo.execute(pasteCommand);
undoSequenceBreak();
sequenceBreak();
codeArea.notifyDataChanged();
revealCursor();
clearSelection();
@@ -824,14 +833,14 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
}
undoRedo.execute(pasteCommand);
undoSequenceBreak();
sequenceBreak();
codeArea.notifyDataChanged();
revealCursor();
clearSelection();
}
}
} catch (IllegalStateException ex) {
Logger.getLogger(CodeAreaCommandHandler.class.getName()).log(Level.SEVERE, null, ex);
Logger.getLogger(CodeAreaOperationCommandHandler.class.getName()).log(Level.SEVERE, null, ex);
// Clipboard not available - ignore
}
}
@@ -871,7 +880,7 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
((CaretCapable) codeArea).setActiveCaretPosition(caretPosition);
updateSelection(selecting, caretPosition);
undoSequenceBreak();
sequenceBreak();
codeArea.repaint();
}
@@ -929,51 +938,6 @@ public class CodeAreaOperationCommandHandler implements CodeAreaCommandHandler {
}
}
@ParametersAreNonnullByDefault
private static class DeleteSelectionCommand extends CodeAreaCommand {
private final RemoveDataCommand removeCommand;
private final long position;
private final long size;
public DeleteSelectionCommand(CodeAreaCore coreArea) {
super(coreArea);
SelectionRange selection = ((SelectionCapable) coreArea).getSelection();
position = selection.getFirst();
size = selection.getLast() - position + 1;
removeCommand = new RemoveDataCommand(coreArea, position, 0, size);
}
@Override
public void execute() {
removeCommand.redo();
((CaretCapable) codeArea).setActiveCaretPosition(position);
clearSelection();
((ScrollingCapable) codeArea).revealCursor();
codeArea.notifyDataChanged();
}
@Override
public void undo() {
removeCommand.undo();
clearSelection();
((CaretCapable) codeArea).setActiveCaretPosition(position + size);
((ScrollingCapable) codeArea).revealCursor();
codeArea.notifyDataChanged();
}
@Nonnull
@Override
public CodeAreaCommandType getType() {
return CodeAreaCommandType.DATA_REMOVED;
}
private void clearSelection() {
long dataPosition = ((CaretCapable) codeArea).getDataPosition();
((SelectionCapable) codeArea).setSelection(dataPosition, dataPosition);
}
}
@Nonnull
public EnterKeyHandlingMode getEnterKeyHandlingMode() {
return enterKeyHandlingMode;
@@ -15,9 +15,6 @@
*/
package org.exbin.bined.operation.android;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.operation.BinaryDataOperationType;
/**
@@ -25,44 +22,30 @@ import org.exbin.bined.operation.BinaryDataOperationType;
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public enum CodeAreaOperationType implements BinaryDataOperationType {
/**
* Insert data operation.
*/
INSERT_DATA("Insert data"),
INSERT_DATA,
/**
* Remove data operation.
*/
REMOVE_DATA("Remove data"),
REMOVE_DATA,
/**
* Modify data operation.
*/
MODIFY_DATA("Modify data"),
MODIFY_DATA,
/**
* Move data operation.
*/
MOVE_DATA("Move data"),
MOVE_DATA,
/**
* Edit data operation.
*/
EDIT_DATA("Edit data"),
EDIT_DATA,
/**
* Compound operation.
*/
COMPOUND("Compound operation");
@Nullable
private final String name;
CodeAreaOperationType(String name) {
this.name = name;
}
@Nonnull
@Override
public String getName() {
return name;
}
COMPOUND;
}
@@ -36,14 +36,14 @@ import org.exbin.bined.operation.undo.BinaryDataUndoableCommand;
@ParametersAreNonnullByDefault
public class CodeAreaUndoRedo implements BinaryDataUndoRedo, BinaryDataAppendableUndoRedo {
private int undoMaximumCount;
private int undoMaximumSize;
private int usedSize;
private int commandPosition;
private int syncPosition = -1;
private final List<BinaryDataCommand> commands = new ArrayList<>();
private final CodeAreaCore codeArea;
private final List<BinaryDataUndoRedoChangeListener> listeners = new ArrayList<>();
protected int undoMaximumCount;
protected int undoMaximumSize;
protected int usedSize;
protected int commandPosition;
protected int syncPosition = -1;
protected final List<BinaryDataCommand> commands = new ArrayList<>();
protected final CodeAreaCore codeArea;
protected final List<BinaryDataUndoRedoChangeListener> listeners = new ArrayList<>();
/**
* Creates a new instance.
@@ -81,13 +81,14 @@ public class CodeAreaUndoRedo implements BinaryDataUndoRedo, BinaryDataAppendabl
if (lastCommand instanceof BinaryDataAppendableCommand) {
if (((BinaryDataAppendableCommand) lastCommand).appendExecute(command)) {
return true;
} else {
commandAdded(command);
return false;
}
}
} else {
execute(command);
}
commandAdded(command);
execute(command);
return false;
}
@@ -162,9 +163,9 @@ public class CodeAreaUndoRedo implements BinaryDataUndoRedo, BinaryDataAppendabl
@Override
public void clear() {
commands.forEach((command) -> {
for (BinaryDataCommand command : commands) {
command.dispose();
});
}
commands.clear();
init();
undoUpdated();
@@ -35,11 +35,11 @@ import org.exbin.bined.operation.undo.BinaryDataUndoableOperation;
@ParametersAreNonnullByDefault
public class DeleteCharEditDataOperation extends CharEditDataOperation {
private static final char BACKSPACE_CHAR = '\b';
private static final char DELETE_CHAR = (char) 0x7f;
protected static final char BACKSPACE_CHAR = '\b';
protected static final char DELETE_CHAR = (char) 0x7f;
private long position;
private char value;
protected long position;
protected char value;
public DeleteCharEditDataOperation(CodeAreaCore codeArea, long startPosition, char value) {
super(codeArea);
@@ -34,12 +34,12 @@ import org.exbin.bined.operation.undo.BinaryDataUndoableOperation;
@ParametersAreNonnullByDefault
public class DeleteCodeEditDataOperation extends CodeEditDataOperation {
private static final char BACKSPACE_CHAR = '\b';
private static final char DELETE_CHAR = (char) 0x7f;
private final CodeType codeType;
protected static final char BACKSPACE_CHAR = '\b';
protected static final char DELETE_CHAR = (char) 0x7f;
protected final CodeType codeType;
private long position;
private byte value;
protected long position;
protected byte value;
public DeleteCodeEditDataOperation(CodeAreaCore codeArea, long startPosition, CodeType codeType, byte value) {
super(codeArea);
@@ -36,8 +36,8 @@ import org.exbin.bined.operation.undo.BinaryDataUndoableOperation;
@ParametersAreNonnullByDefault
public class InsertCharEditDataOperation extends CharEditDataOperation {
private final long startPosition;
private final char value;
protected final long startPosition;
protected final char value;
public InsertCharEditDataOperation(CodeAreaCore coreArea, long startPosition, char value) {
super(coreArea);
@@ -35,15 +35,15 @@ import org.exbin.bined.operation.undo.BinaryDataUndoableOperation;
@ParametersAreNonnullByDefault
public class InsertCodeEditDataOperation extends CodeEditDataOperation {
private final long startPosition;
private final int startCodeOffset;
private boolean trailing = false;
private EditableBinaryData trailingValue = null;
private final CodeType codeType;
private byte value;
protected final long startPosition;
protected final int startCodeOffset;
protected boolean trailing = false;
protected EditableBinaryData trailingValue = null;
protected final CodeType codeType;
protected byte value;
private long length;
private int codeOffset = 0;
protected long length;
protected int codeOffset = 0;
public InsertCodeEditDataOperation(CodeAreaCore codeArea, long startPosition, int startCodeOffset, byte value) {
super(codeArea);
@@ -31,9 +31,9 @@ import org.exbin.bined.operation.undo.BinaryDataUndoableOperation;
@ParametersAreNonnullByDefault
public class InsertDataOperation extends CodeAreaOperation {
private final long position;
private final int codeOffset;
private final BinaryData data;
protected final long position;
protected final int codeOffset;
protected final BinaryData data;
public InsertDataOperation(CodeAreaCore codeArea, long position, int codeOffset, BinaryData data) {
super(codeArea);
@@ -30,8 +30,8 @@ import org.exbin.bined.operation.undo.BinaryDataUndoableOperation;
@ParametersAreNonnullByDefault
public class ModifyDataOperation extends CodeAreaOperation {
private final long position;
private final BinaryData data;
protected final long position;
protected final BinaryData data;
public ModifyDataOperation(CodeAreaCore codeArea, long position, BinaryData data) {
super(codeArea);
@@ -37,9 +37,9 @@ import org.exbin.bined.operation.undo.BinaryDataUndoableOperation;
@ParametersAreNonnullByDefault
public class OverwriteCharEditDataOperation extends CharEditDataOperation {
private final long startPosition;
private long length = 0;
private char value;
protected final long startPosition;
protected long length = 0;
protected char value;
public OverwriteCharEditDataOperation(CodeAreaCore coreArea, long startPosition, char value) {
super(coreArea);
@@ -34,10 +34,10 @@ import org.exbin.bined.operation.undo.BinaryDataUndoableOperation;
@ParametersAreNonnullByDefault
public class OverwriteCodeEditDataOperation extends CodeEditDataOperation {
private final long startPosition;
private final int codeOffset;
private final CodeType codeType;
private byte value;
protected final long startPosition;
protected final int codeOffset;
protected final CodeType codeType;
protected byte value;
public OverwriteCodeEditDataOperation(CodeAreaCore codeArea, long startPosition, int codeOffset, CodeType codeType, byte value) {
super(codeArea);
@@ -31,9 +31,9 @@ import org.exbin.bined.operation.undo.BinaryDataUndoableOperation;
@ParametersAreNonnullByDefault
public class RemoveDataOperation extends CodeAreaOperation {
private final long position;
private final int codeOffset;
private final long length;
protected final long position;
protected final int codeOffset;
protected final long length;
public RemoveDataOperation(CodeAreaCore codeArea, long position, int codeOffset, long length) {
super(codeArea);
@@ -34,18 +34,4 @@ public abstract class CodeAreaCommand extends BinaryDataAbstractCommand {
public CodeAreaCommand(CodeAreaCore codeArea) {
this.codeArea = codeArea;
}
/**
* Returns type of the command.
*
* @return command type
*/
@Nonnull
public abstract CodeAreaCommandType getType();
@Nonnull
@Override
public String getName() {
return getType().getName();
}
}
@@ -15,8 +15,6 @@
*/
package org.exbin.bined.operation.android.command;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.operation.BinaryDataCommandType;
/**
@@ -24,43 +22,30 @@ import org.exbin.bined.operation.BinaryDataCommandType;
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public enum CodeAreaCommandType implements BinaryDataCommandType {
/**
* Insert data command.
*/
DATA_INSERTED("Data inserted"),
DATA_INSERTED,
/**
* Remove data command.
*/
DATA_REMOVED("Data removed"),
DATA_REMOVED,
/**
* Modify data command.
*/
DATA_MODIFIED("Data modified"),
DATA_MODIFIED,
/**
* Move data command.
*/
DATA_MOVED("Data moved"),
DATA_MOVED,
/**
* Compound command.
*/
COMPOUND("Compound"),
COMPOUND,
/**
* Edit data command.
*/
DATA_EDITED("Data edited");
private final String name;
CodeAreaCommandType(String name) {
this.name = name;
}
@Nonnull
@Override
public String getName() {
return name;
}
DATA_EDITED;
}
@@ -0,0 +1,84 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.operation.android.command;
import org.exbin.bined.SelectionRange;
import org.exbin.bined.android.CodeAreaCore;
import org.exbin.bined.capability.CaretCapable;
import org.exbin.bined.capability.ScrollingCapable;
import org.exbin.bined.capability.SelectionCapable;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* Command for deleting data.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class DeleteSelectionCommand extends CodeAreaCommand {
private final RemoveDataCommand removeCommand;
private final long position;
private final long size;
public DeleteSelectionCommand(CodeAreaCore coreArea) {
super(coreArea);
SelectionRange selection = ((SelectionCapable) coreArea).getSelection();
position = selection.getFirst();
size = selection.getLast() - position + 1;
removeCommand = new RemoveDataCommand(coreArea, position, 0, size);
}
@Override
public void execute() {
removeCommand.execute();
((CaretCapable) codeArea).setActiveCaretPosition(position);
clearSelection();
((ScrollingCapable) codeArea).revealCursor();
codeArea.notifyDataChanged();
}
@Override
public void redo() {
removeCommand.redo();
((CaretCapable) codeArea).setActiveCaretPosition(position);
clearSelection();
((ScrollingCapable) codeArea).revealCursor();
codeArea.notifyDataChanged();
}
@Override
public void undo() {
removeCommand.undo();
clearSelection();
((CaretCapable) codeArea).setActiveCaretPosition(position + size);
((ScrollingCapable) codeArea).revealCursor();
codeArea.notifyDataChanged();
}
@Nonnull
@Override
public CodeAreaCommandType getType() {
return CodeAreaCommandType.DATA_REMOVED;
}
private void clearSelection() {
long dataPosition = ((CaretCapable) codeArea).getDataPosition();
((SelectionCapable) codeArea).setSelection(dataPosition, dataPosition);
}
}
@@ -37,14 +37,14 @@ import org.exbin.bined.android.CodeAreaCore;
public class EditCharDataCommand extends EditDataCommand implements BinaryDataAppendableCommand {
@Nonnull
private final EditCommandType commandType;
protected final EditOperationType editOperationType;
protected BinaryDataCommandPhase phase = BinaryDataCommandPhase.CREATED;
private BinaryDataUndoableOperation activeOperation;
protected BinaryDataUndoableOperation activeOperation;
public EditCharDataCommand(CodeAreaCore codeArea, EditCommandType commandType, long position, char charData) {
public EditCharDataCommand(CodeAreaCore codeArea, EditOperationType editOperationType, long position, char charData) {
super(codeArea);
this.commandType = commandType;
switch (commandType) {
this.editOperationType = editOperationType;
switch (editOperationType) {
case INSERT: {
activeOperation = new InsertCharEditDataOperation(codeArea, position, charData);
break;
@@ -58,7 +58,7 @@ public class EditCharDataCommand extends EditDataCommand implements BinaryDataAp
break;
}
default:
throw CodeAreaUtils.getInvalidTypeException(commandType);
throw CodeAreaUtils.getInvalidTypeException(editOperationType);
}
}
@@ -106,7 +106,12 @@ public class EditCharDataCommand extends EditDataCommand implements BinaryDataAp
@Override
public boolean appendExecute(BinaryDataCommand command) {
if (phase != BinaryDataCommandPhase.EXECUTED) {
throw new IllegalStateException();
}
command.execute();
if (command instanceof EditCharDataCommand && activeOperation instanceof BinaryDataAppendableOperation) {
return ((BinaryDataAppendableOperation) activeOperation).appendOperation(((EditCharDataCommand) command).activeOperation);
}
@@ -116,8 +121,8 @@ public class EditCharDataCommand extends EditDataCommand implements BinaryDataAp
@Nonnull
@Override
public EditCommandType getCommandType() {
return commandType;
public EditOperationType getEditOperationType() {
return editOperationType;
}
@Override
@@ -37,14 +37,14 @@ import org.exbin.bined.android.CodeAreaCore;
@ParametersAreNonnullByDefault
public class EditCodeDataCommand extends EditDataCommand implements BinaryDataAppendableCommand {
private final EditCommandType commandType;
protected final EditOperationType editOperationType;
protected BinaryDataCommandPhase phase = BinaryDataCommandPhase.CREATED;
private BinaryDataUndoableOperation activeOperation;
protected BinaryDataUndoableOperation activeOperation;
public EditCodeDataCommand(CodeAreaCore codeArea, EditCommandType commandType, long position, int positionCodeOffset, byte value) {
public EditCodeDataCommand(CodeAreaCore codeArea, EditOperationType editOperationType, long position, int positionCodeOffset, byte value) {
super(codeArea);
this.commandType = commandType;
switch (commandType) {
this.editOperationType = editOperationType;
switch (editOperationType) {
case INSERT: {
activeOperation = new InsertCodeEditDataOperation(codeArea, position, positionCodeOffset, value);
break;
@@ -58,7 +58,7 @@ public class EditCodeDataCommand extends EditDataCommand implements BinaryDataAp
break;
}
default:
throw CodeAreaUtils.getInvalidTypeException(commandType);
throw CodeAreaUtils.getInvalidTypeException(editOperationType);
}
}
@@ -106,6 +106,10 @@ public class EditCodeDataCommand extends EditDataCommand implements BinaryDataAp
@Override
public boolean appendExecute(BinaryDataCommand command) {
if (phase != BinaryDataCommandPhase.EXECUTED) {
throw new IllegalStateException();
}
command.execute();
if (command instanceof EditCodeDataCommand && activeOperation instanceof BinaryDataAppendableOperation) {
@@ -117,8 +121,8 @@ public class EditCodeDataCommand extends EditDataCommand implements BinaryDataAp
@Nonnull
@Override
public EditCommandType getCommandType() {
return commandType;
public EditOperationType getEditOperationType() {
return editOperationType;
}
@Override
@@ -38,9 +38,9 @@ public abstract class EditDataCommand extends CodeAreaCommand {
}
@Nonnull
public abstract EditCommandType getCommandType();
public abstract EditOperationType getEditOperationType();
public enum EditCommandType {
public enum EditOperationType {
INSERT, OVERWRITE, DELETE
}
}
@@ -30,8 +30,8 @@ import org.exbin.bined.android.CodeAreaCore;
@ParametersAreNonnullByDefault
public class InsertDataCommand extends OpCodeAreaCommand {
private final long position;
private final long dataLength;
protected final long position;
protected final long dataLength;
public InsertDataCommand(CodeAreaCore codeArea, long position, int codeOffset, BinaryData data) {
super(codeArea);
@@ -58,7 +58,7 @@ public abstract class OpCodeAreaCommand extends CodeAreaCommand {
@Override
public void undo() {
if (phase == BinaryDataCommandPhase.REVERTED) {
if (phase != BinaryDataCommandPhase.EXECUTED) {
throw new IllegalStateException();
}
@@ -70,7 +70,7 @@ public abstract class OpCodeAreaCommand extends CodeAreaCommand {
@Override
public void redo() {
if (phase == BinaryDataCommandPhase.EXECUTED) {
if (phase != BinaryDataCommandPhase.REVERTED) {
throw new IllegalStateException();
}
@@ -0,0 +1,182 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.framework.bined;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.CodeAreaSection;
import org.exbin.bined.highlight.android.NonAsciiCodeAreaColorAssessor;
import org.exbin.bined.highlight.android.NonprintablesCodeAreaAssessor;
import org.exbin.bined.highlight.android.SearchCodeAreaColorAssessor;
import org.exbin.bined.android.CodeAreaCharAssessor;
import org.exbin.bined.android.CodeAreaColorAssessor;
import org.exbin.bined.android.CodeAreaPaintState;
/**
* Color assessor for binary editor with registrable modifiers.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class BinEdCodeAreaAssessor implements CodeAreaColorAssessor, CodeAreaCharAssessor {
private final List<PositionColorModifier> priorityColorModifiers = new ArrayList<>();
private final List<PositionColorModifier> colorModifiers = new ArrayList<>();
private final CodeAreaColorAssessor parentColorAssessor;
private final CodeAreaCharAssessor parentCharAssessor;
public BinEdCodeAreaAssessor(@Nullable CodeAreaColorAssessor parentColorAssessor, @Nullable CodeAreaCharAssessor parentCharAssessor) {
NonAsciiCodeAreaColorAssessor nonAsciiCodeAreaColorAssessor = new NonAsciiCodeAreaColorAssessor(parentColorAssessor);
NonprintablesCodeAreaAssessor nonprintablesCodeAreaAssessor = new NonprintablesCodeAreaAssessor(nonAsciiCodeAreaColorAssessor, parentCharAssessor);
SearchCodeAreaColorAssessor searchCodeAreaColorAssessor = new SearchCodeAreaColorAssessor(nonprintablesCodeAreaAssessor);
this.parentColorAssessor = searchCodeAreaColorAssessor;
this.parentCharAssessor = nonprintablesCodeAreaAssessor;
}
public void addColorModifier(PositionColorModifier colorModifier) {
colorModifiers.add(colorModifier);
}
public void removeColorModifier(PositionColorModifier colorModifier) {
colorModifiers.remove(colorModifier);
}
public void addPriorityColorModifier(PositionColorModifier colorModifier) {
priorityColorModifiers.add(colorModifier);
}
public void removePriorityColorModifier(PositionColorModifier colorModifier) {
priorityColorModifiers.remove(colorModifier);
}
@Override
public void startPaint(CodeAreaPaintState codeAreaPaintState) {
for (PositionColorModifier colorModifier : priorityColorModifiers) {
colorModifier.resetColors();
}
for (PositionColorModifier colorModifier : colorModifiers) {
colorModifier.resetColors();
}
if (parentColorAssessor != null) {
parentColorAssessor.startPaint(codeAreaPaintState);
}
}
@Nullable
@Override
public Integer getPositionBackgroundColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section, boolean inSelection) {
for (PositionColorModifier colorModifier : priorityColorModifiers) {
Integer positionBackgroundColor = colorModifier.getPositionBackgroundColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);
if (positionBackgroundColor != null) {
return positionBackgroundColor;
}
}
if (!inSelection) {
for (PositionColorModifier colorModifier : colorModifiers) {
Integer positionBackgroundColor = colorModifier.getPositionBackgroundColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);
if (positionBackgroundColor != null) {
return positionBackgroundColor;
}
}
}
if (parentColorAssessor != null) {
return parentColorAssessor.getPositionBackgroundColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);
}
// if (color == null || inSelection) {
// long dataPosition = rowDataPosition + byteOnRow;
// if (dataPosition > 100 && dataPosition < 300) {
// if (inSelection && color != null) {
// return new Color(
// (((int) (dataPosition * 17) % 255) + color.getRed()) / 2,
// (((int) (dataPosition * 37) % 255) + color.getGreen()) / 2,
// (((int) (dataPosition * 13) % 255) + color.getBlue()) / 2);
// }
// return new Color((int) (dataPosition * 17) % 255, (int) (dataPosition * 37) % 255, (int) (dataPosition * 13) % 255);
// }
// }
return null;
}
@Nullable
@Override
public Integer getPositionTextColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section, boolean inSelection) {
for (PositionColorModifier colorModifier : priorityColorModifiers) {
Integer positionTextColor = colorModifier.getPositionTextColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);
if (positionTextColor != null) {
return positionTextColor;
}
}
if (!inSelection) {
for (PositionColorModifier colorModifier : colorModifiers) {
Integer positionTextColor = colorModifier.getPositionTextColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);
if (positionTextColor != null) {
return positionTextColor;
}
}
}
if (parentColorAssessor != null) {
return parentColorAssessor.getPositionTextColor(rowDataPosition, byteOnRow, charOnRow, section, inSelection);
}
return null;
}
@Override
public char getPreviewCharacter(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section) {
return parentCharAssessor != null ? parentCharAssessor.getPreviewCharacter(rowDataPosition, byteOnRow, charOnRow, section) : ' ';
}
@Override
public char getPreviewCursorCharacter(long rowDataPosition, int byteOnRow, int charOnRow, byte[] cursorData, int cursorDataLength, CodeAreaSection section) {
return parentCharAssessor != null ? parentCharAssessor.getPreviewCursorCharacter(rowDataPosition, byteOnRow, charOnRow, cursorData, cursorDataLength, section) : ' ';
}
@Nonnull
@Override
public Optional<CodeAreaCharAssessor> getParentCharAssessor() {
return Optional.ofNullable(parentCharAssessor);
}
@Nonnull
@Override
public Optional<CodeAreaColorAssessor> getParentColorAssessor() {
return Optional.ofNullable(parentColorAssessor);
}
@ParametersAreNonnullByDefault
public interface PositionColorModifier {
@Nullable
Integer getPositionBackgroundColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section, boolean inSelection);
@Nullable
Integer getPositionTextColor(long rowDataPosition, int byteOnRow, int charOnRow, CodeAreaSection section, boolean inSelection);
void resetColors();
}
}
+19 -5
View File
@@ -1,5 +1,6 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -15,22 +16,35 @@
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="180dp"
android:layout_height="160dp"
android:contentDescription="@string/bined_logo_description"
android:paddingLeft="10dp"
android:paddingRight="10dp"
app:srcCompat="@drawable/app_icon"
android:contentDescription="@string/bined_logo_description" />
app:srcCompat="@drawable/app_icon" />
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true"
android:linksClickable="true"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:text="@string/app_about"
android:focusable="true"/>
android:text="@string/app_about" />
<TextView
android:id="@+id/contributors"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true"
android:linksClickable="true"
android:paddingLeft="10dp"
android:paddingTop="10dp"
android:paddingRight="10dp"
android:text="Miroslav Hajda\nGovind S Nair (Hindi translation)"
tools:ignore="HardcodedText" />
</LinearLayout>
</ScrollView>
+1
View File
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/contentMain"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
+1 -1
View File
@@ -225,7 +225,7 @@
<TableLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="5dp">
android:layout_marginStart="5dp">
<TableRow
android:layout_width="match_parent"
+4 -4
View File
@@ -88,11 +88,11 @@
android:checked="true"
android:title="@string/code_colorization" />
<item
android:id="@+id/non_printing_characters"
android:id="@+id/non_printable_characters"
android:checkable="true"
android:checked="false"
android:enabled="false"
android:title="@string/non_printing_characters" />
android:checked="true"
android:enabled="true"
android:title="@string/nonprintable_characters" />
</menu>
</item>
<item
+7 -3
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">BinEd</string>
<string name="app_about">Název: BinEd binární/hexadecimální editor\nVerze: 0.2.1\nDodavatel: <a href="https://exbin.org">Projekt ExBin</a>\nLicence: <a href="https://www.apache.org/licenses/LICENSE-2.0">Licence Apache, verze 2.0</a>\nDomovská stránka: <a href="https://bined.exbin.org/android ">https://bined.exbin.org/android</a></string>
<string name="app_about">Název: BinEd binární/hexadecimální editor\nVerze: %1$s\nDodavatel: &lt;a href="https://exbin.org"&gt;Projekt ExBin&lt;/a&gt;\nLicence: &lt;a href="https://www.apache.org/licenses/LICENSE-2.0"&gt;Licence Apache, verze 2.0&lt;/a&gt;\nDomovská stránka: &lt;a href="https://bined.exbin.org/android "&gt;https://bined.exbin.org/android&lt;/a&gt;\n\nPřispěvatelé:</string>
<string name="bined_logo_description">Logo BinEd</string>
<string name="title_activity_settings">Nastavení</string>
@@ -38,7 +38,7 @@
<string name="hex_characters_case">Velikost hexadecimálních znaků</string>
<string name="encoding">Kódování</string>
<string name="code_colorization">Zbarvení kódu</string>
<string name="non_printing_characters">Netisknutelné znaky</string>
<string name="nonprintable_characters">Netisknutelné znaky</string>
<string name="go_to_position">Přejít na pozici</string>
<string name="edit_selection">Upravit výběr</string>
<string name="insert_data">Vložit data</string>
@@ -57,14 +57,18 @@
<string name="application_about">O aplikaci</string>
<string name="select_file">Vyberte soubor</string>
<string name="save_as_file">Uložit jako soubor</string>
<string name="file_modified">Soubor by upraven</string>
<string name="file_modified">Soubor byl upraven</string>
<string name="storage_permission_is_not_granted">Není uděleno oprávnění k úložišti</string>
<string name="storage_permission_request">Je třeba povolit oprávnění úložiště</string>
<string name="button_close">Zavřít</string>
<string name="button_donate">Podpořit</string>
<string name="button_cancel">Zrušit</string>
<string name="button_set">Nastavit</string>
<string name="button_go_to">Přejít na</string>
<string name="button_save">Uložit</string>
<string name="button_discard">Zahodit</string>
<string name="button_request">Vyžádat</string>
<string name="pref_header_appearance">Vzhled</string>
<string name="pref_view_header">Zobrazit</string>
+28
View File
@@ -0,0 +1,28 @@
<resources>
<string-array name="bytes_per_row_entries">
<item>स्क्रीन भरें</item>
<item>4 बाइट</item>
<item>8 बाइट</item>
<item>12 बाइट</item>
<item>16 बाइट</item>
<item>पसंद के अनुसार</item>
</string-array>
<string-array name="view_mode_entries">
<item>द्वैतिक</item>
<item>कोड मैट्रिक्स</item>
<item>पाठ पूर्वावलोकन</item>
</string-array>
<string-array name="code_type_entries">
<item>बाइनरी</item>
<item>ऑक्टल</item>
<item>दशमलव</item>
<item>हैक्साडेसिमल</item>
</string-array>
<string-array name="hex_chars_case_entries">
<item>निम्न वर्ण</item>
<item>उच्च वर्ण</item>
</string-array>
</resources>
+79
View File
@@ -0,0 +1,79 @@
<resources>
<string name="app_name">BinEd</string>
<string name="app_about">नाम: BinEd - बाइनरी/हेक्स संपादक\nवर्ज़न: %1$s\nविक्रेता: &lt;a href="https://exbin.org"&gt;ExBin प्रोजेक्ट&lt;/a&gt;\nलाइसेंस: &lt;a href="https://www.apache.org/licenses/LICENSE-2.0"&gt;Apache लाइसेंस, संस्करण 2.0&lt;/a&gt;\nमुखपृष्ठ: &lt;a href="https://bined.exbin.org/android"&gt;https://bined.exbin.org/android&lt;/a&gt;\n\nयोगदानकर्ताओं:</string>
<string name="bined_logo_description">BinEd लोगो</string>
<!-- Actions & menu -->
<string name="title_activity_settings">सेटिंग्स</string>
<string name="action_new">नया</string>
<string name="action_open">खोलें</string>
<string name="action_save">सहेजें</string>
<string name="action_save_as">के रूप में सहेजें</string>
<string name="action_settings">सेटिंग्स</string>
<string name="action_about">के बारे में</string>
<string name="action_search">खोजें</string>
<string name="action_undo">पूर्ववत करें</string>
<string name="action_redo">फिर से करें</string>
<string name="action_selection_start">चयन प्रारंभ</string>
<string name="action_selection_end">चयन अंत</string>
<string name="action_clear_selection">चयन साफ़ करें</string>
<string name="action_cut">काटें</string>
<string name="action_copy">कॉपी करें</string>
<string name="action_copy_as_code">कोड के रूप में कॉपी करें</string>
<string name="action_paste">पेस्ट करें</string>
<string name="action_paste_from_code">कोड से पेस्ट करें</string>
<string name="action_delete">हटाएँ</string>
<string name="action_select_all">सभी का चयन करें</string>
<string name="action_exit">बाहर निकलें</string>
<string name="view_menu">देखे</string>
<string name="visual_theme">दृश्य थीम</string>
<string name="language">भाषा</string>
<string name="view_mode">दृश्य मोड</string>
<string name="code_type">कोड प्रकार</string>
<string name="hex_characters_case">हेक्स वर्ण केस</string>
<string name="encoding">कोडिंग</string>
<string name="code_colorization">कोड रंगीकरण</string>
<string name="nonprintable_characters">गैर-मुद्रण वर्ण</string>
<string name="go_to_position">स्थान पर जाएँ</string>
<string name="edit_selection">चयन संपादित करें</string>
<string name="insert_data">डेटा डालें</string>
<string name="convert_data">डेटा परिवर्तित करें</string>
<string name="edit_menu">संपादित करें</string>
<string name="font">फॉन्ट</string>
<string name="bytes_per_row">प्रति पंक्ति बाइट्स</string>
<string name="bytes_per_row_fill">स्क्रीन भरें</string>
<string name="bytes_per_row_4">4 बाइट</string>
<string name="bytes_per_row_8">8 बाइट</string>
<string name="bytes_per_row_12">12 बाइट</string>
<string name="bytes_per_row_16">16 बाइट</string>
<string name="bytes_per_row_custom">पसंद के अनुसार</string>
<string name="tools_menu">उपकरण</string>
<string name="application_about">ऐप के बारे में</string>
<string name="select_file">एक फ़ाइल चुनें</string>
<string name="save_as_file">फ़ाइल के रूप में सहेजें</string>
<string name="file_modified">फ़ाइल को संशोधित किया गया</string>
<string name="storage_permission_is_not_granted">स्टोरेज अनुमति नहीं दी गई</string>
<string name="storage_permission_request">स्टोरेज अनुमतियाँ सक्षम करने की आवश्यकता है</string>
<string name="button_close">बंद करें</string>
<string name="button_donate">उपहार देना</string>
<string name="button_cancel">रद्द करें</string>
<string name="button_set">सेट करें</string>
<string name="button_go_to">जाएँ</string>
<string name="button_save">सहेजें</string>
<string name="button_discard">खारिज करें</string>
<string name="button_request">अनुरोध करें</string>
<!-- Preference Titles -->
<string name="pref_header_appearance">दृश्यता</string>
<string name="pref_view_header">दृश्य</string>
</resources>
+2
View File
@@ -15,12 +15,14 @@
<item>Default (system)</item>
<item>English</item>
<item>Česky</item>
<item>हिंदी</item>
</string-array>
<string-array name="language_values">
<item>default</item>
<item>en</item>
<item>cs</item>
<item>hi</item>
</string-array>
<string-array name="bytes_per_row_entries">
+6 -2
View File
@@ -1,6 +1,6 @@
<resources>
<string name="app_name">BinEd</string>
<string name="app_about">Name: BinEd - Binary/Hex Editor\nVersion: 0.2.1\nVendor: <a href="https://exbin.org">ExBin Project</a>\nLicense: <a href="https://www.apache.org/licenses/LICENSE-2.0">Apache License, Version 2.0</a>\nHomepage: <a href="https://bined.exbin.org/android">https://bined.exbin.org/android</a></string>
<string name="app_about">Name: BinEd - Binary/Hex Editor\nVersion: %1$s\nVendor: &lt;a href="https://exbin.org"&gt;ExBin Project&lt;/a&gt;\nLicense: &lt;a href="https://www.apache.org/licenses/LICENSE-2.0"&gt;Apache License, Version 2.0&lt;/a&gt;\nHomepage: &lt;a href="https://bined.exbin.org/android"&gt;https://bined.exbin.org/android&lt;/a&gt;\n\nContributors:</string>
<string name="bined_logo_description">BinEd Logo</string>
<!-- Actions & menu -->
@@ -39,7 +39,7 @@
<string name="hex_characters_case">Hex Characters Case</string>
<string name="encoding">Encoding</string>
<string name="code_colorization">Code Colorization</string>
<string name="non_printing_characters">Non-printing Characters</string>
<string name="nonprintable_characters">Nonprintable Characters</string>
<string name="go_to_position">Go To Position</string>
<string name="edit_selection">Edit Selection</string>
<string name="insert_data">Insert Data</string>
@@ -59,13 +59,17 @@
<string name="select_file">Select a file</string>
<string name="save_as_file">Save as file</string>
<string name="file_modified">File was modified</string>
<string name="storage_permission_is_not_granted">Storage permission is not granted</string>
<string name="storage_permission_request">Need to enable storage permissions</string>
<string name="button_close">Close</string>
<string name="button_donate">Donate</string>
<string name="button_cancel">Cancel</string>
<string name="button_set">Set</string>
<string name="button_go_to">Go To</string>
<string name="button_save">Save</string>
<string name="button_discard">Discard</string>
<string name="button_request">Request</string>
<!-- Preference Titles -->
+2 -3
View File
@@ -49,7 +49,6 @@
app:title="@string/code_colorization" />
<SwitchPreferenceCompat
app:key="non_printing_characters"
app:enabled="false"
app:title="@string/non_printing_characters" />
app:key="nonprintable_characters"
app:title="@string/nonprintable_characters" />
</PreferenceScreen>
@@ -0,0 +1,54 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.ByteArrayEditableData;
import org.exbin.auxiliary.binary_data.EditableBinaryData;
/**
* Tests for codeArea component.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class CodeAreaTest {
public static final String SAMPLE_FILES_PATH = "/org/exbin/bined/resources/test/";
public static final String SAMPLE_5BYTES = SAMPLE_FILES_PATH + "5bytes.dat";
public static final String SAMPLE_10BYTES = SAMPLE_FILES_PATH + "10bytes.dat";
public static final String SAMPLE_ALLBYTES = SAMPLE_FILES_PATH + "allbytes.dat";
private CodeAreaTest() {
}
@Nonnull
public static EditableBinaryData getSampleData(String dataPath) {
ByteArrayEditableData data = new ByteArrayEditableData();
try (InputStream stream = CodeAreaTest.class.getResourceAsStream(dataPath)) {
data.loadFromStream(stream);
} catch (IOException ex) {
Logger.getLogger(CodeAreaTest.class.getName()).log(Level.SEVERE, null, ex);
}
return data;
}
}
@@ -0,0 +1,71 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.BinaryData;
import org.exbin.auxiliary.binary_data.ByteArrayEditableData;
import org.exbin.auxiliary.binary_data.EditableBinaryData;
import org.junit.Assert;
import org.junit.Test;
/**
* Test CodeAreaUtils class.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class CodeAreaUtilsTest {
@Test
public void testInsertHexStringIntoDataHexadecimal() {
byte[] expectedData = new byte[33];
for (int i = 0; i < 32; i++) {
expectedData[i] = (byte) i;
}
expectedData[32] = (byte) 0xff;
EditableBinaryData resultData = new ByteArrayEditableData();
CodeAreaUtils.insertHexStringIntoData("00 01 02 03 04 05 06 07 08 9 0A 0b c 0D E 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f ff", resultData, CodeType.HEXADECIMAL);
checkResultData(expectedData, resultData);
resultData = new ByteArrayEditableData();
CodeAreaUtils.insertHexStringIntoData("000102030405060708090A0b0c0D0E0f101112131415161718191a1b1c1d1e1fFf", resultData, CodeType.HEXADECIMAL);
checkResultData(expectedData, resultData);
}
@Test
public void testInsertHexStringIntoDataDecimal() {
byte[] expectedData = new byte[23];
for (int i = 0; i < 20; i++) {
expectedData[i] = (byte) i;
}
expectedData[20] = 100;
expectedData[21] = (byte) 200;
expectedData[22] = (byte) 255;
EditableBinaryData resultData = new ByteArrayEditableData();
CodeAreaUtils.insertHexStringIntoData("00 01 02 03 04 05 06 07 08 9 10 11 12 13 14 15 16 17 18 19 100 200 255", resultData, CodeType.DECIMAL);
checkResultData(expectedData, resultData);
}
public static void checkResultData(byte[] expectedData, BinaryData data) {
Assert.assertEquals(expectedData.length, data.getDataSize());
byte[] resultData = new byte[expectedData.length];
data.copyToArray(0, resultData, 0, expectedData.length);
Assert.assertArrayEquals(expectedData, resultData);
}
}
@@ -0,0 +1,87 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.annotation.Nonnull;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.BinaryData;
import org.exbin.auxiliary.binary_data.ByteArrayData;
/**
* Simulation of huge binary data source.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class HugeBinaryData implements BinaryData {
@Override
public boolean isEmpty() {
return false;
}
@Override
public long getDataSize() {
return Long.MAX_VALUE / 2;
}
@Override
public byte getByte(long longValue) {
return (byte) (longValue % 0xff);
}
@Nonnull
@Override
public BinaryData copy() {
return new HugeBinaryData();
}
@Nonnull
@Override
public BinaryData copy(long startFrom, long length) {
if (length > Integer.MAX_VALUE) {
throw new IllegalStateException("Unable to copy too huge memory segment");
}
byte[] dataArray = new byte[(int) length];
copyToArray(startFrom, dataArray, 0, (int) length);
return new ByteArrayData(dataArray);
}
@Override
public void copyToArray(long startFrom, byte[] target, int offset, int length) {
for (int position = 0; position < length; position++) {
target[offset + position] = getByte(startFrom + position);
}
}
@Override
public void saveToStream(OutputStream out) throws IOException {
throw new UnsupportedOperationException("Not supported yet.");
}
@Nonnull
@Override
public InputStream getDataInputStream() {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void dispose() {
}
}
@@ -0,0 +1,242 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.android.basic;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.BinaryData;
import org.exbin.bined.EditOperation;
import org.exbin.bined.capability.CaretCapable;
import org.exbin.auxiliary.binary_data.EditableBinaryData;
import org.exbin.bined.CodeAreaTest;
import org.exbin.bined.EditMode;
import org.exbin.bined.android.CodeAreaAndroidUtils;
import org.junit.Assert;
import org.junit.Test;
import org.exbin.bined.capability.EditModeCapable;
import org.exbin.bined.capability.SelectionCapable;
import org.exbin.bined.android.CodeAreaCore;
/**
* Tests clipboard actions for CodeArea command component.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class CodeAreaCommandClipboardTest extends CodeAreaComponentTest {
public CodeAreaCommandClipboardTest() {
}
/*
@Test
public void testDeleteAll() {
CodeAreaCore codeArea = createCodeArea();
codeArea.setContentData(CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES));
codeArea.selectAll();
codeArea.delete();
Assert.assertEquals(0, codeArea.getDataSize());
}
@Test
public void testCopySelection() {
CodeAreaCore codeArea = createCodeArea();
EditableBinaryData sampleData = CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES);
byte[] expectedData = new byte[2];
sampleData.copyToArray(5, expectedData, 0, 2);
codeArea.setContentData(sampleData);
((SelectionCapable) codeArea).setSelection(5, 7);
codeArea.copy();
try {
String text = (String) CodeAreaAndroidUtils.getClipboard().getData(DataFlavor.stringFlavor);
Assert.assertArrayEquals(expectedData, text.getBytes("UTF-8"));
} catch (UnsupportedFlavorException | IOException ex) {
Logger.getLogger(CodeAreaCommandClipboardTest.class.getName()).log(Level.SEVERE, null, ex);
Assert.fail();
}
}
@Test
public void testCopyPasteAllInOverwriteMode() {
CodeAreaCore codeArea = createCodeArea();
EditableBinaryData sampleData = CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES);
codeArea.setContentData(sampleData);
long dataSize = sampleData.getDataSize();
codeArea.selectAll();
codeArea.copy();
codeArea.clearSelection();
codeArea.paste();
Assert.assertEquals(dataSize, codeArea.getDataSize());
}
@Test
public void testCopyPasteAllInInsertMode() {
CodeAreaCore codeArea = createCodeArea();
((EditModeCapable) codeArea).setEditOperation(EditOperation.INSERT);
EditableBinaryData sampleData = CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES);
codeArea.setContentData(sampleData);
long dataSize = sampleData.getDataSize();
codeArea.selectAll();
codeArea.copy();
codeArea.clearSelection();
codeArea.paste();
Assert.assertEquals(dataSize * 2, codeArea.getDataSize());
}
@Test
public void testCopyPasteAllInInplaceMode() {
CodeAreaCore codeArea = createCodeArea();
((EditModeCapable) codeArea).setEditMode(EditMode.INPLACE);
EditableBinaryData sampleData = CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES);
codeArea.setContentData(sampleData);
long dataSize = sampleData.getDataSize();
codeArea.selectAll();
codeArea.copy();
codeArea.clearSelection();
codeArea.paste();
Assert.assertEquals(dataSize, codeArea.getDataSize());
}
@Test
public void testCopyPasteInOverwriteAtTheEnd() {
CodeAreaCore codeArea = createCodeArea();
EditableBinaryData sampleData = CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES);
codeArea.setContentData(sampleData);
long dataSize = sampleData.getDataSize();
codeArea.selectAll();
codeArea.copy();
codeArea.clearSelection();
((CaretCapable) codeArea).setActiveCaretPosition(dataSize);
codeArea.paste();
Assert.assertEquals(dataSize * 2, codeArea.getDataSize());
}
@Test
public void testCopyPasteInInsertAtTheEnd() {
CodeAreaCore codeArea = createCodeArea();
((EditModeCapable) codeArea).setEditOperation(EditOperation.INSERT);
EditableBinaryData sampleData = CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES);
codeArea.setContentData(sampleData);
long dataSize = sampleData.getDataSize();
codeArea.selectAll();
codeArea.copy();
codeArea.clearSelection();
((CaretCapable) codeArea).setActiveCaretPosition(dataSize);
codeArea.paste();
Assert.assertEquals(dataSize * 2, codeArea.getDataSize());
}
@Test
public void testCopyPasteInInplaceAtTheEnd() {
CodeAreaCore codeArea = createCodeArea();
((EditModeCapable) codeArea).setEditMode(EditMode.INPLACE);
EditableBinaryData sampleData = CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES);
codeArea.setContentData(sampleData);
long dataSize = sampleData.getDataSize();
codeArea.selectAll();
codeArea.copy();
codeArea.clearSelection();
((CaretCapable) codeArea).setActiveCaretPosition(dataSize);
codeArea.paste();
Assert.assertEquals(dataSize, codeArea.getDataSize());
}
@Test
public void testCopyPasteInOverwriteWithOverflow() {
CodeAreaCore codeArea = createCodeArea();
EditableBinaryData sampleData = CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES);
int dataSize = (int) sampleData.getDataSize();
int expectedSize = (int) (dataSize / 2 + dataSize);
byte[] expectedData = new byte[expectedSize];
sampleData.copyToArray(0, expectedData, 0, dataSize / 2);
sampleData.copyToArray(0, expectedData, dataSize / 2, dataSize);
codeArea.setContentData(sampleData);
codeArea.selectAll();
codeArea.copy();
codeArea.clearSelection();
((CaretCapable) codeArea).setActiveCaretPosition(dataSize / 2);
codeArea.paste();
Assert.assertEquals(expectedSize, codeArea.getDataSize());
checkResultData(expectedData, codeArea.getContentData());
}
@Test
public void testCopyPasteInInsertWithOverflow() {
CodeAreaCore codeArea = createCodeArea();
((EditModeCapable) codeArea).setEditOperation(EditOperation.INSERT);
EditableBinaryData sampleData = CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES);
int dataSize = (int) sampleData.getDataSize();
int expectedSize = dataSize * 2;
byte[] expectedData = new byte[expectedSize];
sampleData.copyToArray(0, expectedData, 0, dataSize / 2);
sampleData.copyToArray(0, expectedData, dataSize / 2, dataSize);
sampleData.copyToArray(dataSize / 2, expectedData, dataSize + dataSize / 2, dataSize / 2);
codeArea.setContentData(sampleData);
codeArea.selectAll();
codeArea.copy();
codeArea.clearSelection();
((CaretCapable) codeArea).setActiveCaretPosition(dataSize / 2);
codeArea.paste();
Assert.assertEquals(expectedSize, codeArea.getDataSize());
checkResultData(expectedData, codeArea.getContentData());
}
@Test
public void testCopyPasteInInplaceWithOverflow() {
CodeAreaCore codeArea = createCodeArea();
((EditModeCapable) codeArea).setEditMode(EditMode.INPLACE);
EditableBinaryData sampleData = CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES);
int dataSize = (int) sampleData.getDataSize();
int expectedSize = dataSize;
byte[] expectedData = new byte[expectedSize];
sampleData.copyToArray(0, expectedData, 0, dataSize / 2);
sampleData.copyToArray(0, expectedData, dataSize / 2, dataSize / 2);
codeArea.setContentData(sampleData);
codeArea.selectAll();
codeArea.copy();
codeArea.clearSelection();
((CaretCapable) codeArea).setActiveCaretPosition(dataSize / 2);
codeArea.paste();
Assert.assertEquals(expectedSize, codeArea.getDataSize());
checkResultData(expectedData, codeArea.getContentData());
}
public static void checkResultData(byte[] expectedData, BinaryData data) {
Assert.assertEquals(expectedData.length, data.getDataSize());
byte[] resultData = new byte[expectedData.length];
data.copyToArray(0, resultData, 0, expectedData.length);
Assert.assertArrayEquals(expectedData, resultData);
} */
}
@@ -0,0 +1,269 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.android.basic;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.auxiliary.binary_data.BinaryData;
import org.exbin.auxiliary.binary_data.EditableBinaryData;
import org.exbin.bined.CodeAreaCaretPosition;
import org.exbin.bined.CodeAreaTest;
import org.exbin.bined.EditOperation;
import org.exbin.bined.capability.CaretCapable;
import org.exbin.bined.capability.EditModeCapable;
import org.exbin.bined.android.CodeAreaCore;
import org.junit.Assert;
import org.junit.Test;
/**
* Tests for CodeArea component.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class CodeAreaCommandEditTest extends CodeAreaComponentTest {
public CodeAreaCommandEditTest() {
}
/*
@Test
public void testOverwriteCodeBegin() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
EditableBinaryData sampleData = CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES);
int dataSize = (int) sampleData.getDataSize();
int expectedSize = dataSize;
byte[] expectedData = new byte[expectedSize];
expectedData[0] = (byte) 0xa0;
sampleData.copyToArray(1, expectedData, 1, dataSize - 1);
codeArea.setContentData(sampleData);
emulateKeyTyped(codeArea, KeyEvent.VK_UNDEFINED, 'a');
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(1, caretPosition.getCodeOffset());
Assert.assertEquals(0, caretPosition.getDataPosition());
checkResultData(expectedData, codeArea.getContentData());
}
@Test
public void testInsertCodeBegin() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
EditableBinaryData sampleData = CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES);
int dataSize = (int) sampleData.getDataSize();
int expectedSize = dataSize + 1;
byte[] expectedData = new byte[expectedSize];
expectedData[0] = (byte) 0xa0;
sampleData.copyToArray(0, expectedData, 1, dataSize);
((EditModeCapable) codeArea).setEditOperation(EditOperation.INSERT);
codeArea.setContentData(sampleData);
emulateKeyTyped(codeArea, KeyEvent.VK_UNDEFINED, 'a');
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(1, caretPosition.getCodeOffset());
Assert.assertEquals(0, caretPosition.getDataPosition());
checkResultData(expectedData, codeArea.getContentData());
}
@Test
public void testOverwriteCodeEnd() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
EditableBinaryData sampleData = CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES);
int dataSize = (int) sampleData.getDataSize();
int expectedSize = dataSize + 1;
byte[] expectedData = new byte[expectedSize];
expectedData[256] = (byte) 0xa0;
sampleData.copyToArray(0, expectedData, 0, dataSize);
codeArea.setContentData(sampleData);
((CaretCapable) codeArea).setActiveCaretPosition(256);
emulateKeyTyped(codeArea, KeyEvent.VK_UNDEFINED, 'a');
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(1, caretPosition.getCodeOffset());
Assert.assertEquals(256, caretPosition.getDataPosition());
checkResultData(expectedData, codeArea.getContentData());
}
@Test
public void testInsertCodeEnd() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
EditableBinaryData sampleData = CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES);
int dataSize = (int) sampleData.getDataSize();
int expectedSize = dataSize + 1;
byte[] expectedData = new byte[expectedSize];
expectedData[256] = (byte) 0xa0;
sampleData.copyToArray(0, expectedData, 0, dataSize);
((EditModeCapable) codeArea).setEditOperation(EditOperation.INSERT);
codeArea.setContentData(sampleData);
((CaretCapable) codeArea).setActiveCaretPosition(256);
emulateKeyTyped(codeArea, KeyEvent.VK_UNDEFINED, 'a');
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(1, caretPosition.getCodeOffset());
Assert.assertEquals(256, caretPosition.getDataPosition());
checkResultData(expectedData, codeArea.getContentData());
}
@Test
public void testDeleteKeyEmpty() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
emulateKeyPressed(codeArea, KeyEvent.VK_UNDEFINED, KeyEvent.VK_DELETE);
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(0, caretPosition.getCodeOffset());
Assert.assertEquals(0, caretPosition.getDataPosition());
Assert.assertEquals(0, codeArea.getDataSize());
}
@Test
public void testDeleteKeyBegin() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
EditableBinaryData sampleData = CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES);
int dataSize = (int) sampleData.getDataSize();
int expectedSize = dataSize - 1;
byte[] expectedData = new byte[expectedSize];
sampleData.copyToArray(1, expectedData, 0, dataSize - 1);
codeArea.setContentData(sampleData);
emulateKeyPressed(codeArea, KeyEvent.VK_UNDEFINED, KeyEvent.VK_DELETE);
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(0, caretPosition.getCodeOffset());
Assert.assertEquals(0, caretPosition.getDataPosition());
checkResultData(expectedData, codeArea.getContentData());
}
@Test
public void testDeleteKeyEnd() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
EditableBinaryData sampleData = CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES);
codeArea.setContentData(sampleData);
int dataSize = (int) sampleData.getDataSize();
int expectedSize = dataSize;
byte[] expectedData = new byte[expectedSize];
sampleData.copyToArray(0, expectedData, 0, dataSize);
((CaretCapable) codeArea).setActiveCaretPosition(256);
emulateKeyPressed(codeArea, KeyEvent.VK_UNDEFINED, KeyEvent.VK_DELETE);
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(0, caretPosition.getCodeOffset());
Assert.assertEquals(256, caretPosition.getDataPosition());
checkResultData(expectedData, codeArea.getContentData());
}
@Test
public void testBackspaceKeyEmpty() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
emulateKeyPressed(codeArea, KeyEvent.VK_UNDEFINED, KeyEvent.VK_BACK_SPACE);
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(0, caretPosition.getCodeOffset());
Assert.assertEquals(0, caretPosition.getDataPosition());
Assert.assertEquals(0, codeArea.getDataSize());
}
@Test
public void testBackspaceKeyBegin() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
EditableBinaryData sampleData = CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES);
int dataSize = (int) sampleData.getDataSize();
int expectedSize = dataSize;
byte[] expectedData = new byte[expectedSize];
sampleData.copyToArray(0, expectedData, 0, dataSize);
codeArea.setContentData(sampleData);
emulateKeyPressed(codeArea, KeyEvent.VK_UNDEFINED, KeyEvent.VK_BACK_SPACE);
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(0, caretPosition.getCodeOffset());
Assert.assertEquals(0, caretPosition.getDataPosition());
checkResultData(expectedData, codeArea.getContentData());
}
@Test
public void testBackspaceKeyEnd() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
EditableBinaryData sampleData = CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES);
codeArea.setContentData(sampleData);
int dataSize = (int) sampleData.getDataSize();
int expectedSize = dataSize - 1;
byte[] expectedData = new byte[expectedSize];
sampleData.copyToArray(0, expectedData, 0, dataSize - 1);
((CaretCapable) codeArea).setActiveCaretPosition(256);
emulateKeyPressed(codeArea, KeyEvent.VK_UNDEFINED, KeyEvent.VK_BACK_SPACE);
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(0, caretPosition.getCodeOffset());
Assert.assertEquals(255, caretPosition.getDataPosition());
checkResultData(expectedData, codeArea.getContentData());
}
private static void emulateKeyTyped(Component component, int keyEvent, char keyChar) {
component.dispatchEvent(new KeyEvent(component, KeyEvent.KEY_TYPED, System.currentTimeMillis(), 0, keyEvent, keyChar));
}
private static void emulateKeyPressed(Component component, int keyCode, int keyEvent) {
component.dispatchEvent(new KeyEvent(component, KeyEvent.KEY_PRESSED, System.currentTimeMillis(), keyCode, keyEvent, ' '));
}
public static void checkResultData(byte[] expectedData, BinaryData data) {
Assert.assertEquals(expectedData.length, data.getDataSize());
byte[] resultData = new byte[expectedData.length];
data.copyToArray(0, resultData, 0, expectedData.length);
Assert.assertArrayEquals(expectedData, resultData);
} */
}
@@ -0,0 +1,245 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.android.basic;
import javax.annotation.ParametersAreNonnullByDefault;
import org.exbin.bined.CodeAreaCaretPosition;
import org.exbin.bined.CodeAreaTest;
import org.exbin.bined.capability.CaretCapable;
import org.exbin.bined.android.CodeAreaCore;
import org.junit.Assert;
import org.junit.Test;
/**
* Tests for CodeArea component.
*
* @author ExBin Project (https://exbin.org)
*/
@ParametersAreNonnullByDefault
public class CodeAreaCommandMovementTest extends CodeAreaComponentTest {
public CodeAreaCommandMovementTest() {
}
/*
@Test
public void testMoveBeginLeft() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
codeArea.setContentData(CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES));
emulateKeyPressed(codeArea, KeyEvent.VK_LEFT, KeyEvent.CHAR_UNDEFINED);
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(0, caretPosition.getCodeOffset());
Assert.assertEquals(0, caretPosition.getDataPosition());
}
@Test
public void testMoveMiddleLeft() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
codeArea.setContentData(CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES));
((CaretCapable) codeArea).setActiveCaretPosition(128);
emulateKeyPressed(codeArea, KeyEvent.VK_LEFT, KeyEvent.CHAR_UNDEFINED);
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(1, caretPosition.getCodeOffset());
Assert.assertEquals(127, caretPosition.getDataPosition());
}
@Test
public void testMoveEndLeft() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
codeArea.setContentData(CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES));
((CaretCapable) codeArea).setActiveCaretPosition(257);
emulateKeyPressed(codeArea, KeyEvent.VK_LEFT, KeyEvent.CHAR_UNDEFINED);
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(1, caretPosition.getCodeOffset());
Assert.assertEquals(256, caretPosition.getDataPosition());
}
@Test
public void testMoveBeginRight() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
codeArea.setContentData(CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES));
emulateKeyPressed(codeArea, KeyEvent.VK_RIGHT, KeyEvent.CHAR_UNDEFINED);
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(1, caretPosition.getCodeOffset());
Assert.assertEquals(0, caretPosition.getDataPosition());
}
@Test
public void testMoveBeginRightTwice() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
codeArea.setContentData(CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES));
emulateKeyPressed(codeArea, KeyEvent.VK_RIGHT, KeyEvent.CHAR_UNDEFINED);
emulateKeyPressed(codeArea, KeyEvent.VK_RIGHT, KeyEvent.CHAR_UNDEFINED);
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(0, caretPosition.getCodeOffset());
Assert.assertEquals(1, caretPosition.getDataPosition());
}
@Test
public void testMoveMiddleRight() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
codeArea.setContentData(CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES));
((CaretCapable) codeArea).setActiveCaretPosition(128);
emulateKeyPressed(codeArea, KeyEvent.VK_RIGHT, KeyEvent.CHAR_UNDEFINED);
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(1, caretPosition.getCodeOffset());
Assert.assertEquals(128, caretPosition.getDataPosition());
}
@Test
public void testMoveEndRight() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
codeArea.setContentData(CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES));
((CaretCapable) codeArea).setActiveCaretPosition(257);
emulateKeyPressed(codeArea, KeyEvent.VK_RIGHT, KeyEvent.CHAR_UNDEFINED);
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(0, caretPosition.getCodeOffset());
Assert.assertEquals(257, caretPosition.getDataPosition());
}
@Test
public void testMoveBeginUp() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
codeArea.setContentData(CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES));
emulateKeyPressed(codeArea, KeyEvent.VK_UP, KeyEvent.CHAR_UNDEFINED);
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(0, caretPosition.getCodeOffset());
Assert.assertEquals(0, caretPosition.getDataPosition());
}
@Test
public void testMoveMiddleUp() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
codeArea.setContentData(CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES));
((CaretCapable) codeArea).setActiveCaretPosition(128);
emulateKeyPressed(codeArea, KeyEvent.VK_UP, KeyEvent.CHAR_UNDEFINED);
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(0, caretPosition.getCodeOffset());
Assert.assertEquals(112, caretPosition.getDataPosition());
}
@Test
public void testMoveEndUp() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
codeArea.setContentData(CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES));
((CaretCapable) codeArea).setActiveCaretPosition(257);
emulateKeyPressed(codeArea, KeyEvent.VK_UP, KeyEvent.CHAR_UNDEFINED);
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(0, caretPosition.getCodeOffset());
Assert.assertEquals(241, caretPosition.getDataPosition());
}
@Test
public void testMoveBeginDown() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
codeArea.setContentData(CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES));
emulateKeyPressed(codeArea, KeyEvent.VK_DOWN, KeyEvent.CHAR_UNDEFINED);
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(0, caretPosition.getCodeOffset());
Assert.assertEquals(16, caretPosition.getDataPosition());
}
@Test
public void testMoveMiddleDown() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
codeArea.setContentData(CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES));
((CaretCapable) codeArea).setActiveCaretPosition(128);
emulateKeyPressed(codeArea, KeyEvent.VK_DOWN, KeyEvent.CHAR_UNDEFINED);
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(0, caretPosition.getCodeOffset());
Assert.assertEquals(144, caretPosition.getDataPosition());
}
@Test
public void testMoveEndDown() {
CodeAreaCore codeArea = createCodeArea();
JFrame frame = new JFrame();
frame.add(codeArea);
frame.setVisible(true);
codeArea.setContentData(CodeAreaTest.getSampleData(CodeAreaTest.SAMPLE_ALLBYTES));
((CaretCapable) codeArea).setActiveCaretPosition(257);
emulateKeyPressed(codeArea, KeyEvent.VK_DOWN, KeyEvent.CHAR_UNDEFINED);
CodeAreaCaretPosition caretPosition = ((CaretCapable) codeArea).getActiveCaretPosition();
Assert.assertEquals(0, caretPosition.getCodeOffset());
Assert.assertEquals(257, caretPosition.getDataPosition());
}
private void emulateKeyPressed(Component component, int keyEvent, char keyChar) {
component.dispatchEvent(new KeyEvent(component, KeyEvent.KEY_PRESSED, System.currentTimeMillis(), 0, keyEvent, keyChar));
component.dispatchEvent(new KeyEvent(component, KeyEvent.KEY_RELEASED, System.currentTimeMillis(), 0, keyEvent, keyChar));
} */
}
@@ -0,0 +1,43 @@
/*
* Copyright (C) ExBin Project
*
* 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
*
* https://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.exbin.bined.android.basic;
import android.content.Context;
import android.util.AttributeSet;
import androidx.test.core.app.ApplicationProvider;
import org.exbin.bined.android.CodeAreaCore;
import javax.annotation.Nonnull;
/**
* Tests for CodeArea component.
*
* @author ExBin Project (https://exbin.org)
*/
public class CodeAreaComponentTest {
public CodeAreaComponentTest() {
}
@Nonnull
public CodeAreaCore createCodeArea() {
Context applicationContext = ApplicationProvider.getApplicationContext();
AttributeSet attributeSet = null;
return new CodeArea(applicationContext, attributeSet);
}
}

Some files were not shown because too many files have changed in this diff Show More