Jump to content

Recommended Posts

Posted
51 minutes ago, Kanashius said:

I hope this is what you had in mind :)

This is seriously awesome! :thumbsup:😁

Not only did you prove that it is possible to run these file operations in a separate sub-process, but you did it in such a well organized way. I am speechless!

The sub-process starts/ends only when needed, does its job perfectly and does not hinder the performance of the main GUI process. I tested it by copying a few GB's of files and folders and it was perfect. This is incredible work, @Kanashius.

I see only one area of concern at the moment. I'll comment in the code below:

If $sTargetPathAbs<>Default Then
            ; determine if IFileOperation needs to copy or move files
            Switch $iFinalEffect
                Case $DROPEFFECT_COPY
                    __FilesOperation_DoInSub($__FileOperation_Copy, $sTargetPathAbs, $arPaths)
                    ; we need a response here from _IFileOperationFile from sub-process from __FileOperations_SubCopyOrMove
                Case $DROPEFFECT_MOVE
                    __FilesOperation_DoInSub($__FileOperation_Move, $sTargetPathAbs, $arPaths)
                    ; we need a response here from _IFileOperationFile from sub-process from __FileOperations_SubCopyOrMove
                    ; especially here because we don't want to send DROPEFFECT_NONE and __SetPerformedDropEffect back to
                    ; source of drag (whether it's File Explorer, etc.) until file operation is complete
                    $tEffect.iEffect = $DROPEFFECT_NONE
                    __SetPerformedDropEffect($pDataObject, $DROPEFFECT_NONE)
            EndSwitch
        EndIf

From what I can tell, you already have the structure in place to send/receive messages between the main process and the sub-process. I just don't fully understand it all yet. I think what we need there is for the sub-process to send the result from _IFileOperationFile.

Although I don't think we need to hold up the process at that very point. The main thing is that I don't think we want to send:

__SetPerformedDropEffect($pDataObject, $DROPEFFECT_NONE)

... until after the file operation (move) is complete because that is what tells the source of the drag or copy (eg. File Explorer) what happened and how it should (or should not) handle the file operation on its end. I'll have to test more and see. But we definitely need to be careful with Move. Copy is not an issue.

Other than that concern, I am absolutely shocked and surprised with how awesome you just made this with multi-process file operations. I feel just like a little kid walking into a candy store right now. 😆

On a side note, I have made some great progress with the IFileOperation stuff recently. We don't need to worry about updating the IFileOperation stuff yet though. We probably need to wait until all of the dust settles from all of these recent changes.

Posted

I'm glad you like it :)
 

1 hour ago, WildByDesign said:

From what I can tell, you already have the structure in place to send/receive messages between the main process and the sub-process. I just don't fully understand it all yet. I think what we need there is for the sub-process to send the result from _IFileOperationFile

I changed it to sent the __SetPerformedDropEffect. The objects are already at the main process, so that process should handle it.
I've put it in the "Successful copy/move" part, so it is only done, when the operation was successful. Maybe that should change.
Currently the Local $iResult = _IFileOperationFile is handled as a bool for success/failure, if it carries more data, it could also be returned and sent with __IPC_SubSendCmd($bResult?$__FileOperation_Successful:$__FileOperation_Failed, $iResult) to make it available in the main process.

I also thought about doing a little refactoring soonish. Orienting myself in the project was a little bit confusing, so doing stuff like: functions/global variable names start with the filename, they are in,... I think that would be a good starting point to give the project a littlebit of clarity on the structure side. :)

Posted
8 hours ago, Kanashius said:

I changed it to sent the __SetPerformedDropEffect. The objects are already at the main process, so that process should handle it.
I've put it in the "Successful copy/move" part, so it is only done, when the operation was successful. Maybe that should change.
Currently the Local $iResult = _IFileOperationFile is handled as a bool for success/failure, if it carries more data, it could also be returned and sent with __IPC_SubSendCmd($bResult?$__FileOperation_Successful:$__FileOperation_Failed, $iResult) to make it available in the main process.

I finally got a chance to spend some time testing it more thoroughly with your multi-process file operation changes. The more times that I test drag and drop with larger sets of files, the more impressed I am. It works so incredibly well.

I also needed to spend some time thinking about what data we needed to send and whether or not it mattered if we sent it immediately or if we waited until the file operation was truly complete. I think the fact that we are sending DROPEFFECT_NONE doesn't matter as much when we send it.

With the Fix drop messaging to sender commit, it is giving errors on the line with "$tEffect.iEffect = $DROPEFFECT_NONE" which I believe was line 100. I don't have the exact error right now but it was referring to not being an object.

That is when I started thinking, "Do we really need to send it after?". That is when I decided to revert back to the Implement Copy/Move over IPC at a different process commit which was your initial multi-process commit. I spent quite a bit more time testing larger drag and drop operations here. Everything worked perfectly well.

