Arbeit an Vorschau-Bereich
This commit is contained in:
24
pom.xml
24
pom.xml
@@ -4,7 +4,8 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>BelegScanner</groupId>
|
||||
<artifactId>BelegScanner</artifactId>
|
||||
<version>0.0.3</version>
|
||||
<version>0.0.4</version>
|
||||
|
||||
<name>BelegScanner</name>
|
||||
<packaging>jar</packaging>
|
||||
<description>BelegScanner</description>
|
||||
@@ -17,6 +18,7 @@
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<icepdf.version>6.2.2</icepdf.version>
|
||||
</properties>
|
||||
|
||||
<licenses>
|
||||
@@ -64,6 +66,26 @@
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.2.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.icepdf.os/icepdf-core -->
|
||||
<dependency>
|
||||
<groupId>org.icepdf.os</groupId>
|
||||
<artifactId>icepdf-core</artifactId>
|
||||
<version>${icepdf.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>javax.media</groupId>
|
||||
<artifactId>jai_core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.icepdf.os/icepdf-viewer -->
|
||||
<dependency>
|
||||
<groupId>org.icepdf.os</groupId>
|
||||
<artifactId>icepdf-viewer</artifactId>
|
||||
<version>${icepdf.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</logger>
|
||||
|
||||
<logger name="de.srsoftware.belegscanner.gui.DocTable" level="debug" additivity="false">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</logger>
|
||||
|
||||
<root level="error">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</root>
|
||||
|
||||
@@ -3,5 +3,6 @@ package de.srsoftware.belegscanner;
|
||||
public class Constants {
|
||||
|
||||
public static final String APPLICATION_NAME = "BelegScanner";
|
||||
public static final String FILE_BROWSER = "thunar";
|
||||
|
||||
}
|
||||
|
||||
153
src/main/java/de/srsoftware/belegscanner/gui/DocTable.java
Normal file
153
src/main/java/de/srsoftware/belegscanner/gui/DocTable.java
Normal file
@@ -0,0 +1,153 @@
|
||||
package de.srsoftware.belegscanner.gui;
|
||||
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.GridLayout;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import de.srsoftware.belegscanner.Constants;
|
||||
|
||||
public class DocTable extends JPanel{
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DocTable.class);
|
||||
private static FilenameFilter PAGES = (dir,name) -> name.toLowerCase().endsWith("pdf") && name.toLowerCase().startsWith("page");
|
||||
private static FilenameFilter PDFS = (dir,name) -> name.toLowerCase().endsWith("pdf");
|
||||
|
||||
public interface PreviewListener{
|
||||
public void show(String filename);
|
||||
}
|
||||
|
||||
private class Row{
|
||||
|
||||
private JLabel status;
|
||||
|
||||
public Row(String path) {
|
||||
rows.put(path, this);
|
||||
add(new JLabel(path));
|
||||
add(status = new JLabel("neu"));
|
||||
|
||||
JPanel buttons = new JPanel();
|
||||
buttons.setLayout(new FlowLayout());
|
||||
|
||||
JButton folderButton = new JButton("Ordner öffnen");
|
||||
folderButton.addActionListener(ev -> openFolder(path));
|
||||
buttons.add(folderButton);
|
||||
|
||||
JButton joinButton = new JButton("Zusammenfügen");
|
||||
joinButton.addActionListener(ev -> joinDocs(path));
|
||||
buttons.add(joinButton);
|
||||
|
||||
JButton preview = new JButton("Vorschau");
|
||||
preview.addActionListener(ev -> preview(path));
|
||||
buttons.add(preview);
|
||||
|
||||
add(buttons);
|
||||
}
|
||||
|
||||
public Row status(String status) {
|
||||
this.status.setText(status);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1073955198529023744L;
|
||||
|
||||
private final HashMap<String,Row> rows = new HashMap<>();
|
||||
private HashSet<PreviewListener> previewListeners;
|
||||
|
||||
public DocTable() {
|
||||
setLayout(new GridLayout(0, 3));
|
||||
add(new JLabel("Ordner"));
|
||||
add(new JLabel("Status"));
|
||||
add(new JLabel("Aktionen"));
|
||||
previewListeners = new HashSet<>();
|
||||
}
|
||||
|
||||
public DocTable addPreviewListener(PreviewListener listener) {
|
||||
previewListeners.add(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void preview(String path) {
|
||||
LOG.debug("preview({})",path);
|
||||
File folder = new File(path);
|
||||
if (!folder.exists()) return;
|
||||
List<String> pdfs = Arrays.asList(folder.list(PDFS));
|
||||
LOG.debug("PDFs: {}",pdfs);
|
||||
if (!pdfs.isEmpty()) previewListeners.forEach(l -> l.show(Path.of(path,pdfs.get(0)).toString()));
|
||||
}
|
||||
|
||||
public void joinDocs(String path) {
|
||||
|
||||
LOG.debug("joinFiles({})",path);
|
||||
File folder = new File(path);
|
||||
if (!folder.exists()) return;
|
||||
|
||||
List<String> pdfs = Arrays.asList(folder.list(PAGES));
|
||||
Collections.sort(pdfs);
|
||||
|
||||
Vector<String> cmd = new Vector<>();
|
||||
cmd.add("pdftk");
|
||||
cmd.addAll(pdfs);
|
||||
cmd.add("cat");
|
||||
cmd.add("output");
|
||||
cmd.add(folder.getName()+".pdf");
|
||||
LOG.debug("executing {}",cmd);
|
||||
ProcessBuilder builder = new ProcessBuilder(cmd);
|
||||
builder.directory(folder);
|
||||
try {
|
||||
Process process = builder.start();
|
||||
setState(path,"Verbinde PDFs…");
|
||||
int errorCode = process.waitFor();
|
||||
if (errorCode != 0) {
|
||||
LOG.error("error code: {} for {}",errorCode,cmd);
|
||||
} else LOG.debug("error code: {}",errorCode);
|
||||
} catch (InterruptedException | IOException e) {
|
||||
LOG.error("{} terminated: ",builder,e);
|
||||
}
|
||||
for (String page : pdfs) Path.of(path,page).toFile().delete();
|
||||
setState(path,"Zusammengefügt.");
|
||||
}
|
||||
|
||||
public void openFolder(String path) {
|
||||
Process process = null;
|
||||
try {
|
||||
process = new ProcessBuilder(List.of("killall",Constants.FILE_BROWSER)).start();
|
||||
process.waitFor();
|
||||
Thread.sleep(100);
|
||||
} catch (IOException | InterruptedException e) {
|
||||
LOG.error("{} terminated: ",process,e);
|
||||
}
|
||||
|
||||
try {
|
||||
process = new ProcessBuilder(List.of(Constants.FILE_BROWSER,path)).start();
|
||||
} catch (IOException e) {
|
||||
LOG.error("{} terminated: ",process,e);
|
||||
}
|
||||
}
|
||||
|
||||
public void add(String path) {
|
||||
if (!rows.containsKey(path)) new Row(path).status("neu");
|
||||
}
|
||||
|
||||
public void setState(String path, String state) {
|
||||
Row row = rows.get(path);
|
||||
if (row != null) row.status(state);
|
||||
}
|
||||
}
|
||||
@@ -5,15 +5,11 @@ import java.awt.Dimension;
|
||||
import java.awt.Point;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Vector;
|
||||
import java.util.regex.Matcher;
|
||||
@@ -35,7 +31,14 @@ public class MainFrame extends JFrame {
|
||||
|
||||
private static final String HOME = System.getProperty("user.home");
|
||||
private static final Pattern MARKEN = Pattern.compile("\\$([a-zA-Z]+)");
|
||||
private static FilenameFilter PAGES = (dir,name) -> name.toLowerCase().endsWith("pdf") && name.toLowerCase().startsWith("page");
|
||||
|
||||
private static final String APP_WIDTH = "app.main.dimenstion.w";
|
||||
|
||||
private static final String APP_HEIGHT = "app.main.dimenstion.h";
|
||||
|
||||
private static final String APP_X = "app.main.position.x";
|
||||
|
||||
private static final String APP_Y = "app.main.position.y";
|
||||
|
||||
private StatusBar statusBar;
|
||||
private Toolbar toolbar;
|
||||
@@ -57,30 +60,38 @@ public class MainFrame extends JFrame {
|
||||
private int resoultion = 150;
|
||||
private String mode = "Color";
|
||||
|
||||
private DocTable docTable;
|
||||
|
||||
private Preview preview;
|
||||
|
||||
public MainFrame(Configuration config) {
|
||||
super("BelegScanner");
|
||||
this.config = config;
|
||||
int width = config.getOrCreate("app.main.dimenstion.w",800);
|
||||
int height = config.getOrCreate("app.main.dimenstion.h",600);
|
||||
int width = config.getOrCreate(APP_WIDTH,800);
|
||||
int height = config.getOrCreate(APP_HEIGHT,600);
|
||||
|
||||
BorderLayout layout = new BorderLayout();
|
||||
setLayout(layout);
|
||||
|
||||
toolbar = new Toolbar(config);
|
||||
statusBar = new StatusBar();
|
||||
docTable = new DocTable();
|
||||
preview = new Preview();
|
||||
|
||||
add(toolbar,BorderLayout.EAST);
|
||||
add(statusBar,BorderLayout.SOUTH);
|
||||
add(docTable,BorderLayout.NORTH);
|
||||
add(preview,BorderLayout.CENTER);
|
||||
toolbar.addCategoryListener(this::setCategory)
|
||||
.addDateListener(this::setDate)
|
||||
.addFieldListener(this::setField)
|
||||
.addFolderListener(this::openFolder)
|
||||
.addJoinListener(this::join)
|
||||
.addPathListener(this::setPath)
|
||||
.addScanListener(this::scan);
|
||||
.addScanListener(this::scan);
|
||||
|
||||
int x = config.getOrCreate("app.main.position.x", 20);
|
||||
int y = config.getOrCreate("app.main.position.y", 20);
|
||||
docTable.addPreviewListener(preview::display);
|
||||
|
||||
int x = config.getOrCreate(APP_X, 20);
|
||||
int y = config.getOrCreate(APP_Y, 20);
|
||||
setLocation(new Point(x, y));
|
||||
|
||||
setPreferredSize(new Dimension(width,height));
|
||||
@@ -94,66 +105,6 @@ public class MainFrame extends JFrame {
|
||||
while (matches.find()) marks.add(matches.group(1));
|
||||
toolbar.addFieldsFor(marks);
|
||||
};
|
||||
|
||||
private void join(ActionEvent ev) {
|
||||
new Thread(() -> joinFiles(patchedPath)).start();
|
||||
}
|
||||
|
||||
private void joinFiles(String path) {
|
||||
LOG.debug("joinFiles({})",path);
|
||||
File folder = new File(path);
|
||||
if (!folder.exists()) return;
|
||||
|
||||
List<String> pdfs = Arrays.asList(folder.list(PAGES));
|
||||
Collections.sort(pdfs);
|
||||
|
||||
Vector<String> cmd = new Vector<>();
|
||||
cmd.add("pdftk");
|
||||
cmd.addAll(pdfs);
|
||||
cmd.add("cat");
|
||||
cmd.add("output");
|
||||
cmd.add(folder.getName()+".pdf");
|
||||
LOG.debug("executing {}",cmd);
|
||||
ProcessBuilder builder = new ProcessBuilder(cmd);
|
||||
builder.directory(folder);
|
||||
try {
|
||||
Process process = builder.start();
|
||||
statusBar.setAction("Verbinde PDFs…");
|
||||
int errorCode = process.waitFor();
|
||||
if (errorCode != 0) {
|
||||
LOG.error("error code: {} for {}",errorCode,cmd);
|
||||
} else LOG.debug("error code: {}",errorCode);
|
||||
} catch (InterruptedException | IOException e) {
|
||||
LOG.error("{} terminated: ",builder,e);
|
||||
}
|
||||
for (String page : pdfs) Path.of(path,page).toFile().delete();
|
||||
statusBar.setAction("Bereit.");
|
||||
}
|
||||
|
||||
private void openFolder(ActionEvent ev) {
|
||||
new Thread(() -> openFolder(patchedPath)).start();
|
||||
}
|
||||
|
||||
private void openFolder(String path) {
|
||||
File folder = new File(path);
|
||||
if (!folder.exists()) return;
|
||||
|
||||
ProcessBuilder builder = new ProcessBuilder(List.of("killall","thunar"));
|
||||
builder.directory(folder);
|
||||
try {
|
||||
builder.start().waitFor();
|
||||
} catch (IOException | InterruptedException e) {
|
||||
LOG.error("{} terminated: ",builder,e);
|
||||
}
|
||||
|
||||
builder = new ProcessBuilder("thunar");
|
||||
builder.directory(folder);
|
||||
try {
|
||||
builder.start();
|
||||
} catch (IOException e) {
|
||||
LOG.error("{} terminated: ",builder,e);
|
||||
}
|
||||
}
|
||||
|
||||
private void performScan(String path) {
|
||||
LOG.debug("performScan({})",path);
|
||||
@@ -164,6 +115,8 @@ public class MainFrame extends JFrame {
|
||||
folder.mkdirs();
|
||||
}
|
||||
|
||||
docTable.add(path);
|
||||
|
||||
long timestamp = new Date().getTime();
|
||||
String raw = timestamp+".jpg";
|
||||
|
||||
@@ -185,7 +138,7 @@ public class MainFrame extends JFrame {
|
||||
builder.directory(folder);
|
||||
try {
|
||||
Process process = builder.start();
|
||||
statusBar.setAction("Scanne nach "+path+"…");
|
||||
docTable.setState(path,"Scanne…");
|
||||
int errorCode = process.waitFor();
|
||||
if (errorCode != 0) {
|
||||
LOG.error("error code: {} for {}",errorCode,cmd);
|
||||
@@ -204,7 +157,7 @@ public class MainFrame extends JFrame {
|
||||
builder.directory(folder);
|
||||
try {
|
||||
Process process = builder.start();
|
||||
statusBar.setAction("Kovertiere zu PDF…");
|
||||
docTable.setState(path,"Kovertiere zu PDF…");
|
||||
toolbar.readyToScan(true);
|
||||
int errorCode = process.waitFor();
|
||||
if (errorCode != 0) {
|
||||
@@ -231,7 +184,7 @@ public class MainFrame extends JFrame {
|
||||
builder.directory(folder);
|
||||
try {
|
||||
Process process = builder.start();
|
||||
statusBar.setAction("Texterkennung…");
|
||||
docTable.setState(path,"Texterkennung…");
|
||||
toolbar.readyToScan(true);
|
||||
int errorCode = process.waitFor();
|
||||
if (errorCode != 0) {
|
||||
@@ -242,7 +195,7 @@ public class MainFrame extends JFrame {
|
||||
LOG.error("{} terminated: ",builder,e);
|
||||
}
|
||||
Path.of(path, pdf).toFile().delete();
|
||||
statusBar.setAction("Bereit.");
|
||||
docTable.setState(path,"Texterkennung beendet.");
|
||||
}
|
||||
|
||||
private void scan(ActionEvent ev) {
|
||||
@@ -287,8 +240,12 @@ public class MainFrame extends JFrame {
|
||||
}
|
||||
config.set(prefix+"path",path);
|
||||
Point loc = getLocation();
|
||||
config.set("app.main.position.x", loc.x);
|
||||
config.set("app.main.position.y", loc.y);
|
||||
config.set(APP_X, loc.x);
|
||||
config.set(APP_Y, loc.y);
|
||||
Dimension dim = getSize();
|
||||
config.set(APP_WIDTH, dim.width);
|
||||
config.set(APP_HEIGHT, dim.height);
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
|
||||
47
src/main/java/de/srsoftware/belegscanner/gui/Preview.java
Normal file
47
src/main/java/de/srsoftware/belegscanner/gui/Preview.java
Normal file
@@ -0,0 +1,47 @@
|
||||
package de.srsoftware.belegscanner.gui;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Container;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import org.icepdf.ri.common.ComponentKeyBinding;
|
||||
import org.icepdf.ri.common.SwingController;
|
||||
import org.icepdf.ri.common.SwingViewBuilder;
|
||||
|
||||
public class Preview extends JPanel{
|
||||
|
||||
private static final long serialVersionUID = -4534327261273493367L;
|
||||
private SwingController controller;
|
||||
|
||||
public Preview() {
|
||||
// Instance the controller
|
||||
controller = new SwingController();
|
||||
// We created the SwingViewFactory configured with the controller
|
||||
SwingViewBuilder factory = new SwingViewBuilder(controller);
|
||||
// We use the factory to build a preconfigured JPanel
|
||||
// with a full and active viewer user interface.
|
||||
JPanel viewerComponentPanel = factory.buildViewerPanel();
|
||||
// viewerComponentPanel.setPreferredSize(new Dimension(400, 243));
|
||||
// viewerComponentPanel.setMaximumSize(new Dimension(400, 243));
|
||||
// We add keyboard command
|
||||
ComponentKeyBinding.install(controller, viewerComponentPanel);
|
||||
// add interactive mouse link annotation support via callback
|
||||
controller.getDocumentViewController().setAnnotationCallback(
|
||||
new org.icepdf.ri.common.MyAnnotationCallback(
|
||||
controller.getDocumentViewController()));
|
||||
|
||||
Container reportViewerContainer = this;
|
||||
// We add the component to visualize the report
|
||||
reportViewerContainer.add(viewerComponentPanel, BorderLayout.CENTER);
|
||||
reportViewerContainer.invalidate();
|
||||
// We open the generated document
|
||||
|
||||
}
|
||||
|
||||
public void display(String filename) {
|
||||
System.err.println("display "+filename);
|
||||
controller.openDocument(filename);;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,11 +14,11 @@ public class StatusBar extends JPanel {
|
||||
private static final long serialVersionUID = 8102800846089594705L;
|
||||
private JLabel path;
|
||||
|
||||
private JLabel action;
|
||||
//private JLabel action;
|
||||
|
||||
public StatusBar() {
|
||||
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
|
||||
add(action = new JLabel("Bereit."));
|
||||
//add(action = new JLabel("Bereit."));
|
||||
add(path = new JLabel("Kein Pfad gewält."));
|
||||
}
|
||||
|
||||
@@ -27,9 +27,4 @@ public class StatusBar extends JPanel {
|
||||
this.path.setText(path);
|
||||
return this;
|
||||
}
|
||||
|
||||
public StatusBar setAction(String action) {
|
||||
this.action.setText(action);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public class Toolbar extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = -5834326573752788233L;
|
||||
|
||||
private static final int OFFSET = 4;
|
||||
private static final int OFFSET = 2;
|
||||
|
||||
private Vector<Object> categories = new Vector<>();
|
||||
private Vector<Object> paths = new Vector<>();
|
||||
@@ -58,8 +58,6 @@ public class Toolbar extends JPanel {
|
||||
private HashSet<DateListener> dateListeners = new HashSet<>();
|
||||
private HashSet<FieldListener> fieldListeners = new HashSet<>();
|
||||
private HashSet<PathListener> pathListeners = new HashSet<>();
|
||||
private HashSet<ActionListener> folderListeners = new HashSet<>();
|
||||
private HashSet<ActionListener> joinListeners = new HashSet<>();
|
||||
private HashSet<ActionListener> scanListeners = new HashSet<>();
|
||||
|
||||
private DateChooser date;
|
||||
@@ -70,9 +68,6 @@ public class Toolbar extends JPanel {
|
||||
|
||||
private JButton scanButton;
|
||||
|
||||
private JButton joinButton;
|
||||
private JButton folderButton;
|
||||
|
||||
public Toolbar(Configuration config) {
|
||||
this.config = config;
|
||||
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
|
||||
@@ -80,8 +75,6 @@ public class Toolbar extends JPanel {
|
||||
add(input("Kategorie",categoryPicker(config)));
|
||||
add(input("Pfad",pathPicker = pathPicker()));
|
||||
add(scanButton());
|
||||
add(joinButton());
|
||||
add(folderButton());
|
||||
add(externButton());
|
||||
}
|
||||
|
||||
@@ -116,17 +109,6 @@ public class Toolbar extends JPanel {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Toolbar addFolderListener(ActionListener listener) {
|
||||
folderListeners.add(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Toolbar addJoinListener(ActionListener listener) {
|
||||
joinListeners.add(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public Toolbar addPathListener(PathListener listener){
|
||||
pathListeners.add(listener);
|
||||
return this;
|
||||
@@ -157,16 +139,6 @@ public class Toolbar extends JPanel {
|
||||
return btn;
|
||||
}
|
||||
|
||||
private Component folderButton() {
|
||||
folderButton = new JButton("Ordner öffnen!");
|
||||
folderButton.addActionListener(this::folderPressed);
|
||||
return folderButton;
|
||||
}
|
||||
|
||||
private void folderPressed(ActionEvent evt) {
|
||||
folderListeners.forEach(l->l.actionPerformed(evt));
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date.getSelectedDate();
|
||||
}
|
||||
@@ -189,17 +161,6 @@ public class Toolbar extends JPanel {
|
||||
return panel;
|
||||
}
|
||||
|
||||
private Component joinButton() {
|
||||
joinButton = new JButton("zusammenfügen!");
|
||||
joinButton.addActionListener(this::joinPressed);
|
||||
return joinButton;
|
||||
}
|
||||
|
||||
private void joinPressed(ActionEvent evt) {
|
||||
joinListeners.forEach(l->l.actionPerformed(evt));
|
||||
}
|
||||
|
||||
|
||||
private SelectComboBox pathPicker() {
|
||||
JSONObject cats = config.getOrCreate("app.categories",new JSONObject());
|
||||
for (String catName : cats.keySet()) {
|
||||
|
||||
Reference in New Issue
Block a user