diff --git a/CMakeLists.txt b/CMakeLists.txt index ed437c0342b50..537d558c91c13 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,9 @@ endif() set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") +set(NMC_RCC_FILE "nmctheme_v1.rcc") +configure_file(${CMAKE_SOURCE_DIR}/${NMC_RCC_FILE} "${BIN_OUTPUT_DIRECTORY}/${NMC_RCC_FILE}" COPYONLY) + include(${CMAKE_SOURCE_DIR}/NEXTCLOUD.cmake) set(QT_VERSION_MAJOR "6") @@ -366,6 +369,7 @@ configure_file(version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h) if(BUILD_OWNCLOUD_OSX_BUNDLE) install(FILES sync-exclude.lst DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/Resources/) configure_file(sync-exclude.lst bin/${OWNCLOUD_OSX_BUNDLE}/Contents/Resources/sync-exclude.lst COPYONLY) + install(FILES nmctheme_v1.rcc DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/Resources/) elseif(BUILD_CLIENT) install( FILES sync-exclude.lst DESTINATION ${SYSCONFDIR}/${APPLICATION_SHORTNAME} ) configure_file(sync-exclude.lst bin/sync-exclude.lst COPYONLY) diff --git a/cmake/modules/MacOSXBundleInfo.plist.in b/cmake/modules/MacOSXBundleInfo.plist.in index a94e577d5d8fd..5d5960cbc65b9 100644 --- a/cmake/modules/MacOSXBundleInfo.plist.in +++ b/cmake/modules/MacOSXBundleInfo.plist.in @@ -35,13 +35,17 @@ CFBundleShortVersionString @MIRALL_VERSION_STRING@ NSHumanReadableCopyright - (C) 2014-2025 @APPLICATION_VENDOR_XML_ESCAPED@ + (C) 2026 @APPLICATION_VENDOR_XML_ESCAPED@ NSSupportsAutomaticGraphicsSwitching NSRequiresAquaSystemAppearance SUShowReleaseNotes + SUEnableAutomaticChecks + + SUAutomaticallyUpdate + SUPublicDSAKeyFile dsa_pub.pem SUPublicEDKey diff --git a/nmctheme_v1.rcc b/nmctheme_v1.rcc new file mode 100644 index 0000000000000..1e673f886e4f5 Binary files /dev/null and b/nmctheme_v1.rcc differ diff --git a/shell_integration/MacOSX/CMakeLists.txt b/shell_integration/MacOSX/CMakeLists.txt index 6da1e35c84dc1..8bcb614814fe3 100644 --- a/shell_integration/MacOSX/CMakeLists.txt +++ b/shell_integration/MacOSX/CMakeLists.txt @@ -2,7 +2,14 @@ # SPDX-FileCopyrightText: 2014 ownCloud GmbH # SPDX-License-Identifier: GPL-2.0-or-later if(APPLE) - set(OC_OEM_SHARE_ICNS "${CMAKE_BINARY_DIR}/src/gui/${APPLICATION_ICON_NAME}.icns") + # src/gui uses the branded application short name for the generated + # .icns file, while Nextcloud.cmake may still define APPLICATION_ICON_NAME + # as "Nextcloud". + if(NOT APPLICATION_NAME STREQUAL "Nextcloud") + set(OC_OEM_SHARE_ICNS "${CMAKE_BINARY_DIR}/src/gui/${APPLICATION_SHORTNAME}.icns") + else() + set(OC_OEM_SHARE_ICNS "${CMAKE_BINARY_DIR}/src/gui/${APPLICATION_ICON_NAME}.icns") + endif() if (NEXTCLOUD_DEV) set(XCODE_TARGET_CONFIGURATION "Debug") diff --git a/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSync.m b/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSync.m index e5fd6bef26d96..7cf9a3255caeb 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSync.m +++ b/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSync.m @@ -16,6 +16,10 @@ @interface FinderSync() NSCondition *_menuIsComplete; os_log_t _log; } + +- (NSString *)socketPath; +- (void)connectToSocketAtPath:(NSString *)socketPath; + @end static os_log_t getFinderSyncLogger(void) { @@ -35,20 +39,71 @@ - (NSString *)socketPath { NSBundle *extBundle = [NSBundle bundleForClass:[self class]]; NSString *groupIdentifier = [extBundle objectForInfoDictionaryKey:@"NCApplicationGroupIdentifier"]; + if (!groupIdentifier.length) { + os_log_error(_log, "NCApplicationGroupIdentifier is missing."); return nil; } + NSURL *container = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:groupIdentifier]; + if (!container) { + os_log_error(_log, "Could not resolve app group container for identifier: %{public}@", groupIdentifier); + return nil; + } + return [container URLByAppendingPathComponent:@"s" isDirectory:NO].path; } +- (void)connectToSocketAtPath:(NSString *)socketPath +{ + if (self.localSocketClient) { + return; + } + + // FinderSync may start before the main application has created its socket. + if (![[NSFileManager defaultManager] fileExistsAtPath:socketPath]) { + os_log_debug(_log, "Socket does not exist yet, retrying: %{public}@", socketPath); + + __weak typeof(self) weakSelf = self; + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + __strong typeof(self) self = weakSelf; + if (!self) { + return; + } + + [self connectToSocketAtPath:socketPath]; + }); + + return; + } + + os_log_debug(_log, "Socket path determined and exists: %{public}@", socketPath); + + self.lineProcessor = [[FinderSyncSocketLineProcessor alloc] initWithDelegate:self]; + self.localSocketClient = [[LocalSocketClient alloc] initWithSocketPath:socketPath lineProcessor:self.lineProcessor]; + + __weak typeof(self) weakSelf = self; + self.localSocketClient.connectionEstablishedHandler = ^{ + __strong typeof(self) self = weakSelf; + if (!self) { + return; + } + + os_log_debug(self->_log, "Socket connection established."); + [self.localSocketClient askOnSocket:@"" query:@"GET_STRINGS"]; + }; + + [self.localSocketClient start]; +} + - (instancetype)init { - self = [super init]; + self = [super init]; if (self) { _log = getFinderSyncLogger(); os_log_debug(_log, "Initializing..."); + FIFinderSyncController *syncController = [FIFinderSyncController defaultController]; NSBundle *extBundle = [NSBundle bundleForClass:[self class]]; @@ -74,22 +129,14 @@ - (instancetype)init _menuIsComplete = [[NSCondition alloc] init]; NSString *socketPath = [self socketPath]; - os_log_debug(_log, "Socket path: %{public}@", socketPath); if (socketPath) { - self.lineProcessor = [[FinderSyncSocketLineProcessor alloc] initWithDelegate:self]; - self.localSocketClient = [[LocalSocketClient alloc] initWithSocketPath:socketPath - lineProcessor:self.lineProcessor]; - __weak typeof(self) weakSelf = self; - self.localSocketClient.connectionEstablishedHandler = ^{ - __strong typeof(self) self = weakSelf; - [self.localSocketClient askOnSocket:@"" query:@"GET_STRINGS"]; - }; - [self.localSocketClient start]; + [self connectToSocketAtPath:socketPath]; } else { os_log_error(_log, "No socket path available. Not initiating local socket client."); } + os_log_debug(_log, "Initialization completed."); } @@ -100,173 +147,216 @@ - (instancetype)init - (void)requestBadgeIdentifierForURL:(NSURL *)url { - os_log_debug(_log, "Requesting badge identifier for URL: %{public}@", url.path); - BOOL isDir; - if ([[NSFileManager defaultManager] fileExistsAtPath:[url path] isDirectory: &isDir] == NO) { - os_log_error(_log, "Could not determine file type of %{public}@", [url path]); - isDir = NO; - } - - NSString* normalizedPath = [[url path] decomposedStringWithCanonicalMapping]; - [self.localSocketClient askForIcon:normalizedPath isDirectory:isDir]; - os_log_debug(_log, "Badge identifier request completed for: %{public}@", normalizedPath); + os_log_debug(_log, "Requesting badge identifier for URL: %{public}@", url.path); + + BOOL isDir; + if ([[NSFileManager defaultManager] fileExistsAtPath:[url path] isDirectory:&isDir] == NO) { + os_log_error(_log, "Could not determine file type of %{public}@", [url path]); + isDir = NO; + } + + NSString *normalizedPath = [[url path] decomposedStringWithCanonicalMapping]; + [self.localSocketClient askForIcon:normalizedPath isDirectory:isDir]; + + os_log_debug(_log, "Badge identifier request completed for: %{public}@", normalizedPath); } #pragma mark - Menu and toolbar item support -- (NSString*) selectedPathsSeparatedByRecordSeparator +- (NSString *)selectedPathsSeparatedByRecordSeparator { - os_log_debug(_log, "Building selected paths string with record separators"); - FIFinderSyncController *syncController = [FIFinderSyncController defaultController]; - NSMutableString *string = [[NSMutableString alloc] init]; - [syncController.selectedItemURLs enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) { - if (string.length > 0) { - [string appendString:@"\x1e"]; // record separator - } - NSString* normalizedPath = [[obj path] decomposedStringWithCanonicalMapping]; - [string appendString:normalizedPath]; - }]; - os_log_debug(_log, "Selected paths string built: %lu paths", (unsigned long)syncController.selectedItemURLs.count); - return string; + os_log_debug(_log, "Building selected paths string with record separators"); + + FIFinderSyncController *syncController = [FIFinderSyncController defaultController]; + NSMutableString *string = [[NSMutableString alloc] init]; + + [syncController.selectedItemURLs enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + if (string.length > 0) { + [string appendString:@"\x1e"]; + } + + NSString *normalizedPath = [[obj path] decomposedStringWithCanonicalMapping]; + [string appendString:normalizedPath]; + }]; + + os_log_debug(_log, "Selected paths string built: %lu paths", (unsigned long)syncController.selectedItemURLs.count); + return string; } - (void)waitForMenuToArrive { os_log_debug(_log, "Waiting for menu to arrive"); + [self->_menuIsComplete lock]; [self->_menuIsComplete wait]; [self->_menuIsComplete unlock]; + os_log_debug(_log, "Menu arrival wait completed"); } - (NSMenu *)menuForMenuKind:(FIMenuKind)whichMenu { os_log_debug(_log, "Building menu for menu kind: %lu", (unsigned long)whichMenu); - if(![self.localSocketClient isConnected]) { + + if (![self.localSocketClient isConnected]) { os_log_error(_log, "Local socket client not connected, cannot build menu"); return nil; } - - FIFinderSyncController *syncController = [FIFinderSyncController defaultController]; - NSMutableSet *rootPaths = [[NSMutableSet alloc] init]; - [syncController.directoryURLs enumerateObjectsUsingBlock: ^(id obj, BOOL *stop) { - [rootPaths addObject:[obj path]]; - }]; - - // The server doesn't support sharing a root directory so do not show the option in this case. - // It is still possible to get a problematic sharing by selecting both the root and a child, - // but this is so complicated to do and meaningless that it's not worth putting this check - // also in shareMenuAction. - __block BOOL onlyRootsSelected = YES; - [syncController.selectedItemURLs enumerateObjectsUsingBlock: ^(id obj, NSUInteger idx, BOOL *stop) { - if (![rootPaths member:[obj path]]) { - onlyRootsSelected = NO; - *stop = YES; - } - }]; - os_log_debug(_log, "Root directories check: onlyRootsSelected = %d", onlyRootsSelected); - - NSString *paths = [self selectedPathsSeparatedByRecordSeparator]; - [self.localSocketClient askOnSocket:paths query:@"GET_MENU_ITEMS"]; - - // Since the LocalSocketClient communicates asynchronously. wait here until the menu - // is delivered by another thread + + FIFinderSyncController *syncController = [FIFinderSyncController defaultController]; + NSMutableSet *rootPaths = [[NSMutableSet alloc] init]; + + [syncController.directoryURLs enumerateObjectsUsingBlock:^(id obj, BOOL *stop) { + [rootPaths addObject:[obj path]]; + }]; + + // The server doesn't support sharing a root directory so do not show the option in this case. + // It is still possible to get a problematic sharing by selecting both the root and a child, + // but this is so complicated to do and meaningless that it's not worth putting this check + // also in shareMenuAction. + __block BOOL onlyRootsSelected = YES; + + [syncController.selectedItemURLs enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + if (![rootPaths member:[obj path]]) { + onlyRootsSelected = NO; + *stop = YES; + } + }]; + + os_log_debug(_log, "Root directories check: onlyRootsSelected = %d", onlyRootsSelected); + + NSString *paths = [self selectedPathsSeparatedByRecordSeparator]; + [self.localSocketClient askOnSocket:paths query:@"GET_MENU_ITEMS"]; + + // Since LocalSocketClient communicates asynchronously, wait until the menu + // is delivered by another thread. [self waitForMenuToArrive]; - id contextMenuTitle = [_strings objectForKey:@"CONTEXT_MENU_TITLE"]; - if (contextMenuTitle && !onlyRootsSelected) { - os_log_debug(_log, "Creating context menu with title: %{public}@", contextMenuTitle); - NSMenu *menu = [[NSMenu alloc] initWithTitle:@""]; - NSMenu *subMenu = [[NSMenu alloc] initWithTitle:@""]; - NSMenuItem *subMenuItem = [menu addItemWithTitle:contextMenuTitle action:nil keyEquivalent:@""]; - subMenuItem.submenu = subMenu; - subMenuItem.image = [[NSBundle mainBundle] imageForResource:@"app.icns"]; - - // There is an annoying bug in macOS (at least 10.13.3), it does not use/copy over the representedObject of a menu item - // So we have to use tag instead. - int idx = 0; - for (NSArray* item in _menuItems) { - NSMenuItem *actionItem = [subMenu addItemWithTitle:[item valueForKey:@"text"] - action:@selector(subMenuActionClicked:) - keyEquivalent:@""]; - [actionItem setTag:idx]; - [actionItem setTarget:self]; - NSString *flags = [item valueForKey:@"flags"]; // e.g. "d" - if ([flags rangeOfString:@"d"].location != NSNotFound) { - [actionItem setEnabled:false]; - } - idx++; - } - os_log_debug(_log, "Context menu created with %d items", idx); - return menu; - } - os_log_debug(_log, "No context menu created: contextMenuTitle=%@, onlyRootsSelected=%d", contextMenuTitle != nil ? @"present" : @"absent", onlyRootsSelected); - return nil; + id contextMenuTitle = [_strings objectForKey:@"CONTEXT_MENU_TITLE"]; + + if (contextMenuTitle && !onlyRootsSelected) { + os_log_debug(_log, "Creating context menu with title: %{public}@", contextMenuTitle); + + NSMenu *menu = [[NSMenu alloc] initWithTitle:@""]; + NSMenu *subMenu = [[NSMenu alloc] initWithTitle:@""]; + NSMenuItem *subMenuItem = [menu addItemWithTitle:contextMenuTitle action:nil keyEquivalent:@""]; + + subMenuItem.submenu = subMenu; + subMenuItem.image = [[NSBundle mainBundle] imageForResource:@"app.icns"]; + + // macOS does not reliably copy representedObject for Finder menu items, + // therefore use the tag as item index. + int idx = 0; + + for (NSArray *item in _menuItems) { + NSMenuItem *actionItem = [subMenu addItemWithTitle:[item valueForKey:@"text"] + action:@selector(subMenuActionClicked:) + keyEquivalent:@""]; + + [actionItem setTag:idx]; + [actionItem setTarget:self]; + + NSString *flags = [item valueForKey:@"flags"]; + if ([flags rangeOfString:@"d"].location != NSNotFound) { + [actionItem setEnabled:false]; + } + + idx++; + } + + os_log_debug(_log, "Context menu created with %d items", idx); + return menu; + } + + os_log_debug(_log, "No context menu created: contextMenuTitle=%@, onlyRootsSelected=%d", + contextMenuTitle != nil ? @"present" : @"absent", + onlyRootsSelected); + + return nil; } -- (void)subMenuActionClicked:(id)sender { - long idx = [(NSMenuItem*)sender tag]; - os_log_debug(_log, "Menu item clicked at index: %ld", idx); - NSString *command = [[_menuItems objectAtIndex:idx] valueForKey:@"command"]; - NSString *paths = [self selectedPathsSeparatedByRecordSeparator]; - os_log_debug(_log, "Executing command: %{public}@", command); - [self.localSocketClient askOnSocket:paths query:command]; - os_log_debug(_log, "Command execution completed"); +- (void)subMenuActionClicked:(id)sender +{ + long idx = [(NSMenuItem *)sender tag]; + + os_log_debug(_log, "Menu item clicked at index: %ld", idx); + + NSString *command = [[_menuItems objectAtIndex:idx] valueForKey:@"command"]; + NSString *paths = [self selectedPathsSeparatedByRecordSeparator]; + + os_log_debug(_log, "Executing command: %{public}@", command); + + [self.localSocketClient askOnSocket:paths query:command]; + + os_log_debug(_log, "Command execution completed"); } #pragma mark - SyncClientProxyDelegate implementation -- (void)setResult:(NSString *)result forPath:(NSString*)path +- (void)setResult:(NSString *)result forPath:(NSString *)path { os_log_debug(_log, "Setting result: %{public}@ for path: %{public}@", result, path); + NSString *const normalizedPath = path.decomposedStringWithCanonicalMapping; NSURL *const urlForPath = [NSURL fileURLWithPath:normalizedPath]; + if (urlForPath == nil) { os_log_error(_log, "Failed to create URL for path: %{public}@", normalizedPath); return; } + [FIFinderSyncController.defaultController setBadgeIdentifier:result forURL:urlForPath]; + os_log_debug(_log, "Badge identifier set successfully"); } -- (void)reFetchFileNameCacheForPath:(NSString*)path +- (void)reFetchFileNameCacheForPath:(NSString *)path { os_log_debug(_log, "Refetching file name cache for path: %{public}@", path); } -- (void)registerPath:(NSString*)path +- (void)registerPath:(NSString *)path { - os_log_debug(_log, "Registering path: %{public}@", path); - NSAssert(_registeredDirectories, @"Registered directories should be a valid set!"); - [_registeredDirectories addObject:[NSURL fileURLWithPath:path]]; - [FIFinderSyncController defaultController].directoryURLs = _registeredDirectories; - os_log_debug(_log, "Path registration completed"); + os_log_debug(_log, "Registering path: %{public}@", path); + + NSAssert(_registeredDirectories, @"Registered directories should be a valid set!"); + + [_registeredDirectories addObject:[NSURL fileURLWithPath:path]]; + [FIFinderSyncController defaultController].directoryURLs = _registeredDirectories; + + os_log_debug(_log, "Path registration completed"); } -- (void)unregisterPath:(NSString*)path +- (void)unregisterPath:(NSString *)path { - os_log_debug(_log, "Unregistering path: %{public}@", path); - [_registeredDirectories removeObject:[NSURL fileURLWithPath:path]]; - [FIFinderSyncController defaultController].directoryURLs = _registeredDirectories; - os_log_debug(_log, "Path unregistration completed"); + os_log_debug(_log, "Unregistering path: %{public}@", path); + + [_registeredDirectories removeObject:[NSURL fileURLWithPath:path]]; + [FIFinderSyncController defaultController].directoryURLs = _registeredDirectories; + + os_log_debug(_log, "Path unregistration completed"); } -- (void)setString:(NSString*)key value:(NSString*)value +- (void)setString:(NSString *)key value:(NSString *)value { - os_log_debug(_log, "Setting string: %{public}@ = %{public}@", key, value); - [_strings setObject:value forKey:key]; + os_log_debug(_log, "Setting string: %{public}@ = %{public}@", key, value); + [_strings setObject:value forKey:key]; } - (void)resetMenuItems { - os_log_debug(_log, "Resetting menu items"); - _menuItems = [[NSMutableArray alloc] init]; - os_log_debug(_log, "Menu items reset completed"); + os_log_debug(_log, "Resetting menu items"); + _menuItems = [[NSMutableArray alloc] init]; + os_log_debug(_log, "Menu items reset completed"); } -- (void)addMenuItem:(NSDictionary *)item { + +- (void)addMenuItem:(NSDictionary *)item +{ os_log_debug(_log, "Adding menu item with title: %{public}@", [item valueForKey:@"text"] ?: @"(no title)"); - [_menuItems addObject:item]; - os_log_debug(_log, "Menu item added, total items: %lu", (unsigned long)_menuItems.count); + + [_menuItems addObject:item]; + + os_log_debug(_log, "Menu item added, total items: %lu", (unsigned long)_menuItems.count); } - (void)menuHasCompleted @@ -278,18 +368,22 @@ - (void)menuHasCompleted - (void)connectionDidDie { - os_log_error(_log, "Connection to sync client died"); - [_strings removeAllObjects]; - [_registeredDirectories removeAllObjects]; - // For some reason the FIFinderSync cache doesn't seem to be cleared for the root item when - // we reset the directoryURLs (seen on macOS 10.12 at least). - // First setting it to the FS root and then setting it to nil seems to work around the issue. - [FIFinderSyncController defaultController].directoryURLs = [NSSet setWithObject:[NSURL fileURLWithPath:@"/"]]; - // This will tell Finder that this extension isn't attached to any directory - // until we can reconnect to the sync client. - [FIFinderSyncController defaultController].directoryURLs = nil; - os_log_error(_log, "Connection cleanup completed, waiting for reconnection"); -} + os_log_error(_log, "Connection to sync client died"); -@end + [_strings removeAllObjects]; + [_registeredDirectories removeAllObjects]; + + // For some reason the FIFinderSync cache doesn't seem to be cleared for the root item when + // we reset the directoryURLs (seen on macOS 10.12 at least). + // First setting it to the FS root and then setting it to nil seems to work around the issue. + [FIFinderSyncController defaultController].directoryURLs = + [NSSet setWithObject:[NSURL fileURLWithPath:@"/"]]; + + // This will tell Finder that this extension isn't attached to any directory + // until we can reconnect to the sync client. + [FIFinderSyncController defaultController].directoryURLs = nil; + + os_log_error(_log, "Connection cleanup completed, waiting for reconnection"); +} +@end \ No newline at end of file diff --git a/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Build.xcconfig b/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Build.xcconfig index 2d11ace5ed34e..cfe490eb61326 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Build.xcconfig +++ b/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Build.xcconfig @@ -16,7 +16,7 @@ CURRENT_PROJECT_VERSION=$(CURRENT_PROJECT_VERSION:default=1) // // The development team identifier registered with Apple. // -DEVELOPMENT_TEAM=$(DEVELOPMENT_TEAM:default=NKUJUXUJ3B) +DEVELOPMENT_TEAM=$(DEVELOPMENT_TEAM:default=S5724FNY84) // // The app name. diff --git a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj index c5739990c9e36..0a7c8f2b2fcee 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj +++ b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj @@ -1400,7 +1400,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = NKUJUXUJ3B; + DEVELOPMENT_TEAM = S5724FNY84; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; ENABLE_TESTING_SEARCH_PATHS = YES; @@ -1442,7 +1442,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = NKUJUXUJ3B; + DEVELOPMENT_TEAM = S5724FNY84; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTING_SEARCH_PATHS = YES; GCC_NO_COMMON_BLOCKS = YES; diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 7f45acfeba5ed..6ce8887497080 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -20,9 +20,69 @@ if(APPLICATION_NAME STREQUAL "Nextcloud") set(STATE_SUBDIR "nextcloud/") endif() -configure_file(${CMAKE_SOURCE_DIR}/theme.qrc.in ${CMAKE_SOURCE_DIR}/theme.qrc) +# Remember the icon source name supplied by the Brander before replacing +# it with the branded application name for generated resources. +if(NOT DEFINED APPLICATION_ICON_NAME) + set(APPLICATION_ICON_NAME "${APPLICATION_SHORTNAME}") +endif() + +set(APPLICATION_ICON_SOURCE_NAME "${APPLICATION_ICON_NAME}") + +if(NOT APPLICATION_NAME STREQUAL "Nextcloud") + set(APPLICATION_ICON_NAME "${APPLICATION_SHORTNAME}") +endif() + +message(STATUS "APPLICATION_ICON_SOURCE_NAME: ${APPLICATION_ICON_SOURCE_NAME}") +message(STATUS "APPLICATION_ICON_NAME: ${APPLICATION_ICON_NAME}") + set(theme_dir ${CMAKE_SOURCE_DIR}/theme) +# The Brander still provides the customized source assets using the +# Nextcloud filenames. Create branded aliases used by the build. +if(NOT APPLICATION_ICON_SOURCE_NAME STREQUAL APPLICATION_ICON_NAME) + + set(APP_ICON_SOURCE_SVG + "${theme_dir}/colored/${APPLICATION_ICON_SOURCE_NAME}-icon.svg") + set(APP_ICON_BRANDED_SVG + "${theme_dir}/colored/${APPLICATION_ICON_NAME}-icon.svg") + + if(NOT EXISTS "${APP_ICON_SOURCE_SVG}") + message(FATAL_ERROR + "Application icon source does not exist: ${APP_ICON_SOURCE_SVG}") + endif() + + configure_file( + "${APP_ICON_SOURCE_SVG}" + "${APP_ICON_BRANDED_SVG}" + COPYONLY + ) + + if(APPLE) + # Prefer the macOS-specific sidebar source. + set(MACOS_SIDEBAR_SOURCE_SVG + "${theme_dir}/colored/${APPLICATION_ICON_SOURCE_NAME}-macOS-sidebar.svg") + + # Fallback for themes using the old generic sidebar filename. + if(NOT EXISTS "${MACOS_SIDEBAR_SOURCE_SVG}") + set(MACOS_SIDEBAR_SOURCE_SVG + "${theme_dir}/colored/${APPLICATION_ICON_SOURCE_NAME}-sidebar.svg") + endif() + + if(NOT EXISTS "${MACOS_SIDEBAR_SOURCE_SVG}") + message(FATAL_ERROR + "macOS sidebar icon source does not exist: ${MACOS_SIDEBAR_SOURCE_SVG}") + endif() + + configure_file( + "${MACOS_SIDEBAR_SOURCE_SVG}" + "${theme_dir}/colored/${APPLICATION_ICON_NAME}-sidebar.svg" + COPYONLY + ) + endif() +endif() + +configure_file(${CMAKE_SOURCE_DIR}/theme.qrc.in ${CMAKE_SOURCE_DIR}/theme.qrc) + set(client_UI_SRCS accountsettings.ui conflictdialog.ui @@ -588,6 +648,7 @@ if(NOT BUILD_OWNCLOUD_OSX_BUNDLE) install(FILES ${VISUAL_ELEMENTS} DESTINATION bin/visualelements) install(FILES "${theme_dir}/${APPLICATION_EXECUTABLE}.VisualElementsManifest.xml" DESTINATION bin) install(FILES ${client_I18N} DESTINATION i18n) + install(FILES ${CMAKE_SOURCE_DIR}/nmctheme_v1.rcc DESTINATION bin) endif() # we may not add MACOSX_BUNDLE here, if not building one diff --git a/src/gui/application.cpp b/src/gui/application.cpp index 0a3fa090bb88b..cb82d21008594 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -248,11 +248,12 @@ Application::Application(int &argc, char **argv) // TODO: Can't set this without breaking current config paths // setOrganizationName(QLatin1String(APPLICATION_VENDOR)); setOrganizationDomain(QLatin1String(APPLICATION_REV_DOMAIN)); - setDesktopFileName(QString(LINUX_APPLICATION_ID)); - setApplicationName(_theme->appName()); + +#ifndef Q_OS_MACOS setWindowIcon(_theme->applicationIcon()); +#endif parseOptions(arguments()); //no need to waste time; diff --git a/src/gui/main.cpp b/src/gui/main.cpp index fe1089110940f..db9b63386e7fc 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -64,6 +64,17 @@ int main(int argc, char **argv) qputenv("QML_IMPORT_PATH", (QDir::currentPath() + QStringLiteral("/qml")).toLatin1()); #endif + bool resourceLoaded = false; + const QString currentPath = QDir::currentPath(); + + if (Utility::isMac()) { + resourceLoaded = QResource::registerResource(QDir::toNativeSeparators("/Applications/MagentaCLOUD.app/Contents/Resources/nmctheme_v1.rcc")); + } + + if (Utility::isWindows() || !resourceLoaded) { + resourceLoaded = QResource::registerResource(QDir::toNativeSeparators(currentPath + "/nmctheme_v1.rcc")); + } + Q_INIT_RESOURCE(resources); Q_INIT_RESOURCE(theme); diff --git a/theme.qrc.in b/theme.qrc.in index 8ebb6e3d7b590..4336d90164fe6 100644 --- a/theme.qrc.in +++ b/theme.qrc.in @@ -146,6 +146,37 @@ + + theme/NMCIcons/accountAvatarIcon.svg + theme/NMCIcons/action-add.svg + theme/NMCIcons/cloud-security.svg + theme/NMCIcons/collapse-down.svg + theme/NMCIcons/collapse-right.svg + theme/NMCIcons/configuration1.png + theme/NMCIcons/configuration2.png + theme/NMCIcons/configuration3.png + theme/NMCIcons/logout.svg + theme/NMCIcons/navigation-left.svg + theme/NMCIcons/navigation-right.svg + theme/NMCIcons/pause.svg + theme/NMCIcons/remove.svg + theme/NMCIcons/website.svg + + + theme/NMCIcons/accountAvatarIcon-white.svg + theme/NMCIcons/logout-white.svg + theme/NMCIcons/pause-white.svg + theme/NMCIcons/remove-white.svg + theme/NMCIcons/website-white.svg + theme/close-white.svg + theme/more-white.svg + theme/settings-white.svg + + + theme/NMCIcons/ApplicationLogo.svg + theme/NMCIcons/folderLogo.svg + theme/NMCIcons/tlogocarrier.svg + theme/colored/wizard_logo.png theme/colored/wizard_logo@2x.png diff --git a/theme/NMCIcons/ApplicationLogo.svg b/theme/NMCIcons/ApplicationLogo.svg new file mode 100644 index 0000000000000..efc8c94e1df28 --- /dev/null +++ b/theme/NMCIcons/ApplicationLogo.svg @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/theme/NMCIcons/accountAvatarIcon-white.svg b/theme/NMCIcons/accountAvatarIcon-white.svg new file mode 100644 index 0000000000000..1071c83ea6ef8 --- /dev/null +++ b/theme/NMCIcons/accountAvatarIcon-white.svg @@ -0,0 +1,7 @@ + + + icon/user_file/user/default@svg + + + + \ No newline at end of file diff --git a/theme/NMCIcons/accountAvatarIcon.svg b/theme/NMCIcons/accountAvatarIcon.svg new file mode 100644 index 0000000000000..ba966999fc6cf --- /dev/null +++ b/theme/NMCIcons/accountAvatarIcon.svg @@ -0,0 +1,7 @@ + + + icon/user_file/user/default@svg + + + + diff --git a/theme/NMCIcons/action-add.svg b/theme/NMCIcons/action-add.svg new file mode 100644 index 0000000000000..46df2f888cc15 --- /dev/null +++ b/theme/NMCIcons/action-add.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/theme/NMCIcons/cloud-security.svg b/theme/NMCIcons/cloud-security.svg new file mode 100644 index 0000000000000..bfa4b9cfc802a --- /dev/null +++ b/theme/NMCIcons/cloud-security.svg @@ -0,0 +1,7 @@ + + + /svg/icon/content/cloud-security/default + + + + \ No newline at end of file diff --git a/theme/NMCIcons/collapse-down.svg b/theme/NMCIcons/collapse-down.svg new file mode 100644 index 0000000000000..b63406256c813 --- /dev/null +++ b/theme/NMCIcons/collapse-down.svg @@ -0,0 +1,7 @@ + + + /svg/icon/navigation/collapse-down/default + + + + \ No newline at end of file diff --git a/theme/NMCIcons/collapse-right.svg b/theme/NMCIcons/collapse-right.svg new file mode 100644 index 0000000000000..c353781f763c1 --- /dev/null +++ b/theme/NMCIcons/collapse-right.svg @@ -0,0 +1,7 @@ + + + /svg/icon/navigation/right/default + + + + \ No newline at end of file diff --git a/theme/NMCIcons/configuration1.png b/theme/NMCIcons/configuration1.png new file mode 100644 index 0000000000000..7a20bc1a11347 Binary files /dev/null and b/theme/NMCIcons/configuration1.png differ diff --git a/theme/NMCIcons/configuration2.png b/theme/NMCIcons/configuration2.png new file mode 100644 index 0000000000000..dbdac7d23fca2 Binary files /dev/null and b/theme/NMCIcons/configuration2.png differ diff --git a/theme/NMCIcons/configuration3.png b/theme/NMCIcons/configuration3.png new file mode 100644 index 0000000000000..66d2d513c8ad3 Binary files /dev/null and b/theme/NMCIcons/configuration3.png differ diff --git a/theme/NMCIcons/folderLogo.svg b/theme/NMCIcons/folderLogo.svg new file mode 100644 index 0000000000000..45f4cb770d6bb --- /dev/null +++ b/theme/NMCIcons/folderLogo.svg @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/theme/NMCIcons/logout-white.svg b/theme/NMCIcons/logout-white.svg new file mode 100644 index 0000000000000..f449764e1a279 --- /dev/null +++ b/theme/NMCIcons/logout-white.svg @@ -0,0 +1,7 @@ + + + /svg/icon/action/logout/default + + + + \ No newline at end of file diff --git a/theme/NMCIcons/logout.svg b/theme/NMCIcons/logout.svg new file mode 100644 index 0000000000000..babca11ccf863 --- /dev/null +++ b/theme/NMCIcons/logout.svg @@ -0,0 +1,7 @@ + + + /svg/icon/action/logout/default + + + + \ No newline at end of file diff --git a/theme/NMCIcons/navigation-left.svg b/theme/NMCIcons/navigation-left.svg new file mode 100644 index 0000000000000..bfa5fa38304b4 --- /dev/null +++ b/theme/NMCIcons/navigation-left.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/theme/NMCIcons/navigation-right.svg b/theme/NMCIcons/navigation-right.svg new file mode 100644 index 0000000000000..a008c1d637d63 --- /dev/null +++ b/theme/NMCIcons/navigation-right.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/theme/NMCIcons/pause-white.svg b/theme/NMCIcons/pause-white.svg new file mode 100644 index 0000000000000..41fd92da98393 --- /dev/null +++ b/theme/NMCIcons/pause-white.svg @@ -0,0 +1,7 @@ + + + icon/action/pause/default@svg + + + + \ No newline at end of file diff --git a/theme/NMCIcons/pause.svg b/theme/NMCIcons/pause.svg new file mode 100644 index 0000000000000..7d8cc0999d9e9 --- /dev/null +++ b/theme/NMCIcons/pause.svg @@ -0,0 +1,7 @@ + + + icon/action/pause/default@svg + + + + \ No newline at end of file diff --git a/theme/NMCIcons/remove-white.svg b/theme/NMCIcons/remove-white.svg new file mode 100644 index 0000000000000..9a71790bcff30 --- /dev/null +++ b/theme/NMCIcons/remove-white.svg @@ -0,0 +1,7 @@ + + + /svg/icon/action/remove/default + + + + \ No newline at end of file diff --git a/theme/NMCIcons/remove.svg b/theme/NMCIcons/remove.svg new file mode 100644 index 0000000000000..9896ad3ca78a7 --- /dev/null +++ b/theme/NMCIcons/remove.svg @@ -0,0 +1,7 @@ + + + /svg/icon/action/remove/default + + + + \ No newline at end of file diff --git a/theme/NMCIcons/tlogocarrier.svg b/theme/NMCIcons/tlogocarrier.svg new file mode 100644 index 0000000000000..8fb2ff11a1050 --- /dev/null +++ b/theme/NMCIcons/tlogocarrier.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/theme/NMCIcons/website-white.svg b/theme/NMCIcons/website-white.svg new file mode 100644 index 0000000000000..4db891a1afe17 --- /dev/null +++ b/theme/NMCIcons/website-white.svg @@ -0,0 +1,7 @@ + + + icon/content/news/default@svg + + + + \ No newline at end of file diff --git a/theme/NMCIcons/website.svg b/theme/NMCIcons/website.svg new file mode 100644 index 0000000000000..4cdf4813f3dbe --- /dev/null +++ b/theme/NMCIcons/website.svg @@ -0,0 +1,7 @@ + + + icon/content/news/default@svg + + + + diff --git a/theme/close-white.svg b/theme/close-white.svg new file mode 100644 index 0000000000000..f4f3c0c23cc5e --- /dev/null +++ b/theme/close-white.svg @@ -0,0 +1,7 @@ + + + icon/action/circle-close/default@svg + + + + \ No newline at end of file diff --git a/theme/more-white.svg b/theme/more-white.svg new file mode 100644 index 0000000000000..7efdfcbc6c514 --- /dev/null +++ b/theme/more-white.svg @@ -0,0 +1,7 @@ + + + /svg/icon/action/more/default + + + + \ No newline at end of file diff --git a/theme/settings-white.svg b/theme/settings-white.svg new file mode 100644 index 0000000000000..9004d38106663 --- /dev/null +++ b/theme/settings-white.svg @@ -0,0 +1,7 @@ + + + /svg/icon/service/settings/default + + + + \ No newline at end of file