forked from lxn/walk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
commondialogs.go
140 lines (112 loc) · 3.34 KB
/
commondialogs.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
// Copyright 2010 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package walk
import (
"fmt"
"syscall"
"unsafe"
)
import (
"github.com/lxn/win"
)
type FileDialog struct {
Title string
FilePath string
InitialDirPath string
Filter string
FilterIndex int
}
func (dlg *FileDialog) show(owner Form, fun func(ofn *win.OPENFILENAME) bool) (accepted bool, err error) {
ofn := new(win.OPENFILENAME)
ofn.LStructSize = uint32(unsafe.Sizeof(*ofn))
if owner != nil {
ofn.HwndOwner = owner.Handle()
}
filter := make([]uint16, len(dlg.Filter)+2)
copy(filter, syscall.StringToUTF16(dlg.Filter))
// Replace '|' with the expected '\0'.
for i, c := range filter {
if byte(c) == '|' {
filter[i] = uint16(0)
}
}
ofn.LpstrFilter = &filter[0]
ofn.NFilterIndex = uint32(dlg.FilterIndex)
filePath := make([]uint16, 1024)
copy(filePath, syscall.StringToUTF16(dlg.FilePath))
ofn.LpstrFile = &filePath[0]
ofn.NMaxFile = uint32(len(filePath))
ofn.LpstrInitialDir = syscall.StringToUTF16Ptr(dlg.InitialDirPath)
ofn.LpstrTitle = syscall.StringToUTF16Ptr(dlg.Title)
ofn.Flags = win.OFN_FILEMUSTEXIST
if !fun(ofn) {
errno := win.CommDlgExtendedError()
if errno != 0 {
err = newError(fmt.Sprintf("Error %d", errno))
}
return
}
dlg.FilePath = syscall.UTF16ToString(filePath)
accepted = true
return
}
func (dlg *FileDialog) ShowOpen(owner Form) (accepted bool, err error) {
return dlg.show(owner, win.GetOpenFileName)
}
func (dlg *FileDialog) ShowSave(owner Form) (accepted bool, err error) {
return dlg.show(owner, win.GetSaveFileName)
}
func (dlg *FileDialog) ShowBrowseFolder(owner Form) (accepted bool, err error) {
// Calling OleInitialize (or similar) is required for BIF_NEWDIALOGSTYLE.
if hr := win.OleInitialize(); hr != win.S_OK && hr != win.S_FALSE {
return false, newError(fmt.Sprint("OleInitialize Error: ", hr))
}
defer win.OleUninitialize()
pathFromPIDL := func(pidl uintptr) (string, error) {
var path [win.MAX_PATH]uint16
if !win.SHGetPathFromIDList(pidl, &path[0]) {
return "", newError("SHGetPathFromIDList failed")
}
return syscall.UTF16ToString(path[:]), nil
}
// We use this callback to disable the OK button in case of "invalid"
// selections.
callback := func(hwnd win.HWND, msg uint32, lp, wp uintptr) uintptr {
const BFFM_SELCHANGED = 2
if msg == BFFM_SELCHANGED {
_, err := pathFromPIDL(lp)
var enabled uintptr
if err == nil {
enabled = 1
}
const BFFM_ENABLEOK = win.WM_USER + 101
win.SendMessage(hwnd, BFFM_ENABLEOK, 0, enabled)
}
return 0
}
var ownerHwnd win.HWND
if owner != nil {
ownerHwnd = owner.Handle()
}
// We need to put the initial path into a buffer of at least MAX_LENGTH
// length, or we may get random crashes.
var buf [win.MAX_PATH]uint16
copy(buf[:], syscall.StringToUTF16(dlg.InitialDirPath))
const BIF_NEWDIALOGSTYLE = 0x00000040
bi := win.BROWSEINFO{
HwndOwner: ownerHwnd,
PszDisplayName: &buf[0],
LpszTitle: syscall.StringToUTF16Ptr(dlg.Title),
UlFlags: BIF_NEWDIALOGSTYLE,
Lpfn: syscall.NewCallback(callback),
}
pidl := win.SHBrowseForFolder(&bi)
if pidl == 0 {
return false, nil
}
defer win.CoTaskMemFree(pidl)
dlg.FilePath, err = pathFromPIDL(pidl)
accepted = dlg.FilePath != ""
return
}