diff --git a/lcl/dialogs.pp b/lcl/dialogs.pp index 24c31cebf2..77840dfdca 100644 --- a/lcl/dialogs.pp +++ b/lcl/dialogs.pp @@ -222,8 +222,9 @@ type ofPickFolders, //Windows Vista+ Turns the dialog into a TSelectDirectoryDialog ofOkButtonNeedsInteraction, //Windows Vista+ The OK button will be disabled until the user navigates the view or edits the filename (if applicable). ofForceFileSystem, //Windows Vista+ Ensures that returned items are file system items - ofAllNonStorageItems //Windows Vista+ Enables the user to choose any item in the Shell namespace, not just those with SFGAO_STREAM or SFAGO_FILESYSTEM attributes. + ofAllNonStorageItems, //Windows Vista+ Enables the user to choose any item in the Shell namespace, not just those with SFGAO_STREAM or SFAGO_FILESYSTEM attributes. // This flag cannot be combined with FOS_FORCEFILESYSTEM. + ofUseXPStyleAsFallBack //Windows Vista+ Use XP style dialog if creating Vista style dialog fails (e.g. when running under Windows Recovery) // Intentionally not supported: ofDefaultNoMiniMode, ofHideMruPlaces: these values are not supported as of Windows 7. ); TOpenOptionsEx = set of TOpenOptionEx; diff --git a/lcl/interfaces/win32/win32wsdialogs.pp b/lcl/interfaces/win32/win32wsdialogs.pp index 62b3ac5d5e..89eb0ab369 100644 --- a/lcl/interfaces/win32/win32wsdialogs.pp +++ b/lcl/interfaces/win32/win32wsdialogs.pp @@ -193,7 +193,7 @@ var implementation uses - CommCtrl, TaskDlgEmulation; + CommCtrl, TaskDlgEmulation, contnrs; function SaveApplicationState: TApplicationState; begin @@ -354,15 +354,6 @@ begin ACommonDialog.UserChoice := mrCancel; end; -function CanUseVistaDialogs(const AOpenDialog: TOpenDialog): Boolean; -begin - {$IFnDEF DisableVistaDialogs} - Result := (WindowsVersion >= wvVista) and not (ofOldStyleDialog in AOpenDialog.Options); - {$ELSE} - Result := False; - {$ENDIF} -end; - { TWin32WSColorDialog } @@ -788,6 +779,47 @@ end; { TWin32WSOpenDialog } +var + XPStyleFallBackList: TFPObjectList = nil; + +procedure MaybeInitXPStyleFallBackList; +begin + if not Assigned(XPStyleFallBackList) then + XPStyleFallBackList := TFPObjectList.Create(False); //don't free objects +end; + +procedure FreeXPStyleFallBackList; +begin + if Assigned(XPStyleFallBackList) then + FreeAndNil(XPStyleFallBackList); +end; + +function IsXPStyleFallBack(const AOpenDialog: TOpenDialog): Boolean; +var + Idx: Integer; +begin + if not Assigned(XPStyleFallBackList) or not (ofUseXPStyleAsFallBack in AOpenDialog.OptionsEx) then + Exit(False); + Idx := XPStyleFallBackList.IndexOf(AOpenDialog); + Result := (Idx <> -1); +end; + +function IsXPStyleFallBack(const AOpenDialog: TOpenDialog; out Idx: Integer): Boolean; +begin + if not Assigned(XPStyleFallBackList) or not (ofUseXPStyleAsFallBack in AOpenDialog.OptionsEx) then + Exit(False); + Idx := XPStyleFallBackList.IndexOf(AOpenDialog); + Result := (Idx <> -1); +end; + +function CanUseVistaDialogs(const AOpenDialog: TOpenDialog): Boolean; +begin + {$IFnDEF DisableVistaDialogs} + Result := (WindowsVersion >= wvVista) and not (ofOldStyleDialog in AOpenDialog.Options); + {$ELSE} + Result := False; + {$ENDIF} +end; class procedure TWin32WSOpenDialog.SetupVistaFileDialog(ADialog: IFileDialog; const AOpenDialog: TOpenDialog); var @@ -998,8 +1030,9 @@ var HRes: HRESULT; DlgType: TIID; CLS_ID: TGUID; + AOpenDialog: TOpenDialog absolute ACommonDialog; begin - if CanUseVistaDialogs(TOpenDialog(ACommonDialog)) then + if CanUseVistaDialogs(AOpenDialog) then begin if (ACommonDialog is TSaveDialog) then begin @@ -1015,29 +1048,49 @@ begin if Succeeded(HRes) and Assigned(Dialog) then begin Dialog._AddRef; - SetupVistaFileDialog(Dialog, TOpenDialog(ACommonDialog)); + SetupVistaFileDialog(Dialog, AOpenDialog); Result := THandle(Dialog); end else - Result := INVALID_HANDLE_VALUE; - end + begin + if (ofUseXPStyleAsFallback in AOpenDialog.OptionsEx) then + begin + MaybeInitXPStyleFallBackList; + XPStyleFallbackList.Add(ACommonDialog); + //debugln(['TWin32WSOpenDialog.CreateHandle: Added ',DbgSName(AOpenDialog),' to XPStyleFallbackList']); + Result := CreateFileDialogHandle(AOpenDialog); + end + else + Result := INVALID_HANDLE_VALUE; + end; + end//CanUseVistaDialogs else - Result := CreateFileDialogHandle(TOpenDialog(ACommonDialog)); + Result := CreateFileDialogHandle(AOpenDialog); end; class procedure TWin32WSOpenDialog.DestroyHandle(const ACommonDialog: TCommonDialog); var Dialog: IFileDialog; + Idx: Integer; begin if (ACommonDialog.Handle <> 0) and (ACommonDialog.Handle <> INVALID_HANDLE_VALUE) then - if CanUseVistaDialogs(TOpenDialog(ACommonDialog)) then + begin + if CanUseVistaDialogs(TOpenDialog(ACommonDialog)) and not IsXPStyleFallBack(TOpenDialog(ACommonDialog), Idx) then begin Dialog := IFileDialog(ACommonDialog.Handle); Dialog._Release; Dialog := nil; end else + begin + if (Idx <> -1) then + begin + //debugln(['TWin32WSOpenDialog.CreateHandle: Removing ',DbgSName(ACommonDialog),' from XPStyleFallbackList']); + XPStyleFallBackList.Delete(Idx); + end; DestroyFileDialogHandle(ACommonDialog.Handle) + end; + end; end; class procedure TWin32WSOpenDialog.ShowModal(const ACommonDialog: TCommonDialog); @@ -1054,7 +1107,7 @@ begin lInitialDir := TOpenDialog(ACommonDialog).InitialDir; if lInitialDir <> '' then SetCurrentDirUTF8(lInitialDir); - if CanUseVistaDialogs(TOpenDialog(ACommonDialog)) then + if CanUseVistaDialogs(TOpenDialog(ACommonDialog)) and not IsXPStyleFallBack(TOpenDialog(ACommonDialog)) then begin Dialog := IFileOpenDialog(ACommonDialog.Handle); VistaDialogShowModal(Dialog, TOpenDialog(ACommonDialog)); @@ -1136,7 +1189,7 @@ begin lInitialDir := TSaveDialog(ACommonDialog).InitialDir; if lInitialDir <> '' then SetCurrentDirUTF8(lInitialDir); - if CanUseVistaDialogs(TOpenDialog(ACommonDialog)) then + if CanUseVistaDialogs(TOpenDialog(ACommonDialog)) and not IsXPStyleFallBack(TOpenDialog(ACommonDialog)) then begin Dialog := IFileSaveDialog(ACommonDialog.Handle); TWin32WSOpenDialog.VistaDialogShowModal(Dialog, TOpenDialog(ACommonDialog)); @@ -2080,4 +2133,7 @@ initialization else OpenFileNameSize := SizeOf(OPENFILENAME); InitTaskDialogIndirect; + +finalization + FreeXPStyleFallBackList end.