FIX: MacPreview - open only supported file types (issue #1481)

(cherry picked from commit d2bef610d8)
This commit is contained in:
Alexander Koblov 2024-04-18 20:45:12 +03:00
commit 88342a221c
2 changed files with 221 additions and 2 deletions

View file

@ -25,8 +25,21 @@ library MacPreview;
{$mode objfpc}{$H+}
{$modeswitch objectivec1}
// Uniform Type Identifiers framework is available from macOS 11
// TODO: Switch to Uniform Type Identifiers framework also under
// x86_64 target when we drop an old macOS support
{$IF DEFINED(CPUAARCH64)}
{$DEFINE BIG_SUR}
{$ENDIF}
uses
SysUtils, WlxPlugin, CocoaAll, QuickLookUI;
SysUtils, WlxPlugin, CocoaAll, QuickLookUI, CFPropertyList
{$IF DEFINED(BIG_SUR)}
, UniformTypeIdentifiers
{$ELSE}
, MacOSAll
{$ENDIF}
;
type
TQLPItem = objcclass(NSObject, QLPreviewItemProtocol)
@ -42,6 +55,20 @@ type
function acceptsFirstResponder: ObjCBOOL; override;
end;
var
QLContentTypes: NSMutableArray;
{$IF DEFINED(BIG_SUR)}
QLContentUTTypes: NSMutableArray;
{$ENDIF}
const
ExcludeList: array[0..2] of PAnsiChar =
(
'public.plain-text',
'public.json',
'public.xml'
);
// copy from uMyDarwin
function StringToNSString(const S: String): NSString;
begin
@ -67,6 +94,147 @@ begin
end;
end;
procedure AddContentType(AType: NSString);
{$IF NOT DEFINED(BIG_SUR)}
begin
QLContentTypes.addObject(AType);
end;
{$ELSE}
var
anObject: id;
begin
QLContentTypes.addObject(AType);
anObject:= UTType.typeWithIdentifier(AType);
if Assigned(anObject) then QLContentUTTypes.addObject(anObject);
end;
{$ENDIF}
function CheckContentType(const Name: String; FileType: NSString): Boolean;
var
Index: Integer;
begin
// Special case
if (Name = 'Text.qlgenerator') then
begin
for Index:= 0 to High(ExcludeList) do
begin
if StrComp(FileType.UTF8String, ExcludeList[Index]) = 0 then
Exit(False)
end;
end;
Result:= True;
end;
procedure ParseFile(const Path, Name: String);
var
Data: NSData;
I, J: Integer;
Dict: NSDictionary;
FileType: NSString;
DocumentTypes, ContentTypes: NSArray;
begin
Data:= NSData.dataWithContentsOfFile(StringToNSString(Path + Name + '/Contents/Info.plist'));
if Assigned(Data) then
begin
Dict:= NSDictionary(NSPropertyListSerialization.propertyListWithData_options_format_error(Data, NSPropertyListImmutable, nil, nil));
if Assigned(Dict) then
begin
DocumentTypes:= NSArray(Dict.valueForKey(StringToNSString('CFBundleDocumentTypes')));
if Assigned(DocumentTypes) then
begin
for I:= 0 to Integer(DocumentTypes.count) - 1 do
begin
Dict:= NSDictionary(DocumentTypes.objectAtIndex(I));
ContentTypes:= NSArray(Dict.valueForKey(StringToNSString('LSItemContentTypes')));
if Assigned(ContentTypes) then
begin
for J:= 0 to Integer(ContentTypes.count - 1) do
begin
FileType:= NSString(ContentTypes.objectAtIndex(J));
if CheckContentType(Name, FileType) then AddContentType(FileType);
end;
end;
end;
end;
end;
end;
end;
procedure ParseFolder(const Path: String);
var
FindData: TSearchRec;
begin
if FindFirst(Path + '*.qlgenerator', faDirectory, FindData) = 0 then
begin
repeat
ParseFile(Path, FindData.Name);
until FindNext(FindData) <> 0;
FindClose(FindData);
end;
end;
function CheckFile(const FileName: String): Boolean;
{$IF NOT DEFINED(BIG_SUR)}
var
Index: Integer;
QLType: NSString;
FileType: NSString;
begin
FileType:= NSWorkspace.sharedWorkspace.typeOfFile_error(StringToNSString(FileName), nil);
if (FileType = nil) then
begin
Result:= False;
end
else begin
for Index:= 0 to QLContentTypes.Count - 1 do
begin
QLType:= NSString(QLContentTypes.objectAtIndex(Index));
// Direct comparison
if (FileType.compare(QLType) = NSOrderedSame) then
Exit(True);
// Conforms checking
if UTTypeConformsTo(CFStringRef(FileType), CFStringRef(QLType)) then
Exit(True);
end;
Result:= False;
end;
end;
{$ELSE}
var
Index: Integer;
FileExt: String;
QLUTType: UTType;
QLType: NSString;
FileType: NSString;
FileUTType: UTType;
begin
FileExt:= ExtractOnlyFileExt(FileName);
FileUTType:= UTType.typeWithFilenameExtension(StringToNSString(FileExt));
if (FileUTType = nil) then
begin
Result:= False;
end
else begin
FileType:= FileUTType.identifier;
// Direct comparison
for Index:= 0 to QLContentTypes.Count - 1 do
begin
QLType:= NSString(QLContentTypes.objectAtIndex(Index));
if (FileType.compare(QLType) = NSOrderedSame) then
Exit(True);
end;
// Conforms checking
for Index:= 0 to QLContentUTTypes.Count - 1 do
begin
QLUTType:= UTType(QLContentUTTypes.objectAtIndex(Index));
if FileUTType.conformsToType(QLUTType) then
Exit(True);
end;
Result:= False;
end;
end;
{$ENDIF}
function TQLPItem.previewItemURL: NSURL;
begin
Result:= url;
@ -100,6 +268,9 @@ function ListLoad( ParentWin:THandle; FileToLoad:pchar; {%H-}ShowFlags:integer):
var
view: QLPreviewView;
begin
if not CheckFile(FileToLoad) then
Exit(wlxInvalidHandle);
view:= DCQLPreview.alloc.init;
view.setAutostarts( true );
view.setShouldCloseWithWindow( false );
@ -122,6 +293,9 @@ function ListLoadNext( {%H-}ParentWin,PluginWin:THandle; FileToLoad:pchar; {%H-}
var
view: QLPreviewView;
begin
if not CheckFile(FileToLoad) then
Exit(LISTPLUGIN_ERROR);
view:= QLPreviewView(PluginWin);
// workaround for the bug of MacOS Quick Look:
@ -140,6 +314,17 @@ begin
QLPreviewView(ListWin).removeFromSuperview;
end;
procedure ListSetDefaultParams(dps: PListDefaultParamStruct); cdecl;
begin
QLContentTypes:= NSMutableArray.alloc.init;
{$IF DEFINED(BIG_SUR)}
QLContentUTTypes:= NSMutableArray.alloc.init;
{$ENDIF}
ParseFolder('/Library/QuickLook/');
ParseFolder('/System/Library/QuickLook/');
ParseFolder(IncludeTrailingBackslash(GetUserDir) + 'Library/QuickLook/');
end;
procedure ListGetDetectString(DetectString:pchar;maxlen:integer); cdecl;
begin
StrLCopy(DetectString, '(EXT!="")', MaxLen);
@ -149,7 +334,8 @@ exports
ListLoad,
ListLoadNext,
ListCloseWindow,
ListGetDetectString;
ListGetDetectString,
ListSetDefaultParams;
end.

View file

@ -0,0 +1,33 @@
unit UniformTypeIdentifiers;
{$mode delphi}
{$modeswitch objectivec1}
interface
{$linkframework UniformTypeIdentifiers}
uses
SysUtils, CocoaAll;
type
UTType = objcclass external(NSObject, NSCopyingProtocol, NSSecureCodingProtocol)
public
class function typeWithIdentifier(identifier: NSString): id; message 'typeWithIdentifier:';
class function typeWithFilenameExtension(filenameExtension: NSString): id; message 'typeWithFilenameExtension:';
function identifier: NSString; message 'identifier';
function conformsToType(type_: UTType): ObjCBOOL; message 'conformsToType:';
// NSCopyingProtocol
function copyWithZone(zone: NSZonePtr): id; message 'copyWithZone:';
// NSCodingProtocol
procedure encodeWithCoder(aCoder: NSCoder); message 'encodeWithCoder:';
function initWithCoder(aDecoder: NSCoder): id; message 'initWithCoder:';
// NSSecureCodingProtocol
class function supportsSecureCoding: ObjCBOOL; message 'supportsSecureCoding';
end;
implementation
end.