--- a/WinWorkstation.st Tue Nov 06 11:37:13 2018 +0100
+++ b/WinWorkstation.st Thu Nov 08 13:57:33 2018 +0100
@@ -1,3 +1,5 @@
+"{ Encoding: utf8 }"
+
"
COPYRIGHT (c) 1996 by Claus Gittinger
All Rights Reserved
@@ -9,6 +11,8 @@
other person. No title to or ownership of the software is
hereby transferred.
"
+'From Smalltalk/X, Version:7.1.0.0 on 08-11-2018 at 11:26:08' !
+
"{ Package: 'stx:libview' }"
"{ NameSpace: Smalltalk }"
@@ -46,6 +50,13 @@
privateIn:WinWorkstation
!
+Object subclass:#NativeFileDialogCreationData
+ instanceVariableNames:'dataAddress sharedMemoryMapFileHandleAddress stxFileDialogProcess'
+ classVariableNames:''
+ poolDictionaries:''
+ privateIn:WinWorkstation
+!
+
Object subclass:#NativeFileDialogReturnData
instanceVariableNames:'targetFileOrDirectory multiSelectBaseNames selectedFilterIndex'
classVariableNames:''
@@ -951,16 +962,16 @@
} registerHotKeyInfo;
typedef struct fileDialogDataStruct {
- wchar_t filename[1000 * MAX_PATH]; // big buffer to support multiselect
- wchar_t directory[MAX_PATH];
- wchar_t title[MAX_PATH];
- HWND owningWindow;
- wchar_t filter[10 * MAX_PATH]; // there could be many filters
- int filterIndex;
+ WCHAR filename[1000 * MAX_PATH]; // big buffer to support multiselect
+ WCHAR directory[MAX_PATH];
+ WCHAR title[MAX_PATH];
+ DWORD owningWindow; // HWND as DWORD to be 32 and 64 bit compatible
+ WCHAR filter[10 * MAX_PATH]; // there could be many filters
+ DWORD filterIndex;
BOOL trueForSave;
BOOL trueForMultiSelect;
BOOL trueForPromptOverwrite;
-
+
DWORD fileDialogThreadId;
BOOL fileDialogDidReturn;
BOOL didPerformCroppedBaseNameHack; /* the open dialog cropps the default basename https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/644328 */
@@ -981,7 +992,7 @@
ofn.lpstrTitle = pFdd->title;
ofn.lpstrFilter = pFdd->filter;
ofn.nFilterIndex = pFdd->filterIndex;
- ofn.hwndOwner = pFdd->owningWindow;
+ ofn.hwndOwner = (HWND)pFdd->owningWindow;
if (pFdd->trueForSave) {
if (pFdd->trueForPromptOverwrite) {
@@ -2172,7 +2183,7 @@
}
goto again;
}
- /* fail evtl. später ändern und in st verzögert aufrufen
+ /* fail evtl. später ändern und in st verzögert aufrufen
*/
console_fprintf(stderr, "WinWorkstation [info]: UnregisterClass %s failed.\n",(char*)ev->ev_arg1);
}
@@ -2706,7 +2717,11 @@
// case WM_SIZE:
// case WM_MOVE:
case WM_WINDOWPOSCHANGED:
- Sleep(1);
+ // we think this happens only to the parent window
+ // if we do it for all we have a significant performance lost
+ if (GetParent(hWnd == NULL)) {
+ Sleep(1);
+ }
break;
default: break;
@@ -16642,6 +16657,485 @@
!WinWorkstation methodsFor:'native file dialog'!
+_nativeFileDialogWithTitle:dialogTitleArg
+ defaultFilename:defaultFilenameArg
+ owningTopView:owningTopView
+ filter:filterStringOrArrayOfPairs
+ filterIndex:filterIndexArg
+ trueForSave:trueForSave
+ trueForMultiSelect:trueForMultiSelect
+ trueForPromptOverwrite:trueForPromptOverwrite
+
+ "opens a native windows file dialog,
+ supports unicode 16 strings (like windows does it for filenames)
+ returns nil or the full path to the selected file
+ see example at the end of the method code"
+
+ |dialogTitle defaultBaseName defaultDirectory owningViewId windowGroup filterString filterArrayOrPairs
+ null filterStringParts
+ nativeFileDialogCreationData returnValue
+ nativeFileDialogReturnData targetFileOrDirectory multiSelectBaseNames selectedSuffixInfo selectedSuffix
+ needsSlash|
+
+ dialogTitleArg notEmptyOrNil ifTrue:[
+ dialogTitle := dialogTitleArg asUnicode16String.
+ ].
+
+ defaultFilenameArg notNil ifTrue:[
+ |defaultFilename|
+
+ defaultFilename := defaultFilenameArg asFilename.
+ defaultFilename isDirectory ifTrue:[
+ defaultDirectory := defaultFilename.
+ ] ifFalse:[
+ defaultDirectory := defaultFilename directory.
+ defaultBaseName := defaultFilename baseName asUnicode16String.
+ ].
+
+ defaultDirectory := defaultDirectory pathName asUnicode16String.
+ ].
+
+ owningTopView notNil ifTrue:[
+ owningViewId := owningTopView id.
+ windowGroup := owningTopView windowGroup.
+ ].
+
+ filterStringOrArrayOfPairs notEmptyOrNil ifTrue:[
+ null := (String new:1)
+ at:1 put:Character null;
+ yourself.
+
+ filterStringOrArrayOfPairs notNil ifTrue:[
+ filterStringOrArrayOfPairs isString ifTrue:[
+ filterString := filterStringOrArrayOfPairs.
+ filterArrayOrPairs := OrderedCollection new.
+ filterStringParts := filterStringOrArrayOfPairs subStrings:null.
+
+ 1
+ to:filterStringParts size
+ by:2
+ do:[:index |
+ filterArrayOrPairs
+ add:(Array
+ with:(filterStringParts at:index)
+ with:(filterStringParts at:index + 1)).
+ ].
+ ] ifFalse:[
+ filterArrayOrPairs := filterStringOrArrayOfPairs.
+ filterString := ((filterStringOrArrayOfPairs
+ collect:[:eachPair |
+ eachPair first, null, eachPair second, null
+ ])
+ asStringWith:''), null.
+ ].
+
+ filterString := filterString asUnicode16String.
+ ].
+ ].
+
+ [
+ nativeFileDialogCreationData := self
+ _primNativeFileDialogWithTitle:dialogTitle
+ defaultBaseName:defaultBaseName
+ defaultDirectory:defaultDirectory
+ owningViewId:owningViewId
+ filterString:filterString
+ filterIndex:filterIndexArg ? 1
+ trueForSave:trueForSave
+ trueForMultiSelect:trueForMultiSelect ? false
+ trueForPromptOverwrite:trueForPromptOverwrite ? true.
+
+ trueForSave ifFalse:[
+ "hack for display bug (cropped default basename)
+ https://software.intel.com/en-us/forums/intel-visual-fortran-compiler-for-windows/topic/644328"
+ [
+ self primTryCroppedBaseNameHackByDataAddress:nativeFileDialogCreationData dataAddress
+ ] whileFalse:[
+ "/ loop until the dialog has opened
+ Delay waitForMilliseconds:10.
+ ].
+ ].
+
+ [
+ windowGroup notNil ifTrue:[
+ windowGroup sensor eventSemaphore waitWithTimeout:100 milliseconds.
+ "sr: we can process all events (also redraws),
+ because the operating system handles the modal,
+ user events are blocked by operating system"
+ windowGroup processEvents.
+ ] ifFalse:[
+ Delay waitForMilliseconds:100.
+ ].
+
+ returnValue := self _primGetNativeFileDialogResultByCreationData:nativeFileDialogCreationData.
+ ] doWhile:[
+ returnValue isNil
+ ].
+ ] ensure:[
+ (nativeFileDialogCreationData notNil
+ "/ if #returnValue is not nil, we already finished and did the memory free
+ and:[returnValue isNil]) ifTrue:[
+ Display _primCloseNativeFileDialogByByCreationData:nativeFileDialogCreationData.
+ ].
+ ].
+
+ returnValue isEmptyOrNil ifTrue:[
+ "dialog was aborted"
+ ^ nil
+ ].
+
+ nativeFileDialogReturnData := returnValue.
+ targetFileOrDirectory := nativeFileDialogReturnData targetFileOrDirectory.
+ multiSelectBaseNames := nativeFileDialogReturnData multiSelectBaseNames.
+ multiSelectBaseNames notEmptyOrNil ifTrue:[
+ needsSlash := targetFileOrDirectory last ~= $\.
+
+ ^ (multiSelectBaseNames
+ subStrings:$|)
+ reject:[:each | each isEmptyOrNil]
+ thenCollect:[:each |
+ needsSlash ifTrue:[
+ targetFileOrDirectory, '\', each
+ ] ifFalse:[
+ targetFileOrDirectory, each
+ ]
+ ]
+ ].
+
+ (trueForSave
+ and:[filterArrayOrPairs notEmptyOrNil
+ and:[targetFileOrDirectory asFilename suffix isEmptyOrNil]]) ifTrue:[
+ selectedSuffixInfo := filterArrayOrPairs
+ at:nativeFileDialogReturnData selectedFilterIndex
+ ifAbsent:nil.
+
+ selectedSuffixInfo notNil ifTrue:[
+ selectedSuffix := (selectedSuffixInfo second
+ subStrings:'.')
+ lastIfEmpty:nil.
+
+ (selectedSuffix notNil
+ and:[selectedSuffix ~= '*']) ifTrue:[
+ ^ targetFileOrDirectory, '.', selectedSuffix
+ ].
+
+ ].
+ ].
+
+ ^ targetFileOrDirectory
+
+ "
+ ########### example1 without owning view ##########
+
+ |null|
+
+ null := (Character codePoint:0) asString.
+
+ Display
+ _nativeFileDialogWithTitle:'Hello World'
+ defaultFilename:'C:\users\sr\desktop\dsfjbgfhkbsdhzbret.ets'
+ owningTopView:nil
+ filter:
+ 'expecco Testsuite', null, '*.ets', null,
+ 'expecco Logfile', null, '*.elf', null,
+ 'All', null, '*.*', null,
+ null
+ filterIndex:3
+ trueForSave:true
+ trueForMultiSelect:nil
+ trueForPromptOverwrite:nil
+
+ ########### example2 without owning view and termination ##########
+
+ |null p|
+
+ null := (Character codePoint:0) asString.
+
+ p :=
+ [
+ Display
+ _nativeFileDialogWithTitle:'Hello World'
+ defaultFilename:'D:\sadasdsad'
+ owningTopView:nil
+ filter:
+ 'expecco Testsuite', null, '*.ets', null,
+ 'expecco Logfile', null, '*.elf', null,
+ null
+ filterIndex:2
+ trueForSave:false
+ trueForMultiSelect:nil
+ trueForPromptOverwrite:nil
+ ] fork.
+
+ Delay waitForSeconds:5.
+
+ p terminate.
+
+ ########### example3 with owning view (makes the launcher modal) ##########
+
+ |owningTopView|
+
+ owningTopView := NewLauncher open window topView.
+ Delay waitForSeconds:1.
+
+ Display
+ _nativeFileDialogWithTitle:'Hello World'
+ defaultFilename:'D:\sadasdsad'
+ owningTopView:owningTopView
+ filter:#(
+ #('expecco Testsuite' '*.ets')
+ #('expecco Logfile' '*.elf')
+ )
+ filterIndex:2
+ trueForSave:false
+ trueForMultiSelect:nil
+ trueForPromptOverwrite:nil.
+
+ ########### example4 with owning view (makes the launcher modal) and termination ##########
+
+ |owningTopView p|
+
+ owningTopView := NewLauncher open window topView.
+ Delay waitForSeconds:1.
+
+ p :=
+ [
+ Display
+ _nativeFileDialogWithTitle:'Hello World'
+ defaultFilename:'D:\sadasdsad'
+ owningTopView:owningTopView
+ filter:#(
+ #('expecco Testsuite' '*.ets')
+ #('expecco Logfile' '*.elf')
+ )
+ filterIndex:2
+ trueForSave:false
+ trueForMultiSelect:nil
+ trueForPromptOverwrite:nil.
+ ] fork.
+
+ Delay waitForSeconds:5.
+
+ p terminate.
+ "
+
+ "Created: / 08-11-2018 / 09:11:25 / sr"
+ "Modified: / 08-11-2018 / 10:39:09 / sr"
+!
+
+_primCloseNativeFileDialogByByCreationData:nativeFileDialogCreationData
+ "this method can cleanly close/destroy an open native file dialog.
+ this is called as an ensure, when the native file dialog process gets terminated
+ this is required because windows does not cleanup the dialog by itself when its thread gets terminated"
+
+ |dataAddress sharedMemoryMapFileHandleAddress|
+
+ dataAddress := nativeFileDialogCreationData dataAddress.
+ sharedMemoryMapFileHandleAddress := nativeFileDialogCreationData sharedMemoryMapFileHandleAddress.
+
+%{ /* STACK: 100000 */
+ if (__isExternalAddress(dataAddress)) {
+ fileDialogData *pFddShared = __externalAddressVal(dataAddress);
+ HANDLE hMapFile = _HWNDVal(sharedMemoryMapFileHandleAddress);
+ DWORD32 fileDialogThreadId = pFddShared->fileDialogThreadId;
+
+ if (fileDialogThreadId != 0) {
+ EnumWindows(enumWindowsToFindAndDestroyFileDialogProc, fileDialogThreadId);
+ }
+ /*
+
+ if (__isExternalAddress(threadHandleAddress)) {
+ HANDLE fileDialogThread = _HWNDVal(threadHandleAddress);
+ TerminateThread(fileDialogThread, 0);
+ CloseHandle(fileDialogThread);
+ };*/
+ /*
+ UnmapViewOfFile(pFddShared);
+ CloseHandle(hMapFile); */
+ }
+%}.
+
+ nativeFileDialogCreationData stxFileDialogProcess terminate.
+
+ "Created: / 08-11-2018 / 10:37:28 / sr"
+!
+
+_primGetNativeFileDialogResultByCreationData:nativeFileDialogCreationData
+ "returns nil if the native file dialog is still open
+ returns an empty string if the file dialog was canceled
+ returns the full path to the user selected file"
+
+ |dataAddress sharedMemoryMapFileHandleAddress
+ targetFileOrNil multiSelectValues selectedFilterIndex
+ nativeFileDialogReturnData|
+
+ dataAddress := nativeFileDialogCreationData dataAddress.
+ sharedMemoryMapFileHandleAddress := nativeFileDialogCreationData sharedMemoryMapFileHandleAddress.
+
+%{ /* STACK: 100000 */
+ if (__isExternalAddress(dataAddress)) {
+ fileDialogData *pFddShared = __externalAddressVal(dataAddress);
+
+ if (pFddShared->fileDialogDidReturn) {
+ HANDLE hMapFile = _HWNDVal(sharedMemoryMapFileHandleAddress);
+ targetFileOrNil = __MKU16STRING(pFddShared->filename);
+
+ if (pFddShared->trueForSave) {
+ selectedFilterIndex = __MKINT(pFddShared->filterIndex);
+ } else {
+ if (pFddShared->trueForMultiSelect) {
+ int len;
+ int pos;
+
+ wchar_t buffer[1000 * MAX_PATH]; // big buffer to support multiselect
+ ZeroMemory(buffer, sizeof(buffer));
+
+ len = wcslen(pFddShared->filename);
+ pos = len + 1;
+
+ while ((len = wcslen(&pFddShared->filename[pos])) > 0) {
+ wcscat(buffer, &pFddShared->filename[pos]);
+ wcscat(buffer, L"|");
+ pos = pos + len + 1;
+ wprintf(L"char '%ls'\n", buffer);
+ }
+
+ multiSelectValues = __MKU16STRING(buffer);
+ }
+ }
+
+ UnmapViewOfFile(pFddShared);
+ CloseHandle(hMapFile);
+ }
+ }
+%}.
+
+ targetFileOrNil isNil ifTrue:[
+ ^ nil
+ ].
+
+ nativeFileDialogCreationData stxFileDialogProcess terminate.
+
+ targetFileOrNil isEmpty ifTrue:[
+ ^ '' "/ to exit the while nil loop (indicated file dialog abort)
+ ].
+
+ ^ NativeFileDialogReturnData new
+ targetFileOrDirectory:targetFileOrNil;
+ multiSelectBaseNames:multiSelectValues;
+ selectedFilterIndex:selectedFilterIndex;
+ yourself
+
+ "Created: / 08-11-2018 / 10:17:13 / sr"
+!
+
+_primNativeFileDialogWithTitle:dialogTitle
+ defaultBaseName:defaultBaseName
+ defaultDirectory:defaultDirectory
+ owningViewId:owningViewId
+ filterString:filterString
+ filterIndex:filterIndexArg
+ trueForSave:trueForSaveArg
+ trueForMultiSelect:trueForMultiSelectArg
+ trueForPromptOverwrite:trueForPromptOverwriteArg
+
+ "do not call this directly, use #nativeFileDialogWithTitle:..."
+
+ |dataAddress sharedMemoryMapFileHandleAddress fileDialogStxProcess|
+
+%{ /* STACK: 100000 */
+ HANDLE hMapFile;
+ fileDialogData *pFddShared;
+ int i;
+
+ hMapFile = CreateFileMapping(
+ INVALID_HANDLE_VALUE,
+ NULL,
+ PAGE_READWRITE,
+ 0,
+ sizeof(fileDialogData),
+ "elefant");
+
+ if (hMapFile == NULL) {
+ printf("Could not create file mapping object (%d).\n", (int)GetLastError());
+ RETURN (false);
+ }
+
+ pFddShared = (fileDialogData*)MapViewOfFile(
+ hMapFile,
+ FILE_MAP_ALL_ACCESS,
+ 0,
+ 0,
+ sizeof(fileDialogData));
+
+ if (pFddShared == NULL) {
+ printf("Could not map view of file (%d).\n", (int)GetLastError());
+ CloseHandle(hMapFile);
+ RETURN (false);
+ }
+
+ ZeroMemory(pFddShared, sizeof(fileDialogData));
+
+ if (__isUnicode16String(defaultBaseName)) {
+ for (i = 0; i < __unicode16StringSize(defaultBaseName); i++) {
+ pFddShared->filename[i] = __unicode16StringVal(defaultBaseName)[i];
+ }
+ pFddShared->filename[i] = 0;
+ };
+ if (__isUnicode16String(defaultDirectory)) {
+ for (i = 0; i < __unicode16StringSize(defaultDirectory); i++) {
+ pFddShared->directory[i] = __unicode16StringVal(defaultDirectory)[i];
+ }
+ pFddShared->directory[i] = 0;
+ };
+ if (__isUnicode16String(dialogTitle)) {
+ for (i = 0; i < __unicode16StringSize(dialogTitle); i++) {
+ pFddShared->title[i] = __unicode16StringVal(dialogTitle)[i];
+ }
+ pFddShared->title[i] = 0;
+ };
+ if (__isExternalAddress(owningViewId)) {
+ pFddShared->owningWindow = _HWNDVal(owningViewId);
+ };
+ if (__isUnicode16String(filterString)) {
+ for (i = 0; i < __unicode16StringSize(filterString); i++) {
+ pFddShared->filter[i] = __unicode16StringVal(filterString)[i];
+ }
+ pFddShared->filter[i] = 0;
+ };
+ if (__isInteger(filterIndexArg)) {
+ pFddShared->filterIndex = __intVal(filterIndexArg);
+ }
+ pFddShared->trueForSave = trueForSaveArg == true;
+ pFddShared->trueForMultiSelect = trueForMultiSelectArg == true;
+ pFddShared->trueForPromptOverwrite = trueForPromptOverwriteArg == true;
+
+ pFddShared->fileDialogDidReturn = FALSE;
+
+ dataAddress = __MKEXTERNALADDRESS(pFddShared);
+ sharedMemoryMapFileHandleAddress = __MKEXTERNALADDRESS(hMapFile);
+
+ // fileDialogThreadHandle = __MKEXTERNALADDRESS(openFileDialogInNewThread(pFdd));
+%}.
+
+ fileDialogStxProcess :=
+ [
+ OperatingSystem
+ executeCommand:'D:\Dropbox\Codingware\C++\FileDialogWithSharedMemory\bin\Release\FileDialogWithSharedMemory.exe'
+ outputTo:Stderr
+ errorTo:Stderr.
+ ] fork.
+
+ ^ NativeFileDialogCreationData new
+ dataAddress:dataAddress;
+ sharedMemoryMapFileHandleAddress:sharedMemoryMapFileHandleAddress;
+ stxFileDialogProcess:fileDialogStxProcess;
+ yourself
+
+ "Created: / 08-11-2018 / 08:54:53 / sr"
+ "Modified: / 08-11-2018 / 10:15:26 / sr"
+!
+
nativeFileDialogWithTitle:dialogTitleArg
defaultFilename:defaultFilenameArg
owningTopView:owningTopView
@@ -17039,7 +17533,7 @@
pFdd->title[i] = 0;
};
if (__isExternalAddress(owningViewId)) {
- pFdd->owningWindow = _HWNDVal(owningViewId);
+ pFdd->owningWindow = (DWORD32)_HWNDVal(owningViewId);
};
if (__isUnicode16String(filterString)) {
for (i = 0; i < __unicode16StringSize(filterString); i++) {
@@ -19774,7 +20268,7 @@
}
%}
"
- (StandardSystemView new label:'äöü') open
+ (StandardSystemView new label:'äöü') open
"
!
@@ -20158,6 +20652,32 @@
^ workY
! !
+!WinWorkstation::NativeFileDialogCreationData methodsFor:'accessing'!
+
+dataAddress
+ ^ dataAddress
+!
+
+dataAddress:something
+ dataAddress := something.
+!
+
+sharedMemoryMapFileHandleAddress
+ ^ sharedMemoryMapFileHandleAddress
+!
+
+sharedMemoryMapFileHandleAddress:something
+ sharedMemoryMapFileHandleAddress := something.
+!
+
+stxFileDialogProcess
+ ^ stxFileDialogProcess
+!
+
+stxFileDialogProcess:something
+ stxFileDialogProcess := something.
+! !
+
!WinWorkstation::NativeFileDialogReturnData class methodsFor:'documentation'!
documentation