diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7dc51c8ed3..33d6232ab5 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -706,6 +706,7 @@ gimp-macos-inhouse:
- _build*/done-dylib.list
# Needed by dist-mac-weekly
- _build-*/config.h
+ - _build-*/plug-ins/file_associations_mac.list
- _build-*/gimp-data/images/logo/gimp-dmg.png
expire_in: 2 days
diff --git a/build/macos/2_bundle-gimp-uni_base.py b/build/macos/2_bundle-gimp-uni_base.py
index 634385c19f..0bd7ecd4cd 100644
--- a/build/macos/2_bundle-gimp-uni_base.py
+++ b/build/macos/2_bundle-gimp-uni_base.py
@@ -141,6 +141,8 @@ shutil.copy2(Path(f"{GIMP_SOURCE}/build/macos/Info.plist"), GIMP_DISTRIB)
### FIXME: Icon (generate Assets.car for Liquid Glass)
(GIMP_DISTRIB / "Resources").mkdir(parents=True, exist_ok=True)
shutil.copy2(Path(f"{os.getenv('MESON_BUILD_ROOT')}/gimp-data/images/logo/gimp.icns"), GIMP_DISTRIB / "Resources/AppIcon.icns")
+shutil.copy2(Path(f"{os.getenv('MESON_BUILD_ROOT')}/build/macos/fileicon-xcf.icns"), GIMP_DISTRIB / "Resources/fileicon-xcf.icns")
+shutil.copy2(Path(f"{os.getenv('MESON_BUILD_ROOT')}/build/macos/fileicon.icns"), GIMP_DISTRIB / "Resources/fileicon.icns")
## BUNDLE BASE (BARE MINIMUM TO RUN GTK APPS).
diff --git a/build/macos/3_dist-gimp-apple.sh b/build/macos/3_dist-gimp-apple.sh
index 8f08b71457..16d8cb1fd1 100644
--- a/build/macos/3_dist-gimp-apple.sh
+++ b/build/macos/3_dist-gimp-apple.sh
@@ -130,7 +130,8 @@ conf_plist "%BUNDLE_NAME%" "$BUNDLE_NAME"
conf_plist "%GIMP_VERSION%" "$CUSTOM_GIMP_VERSION"
#### Needed to differentiate on zsh etc
conf_plist "%MUTEX_SUFFIX%" "$MUTEX_SUFFIX"
-### FIXME: Configure associations
+### List supported filetypes
+sed -i '' "s|%FILE_TYPES%|$(tr -d '\n' < $BUILD_DIR/plug-ins/file_associations_mac.list)|g" "$DMG_MOUNT/$BUNDLE_NAME.app/Contents/Info.plist"
## 4.2 FIXME: Create .DS_Store to set .dmg background and icon layout
printf '(INFO): generating .DS_Store\n'
diff --git a/build/macos/Info.plist b/build/macos/Info.plist
index be349640df..ff3234ebe1 100644
--- a/build/macos/Info.plist
+++ b/build/macos/Info.plist
@@ -39,5 +39,30 @@
NLSRequiresNativeExecution
+ CFBundleDocumentTypes
+
+
+ CFBundleTypeExtensions
+
+ xcf
+ XCF
+
+ CFBundleTypeMIMETypes
+
+ image/x-xcf
+
+ CFBundleTypeIconFiles
+
+ fileicon-xcf.icns
+
+ CFBundleTypeName
+ eXperimental Computing Facility file
+ CFBundleTypeRole
+ Editor
+ LSHandlerRank
+ Default
+
+ %FILE_TYPES%
+
diff --git a/plug-ins/generate_mime_ext.py b/plug-ins/generate_mime_ext.py
index bd1325d9fb..6c497150c5 100644
--- a/plug-ins/generate_mime_ext.py
+++ b/plug-ins/generate_mime_ext.py
@@ -3,7 +3,7 @@ import sys
import os
import re
-types_associations_list = set()
+types_associations_dict = {}
#Read file loading plug-ins sourcecode
source_files = sys.argv[3:]
@@ -16,39 +16,108 @@ for source_file in source_files:
continue
#Parse MIME types or extensions declared in the sourcecode
- mode = sys.argv[1]
- if mode == "--mime":
- function_suffix = 'set_mime_types'
- elif mode == "--association":
- function_suffix = 'set_extensions'
source_file_ext = os.path.splitext(source_file)[1].lower()
if source_file_ext == '.c':
if "LOAD_PROC" not in content and "load_procedure" not in content:
continue
- regex = (fr'gimp_file_procedure_{function_suffix}\s*'
- fr'\(\s*GIMP_FILE_PROCEDURE\s*\(\s*procedure\s*\)\s*,\s*"([^"]+)"')
+ proc_regex = r'gimp_(load|vector_load)_procedure_new'
+ mime_regex = r'gimp_file_procedure_set_mime_types\s*\([^,]+,\s*"([^"]+)"'
+ ext_regex = r'gimp_file_procedure_set_extensions\s*\([^,]+,\s*"([^"]+)"'
+ label_regex = r'gimp_procedure_set_menu_label\s*\([^,]+,\s*_\("([^"]+)"\)\s*\)'
elif source_file_ext == '.py':
if "LoadProcedure" not in content:
continue
- regex = fr'procedure\.{function_suffix}\s*\(\s*"([^"]+)"\s*\)'
+ proc_regex = r'(LoadProcedure|VectorLoadProcedure)\.new'
+ mime_regex = r'procedure\.set_mime_types\s*\(\s*"([^"]+)"\s*\)'
+ ext_regex = r'procedure\.set_extensions\s*\(\s*"([^"]+)"\s*\)'
+ label_regex = r'procedure\.set_menu_label\s*\(\s*_\("([^"]+)"\)\s*\)'
else:
continue
- for match in re.findall(regex, content, re.DOTALL):
- #(Take care of extensions separated by commas)
- for mime_extension in match.split(','):
- trimmed = mime_extension.strip()
- if trimmed:
- types_associations_list.add(trimmed)
+ for proc_match in re.finditer(proc_regex, content):
+ #proc_name = proc_match.group(1).strip()
+ #key = (source_file, f"{proc_name}_{proc_match.start()}")
+ key = (source_file, proc_match.start())
+ if key not in types_associations_dict:
+ types_associations_dict[key] = {"mime": set(), "extensions": set(), "label": set()}
+ mime_match = re.search(mime_regex, content[proc_match.end():])
+ if mime_match:
+ #(Take care of mimes separated by commas)
+ for mime in mime_match.group(1).split(','):
+ trimmed_mime = mime.strip()
+ if trimmed_mime:
+ types_associations_dict[key]["mime"].add(trimmed_mime)
+ ext_match = re.search(ext_regex, content[proc_match.end():])
+ if ext_match:
+ #(Take care of extensions separated by commas)
+ for ext in ext_match.group(1).split(','):
+ trimmed_ext = ext.strip()
+ if trimmed_ext:
+ types_associations_dict[key]["extensions"].add(trimmed_ext)
+ label_match = re.search(label_regex, content[proc_match.end():])
+ if label_match:
+ types_associations_dict[key]["label"] = label_match.group(1).strip()
+mode = sys.argv[1]
if mode == "--mime":
#Output string with the parsed MIME types
- print(";".join(sorted(types_associations_list)))
+ all_mime = set()
+ for values in types_associations_dict.values():
+ all_mime.update(values["mime"])
+ print(";".join(sorted(all_mime)))
elif mode == "--association":
#Create list with the parsed extensions
output_file = sys.argv[2]
+ all_ext = set()
+ for values in types_associations_dict.values():
+ all_ext.update(values["extensions"])
try:
with open(output_file, 'w', encoding='utf-8') as outf:
- outf.writelines(f"{assoc}\n" for assoc in sorted(types_associations_list))
+ outf.writelines(f"{assoc}\n" for assoc in sorted(all_ext))
except Exception as e:
sys.stderr.write(f"(ERROR): When writing output file {output_file}: {e}\n")
sys.exit(1)
+elif mode == "--pseudo-uti":
+ output_file = sys.argv[2]
+ try:
+ with open(output_file, 'w', encoding='utf-8') as outf:
+ for (source_file, proc_key), values in types_associations_dict.items():
+ extensions = sorted(values["extensions"])
+ mimes = sorted(values["mime"])
+ if not extensions and not mimes:
+ continue
+
+ outf.write("\n")
+ if extensions:
+ outf.write(" CFBundleTypeExtensions\n")
+ outf.write(" \n")
+ for ext in extensions:
+ outf.write(f" {ext}\n")
+ outf.write(f" {ext.upper()}\n")
+ outf.write(" \n")
+
+ if mimes:
+ outf.write(" CFBundleTypeMIMETypes\n")
+ outf.write(" \n")
+ for mime in mimes:
+ outf.write(f" {mime}\n")
+ outf.write(" \n")
+
+ outf.write(" CFBundleTypeIconFile\n")
+ outf.write(" fileicon\n")
+
+ if "label" in values:
+ outf.write(" CFBundleTypeName\n")
+ outf.write(f" {values['label']}\n")
+ elif extensions:
+ outf.write(" CFBundleTypeName\n")
+ outf.write(f" {extensions[0].upper()} file\n")
+
+ outf.write(" CFBundleTypeRole\n")
+ outf.write(" Viewer\n")
+
+ outf.write(" LSHandlerRank\n")
+ outf.write(" Default\n")
+ outf.write("\n")
+ except Exception as e:
+ sys.stderr.write(f"(ERROR): When writing pseudo-UTI file {output_file}: {e}\n")
+ sys.exit(1)
diff --git a/plug-ins/meson.build b/plug-ins/meson.build
index 19a2b9355a..9c2595e12b 100644
--- a/plug-ins/meson.build
+++ b/plug-ins/meson.build
@@ -62,7 +62,7 @@ if get_option('webkit-unmaintained')
'name': 'help-browser',
}
endif
-
+
if platform_windows and host_cpu_family == 'x86'
complex_plugins_list += {
'name': 'twain',
@@ -87,7 +87,7 @@ endforeach
subdir('python')
foreach plugin : python_plugins
- if plugin.get('name').startswith('file-')
+ if plugin.get('name').startswith('file-')
all_plugins_sources += [ meson.current_source_dir() / 'python' / plugin.get('name') + '.py' ]
endif
endforeach
@@ -116,3 +116,17 @@ if get_option('windows-installer') or get_option('ms-store')
build_by_default: true
)
endif
+
+if get_option('dmg')
+ custom_target('file_associations_mac',
+ input : all_plugins_sources,
+ output : 'file_associations_mac.list',
+ command : [
+ python,
+ meson.current_source_dir() / 'generate_mime_ext.py',
+ '--pseudo-uti',
+ '@OUTPUT@',
+ ] + all_plugins_sources,
+ build_by_default: true
+ )
+endif