*** empty log message ***
authorsr
Thu, 08 Nov 2018 13:57:33 +0100
changeset 8567 4af8f0a0fac2
parent 8566 dbd31a916f5c
child 8568 0c1b574dd8c0
*** empty log message ***
WinWorkstation.st
--- 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