Do you think that we should just revert the Fix drop messaging to sender commit?

It seems that maybe it does not have access to the IDataObject ($pDataObject) there anyway. I'm not sure if it just can't access it there or if the data object was already released at that point. But I am thinking that we may not need that commit.

8 hours ago, Kanashius said:

I also thought about doing a little refactoring soonish. Orienting myself in the project was a little bit confusing, so doing stuff like: functions/global variable names start with the filename, they are in,... I think that would be a good starting point to give the project a littlebit of clarity on the structure side. :)

If you would like to do some refactoring, that would be fantastic. I think that we may need to wait until these pull requests are accepted and merged though because there are a lot of structural changes with the shell-dodragdrop branch and of course with your IPC additions as well. I know that Sven has been quite busy this week and may not be able to review and merge until next week. Unless you want to do the review and merge.

I've got so many great ideas and changes ready to go once all of the dust settles from these big changes. :)

Posted
1 hour ago, WildByDesign said:

Do you think that we should just revert the Fix drop messaging to sender commit?

Yes, I'am not sure, what sending the effect should change at the senders end. Except ending the drag/drop.

1 hour ago, WildByDesign said:

if the data object was already released at that point

Probably yes, because the callback returned, so it was probably freed. Same for $piEffect.

1 hour ago, WildByDesign said:

I've got so many great ideas and changes ready to go once all of the dust settles from these big changes.

I cannot wait :)

Posted

We have some instances of IFileOperation used in the main.au3 script as well. I'm not sure if we need multi-process for all of them or not.

There are two instances of IFileOperation in _PasteItems() function which basically follow through with a CopyItems into either TreeView or ListView. I personally think that we should use multi-process here because Paste has the potential to be a larger set of files to copy.

I believe the remaining instances of IFileOperation in main.au3 are for deleting items. I'm not certain if we need multi-process for deleting files. But then again, deleting files can also potentially be a lot of larger files and take some time. Anything that takes more time should be in a separate process.

@Kanashius What do you think about this?

If you believe that it should be multi-process, would you please be willing to extend your IPC to these areas as well?

Posted (edited)

There is a weird bug that should be simple but I cannot figure it out. When you navigate to a directory that is slower to load (250ms), I made it so that it hides the ListView to simulate _GUICtrlListView_DeleteAllItems since that is happening behind the scenes already. The reason why I did that is because it would never show those files disappearing until after the ListView loading was complete. So I did it to make it look cleaner. I also made it so that it shows the MCID_WAIT cursor during that slower load time.

So when you navigate to a slower directory such as System32, that all works perfectly. The problem is that the MCID_WAIT cursor does not show when you hit the Refresh button when you are already in System32.

The interesting thing is that I can tell that it is running through the _ListViewLoadWait() function properly and hiding the ListView. It just seems that, for whatever reason, it does not want to show the MCID_WAIT when clicking Refresh on that slow loading directory (System32).

You will need to run the code from the fix-item-count branch because it fixes some other things in the Loading Callback that prevented the _ListViewLoadWait() function from working properly.

Func _ListViewLoadWait()
    If $bLoadStatus Then
        ; override GUI with loading/waiting cursor on directories that are slower to load
        $bCursorOverride = True
        GUISetCursor($MCID_WAIT, 1, $g_hGUI)
        GUISetCursor($MCID_WAIT, 1, _GUIFrame_GetHandle($iFrame_A, 2))
        GUICtrlSetState($idListview, $GUI_HIDE)
        ; clear statusbar item count
        $g_aText[0] = ""
        _WinAPI_RedrawWindow($g_hStatus)
    EndIf
    AdlibUnRegister("_ListViewLoadWait")
EndFunc   ;==>_ListViewLoadWait

So whether you navigate to System32 or hit Refresh while on System32, it does run through this function and hides the ListView as intended.

What is failing on Refresh (on System32):

  • GUISetCursor overriding the cursor to MCID_WAIT
  • Clearing the item count on the statusbar

I am thoroughly puzzled over this issue. 🙃

EDIT: It looks like the problem is that the GUI is hung up during this time until System32 has loaded. It looks like this may not be fixable.

Edited by WildByDesign
Posted
2 hours ago, WildByDesign said:

Should we have support for languages other than English?

Si, Ja, Oui oui :lol:
The question is: do you know the intricacies of other languages 🤔  
There are languages that are "right to left" and some that are top to bottom ?. I guess one of those who speak/type in that language would know better.
So yes. It'd be beautiful to attend to all languages. I just don't know how.
If one of those ( too strange to English speakers ) languages are brought up by a user, we'll discover it then 🤷‍♂️ 

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Posted
7 hours ago, argumentum said:

