Skip to content

Commit 69ca21c

Browse files
committed
add native file choser
1 parent 919fc4f commit 69ca21c

File tree

8 files changed

+388
-8
lines changed

8 files changed

+388
-8
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ docs.felnull.dev
66

77
### Example user template
88
FNJL2x64.dll
9+
FNJL3x64.dll
910
secring.gpg
1011
test
1112
# IntelliJ project files

src/main/java/dev/felnull/fnjl/BuildIn.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
public class BuildIn {
44
protected static final String VERSION = "1.24";
55

6-
protected static final int NATIVE_LIB_VERSION = 2;
6+
protected static final int NATIVE_LIB_VERSION = 3;
77
}
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
package dev.felnull.fnjl.jni;
2+
3+
import dev.felnull.fnjl.jni.windows.WindowsOpenFileName;
4+
import dev.felnull.fnjl.os.OSs;
5+
6+
import java.io.File;
7+
import java.nio.file.Path;
8+
9+
/**
10+
* MinecraftなどFileChooser、JFileChooserが利用できない環境でファイル選択画面を開くため
11+
*
12+
* @since 1.25
13+
*/
14+
public class NativeFileChooser {
15+
private String title;
16+
private String defExt;
17+
private Flag flag;
18+
private Path initialDirectory;
19+
private String initialName;
20+
private Filter[] filters;
21+
private int initialFilterIndex;
22+
23+
public NativeFileChooser() {
24+
}
25+
26+
public NativeFileChooser(String title) {
27+
this.title = title;
28+
}
29+
30+
public NativeFileChooser(String title, Flag flags, Filter... filters) {
31+
this.title = title;
32+
this.flag = flags;
33+
this.filters = filters;
34+
}
35+
36+
public static boolean isSupport() {
37+
return OSs.isWindows() && OSs.isX64();
38+
}
39+
40+
public void setInitialDirectory(Path initialDirectory) {
41+
this.initialDirectory = initialDirectory;
42+
}
43+
44+
public void setFilters(Filter[] filters) {
45+
this.filters = filters;
46+
}
47+
48+
public void setDefExt(String defExt) {
49+
this.defExt = defExt;
50+
}
51+
52+
public File[] openWindow() {
53+
return openWindow(0);
54+
}
55+
56+
public File[] openWindow(long hwndId) {
57+
if (!isSupport()) return null;
58+
if (OSs.isWindows() && OSs.isX64())
59+
return WindowsOpenFileName.open(this, hwndId);
60+
return null;
61+
}
62+
63+
public Filter[] getFilters() {
64+
return filters;
65+
}
66+
67+
public Flag getFlag() {
68+
return flag;
69+
}
70+
71+
public int getInitialFilterIndex() {
72+
return initialFilterIndex;
73+
}
74+
75+
public Path getInitialDirectory() {
76+
return initialDirectory;
77+
}
78+
79+
public String getDefExt() {
80+
return defExt;
81+
}
82+
83+
public String getTitle() {
84+
return title;
85+
}
86+
87+
public String getInitialName() {
88+
return initialName;
89+
}
90+
91+
//http://wisdom.sakura.ne.jp/system/winapi/common/common6.html
92+
//http://yamatyuu.net/computer/program/sdk/common_dialog/GetOpenFileName/index.html
93+
public static class Flag {
94+
private boolean multiSelect;
95+
private boolean explorer;
96+
private boolean creatEPrompt;
97+
private boolean fileMustExist;
98+
private boolean hideReadOnly;
99+
private boolean nodeReferenceLinks;
100+
private boolean readOnly;
101+
102+
/**
103+
* 複数選択可能かどうか
104+
*
105+
* @param allow 許可
106+
* @return this
107+
*/
108+
public Flag allowMultiSelect(boolean allow) {
109+
this.multiSelect = allow;
110+
return this;
111+
}
112+
113+
/**
114+
* Windowsで複数選択可能時に見た目が古いスタイルで表示しないかどうか
115+
*
116+
* @param explorer 表示させない
117+
* @return this
118+
*/
119+
public Flag explorer(boolean explorer) {
120+
this.explorer = explorer;
121+
return this;
122+
}
123+
124+
/**
125+
* 存在ファイルを選択しようとした際に作成確認を表示させる
126+
*
127+
* @param creatEPrompt 表示するかどうか
128+
* @return this
129+
*/
130+
public Flag creatEPrompt(boolean creatEPrompt) {
131+
this.creatEPrompt = creatEPrompt;
132+
return this;
133+
}
134+
135+
/**
136+
* 存在しないファイルを選択できないようにする
137+
*
138+
* @param fileMustExist 選択できないかどうか
139+
* @return this
140+
*/
141+
public Flag fileMustExist(boolean fileMustExist) {
142+
this.fileMustExist = fileMustExist;
143+
return this;
144+
}
145+
146+
/**
147+
* ファイルが読み取り専用かどうかを隠す
148+
*
149+
* @param hideReadOnly 隠すかどうか
150+
* @return this
151+
*/
152+
public Flag hideReadOnly(boolean hideReadOnly) {
153+
this.hideReadOnly = hideReadOnly;
154+
return this;
155+
}
156+
157+
/**
158+
* ショートカット(.link)を選択するとショートカット先に移動するかショートカット自体を取得するか
159+
*
160+
* @param nodeReferenceLinks どうか
161+
* @return this
162+
*/
163+
public Flag nodeReferenceLinks(boolean nodeReferenceLinks) {
164+
this.nodeReferenceLinks = nodeReferenceLinks;
165+
return this;
166+
}
167+
168+
/**
169+
* 読み取り専用を標準にするかどうか
170+
*
171+
* @param readOnly 読み取り専用かどうか
172+
* @return this
173+
*/
174+
public Flag readOnly(boolean readOnly) {
175+
this.readOnly = readOnly;
176+
return this;
177+
}
178+
179+
public boolean isCreatEPrompt() {
180+
return creatEPrompt;
181+
}
182+
183+
public boolean isExplorer() {
184+
return explorer;
185+
}
186+
187+
public boolean isFileMustExist() {
188+
return fileMustExist;
189+
}
190+
191+
public boolean isHideReadOnly() {
192+
return hideReadOnly;
193+
}
194+
195+
public boolean isMultiSelect() {
196+
return multiSelect;
197+
}
198+
199+
public boolean isNodeReferenceLinks() {
200+
return nodeReferenceLinks;
201+
}
202+
203+
public boolean isReadOnly() {
204+
return readOnly;
205+
}
206+
}
207+
208+
public static class Filter {
209+
private final String description;
210+
private final String[] extension;
211+
212+
public Filter(String description, String... extension) {
213+
this.description = description;
214+
this.extension = extension;
215+
}
216+
217+
public String toWindowsFilterText() {
218+
String exsStr;
219+
String winExsStr;
220+
if (extension == null || extension.length == 0) {
221+
exsStr = "*";
222+
winExsStr = "*.*";
223+
} else {
224+
String[] winExs = new String[extension.length];
225+
for (int i = 0; i < winExs.length; i++) {
226+
winExs[i] = "*." + extension[i];
227+
}
228+
exsStr = String.join("", winExs);
229+
winExsStr = String.join(";", winExs);
230+
}
231+
232+
return description + " (" + exsStr + ")" + "\0" + winExsStr + "\0";
233+
}
234+
}
235+
}

