diff --git a/redo/dialog.go b/redo/dialog.go new file mode 100644 index 0000000..f6a041f --- /dev/null +++ b/redo/dialog.go @@ -0,0 +1,10 @@ +// 18 august 2014 + +package ui + +// OpenFile opens a dialog box that asks the user to choose a file. +// It returns the selected filename, or an empty string if no file was chosen. +// All events stop while OpenFile is executing. (TODO move to doc.go) +func OpenFile() (filename string) { + return openFile() +} diff --git a/redo/dialog_windows.c b/redo/dialog_windows.c new file mode 100644 index 0000000..d5b69f7 --- /dev/null +++ b/redo/dialog_windows.c @@ -0,0 +1,40 @@ +// 18 august 2014 + +#include "winapi_windows.h" +#include "_cgo_export.h" + +// this should be reasonable +#define NFILENAME 4096 + +WCHAR *openFile(void) +{ + OPENFILENAMEW ofn; + DWORD err; + WCHAR *filenameBuffer; + + // freed on the Go side + filenameBuffer = (WCHAR *) malloc((NFILENAME + 1) * sizeof (WCHAR)); + if (filenameBuffer == NULL) + xpanic("memory exhausted in OpenFile()", GetLastError()); + filenameBuffer[0] = L'\0'; // required by GetOpenFileName() to indicate no previous filename + ZeroMemory(&ofn, sizeof (OPENFILENAMEW)); + ofn.lStructSize = sizeof (OPENFILENAMEW); + ofn.hwndOwner = NULL; + ofn.hInstance = hInstance; + ofn.lpstrFilter = NULL; // no filters + ofn.lpstrFile = filenameBuffer; + ofn.nMaxFile = NFILENAME + 1; // TODO include + 1? + ofn.lpstrInitialDir = NULL; // let system decide + ofn.lpstrTitle = NULL; // let system decide + // TODO OFN_SHAREAWARE? + // TODO remove OFN_NODEREFERENCELINKS? or does no filters ensure that anyway? + ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_FORCESHOWHIDDEN | OFN_HIDEREADONLY | OFN_LONGNAMES | OFN_NOCHANGEDIR | OFN_NODEREFERENCELINKS | OFN_NOTESTFILECREATE | OFN_PATHMUSTEXIST; + if (GetOpenFileNameW(&ofn) == FALSE) { + // TODO stringify + err = CommDlgExtendedError(); + if (err == 0) // user cancelled + return NULL; + xpanic("error running open file dialog", GetLastError()); + } + return filenameBuffer; +} diff --git a/redo/dialog_windows.go b/redo/dialog_windows.go new file mode 100644 index 0000000..0346f56 --- /dev/null +++ b/redo/dialog_windows.go @@ -0,0 +1,33 @@ +// 18 august 2014 + +package ui + +import ( + "syscall" + "unsafe" + "reflect" +) + +// #include "winapi_windows.h" +import "C" + +// TODO move to common_windows.go +func wstrToString(wstr *C.WCHAR) string { + n := C.wcslen((*C.wchar_t)(unsafe.Pointer(wstr))) + xbuf := &reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(wstr)), + Len: int(n + 1), + Cap: int(n + 1), + } + buf := (*[]uint16)(unsafe.Pointer(xbuf)) + return syscall.UTF16ToString(*buf) +} + +func openFile() string { + name := C.openFile() + if name == nil { + return "" + } + defer C.free(unsafe.Pointer(name)) + return wstrToString(name) +} diff --git a/redo/uitask_windows.go b/redo/uitask_windows.go index 54b29cb..c3c1884 100644 --- a/redo/uitask_windows.go +++ b/redo/uitask_windows.go @@ -9,7 +9,7 @@ import ( ) // #cgo CFLAGS: --std=c99 -// #cgo LDFLAGS: -luser32 -lkernel32 -lgdi32 -luxtheme -lmsimg32 +// #cgo LDFLAGS: -luser32 -lkernel32 -lgdi32 -luxtheme -lmsimg32 -lcomdlg32 // #include "winapi_windows.h" import "C" diff --git a/redo/winapi_windows.h b/redo/winapi_windows.h index 18db8db..e888c99 100644 --- a/redo/winapi_windows.h +++ b/redo/winapi_windows.h @@ -133,4 +133,7 @@ enum { extern HIMAGELIST checkboxImageList; extern void makeCheckboxImageList(HWND); +// dialog_windows.c +extern WCHAR *openFile(void); + #endif diff --git a/redo/zz_test.go b/redo/zz_test.go index 601d0ec..2477de0 100644 --- a/redo/zz_test.go +++ b/redo/zz_test.go @@ -38,6 +38,8 @@ type testwin struct { festart Button felabel Label festop Button + openbtn Button + fnlabel Label icons []icon il ImageList icontbl Table @@ -89,7 +91,19 @@ func (tw *testwin) addfe() { tw.fe = nil } }) - tw.festack = NewVerticalStack(tw.festart, tw.felabel, tw.festop) + tw.openbtn = NewButton("Open") + tw.openbtn.OnClicked(func() { + fn := OpenFile() + if fn == "" { + fn = "" + } + tw.fnlabel.SetText(fn) + }) + tw.fnlabel = NewStandaloneLabel("") + tw.festack = NewVerticalStack(tw.festart, tw.felabel, tw.festop, + Space(), + tw.openbtn, tw.fnlabel) + tw.festack.SetStretchy(3) tw.t.Append("Foreign Events", tw.festack) }