feat(gui): notify user if code has non-displayable character with current font (PR #2490)
* feat(gui): notify user, if code has non-displayable character in current font (fix #621) * fix(gui): improve check showing undisplayed chars on current font * fix code style --------- Co-authored-by: Skylot <118523+skylot@users.noreply.github.com>
This commit is contained in:
@@ -3,6 +3,7 @@ package jadx.gui.settings.ui.font;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import org.drjekyll.fontchooser.FontChooser;
|
||||
import org.drjekyll.fontchooser.panes.FamilyPane;
|
||||
@@ -23,6 +24,15 @@ public class FontChooserHack {
|
||||
}
|
||||
}
|
||||
|
||||
public static void hidePreview(FontChooser fontChooser) {
|
||||
try {
|
||||
JPanel previewPanel = (JPanel) getPrivateField(fontChooser, "previewPanel");
|
||||
previewPanel.setVisible(false);
|
||||
} catch (Throwable e) {
|
||||
LOG.debug("Failed to hide preview panel", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Object getPrivateField(Object obj, String fieldName) throws NoSuchFieldException, IllegalAccessException {
|
||||
Field f = obj.getClass().getDeclaredField(fieldName);
|
||||
f.setAccessible(true);
|
||||
|
||||
@@ -85,6 +85,9 @@ import jadx.api.plugins.events.types.ReloadProject;
|
||||
import jadx.api.plugins.events.types.ReloadSettingsWindow;
|
||||
import jadx.api.plugins.utils.CommonFileUtils;
|
||||
import jadx.core.Jadx;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.export.TemplateFile;
|
||||
import jadx.core.utils.ListUtils;
|
||||
import jadx.core.utils.StringUtils;
|
||||
@@ -160,6 +163,7 @@ import jadx.gui.ui.tab.TabsController;
|
||||
import jadx.gui.ui.tab.dnd.TabDndController;
|
||||
import jadx.gui.ui.treenodes.StartPageNode;
|
||||
import jadx.gui.ui.treenodes.SummaryNode;
|
||||
import jadx.gui.ui.treenodes.UndisplayedStringsNode;
|
||||
import jadx.gui.update.JadxUpdate;
|
||||
import jadx.gui.utils.CacheObject;
|
||||
import jadx.gui.utils.DesktopEntryUtils;
|
||||
@@ -240,6 +244,7 @@ public class MainWindow extends JFrame {
|
||||
private final List<Consumer<JRoot>> treeUpdateListener = new ArrayList<>();
|
||||
private boolean loaded;
|
||||
private boolean settingsOpen = false;
|
||||
private boolean showUndisplayedCharsDialog;
|
||||
|
||||
private final ShortcutsController shortcutsController;
|
||||
private JadxMenuBar menuBar;
|
||||
@@ -506,6 +511,7 @@ public class MainWindow extends JFrame {
|
||||
// start new project
|
||||
project = new JadxProject(this);
|
||||
project.setFilePaths(paths);
|
||||
showUndisplayedCharsDialog = false;
|
||||
loadFiles(onFinish);
|
||||
}
|
||||
|
||||
@@ -654,6 +660,7 @@ public class MainWindow extends JFrame {
|
||||
runInitialBackgroundJobs();
|
||||
notifyLoadListeners(true);
|
||||
update();
|
||||
checkIfCodeHasNonPrintableChars();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1794,6 +1801,60 @@ public class MainWindow extends JFrame {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkIfCodeHasNonPrintableChars() {
|
||||
if (getSettings().isRenamePrintable() || getSettings().isDeobfuscationOn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (showUndisplayedCharsDialog) {
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder nonDisplayString = new StringBuilder();
|
||||
|
||||
List<ClassNode> classes = wrapper.getRootNode().getClasses(true);
|
||||
Font font = getSettings().getFont();
|
||||
boolean hasNonDisplayable = false;
|
||||
|
||||
for (ClassNode cls : classes) {
|
||||
String className = cls.getRawName();
|
||||
if (!FontUtils.canStringBeDisplayed(className, font)) {
|
||||
hasNonDisplayable = true;
|
||||
nonDisplayString.append(className);
|
||||
nonDisplayString.append("\n");
|
||||
}
|
||||
|
||||
for (MethodNode methodNode : cls.getMethods()) {
|
||||
String methodName = methodNode.getName();
|
||||
if (!FontUtils.canStringBeDisplayed(methodName, font)) {
|
||||
hasNonDisplayable = true;
|
||||
nonDisplayString.append(methodName);
|
||||
nonDisplayString.append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
for (FieldNode fieldNode : cls.getFields()) {
|
||||
String fieldName = fieldNode.getName();
|
||||
if (!FontUtils.canStringBeDisplayed(fieldName, font)) {
|
||||
hasNonDisplayable = true;
|
||||
nonDisplayString.append(fieldName);
|
||||
nonDisplayString.append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasNonDisplayable) {
|
||||
showUndisplayedCharsDialog = true;
|
||||
int dialogResult = JOptionPane.showConfirmDialog(this,
|
||||
NLS.str("msg.non_displayable_chars", font.getFontName()),
|
||||
NLS.str("msg.warning_title"),
|
||||
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
|
||||
if (dialogResult == JOptionPane.YES_OPTION) {
|
||||
tabsController.selectTab(new UndisplayedStringsNode(nonDisplayString.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public RenameMappingsGui getRenameMappings() {
|
||||
return renameMappings;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
package jadx.gui.ui.panel;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Font;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import org.drjekyll.fontchooser.FontChooser;
|
||||
import org.drjekyll.fontchooser.model.FontSelectionModel;
|
||||
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
|
||||
import org.fife.ui.rtextarea.RTextScrollPane;
|
||||
|
||||
import jadx.gui.settings.JadxSettings;
|
||||
import jadx.gui.settings.LineNumbersMode;
|
||||
import jadx.gui.settings.ui.font.FontChooserHack;
|
||||
import jadx.gui.ui.codearea.AbstractCodeArea;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.ui.treenodes.UndisplayedStringsNode;
|
||||
|
||||
public class UndisplayedStringsPanel extends ContentPanel {
|
||||
private static final long serialVersionUID = 695370628262996993L;
|
||||
|
||||
private final RSyntaxTextArea textPane;
|
||||
private final RTextScrollPane codeScrollPane;
|
||||
|
||||
public UndisplayedStringsPanel(TabbedPane panel, UndisplayedStringsNode node) {
|
||||
super(panel, node);
|
||||
setLayout(new BorderLayout());
|
||||
textPane = AbstractCodeArea.getDefaultArea(panel.getMainWindow());
|
||||
|
||||
JadxSettings settings = getSettings();
|
||||
Font selectedFont = settings.getFont();
|
||||
|
||||
FontChooser fontChooser = new FontChooser();
|
||||
fontChooser.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
fontChooser.setSelectedFont(selectedFont);
|
||||
FontChooserHack.hidePreview(fontChooser);
|
||||
|
||||
fontChooser.addChangeListener(event -> {
|
||||
FontSelectionModel model = (FontSelectionModel) event.getSource();
|
||||
settings.setFont(model.getSelectedFont());
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
getMainWindow().loadSettings();
|
||||
});
|
||||
});
|
||||
|
||||
codeScrollPane = new RTextScrollPane(textPane);
|
||||
|
||||
add(codeScrollPane, BorderLayout.CENTER);
|
||||
add(fontChooser, BorderLayout.EAST);
|
||||
|
||||
applySettings();
|
||||
showData(node.makeDescString());
|
||||
}
|
||||
|
||||
private void applySettings() {
|
||||
codeScrollPane.setLineNumbersEnabled(getSettings().getLineNumbersMode() != LineNumbersMode.DISABLE);
|
||||
codeScrollPane.getGutter().setLineNumberFont(getSettings().getFont());
|
||||
textPane.setFont(getSettings().getFont());
|
||||
}
|
||||
|
||||
private void showData(String data) {
|
||||
textPane.setText(data);
|
||||
textPane.setCaretPosition(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadSettings() {
|
||||
applySettings();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package jadx.gui.ui.treenodes;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import jadx.gui.treemodel.JClass;
|
||||
import jadx.gui.treemodel.JNode;
|
||||
import jadx.gui.ui.panel.ContentPanel;
|
||||
import jadx.gui.ui.panel.UndisplayedStringsPanel;
|
||||
import jadx.gui.ui.tab.TabbedPane;
|
||||
import jadx.gui.utils.Icons;
|
||||
import jadx.gui.utils.NLS;
|
||||
|
||||
public class UndisplayedStringsNode extends JNode {
|
||||
private static final long serialVersionUID = 2005158949697898302L;
|
||||
|
||||
private final String undisplayedStings;
|
||||
|
||||
public UndisplayedStringsNode(String undisplayedStings) {
|
||||
this.undisplayedStings = undisplayedStings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentPanel getContentPanel(TabbedPane tabbedPane) {
|
||||
return new UndisplayedStringsPanel(tabbedPane, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String makeString() {
|
||||
return NLS.str("msg.non_displayable_chars.title");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Icon getIcon() {
|
||||
return Icons.FONT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JClass getJParent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String makeDescString() {
|
||||
return undisplayedStings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsQuickTabs() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -87,6 +87,21 @@ public class FontUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean canStringBeDisplayed(String str, Font font) {
|
||||
if (str == null || str.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
int offset = 0;
|
||||
while (offset < str.length()) {
|
||||
int codePoint = str.codePointAt(offset);
|
||||
if (!font.canDisplay(codePoint)) {
|
||||
return false;
|
||||
}
|
||||
offset += Character.charCount(codePoint);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private FontUtils() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,4 +46,6 @@ public class Icons {
|
||||
public static final ImageIcon CHECK = UiUtils.openSvgIcon("ui/checkConstraint");
|
||||
public static final ImageIcon FORMAT = UiUtils.openSvgIcon("ui/toolWindowMessages");
|
||||
public static final ImageIcon RESET = UiUtils.openSvgIcon("ui/reset");
|
||||
|
||||
public static final ImageIcon FONT = UiUtils.openSvgIcon("nodes/fontFile");
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ search_dialog.copy=alles kopieren
|
||||
usage_dialog.title=Verwendungssuche
|
||||
usage_dialog.label=Verwendungen von:
|
||||
|
||||
#usage_dialog_plus.title=Verwendungssuche
|
||||
#usage_dialog_plus.title=Usage tree search
|
||||
usage_dialog_plus.jump_to=Zur aktuellen Position springen
|
||||
usage_dialog_plus.copy_path=Verwendungspfad kopieren
|
||||
usage_dialog_plus.search_complete=Suche abgeschlossen
|
||||
@@ -343,6 +343,8 @@ msg.project_error_title=Fehler
|
||||
msg.project_error=Projekt konnte nicht geladen werden
|
||||
msg.cmd_select_class_error=Klasse\n%s auswählen nicht möglich\nSie existiert nicht.
|
||||
msg.cant_add_comment=Kann hier keinen Kommentar hinzufügen
|
||||
#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font "%s". Show chars?
|
||||
#msg.non_displayable_chars.title=Undisplayed Strings
|
||||
|
||||
#methods_dialog.title=Select methods
|
||||
|
||||
|
||||
@@ -343,6 +343,8 @@ msg.project_error_title=Error
|
||||
msg.project_error=Project could not be loaded
|
||||
msg.cmd_select_class_error=Failed to select the class\n%s\nThe class does not exist.
|
||||
msg.cant_add_comment=Can't add comment here
|
||||
msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font "%s". Show chars?
|
||||
msg.non_displayable_chars.title=Undisplayed Strings
|
||||
|
||||
methods_dialog.title=Select methods
|
||||
|
||||
|
||||
@@ -343,6 +343,8 @@ msg.language_changed=El nuevo idioma se mostrará la próxima vez que la aplicac
|
||||
#msg.project_error=Project could not be loaded
|
||||
#msg.cmd_select_class_error=Failed to select the class\n%s\nThe class does not exist.
|
||||
#msg.cant_add_comment=Can't add comment here
|
||||
#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font "%s". Show chars?
|
||||
#msg.non_displayable_chars.title=Undisplayed Strings
|
||||
|
||||
#methods_dialog.title=Select methods
|
||||
|
||||
|
||||
@@ -343,6 +343,8 @@ msg.project_error_title=Kesalahan
|
||||
msg.project_error=Proyek tidak dapat dimuat
|
||||
msg.cmd_select_class_error=Gagal memilih kelas\n%s\nKelas tidak ada.
|
||||
msg.cant_add_comment=Tidak dapat menambahkan komentar di sini
|
||||
#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font "%s". Show chars?
|
||||
#msg.non_displayable_chars.title=Undisplayed Strings
|
||||
|
||||
#methods_dialog.title=Select methods
|
||||
|
||||
|
||||
@@ -343,6 +343,8 @@ msg.project_error_title=오류
|
||||
msg.project_error=프로젝트를 로드 할 수 없습니다.
|
||||
msg.cmd_select_class_error=클래스를 선택하지 못했습니다.\n%s\n클래스가 없습니다.
|
||||
msg.cant_add_comment=여기에 주석을 추가할수 없음
|
||||
#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font "%s". Show chars?
|
||||
#msg.non_displayable_chars.title=Undisplayed Strings
|
||||
|
||||
#methods_dialog.title=Select methods
|
||||
|
||||
|
||||
@@ -343,6 +343,8 @@ msg.project_error_title=Erro
|
||||
msg.project_error=Projeto não pôde ser carregado
|
||||
msg.cmd_select_class_error=Falha ao selecionar classe\n%s\nA classe não existe.
|
||||
msg.cant_add_comment=Não é possível adicionar comentários aqui
|
||||
#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font "%s". Show chars?
|
||||
#msg.non_displayable_chars.title=Undisplayed Strings
|
||||
|
||||
#methods_dialog.title=Select methods
|
||||
|
||||
|
||||
@@ -343,6 +343,8 @@ msg.project_error_title=Ошибка
|
||||
msg.project_error=Проект не может быть загружен
|
||||
msg.cmd_select_class_error=Ошибка выбора класса\n%s\nЭтот класс не существует.
|
||||
msg.cant_add_comment=Невозможно добавить комментарий сюда
|
||||
#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font "%s". Show chars?
|
||||
#msg.non_displayable_chars.title=Undisplayed Strings
|
||||
|
||||
#methods_dialog.title=Select methods
|
||||
|
||||
|
||||
@@ -343,6 +343,8 @@ msg.project_error_title=错误
|
||||
msg.project_error=项目无法加载
|
||||
msg.cmd_select_class_error=无法选择类\n%s\n该类不存在。
|
||||
msg.cant_add_comment=无法在此添加注释
|
||||
#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font "%s". Show chars?
|
||||
#msg.non_displayable_chars.title=Undisplayed Strings
|
||||
|
||||
#methods_dialog.title=Select methods
|
||||
|
||||
|
||||
@@ -343,6 +343,8 @@ msg.project_error_title=錯誤
|
||||
msg.project_error=無法載入專案
|
||||
msg.cmd_select_class_error=無法選擇類別\n%s\n類別不存在。
|
||||
msg.cant_add_comment=無法在此新增註解
|
||||
#msg.non_displayable_chars=Some characters were found in code are not displayable by currently used font "%s". Show chars?
|
||||
#msg.non_displayable_chars.title=Undisplayed Strings
|
||||
|
||||
#methods_dialog.title=Select methods
|
||||
|
||||
|
||||
Reference in New Issue
Block a user