I just don't know how

I would go for a key/value approach with maps.
So a function to set the language.
Then a file with a bunch of keys/values (could be an ini, which is already implemented in AutoIt). The Values would be in the StringFormat style.
Then a function to load the correct string (for performance probably a loading function to load them once and not open the language file all the time) and a function to get the language string for a specific key. The key should be basically the entire string without any non a-zA-Z, so there are no conflicts and you can see, what string is requested there. (Very long strings could just use the start).

Global $__Lang__LangMap[]

; This is the lang.ini. Could also be split into multiple files, if it gets to large
; google translated arabic, so sorry for that ;)
#cs
[en]
thisIsALongSentence=This is a long sentence.
exampleNumberIs=Example number is %i.
[de]
thisIsALongSentence=Dies ist ein langer Satz.
exampleNumberIs=Beispielzahl ist %i.
[egy]
RightToLeft=true
thisIsALongSentence=هذه جملة طويلة.
exampleNumberIs=الرقم المثالي هو %i.
#ce

__Lang_Init()
ConsoleWrite(__Lang_Get("thisIsALongSentence")&@crlf)
ConsoleWrite(__Lang_Get("exampleNumberIs", 5)&@crlf)
__Lang_Init("de")
ConsoleWrite(__Lang_Get("thisIsALongSentence")&@crlf)
ConsoleWrite(__Lang_Get("exampleNumberIs", 5)&@crlf)
; If __Lang_IsRTL() Then GUICtrlCreateLabel("", 5, 5, 100, 100, BitOR($SS_NOTIFY, $SS_RIGHT))

Func __Lang_Init($sLang=Default, $sLangIni = "lang.ini")
    If $sLang=Default Then
        Local $arSections = IniReadSectionNames($sLangIni)
        If @error Or UBound($arSections)<2 Then Return SetError(1, 2, False)
        $sLang = $arSections[1]
    EndIf
    Local $arLang = IniReadSection($sLangIni, $sLang)
    If @error Then Return SetError(2, 0, False)
    Local $mLangMap[]
    For $i=1 To UBound($arLang)-1
        $mLangMap[$arLang[$i][0]] = $arLang[$i][1]
    Next
    $__Lang__LangMap = $mLangMap
EndFunc

Func __Lang_IsRTL()
    Return MapExists($__Lang__LangMap, "RightToLeft") And StringLower($__Lang__LangMap["RightToLeft"]) = "true"
EndFunc

Func __Lang_Get($sKey, $vParam1 = Default, $vParam2 = Default, $vParam3 = Default, $vParam4 = Default, $vParam5 = Default, _
                        $vParam6 = Default, $vParam7 = Default, $vParam8 = Default, $vParam9 = Default, $vParam10 = Default, _
                        $vParam11 = Default, $vParam12 = Default, $vParam13 = Default, $vParam14 = Default, $vParam15 = Default, _
                        $vParam16 = Default, $vParam17 = Default, $vParam18 = Default, $vParam19 = Default, $vParam20 = Default, _
                        $vParam21 = Default, $vParam22 = Default, $vParam23 = Default, $vParam24 = Default, $vParam25 = Default, _
                        $vParam26 = Default, $vParam27 = Default, $vParam28 = Default, $vParam29 = Default, $vParam30 = Default, _
                        $vParam31 = Default, $vParam32 = Default) ; StringFormat is limited to 32 params, too
    If Not MapExists($__Lang__LangMap, $sKey) Then Return SetError(1, 1, "")
    Local $arArgs[@NumParams+1]
    $arArgs[0] = "CallArgArray" ; This is required, otherwise, Call() will not recognize the array as containing arguments
    $arArgs[1] = $__Lang__LangMap[$sKey]
    For $i=1 To @NumParams-1
        $arArgs[$i+1] = Eval("vParam"&$i)
    Next
    Local $sVal = Call("StringFormat", $arArgs)
    If @error Then Return SetError(2, @error, "")
    Return $sVal
EndFunc

To add automatic changes for labels,... when changing the language, something like this could work to avoid a restart.
But that would require a lot of functions to update all the different types of controls,... I think a restart would be simpler.

Local $idLabel = GuiCtrlCreateLabel("", 5, 5)
__Lang_AddKey("exampleNumberIs", __Lang_GetCtrlLabelCallbackMap($idLabel), 5)

Func __Lang_GetCtrlLabelCallbackMap($idCtrl)
    Local $mCallBack[]
    $mCallBack["GuiCtrlSetData"] = [$idCtrl]
    $mCallBack["GUICtrlSetStyle"] = [$idCtrl, __Lang_IsRTL()?$SS_RIGHT:$SS_LEFT]
EndFunc