src/main/java/dev/felnull/fnjl/jni/windows/WindowsLibrary.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,22 @@ public static String getSystemFontFaceName(int num) {
5555
return WindowsNative.getSystemFontFaceName(num);
5656
}
5757

58+
/**
59+
* GetOpenFileNameを開く
60+
*
61+
* @param hwndId ウィンドウハンドル
62+
* @param title タイトル
63+
* @param initDir 初期ディレクトリ
64+
* @param initName 初期選択名
65+
* @param defExt 初期拡張子
66+
* @param filter フィルター
67+
* @param filterIndex 初期フィルター番号
68+
* @param flags フラグ
69+
* @return ファイルパス
70+
*/
71+
public static byte[] getOpenFileName(long hwndId, String title, String initDir, String initName, String defExt, String filter, int filterIndex, int flags) {
72+
if (!init())
73+
return null;
74+
return WindowsNative.getOpenFileName(hwndId, title, initDir, initName, defExt, filter, filterIndex, flags);
75+
}
5876
}

src/main/java/dev/felnull/fnjl/jni/windows/WindowsNative.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@ public class WindowsNative {
55
protected static native String getSpecialFolderPath(int num);
66

77
protected static native String getSystemFontFaceName(int num);
8+
9+
protected static native byte[] getOpenFileName(long hwndId, String title, String initDir, String initName, String defExt, String filter, int filterIndex, int flags);
810
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package dev.felnull.fnjl.jni.windows;
2+
3+
import dev.felnull.fnjl.jni.NativeFileChooser;
4+
5+
import java.io.File;
6+
import java.nio.file.Path;
7+
import java.nio.file.Paths;
8+
9+
public class WindowsOpenFileName {
10+
public static File[] open(NativeFileChooser fc, long hwnd) {
11+
String title = fc.getTitle() == null ? "" : fc.getTitle();
12+
String initDir = fc.getInitialDirectory() == null ? "" : fc.getInitialDirectory().toAbsolutePath().toString();
13+
String initName = fc.getInitialName() == null ? "" : fc.getInitialName();
14+
String defExt = fc.getDefExt() == null ? "" : fc.getDefExt();
15+
int flgNum = createFlagNum(fc.getFlag());
16+
byte[] op = WindowsLibrary.getOpenFileName(hwnd, title, initDir, initName, defExt, createFilterString(fc.getFilters()), fc.getInitialFilterIndex(), flgNum);
17+
if (op == null) return null;
18+
String str = new String(op);
19+
if (!str.endsWith("\0\0")) return null;
20+
str = str.substring(0, str.length() - 2);
21+
String[] files = str.split(getFlgStr(flgNum));
22+
if (files.length == 0) return new File[]{};
23+
if (files.length == 1) return new File[]{new File(files[0])};
24+
Path dir = Paths.get(files[0]);
25+
File[] Ffiles = new File[files.length - 1];
26+
for (int i = 0; i < Ffiles.length; i++) {
27+
Ffiles[i] = dir.resolve(files[1 + i]).toFile();
28+
}
29+
return Ffiles;
30+
}
31+
32+
private static String getFlgStr(int flg) {
33+
boolean am = (flg & Flag.OFN_ALLOWMULTISELECT.getNum()) != 0;
34+
boolean ex = (flg & Flag.OFN_EXPLORER.getNum()) != 0;
35+
if (am && !ex)
36+
return " ";
37+
return "\0";
38+
}
39+
40+
private static String createFilterString(NativeFileChooser.Filter[] filters) {
41+
if (filters == null) return "";
42+
StringBuilder sb = new StringBuilder();
43+
for (NativeFileChooser.Filter filter : filters) {
44+
sb.append(filter.toWindowsFilterText());
45+
}
46+
return sb.toString();
47+
}
48+
49+
private static int createFlagNum(NativeFileChooser.Flag flags) {
50+
if (flags == null) return 0;
51+
int num = 0;
52+
if (flags.isCreatEPrompt())
53+
num |= Flag.OFN_CREATEPROMPT.getNum();
54+
if (flags.isExplorer())
55+
num |= Flag.OFN_EXPLORER.getNum();
56+
if (flags.isFileMustExist())
57+
num |= Flag.OFN_FILEMUSTEXIST.getNum();
58+
if (flags.isHideReadOnly())
59+
num |= Flag.OFN_HIDEREADONLY.getNum();
60+
if (flags.isMultiSelect())
61+
num |= Flag.OFN_ALLOWMULTISELECT.getNum();
62+
if (flags.isNodeReferenceLinks())
63+
num |= Flag.OFN_NOREADONLYRETURN.getNum();
64+
if (flags.isReadOnly())
65+
num |= Flag.OFN_READONLY.getNum();
66+
return num;
67+
}
68+
69+
public static enum Flag {
70+
OFN_READONLY(0x1),
71+
OFN_OVERWRITEPROMPT(0x2),
72+
OFN_HIDEREADONLY(0x4),
73+
OFN_NOCHANGEDIR(0x8),
74+
OFN_SHOWHELP(0x10),
75+
OFN_ENABLEHOOK(0x20),
76+
OFN_ENABLETEMPLATE(0x40),
77+
OFN_ENABLETEMPLATEHANDLE(0x80),
78+
OFN_NOVALIDATE(0x100),
79+
OFN_ALLOWMULTISELECT(0x200),
80+
OFN_EXTENSIONDIFFERENT(0x400),
81+
OFN_PATHMUSTEXIST(0x800),
82+
OFN_FILEMUSTEXIST(0x1000),
83+
OFN_CREATEPROMPT(0x2000),
84+
OFN_SHAREAWARE(0x4000),
85+
OFN_NOREADONLYRETURN(0x8000),
86+
OFN_NOTESTFILECREATE(0x10000),
87+
OFN_NONETWORKBUTTON(0x20000),
88+
OFN_NOLONGNAMES(0x40000),
89+
OFN_EXPLORER(0x80000),
90+
OFN_NODEREFERENCELINKS(0x100000),
91+
OFN_LONGNAMES(0x200000),
92+
OFN_ENABLEINCLUDENOTIFY(0x400000),
93+
OFN_ENABLESIZING(0x800000),
94+
OFN_DONTADDTORECENT(0x2000000),
95+
OFN_FORCESHOWHIDDEN(0x10000000),
96+
OFN_EX_NOPLACESBAR(0x1),
97+
OFN_SHAREFALLTHROUGH(2),
98+
OFN_SHARENOWARN(1),
99+
OFN_SHAREWARN(0);
100+
private int num;
101+
102+
private Flag(int num) {
103+
this.num = num;
104+
}
105+
106+
public int getNum() {
107+
return num;
108+
}
109+
}
110+
}
9.63 KB
Binary file not shown.

0 commit comments

Comments
 (0)