Merge pull request #348 from Kelimion/master

Add win32.get_cwd for current working directory and convenience functions for open + save dialogs.
This commit is contained in:
Jeroen van Rijn
2019-03-09 12:51:14 +01:00
committed by GitHub
4 changed files with 123 additions and 2 deletions

View File

@@ -329,6 +329,10 @@ is_space :: proc(r: rune) -> bool {
return false;
}
is_null :: proc(r: rune) -> bool {
return r == 0x0000;
}
index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int {
for r, i in s {
if p(r) == truth {
@@ -476,3 +480,16 @@ trim_right_space :: proc(s: string) -> string {
trim_space :: proc(s: string) -> string {
return trim_right_space(trim_left_space(s));
}
trim_left_null :: proc(s: string) -> string {
return trim_left_proc(s, is_null);
}
trim_right_null :: proc(s: string) -> string {
return trim_right_proc(s, is_null);
}
trim_null :: proc(s: string) -> string {
return trim_right_null(trim_left_null(s));
}

View File

@@ -2,6 +2,7 @@
package win32
foreign import "system:comdlg32.lib"
import "core:strings"
OFN_Hook_Proc :: #type proc "stdcall" (hdlg: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Uint_Ptr;
@@ -61,11 +62,82 @@ Open_File_Name_W :: struct {
foreign comdlg32 {
@(link_name="GetOpenFileNameA") get_open_file_name_a :: proc(arg1: ^Open_File_Name_A) -> Bool ---
@(link_name="GetOpenFileNameW") get_open_file_name_w :: proc(arg1: ^Open_File_Name_W) -> Bool ---
@(link_name="GetSaveFileNameA") get_save_file_name_a :: proc(arg1: ^Open_File_Name_A) -> Bool ---
@(link_name="GetSaveFileNameW") get_save_file_name_w :: proc(arg1: ^Open_File_Name_W) -> Bool ---
@(link_name="CommDlgExtendedError") comm_dlg_extended_error :: proc() -> u32 ---
}
OFN_ALLOWMULTISELECT :: 0x00000200;
OPEN_TITLE :: "Select file to open";
OPEN_FLAGS :: u32(OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST);
OPEN_FLAGS_MULTI :: u32(OPEN_FLAGS | OFN_ALLOWMULTISELECT | OFN_EXPLORER);
SAVE_TITLE :: "Select file to save";
SAVE_FLAGS :: u32(OFN_OVERWRITEPROMPT | OFN_EXPLORER);
SAVE_EXT :: "txt";
OpenSaveMode :: enum{
Open = 0,
Save = 1,
}
_open_file_dialog :: proc(title: string, dir: string, filters: []string, default_filter: u32, flags: u32, default_ext: string, mode: OpenSaveMode, allocator := context.temp_allocator) -> (path: string, ok: bool) {
ok = true;
file_buf := make([]u16, MAX_PATH_WIDE, allocator);
// Filters need to be passed as a pair of strings (title, filter)
filter_len := u32(len(filters));
if filter_len % 2 != 0 do return "", false;
default_filter = clamp(default_filter, 1, filter_len / 2);
filter := strings.join(filters, "\u0000", context.temp_allocator);
filter = strings.concatenate({filter, "\u0000"}, context.temp_allocator);
filter_w := utf8_to_wstring(filter, context.temp_allocator);
ofn := Open_File_Name_W{
struct_size = size_of(Open_File_Name_W),
file = Wstring(&file_buf[0]),
max_file = MAX_PATH_WIDE,
title = utf8_to_wstring(title, context.temp_allocator),
filter = filter_w,
initial_dir = utf8_to_wstring(dir, context.temp_allocator),
filter_index = u32(default_filter),
def_ext = utf8_to_wstring(default_ext, context.temp_allocator),
flags = u32(flags),
};
if mode == OpenSaveMode.Open {
ok = bool(get_open_file_name_w(&ofn));
} else if mode == OpenSaveMode.Save {
ok = bool(get_save_file_name_w(&ofn));
} else do ok = false;
if !ok {
delete(file_buf);
return "", false;
}
file_name := ucs2_to_utf8(file_buf[:], allocator);
path = strings.trim_right_null(file_name);
return;
}
select_file_to_open :: proc(title := OPEN_TITLE, dir := ".", filters := []string{"All Files", "*.*"}, default_filter := u32(1), flags := OPEN_FLAGS, allocator := context.temp_allocator) -> (path: string, ok: bool) {
path, ok = _open_file_dialog(title, dir, filters, default_filter, flags, "", OpenSaveMode.Open, allocator);
return;
}
select_file_to_save :: proc(title := SAVE_TITLE, dir := ".", filters := []string{"All Files", "*.*"}, default_filter := u32(1), flags := SAVE_FLAGS, default_ext := SAVE_EXT, allocator := context.temp_allocator) -> (path: string, ok: bool) {
path, ok = _open_file_dialog(title, dir, filters, default_filter, flags, default_ext, OpenSaveMode.Save, allocator);
return;
}
// TODO: Implement convenience function for select_file_to_open with ALLOW_MULTI_SELECT that takes
// it output of the form "path\u0000\file1u\0000file2" and turns it into []string with the path + file pre-concatenated for you.
OFN_ALLOWMULTISELECT :: 0x00000200; // NOTE(Jeroen): Without OFN_EXPLORER it uses the Win3 dialog.
OFN_CREATEPROMPT :: 0x00002000;
OFN_DONTADDTORECENT :: 0x02000000;
OFN_ENABLEHOOK :: 0x00000020;
@@ -91,3 +163,18 @@ OFN_PATHMUSTEXIST :: 0x00000800;
OFN_READONLY :: 0x00000001;
OFN_SHAREAWARE :: 0x00004000;
OFN_SHOWHELP :: 0x00000010;
CDERR_DIALOGFAILURE :: 0x0000FFFF;
CDERR_GENERALCODES :: 0x00000000;
CDERR_STRUCTSIZE :: 0x00000001;
CDERR_INITIALIZATION :: 0x00000002;
CDERR_NOTEMPLATE :: 0x00000003;
CDERR_NOHINSTANCE :: 0x00000004;
CDERR_LOADSTRFAILURE :: 0x00000005;
CDERR_FINDRESFAILURE :: 0x00000006;
CDERR_LOADRESFAILURE :: 0x00000007;
CDERR_LOCKRESFAILURE :: 0x00000008;
CDERR_MEMALLOCFAILURE :: 0x00000009;
CDERR_MEMLOCKFAILURE :: 0x0000000A;
CDERR_NOHOOK :: 0x0000000B;
CDERR_REGISTERMSGFAIL :: 0x0000000C;

14
core/sys/win32/crt.odin Normal file
View File

@@ -0,0 +1,14 @@
package win32
import "core:strings";
foreign {
@(link_name="_wgetcwd") _get_cwd_wide :: proc(buffer: Wstring, buf_len: int) -> ^Wstring ---
}
get_cwd :: proc(allocator := context.temp_allocator) -> string {
buffer := make([]u16, MAX_PATH_WIDE, allocator);
_get_cwd_wide(Wstring(&buffer[0]), MAX_PATH_WIDE);
file := ucs2_to_utf8(buffer[:], allocator);
return strings.trim_right_null(file);
}

View File

@@ -108,6 +108,8 @@ File_Attribute_Data :: struct {
file_size_low: u32,
}
// NOTE(Jeroen): The widechar version might want at least the 32k MAX_PATH_WIDE
// https://docs.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-findfirstfilew#parameters
Find_Data_W :: struct{
file_attributes: u32,
creation_time: Filetime,
@@ -798,6 +800,7 @@ is_key_down :: inline proc(key: Key_Code) -> bool { return get_async_key_state(i
MAX_PATH :: 0x00000104;
MAX_PATH_WIDE :: 0x8000;
HANDLE_FLAG_INHERIT :: 1;
HANDLE_FLAG_PROTECT_FROM_CLOSE :: 2;