Func __Lang_AddKey($sKey, $mCallBack, $vParam1 = Default, $vParam2 = Default, $vParam3 = Default, $vParam4 = Default, $vParam5 = Default, _
                        $vParam6 = Default, $vParam7 = Default, $vParam8 = Default, $vParam9 = Default, $vParam10 = Default, _
                        $vParam11 = Default, $vParam12 = Default, $vParam13 = Default, $vParam14 = Default, $vParam15 = Default, _
                        $vParam16 = Default, $vParam17 = Default, $vParam18 = Default, $vParam19 = Default, $vParam20 = Default, _
                        $vParam21 = Default, $vParam22 = Default, $vParam23 = Default, $vParam24 = Default, $vParam25 = Default, _
                        $vParam26 = Default, $vParam27 = Default, $vParam28 = Default, $vParam29 = Default, $vParam30 = Default, _
                        $vParam31 = Default, $vParam32 = Default)
    ; add $sKey, $mCallBack and the params (as array) to a map to store them and call the callback functions,...
    ; when language is changed, the callback functions can be called again
    ; just needs a way to delete them later... so some identifier, which may get returned by __Lang_AddKey (e.g. MapAppend result or something)
EndFunc

Hope this helps,

LG Kanashius

Posted
29 minutes ago, Kanashius said:

I would go for a key/value approach with maps.
So a function to set the language.
Then a file with a bunch of keys/values (could be an ini, which is already implemented in AutoIt). The Values would be in the StringFormat style.
Then a function to load the correct string (for performance probably a loading function to load them once and not open the language file all the time) and a function to get the language string for a specific key. The key should be basically the entire string without any non a-zA-Z, so there are no conflicts and you can see, what string is requested there. (Very long strings could just use the start).

This is a really smart approach. Very well thought out. I figured it is something we should think about now since we are only going to continue adding more visible GUI options that would be dependant on language.

And of course, many of the contributors to Files Au3 are not English speaking as their first language. So you all deserve to have your own languages supported.

I think that if we have the language structure in place (.ini or whichever), it will be easy for anyone to help adding languages as needed.

47 minutes ago, Kanashius said:

To add automatic changes for labels,... when changing the language, something like this could work to avoid a restart.
But that would require a lot of functions to update all the different types of controls,... I think a restart would be simpler.

Changing languages without restart would certainly be nice. But you're right, it would take a lot of extra work changing menu text and everything. Maybe a simple restart at first and a longer term goal of not needing a restart.

Posted (edited)

For what it's worth, when I was still learning AutoIt I had to come up with more own strings/languages idea for another project:

$OSLanguage = @OSLang
$MUILang = @MUILang

$OSLanguageCode = $OSLanguage
$OSLanguageName = $MUILang

; check if muilang is blank to use oslang instead
If $MUILang = "" Then
    $OSLang = $OSLanguage
Else
    $OSLang = $MUILang
EndIf

If $OSLang = '0409' Then
    $OSLanguageCode = 'en-US'
    $OSLanguageName = 'English - United States'
    $softName = 'Software name'
    ; ... more variables with strings
ElseIf $OSLang = '0407' Then
    $OSLanguageCode = 'de-DE'
    $OSLanguageName = 'Deutsch - Deutschland'
    $softName = 'Software name'
    ; ... more variables with strings
ElseIf $OSLang = '0421' Then
    $OSLanguageCode = 'id-ID'
    $OSLanguageName = 'Indonesian - Indonesia'
    $softName = 'Software name'
    ; ... more variables with strings
ElseIf $OSLang = '0816' Then
    $OSLanguageCode = 'pt-PT'
    $OSLanguageName = 'Português - Portugal'
    $softName = 'Software name'
    ; ... more variables with strings
ElseIf $OSLang = '0416' Then
    $OSLanguageCode = 'pt-BR'
    $OSLanguageName = 'Português - Brasil'
    $softName = 'Software name'
    ; ... more variables with strings
ElseIf $OSLang = '0419' Then
    $OSLanguageCode = 'ru-RU'
    $OSLanguageName = 'Русский - Россия'
    $softName = 'Software name'
    ; ... more variables with strings
ElseIf $OSLang = '042A' Then
    $OSLanguageCode = 'vi-VN'
    $OSLanguageName = 'Tiếng Việt'
    $softName = 'Software name'
    ; ... more variables with strings
Else
    ; fall back to English if there are no other matching languages
    $OSLanguageCode = @OSLang
    $OSLanguageName = @MUILang
    $softName = 'Software name'
    ; ... more variables with strings
EndIf

This was run on GUI startup. Each language had a list of like 20 strings attached to those variables. It was really simplistic but it worked well. The only downside was that it was internal only, so there was no outside .ini file.

EDIT: Clearly this was before I knew about Switch Case... 😄

Edited by WildByDesign

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...