summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
authorMinh Nguyễn <mxn@1ec5.org>2015-12-24 20:49:17 -0800
committerMinh Nguyễn <mxn@1ec5.org>2016-01-04 09:33:00 -0800
commit0712ed904b7cb6385e840eb0d9682d69a7306bda (patch)
tree3bd5547e0325194ed1e4072f5e95ef8709540632 /platform
parent6e4eb094bf2feb9be4f5d2d73b1186aed768539b (diff)
downloadqtlocation-mapboxgl-0712ed904b7cb6385e840eb0d9682d69a7306bda.tar.gz
[osx] Make osxapp document-based
Allows for multiple map windows open at a time.
Diffstat (limited to 'platform')
-rw-r--r--platform/osx/app/AppDelegate.h1
-rw-r--r--platform/osx/app/AppDelegate.m540
-rw-r--r--platform/osx/app/Info.plist15
-rw-r--r--platform/osx/app/MainMenu.xib120
-rw-r--r--platform/osx/app/MapDocument.h14
-rw-r--r--platform/osx/app/MapDocument.m568
-rw-r--r--platform/osx/app/MapDocument.xib129
-rw-r--r--platform/osx/app/mapboxgl-app.gypi3
8 files changed, 746 insertions, 644 deletions
diff --git a/platform/osx/app/AppDelegate.h b/platform/osx/app/AppDelegate.h
index 69b6e0f13a..9f76ee76f9 100644
--- a/platform/osx/app/AppDelegate.h
+++ b/platform/osx/app/AppDelegate.h
@@ -2,6 +2,7 @@
@interface AppDelegate : NSObject <NSApplicationDelegate>
+@property (weak) IBOutlet NSWindow *preferencesWindow;
@end
diff --git a/platform/osx/app/AppDelegate.m b/platform/osx/app/AppDelegate.m
index 6aaf965357..76c5222072 100644
--- a/platform/osx/app/AppDelegate.m
+++ b/platform/osx/app/AppDelegate.m
@@ -1,40 +1,18 @@
#import "AppDelegate.h"
-#import "DroppedPinAnnotation.h"
#import "LocationCoordinate2DTransformer.h"
+#import "MapDocument.h"
#import "NSValue+Additions.h"
#import <Mapbox/Mapbox.h>
static NSString * const MGLMapboxAccessTokenDefaultsKey = @"MGLMapboxAccessToken";
-static NSString * const MGLDroppedPinAnnotationImageIdentifier = @"dropped";
-static const CLLocationCoordinate2D WorldTourDestinations[] = {
- { .latitude = 38.9131982, .longitude = -77.0325453144239 },
- { .latitude = 37.7757368, .longitude = -122.4135302 },
- { .latitude = 12.9810816, .longitude = 77.6368034 },
- { .latitude = -13.15589555, .longitude = -74.2178961777998 },
-};
-
-@interface AppDelegate () <NSApplicationDelegate, NSSharingServicePickerDelegate, NSMenuDelegate, MGLMapViewDelegate>
-
-@property (weak) IBOutlet NSWindow *window;
-@property (weak) IBOutlet MGLMapView *mapView;
-@property (weak) IBOutlet NSMenu *mapViewContextMenu;
-
-@property (weak) IBOutlet NSWindow *preferencesWindow;
+@interface AppDelegate ()
@end
-@implementation AppDelegate {
- NSPoint _mouseLocationForMapViewContextMenu;
- NSUInteger _droppedPinCounter;
- NSNumberFormatter *_spellOutNumberFormatter;
-
- BOOL _showsToolTipsOnDroppedPins;
- BOOL _randomizesCursorsOnDroppedPins;
- BOOL _isTouringWorld;
-}
+@implementation AppDelegate
#pragma mark Lifecycle
@@ -85,16 +63,6 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
[alert runModal];
[self showPreferences:nil];
}
-
- NSURL *savedURL = [[NSUserDefaults standardUserDefaults] URLForKey:@"MBXCurrentStyleURL"];
- if (savedURL) {
- self.mapView.styleURL = savedURL;
- }
-
- _spellOutNumberFormatter = [[NSNumberFormatter alloc] init];
-
- NSPressGestureRecognizer *pressGestureRecognizer = [[NSPressGestureRecognizer alloc] initWithTarget:self action:@selector(handlePressGesture:)];
- [self.mapView addGestureRecognizer:pressGestureRecognizer];
}
- (void)applicationWillTerminate:(NSNotification *)notification {
@@ -106,13 +74,20 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
NSString *accessToken = [userDefaults stringForKey:MGLMapboxAccessTokenDefaultsKey];
if (![accessToken isEqualToString:[MGLAccountManager accessToken]]) {
[MGLAccountManager setAccessToken:accessToken];
- [self reload:self];
+ [self.mainDocument reload:self];
}
}
+- (MapDocument *)mainDocument {
+ NSDocument *mainDocument = [NSApp mainWindow].windowController.document;
+ return [mainDocument isKindOfClass:[MapDocument class]] ? (MapDocument *)mainDocument : nil;
+}
+
#pragma mark Services
- (void)handleGetURLEvent:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent {
+ [[NSDocumentController sharedDocumentController] newDocument:self];
+
NSURL *url = [NSURL URLWithString:[event paramDescriptorForKeyword:keyDirectObject].stringValue];
NS_MUTABLE_DICTIONARY_OF(NSString *, NSString *) *params = [[NSMutableDictionary alloc] init];
for (NSString *param in [url.query componentsSeparatedByString:@"&"]) {
@@ -126,218 +101,20 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
if (centerString) {
NSArray *coordinateValues = [centerString componentsSeparatedByString:@","];
if (coordinateValues.count == 2) {
- self.mapView.centerCoordinate = CLLocationCoordinate2DMake([coordinateValues[0] doubleValue],
- [coordinateValues[1] doubleValue]);
+ self.mainDocument.mapView.centerCoordinate = CLLocationCoordinate2DMake([coordinateValues[0] doubleValue],
+ [coordinateValues[1] doubleValue]);
}
}
NSString *zoomLevelString = params[@"zoom"];
if (zoomLevelString.length) {
- self.mapView.zoomLevel = zoomLevelString.doubleValue;
+ self.mainDocument.mapView.zoomLevel = zoomLevelString.doubleValue;
}
NSString *directionString = params[@"bearing"];
if (directionString.length) {
- self.mapView.direction = directionString.doubleValue;
- }
-}
-
-- (IBAction)showShareMenu:(id)sender {
- NSSharingServicePicker *picker = [[NSSharingServicePicker alloc] initWithItems:@[self.shareURL]];
- picker.delegate = self;
- [picker showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];
-}
-
-- (NSURL *)shareURL {
- NSArray *components = self.mapView.styleURL.pathComponents;
- CLLocationCoordinate2D centerCoordinate = self.mapView.centerCoordinate;
- return [NSURL URLWithString:
- [NSString stringWithFormat:@"https://api.mapbox.com/styles/v1/%@/%@.html?access_token=%@#%.2f/%.5f/%.5f/%.f",
- components[1], components[2], [MGLAccountManager accessToken],
- self.mapView.zoomLevel, centerCoordinate.latitude, centerCoordinate.longitude, self.mapView.direction]];
-}
-
-#pragma mark View methods
-
-- (IBAction)setStyle:(id)sender {
- NSInteger tag;
- if ([sender isKindOfClass:[NSMenuItem class]]) {
- tag = [sender tag];
- } else if ([sender isKindOfClass:[NSPopUpButton class]]) {
- tag = [sender selectedTag];
- }
- NSURL *styleURL;
- switch (tag) {
- case 1:
- styleURL = [MGLStyle streetsStyleURL];
- break;
- case 2:
- styleURL = [MGLStyle emeraldStyleURL];
- break;
- case 3:
- styleURL = [MGLStyle lightStyleURL];
- break;
- case 4:
- styleURL = [MGLStyle darkStyleURL];
- break;
- case 5:
- styleURL = [MGLStyle satelliteStyleURL];
- break;
- case 6:
- styleURL = [MGLStyle hybridStyleURL];
- break;
- default:
- NSAssert(NO, @"Cannot set style from control with tag %li", (long)tag);
- break;
+ self.mainDocument.mapView.direction = directionString.doubleValue;
}
- self.mapView.styleURL = styleURL;
- [[NSUserDefaults standardUserDefaults] setURL:self.mapView.styleURL forKey:@"MBXCurrentStyleURL"];
- [self.window.toolbar validateVisibleItems];
-}
-
-- (IBAction)chooseCustomStyle:(id)sender {
- NSAlert *alert = [[NSAlert alloc] init];
- alert.messageText = @"Apply custom style";
- alert.informativeText = @"Enter the URL to a JSON file that conforms to the Mapbox GL style specification, such as a style designed in Mapbox Studio:";
- NSTextField *textField = [[NSTextField alloc] initWithFrame:NSZeroRect];
- [textField sizeToFit];
- NSRect textFieldFrame = textField.frame;
- textFieldFrame.size.width = 300;
- textField.frame = textFieldFrame;
- NSURL *savedURL = [[NSUserDefaults standardUserDefaults] URLForKey:@"MBXCustomStyleURL"];
- if (savedURL) {
- textField.stringValue = savedURL.absoluteString;
- }
- alert.accessoryView = textField;
- [alert addButtonWithTitle:@"Apply"];
- [alert addButtonWithTitle:@"Cancel"];
- if ([alert runModal] == NSAlertFirstButtonReturn) {
- self.mapView.styleURL = [NSURL URLWithString:textField.stringValue];
- [[NSUserDefaults standardUserDefaults] setURL:self.mapView.styleURL forKey:@"MBXCustomStyleURL"];
- [[NSUserDefaults standardUserDefaults] setURL:self.mapView.styleURL forKey:@"MBXCurrentStyleURL"];
- [self.window.toolbar validateVisibleItems];
- }
-}
-
-- (IBAction)zoomIn:(id)sender {
- [self.mapView setZoomLevel:self.mapView.zoomLevel + 1 animated:YES];
-}
-
-- (IBAction)zoomOut:(id)sender {
- [self.mapView setZoomLevel:self.mapView.zoomLevel - 1 animated:YES];
-}
-
-- (IBAction)snapToNorth:(id)sender {
- [self.mapView setDirection:0 animated:YES];
-}
-
-- (IBAction)reload:(id)sender {
- [self.mapView reloadStyle:sender];
-}
-
-#pragma mark Debug methods
-
-- (IBAction)toggleTileBoundaries:(id)sender {
- self.mapView.debugMask ^= MGLMapDebugTileBoundariesMask;
-}
-
-- (IBAction)toggleTileInfo:(id)sender {
- self.mapView.debugMask ^= MGLMapDebugTileInfoMask;
-}
-
-- (IBAction)toggleTileTimestamps:(id)sender {
- self.mapView.debugMask ^= MGLMapDebugTimestampsMask;
-}
-
-- (IBAction)toggleCollisionBoxes:(id)sender {
- self.mapView.debugMask ^= MGLMapDebugCollisionBoxesMask;
-}
-
-- (IBAction)toggleShowsToolTipsOnDroppedPins:(id)sender {
- _showsToolTipsOnDroppedPins = !_showsToolTipsOnDroppedPins;
-}
-
-- (IBAction)toggleRandomizesCursorsOnDroppedPins:(id)sender {
- _randomizesCursorsOnDroppedPins = !_randomizesCursorsOnDroppedPins;
-}
-
-- (IBAction)dropManyPins:(id)sender {
- [self.mapView removeAnnotations:self.mapView.annotations];
-
- NSRect bounds = self.mapView.bounds;
- NSMutableArray *annotations = [NSMutableArray array];
- for (CGFloat x = NSMinX(bounds); x < NSMaxX(bounds); x += arc4random_uniform(50)) {
- for (CGFloat y = NSMaxY(bounds); y >= NSMinY(bounds); y -= arc4random_uniform(100)) {
- [annotations addObject:[self pinAtPoint:NSMakePoint(x, y)]];
- }
- }
-
- [NSTimer scheduledTimerWithTimeInterval:1/60
- target:self
- selector:@selector(dropOneOfManyPins:)
- userInfo:annotations
- repeats:YES];
-}
-
-- (void)dropOneOfManyPins:(NSTimer *)timer {
- NSMutableArray *annotations = timer.userInfo;
- NSUInteger numberOfAnnotationsToAdd = 50;
- if (annotations.count < numberOfAnnotationsToAdd) {
- numberOfAnnotationsToAdd = annotations.count;
- }
- NSArray *annotationsToAdd = [annotations subarrayWithRange:
- NSMakeRange(0, numberOfAnnotationsToAdd)];
- [self.mapView addAnnotations:annotationsToAdd];
- [annotations removeObjectsInRange:NSMakeRange(0, numberOfAnnotationsToAdd)];
- if (!annotations.count) {
- [timer invalidate];
- }
-}
-
-- (IBAction)removeAllPins:(id)sender {
- [self.mapView removeAnnotations:self.mapView.annotations];
-}
-
-- (IBAction)startWorldTour:(id)sender {
- _isTouringWorld = YES;
-
- [self removeAllPins:sender];
- NSUInteger numberOfAnnotations = sizeof(WorldTourDestinations) / sizeof(WorldTourDestinations[0]);
- NSMutableArray *annotations = [NSMutableArray arrayWithCapacity:numberOfAnnotations];
- for (NSUInteger i = 0; i < numberOfAnnotations; i++) {
- MGLPointAnnotation *annotation = [[MGLPointAnnotation alloc] init];
- annotation.coordinate = WorldTourDestinations[i];
- [annotations addObject:annotation];
- }
- [self.mapView addAnnotations:annotations];
- [self continueWorldTourWithRemainingAnnotations:annotations];
-}
-
-- (void)continueWorldTourWithRemainingAnnotations:(NS_MUTABLE_ARRAY_OF(MGLPointAnnotation *) *)annotations {
- MGLPointAnnotation *nextAnnotation = annotations.firstObject;
- if (!nextAnnotation || !_isTouringWorld) {
- _isTouringWorld = NO;
- return;
- }
-
- [annotations removeObjectAtIndex:0];
- MGLMapCamera *camera = [MGLMapCamera cameraLookingAtCenterCoordinate:nextAnnotation.coordinate
- fromDistance:0
- pitch:arc4random_uniform(60)
- heading:arc4random_uniform(360)];
- __weak AppDelegate *weakSelf = self;
- [self.mapView flyToCamera:camera completionHandler:^{
- AppDelegate *strongSelf = weakSelf;
- [strongSelf performSelector:@selector(continueWorldTourWithRemainingAnnotations:)
- withObject:annotations
- afterDelay:2];
- }];
-}
-
-- (IBAction)stopWorldTour:(id)sender {
- _isTouringWorld = NO;
- // Any programmatic viewpoint change cancels outstanding animations.
- self.mapView.camera = self.mapView.camera;
}
#pragma mark Help methods
@@ -355,13 +132,6 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
[alert runModal];
}
-- (IBAction)giveFeedback:(id)sender {
- CLLocationCoordinate2D centerCoordinate = self.mapView.centerCoordinate;
- NSURL *feedbackURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://www.mapbox.com/map-feedback/#/%.5f/%.5f/%.0f",
- centerCoordinate.longitude, centerCoordinate.latitude, round(self.mapView.zoomLevel)]];
- [[NSWorkspace sharedWorkspace] openURL:feedbackURL];
-}
-
- (IBAction)showPreferences:(id)sender {
[self.preferencesWindow makeKeyAndOrderFront:sender];
}
@@ -370,296 +140,16 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"https://www.mapbox.com/studio/account/tokens/"]];
}
-#pragma mark Mouse events
-
-- (void)handlePressGesture:(NSPressGestureRecognizer *)gestureRecognizer {
- if (gestureRecognizer.state == NSGestureRecognizerStateBegan) {
- NSPoint location = [gestureRecognizer locationInView:self.mapView];
- [self dropPinAtPoint:location];
- }
-}
-
-- (IBAction)dropPin:(NSMenuItem *)sender {
- [self dropPinAtPoint:_mouseLocationForMapViewContextMenu];
-}
-
-- (void)dropPinAtPoint:(NSPoint)point {
- DroppedPinAnnotation *annotation = [self pinAtPoint:point];
- [self.mapView addAnnotation:annotation];
- [self.mapView selectAnnotation:annotation];
-}
-
-- (DroppedPinAnnotation *)pinAtPoint:(NSPoint)point {
- DroppedPinAnnotation *annotation = [[DroppedPinAnnotation alloc] init];
- annotation.coordinate = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
- annotation.title = @"Dropped Pin";
- _spellOutNumberFormatter.numberStyle = NSNumberFormatterSpellOutStyle;
- if (_showsToolTipsOnDroppedPins) {
- NSString *formattedNumber = [_spellOutNumberFormatter stringFromNumber:@(++_droppedPinCounter)];
- annotation.toolTip = formattedNumber;
- }
- return annotation;
-}
-
-- (IBAction)removePin:(NSMenuItem *)sender {
- [self removePinAtPoint:_mouseLocationForMapViewContextMenu];
-}
-
-- (void)removePinAtPoint:(NSPoint)point {
- [self.mapView removeAnnotation:[self.mapView annotationAtPoint:point]];
-}
-
#pragma mark User interface validation
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
- if (menuItem.action == @selector(setStyle:)) {
- NSURL *styleURL = self.mapView.styleURL;
- NSCellStateValue state;
- switch (menuItem.tag) {
- case 1:
- state = [styleURL isEqual:[MGLStyle streetsStyleURL]];
- break;
- case 2:
- state = [styleURL isEqual:[MGLStyle emeraldStyleURL]];
- break;
- case 3:
- state = [styleURL isEqual:[MGLStyle lightStyleURL]];
- break;
- case 4:
- state = [styleURL isEqual:[MGLStyle darkStyleURL]];
- break;
- case 5:
- state = [styleURL isEqual:[MGLStyle satelliteStyleURL]];
- break;
- case 6:
- state = [styleURL isEqual:[MGLStyle hybridStyleURL]];
- break;
- default:
- return NO;
- }
- menuItem.state = state;
- return YES;
- }
- if (menuItem.action == @selector(chooseCustomStyle:)) {
- menuItem.state = self.indexOfStyleInToolbarItem == NSNotFound;
- return YES;
- }
- if (menuItem.action == @selector(zoomIn:)) {
- return self.mapView.zoomLevel < self.mapView.maximumZoomLevel;
- }
- if (menuItem.action == @selector(zoomOut:)) {
- return self.mapView.zoomLevel > self.mapView.minimumZoomLevel;
- }
- if (menuItem.action == @selector(snapToNorth:)) {
- return self.mapView.direction != 0;
- }
- if (menuItem.action == @selector(reload:)) {
- return YES;
- }
- if (menuItem.action == @selector(dropPin:)) {
- BOOL isOverAnnotation = [self.mapView annotationAtPoint:_mouseLocationForMapViewContextMenu];
- menuItem.hidden = isOverAnnotation;
- return YES;
- }
- if (menuItem.action == @selector(removePin:)) {
- BOOL isOverAnnotation = [self.mapView annotationAtPoint:_mouseLocationForMapViewContextMenu];
- menuItem.hidden = !isOverAnnotation;
- return YES;
- }
- if (menuItem.action == @selector(toggleTileBoundaries:)) {
- BOOL isShown = self.mapView.debugMask & MGLMapDebugTileBoundariesMask;
- menuItem.title = isShown ? @"Hide Tile Boundaries" : @"Show Tile Boundaries";
- return YES;
- }
- if (menuItem.action == @selector(toggleTileInfo:)) {
- BOOL isShown = self.mapView.debugMask & MGLMapDebugTileInfoMask;
- menuItem.title = isShown ? @"Hide Tile Info" : @"Show Tile Info";
- return YES;
- }
- if (menuItem.action == @selector(toggleTileTimestamps:)) {
- BOOL isShown = self.mapView.debugMask & MGLMapDebugTimestampsMask;
- menuItem.title = isShown ? @"Hide Tile Timestamps" : @"Show Tile Timestamps";
- return YES;
- }
- if (menuItem.action == @selector(toggleCollisionBoxes:)) {
- BOOL isShown = self.mapView.debugMask & MGLMapDebugCollisionBoxesMask;
- menuItem.title = isShown ? @"Hide Collision Boxes" : @"Show Collision Boxes";
- return YES;
- }
- if (menuItem.action == @selector(toggleShowsToolTipsOnDroppedPins:)) {
- BOOL isShown = _showsToolTipsOnDroppedPins;
- menuItem.title = isShown ? @"Hide Tooltips on Dropped Pins" : @"Show Tooltips on Dropped Pins";
- return YES;
- }
- if (menuItem.action == @selector(toggleRandomizesCursorsOnDroppedPins:)) {
- BOOL isRandom = _randomizesCursorsOnDroppedPins;
- menuItem.title = isRandom ? @"Use Default Cursor for Dropped Pins" : @"Use Random Cursors for Dropped Pins";
- return _showsToolTipsOnDroppedPins;
- }
- if (menuItem.action == @selector(dropManyPins:)) {
- return YES;
- }
- if (menuItem.action == @selector(removeAllPins:)) {
- return self.mapView.annotations.count;
- }
- if (menuItem.action == @selector(startWorldTour:)) {
- return !_isTouringWorld;
- }
- if (menuItem.action == @selector(stopWorldTour:)) {
- return _isTouringWorld;
- }
if (menuItem.action == @selector(showShortcuts:)) {
return YES;
}
- if (menuItem.action == @selector(giveFeedback:)) {
- return YES;
- }
if (menuItem.action == @selector(showPreferences:)) {
return YES;
}
return NO;
}
-- (NSUInteger)indexOfStyleInToolbarItem {
- if (![MGLAccountManager accessToken]) {
- return NSNotFound;
- }
-
- NSArray *styleURLs = @[
- [MGLStyle streetsStyleURL],
- [MGLStyle emeraldStyleURL],
- [MGLStyle lightStyleURL],
- [MGLStyle darkStyleURL],
- [MGLStyle satelliteStyleURL],
- [MGLStyle hybridStyleURL],
- ];
- return [styleURLs indexOfObject:self.mapView.styleURL];
-}
-
-- (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem {
- if (!self.mapView) {
- return NO;
- }
-
- if (toolbarItem.action == @selector(showShareMenu:)) {
- [(NSButton *)toolbarItem.view sendActionOn:NSLeftMouseDownMask];
- if (![MGLAccountManager accessToken]) {
- return NO;
- }
- NSURL *styleURL = self.mapView.styleURL;
- return ([styleURL.scheme isEqualToString:@"mapbox"]
- && [styleURL.pathComponents.firstObject isEqualToString:@"styles"]);
- }
- if (toolbarItem.action == @selector(setStyle:)) {
- NSPopUpButton *popUpButton = (NSPopUpButton *)toolbarItem.view;
- NSUInteger index = self.indexOfStyleInToolbarItem;
- if (index == NSNotFound) {
- [popUpButton addItemWithTitle:@"Custom"];
- index = [popUpButton numberOfItems] - 1;
- }
- [popUpButton selectItemAtIndex:index];
- }
- return NO;
-}
-
-#pragma mark NSApplicationDelegate methods
-
-- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
- return YES;
-}
-
-#pragma mark NSSharingServicePickerDelegate methods
-
-- (NS_ARRAY_OF(NSSharingService *) *)sharingServicePicker:(NSSharingServicePicker *)sharingServicePicker sharingServicesForItems:(NSArray *)items proposedSharingServices:(NS_ARRAY_OF(NSSharingService *) *)proposedServices {
- NSURL *shareURL = self.shareURL;
- NSURL *browserURL = [[NSWorkspace sharedWorkspace] URLForApplicationToOpenURL:shareURL];
- NSImage *browserIcon = [[NSWorkspace sharedWorkspace] iconForFile:browserURL.path];
- NSString *browserName = [[NSFileManager defaultManager] displayNameAtPath:browserURL.path];
- NSString *browserServiceName = [NSString stringWithFormat:@"Open in %@", browserName];
-
- NSSharingService *browserService = [[NSSharingService alloc] initWithTitle:browserServiceName
- image:browserIcon
- alternateImage:nil
- handler:^{
- [[NSWorkspace sharedWorkspace] openURL:self.shareURL];
- }];
-
- NSMutableArray *sharingServices = [proposedServices mutableCopy];
- [sharingServices insertObject:browserService atIndex:0];
- return sharingServices;
-}
-
-#pragma mark NSMenuDelegate methods
-
-- (void)menuWillOpen:(NSMenu *)menu {
- if (menu == self.mapViewContextMenu) {
- _mouseLocationForMapViewContextMenu = [self.window.contentView convertPoint:self.window.mouseLocationOutsideOfEventStream
- toView:self.mapView];
- }
-}
-
-#pragma mark MGLMapViewDelegate methods
-
-- (BOOL)mapView:(MGLMapView *)mapView annotationCanShowCallout:(id <MGLAnnotation>)annotation {
- return YES;
-}
-
-- (MGLAnnotationImage *)mapView:(MGLMapView *)mapView imageForAnnotation:(id <MGLAnnotation>)annotation {
- MGLAnnotationImage *annotationImage = [self.mapView dequeueReusableAnnotationImageWithIdentifier:MGLDroppedPinAnnotationImageIdentifier];
- if (!annotationImage) {
- NSString *imagePath = [[NSBundle bundleForClass:[MGLMapView class]]
- pathForResource:@"default_marker" ofType:@"pdf"];
- NSImage *image = [[NSImage alloc] initWithContentsOfFile:imagePath];
- NSRect alignmentRect = image.alignmentRect;
- alignmentRect.origin.y = NSMidY(alignmentRect);
- alignmentRect.size.height /= 2;
- image.alignmentRect = alignmentRect;
- annotationImage = [MGLAnnotationImage annotationImageWithImage:image
- reuseIdentifier:MGLDroppedPinAnnotationImageIdentifier];
- }
- if (_randomizesCursorsOnDroppedPins) {
- NSArray *cursors = @[
- [NSCursor IBeamCursor],
- [NSCursor crosshairCursor],
- [NSCursor pointingHandCursor],
- [NSCursor disappearingItemCursor],
- [NSCursor IBeamCursorForVerticalLayout],
- [NSCursor operationNotAllowedCursor],
- [NSCursor dragLinkCursor],
- [NSCursor dragCopyCursor],
- [NSCursor contextualMenuCursor],
- ];
- annotationImage.cursor = cursors[arc4random_uniform(cursors.count)];
- } else {
- annotationImage.cursor = nil;
- }
- return annotationImage;
-}
-
-- (void)mapView:(MGLMapView *)mapView didSelectAnnotation:(id <MGLAnnotation>)annotation {
- if ([annotation isKindOfClass:[DroppedPinAnnotation class]]) {
- DroppedPinAnnotation *droppedPin = annotation;
- [droppedPin resume];
- }
-}
-
-- (void)mapView:(MGLMapView *)mapView didDeselectAnnotation:(id <MGLAnnotation>)annotation {
- if ([annotation isKindOfClass:[DroppedPinAnnotation class]]) {
- DroppedPinAnnotation *droppedPin = annotation;
- [droppedPin pause];
- }
-}
-
-@end
-
-@interface ValidatedToolbarItem : NSToolbarItem
-
-@end
-
-@implementation ValidatedToolbarItem
-
-- (void)validate {
- [(AppDelegate *)self.toolbar.delegate validateToolbarItem:self];
-}
-
@end
diff --git a/platform/osx/app/Info.plist b/platform/osx/app/Info.plist
index 118f2c1b11..0ae973842b 100644
--- a/platform/osx/app/Info.plist
+++ b/platform/osx/app/Info.plist
@@ -4,6 +4,21 @@
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
+ <key>CFBundleDocumentTypes</key>
+ <array>
+ <dict>
+ <key>CFBundleTypeExtensions</key>
+ <array>
+ <string>mbx</string>
+ </array>
+ <key>CFBundleTypeName</key>
+ <string>Mapbox GL Map</string>
+ <key>CFBundleTypeRole</key>
+ <string>Editor</string>
+ <key>NSDocumentClass</key>
+ <string>MapDocument</string>
+ </dict>
+ </array>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleGetInfoString</key>
diff --git a/platform/osx/app/MainMenu.xib b/platform/osx/app/MainMenu.xib
index 3f270c23fd..844af08f3e 100644
--- a/platform/osx/app/MainMenu.xib
+++ b/platform/osx/app/MainMenu.xib
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9531" systemVersion="15B42" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9531"/>
</dependencies>
@@ -17,10 +17,7 @@
</customObject>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate">
<connections>
- <outlet property="mapView" destination="v1Z-oA-oHU" id="70S-xO-QIP"/>
- <outlet property="mapViewContextMenu" destination="6rZ-M1-uTn" id="wl2-03-9Z8"/>
<outlet property="preferencesWindow" destination="UWc-yQ-qda" id="Ota-aT-Mz2"/>
- <outlet property="window" destination="QvC-M9-y7g" id="gIp-Ho-8D9"/>
</connections>
</customObject>
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
@@ -544,100 +541,6 @@
</menuItem>
</items>
</menu>
- <window title="Mapbox GL" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" frameAutosaveName="Mapbox" animationBehavior="default" id="QvC-M9-y7g">
- <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" fullSizeContentView="YES"/>
- <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
- <rect key="contentRect" x="388" y="211" width="512" height="360"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
- <view key="contentView" id="EiT-Mj-1SZ">
- <rect key="frame" x="0.0" y="0.0" width="512" height="360"/>
- <autoresizingMask key="autoresizingMask"/>
- <subviews>
- <customView translatesAutoresizingMaskIntoConstraints="NO" id="v1Z-oA-oHU" customClass="MGLMapView">
- <rect key="frame" x="0.0" y="0.0" width="512" height="360"/>
- <connections>
- <outlet property="delegate" destination="Voe-Tx-rLC" id="qJ7-fL-iw1"/>
- <outlet property="menu" destination="6rZ-M1-uTn" id="4sB-UN-zuc"/>
- </connections>
- </customView>
- </subviews>
- <constraints>
- <constraint firstItem="v1Z-oA-oHU" firstAttribute="leading" secondItem="EiT-Mj-1SZ" secondAttribute="leading" id="Z3O-xb-fah"/>
- <constraint firstAttribute="trailing" secondItem="v1Z-oA-oHU" secondAttribute="trailing" id="oYw-bk-P2l"/>
- <constraint firstItem="v1Z-oA-oHU" firstAttribute="top" secondItem="EiT-Mj-1SZ" secondAttribute="top" id="r4Z-4m-bgA"/>
- <constraint firstAttribute="bottom" secondItem="v1Z-oA-oHU" secondAttribute="bottom" id="w3h-H1-baQ"/>
- </constraints>
- </view>
- <toolbar key="toolbar" implicitIdentifier="A3AC6577-4712-4628-813D-113498171A84" allowsUserCustomization="NO" displayMode="iconOnly" sizeMode="regular" id="8nY-8B-Om5">
- <allowedToolbarItems>
- <toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="zzK-Oz-JV8"/>
- <toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="SMs-8a-x2h"/>
- <toolbarItem implicitItemIdentifier="2CB58C0A-7B95-4233-8DD3-F94BFE7D3061" label="Share" paletteLabel="Share" image="NSShareTemplate" id="HtT-hs-jWY" customClass="ValidatedToolbarItem">
- <nil key="toolTip"/>
- <size key="minSize" width="40" height="32"/>
- <size key="maxSize" width="40" height="32"/>
- <button key="view" verticalHuggingPriority="750" id="l0U-ql-5hn">
- <rect key="frame" x="0.0" y="14" width="48" height="32"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="NSShareTemplate" imagePosition="only" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="vxZ-hj-hV2">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="system"/>
- </buttonCell>
- </button>
- <connections>
- <action selector="showShareMenu:" target="-1" id="vDU-9Y-DkS"/>
- </connections>
- </toolbarItem>
- <toolbarItem implicitItemIdentifier="BA3542AF-D63A-4893-9CC7-8F67EF2E82B0" label="Style" paletteLabel="Style" id="ge5-0H-wyr" customClass="ValidatedToolbarItem">
- <nil key="toolTip"/>
- <size key="minSize" width="100" height="26"/>
- <size key="maxSize" width="100" height="26"/>
- <popUpButton key="view" verticalHuggingPriority="750" id="5Cd-Lw-cWm">
- <rect key="frame" x="0.0" y="14" width="100" height="26"/>
- <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
- <popUpButtonCell key="cell" type="roundTextured" title="Streets" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="border" tag="1" imageScaling="proportionallyDown" inset="2" selectedItem="ZnV-nK-Bri" id="esr-2z-V4Y">
- <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
- <font key="font" metaFont="menu"/>
- <menu key="menu" id="uxq-U6-EQW">
- <items>
- <menuItem title="Streets" state="on" tag="1" id="ZnV-nK-Bri"/>
- <menuItem title="Emerald" tag="2" id="LLa-jX-Dox"/>
- <menuItem title="Light" tag="3" id="jDf-NE-hMn"/>
- <menuItem title="Dark" tag="4" id="etJ-y7-M5R">
- <modifierMask key="keyEquivalentModifierMask"/>
- </menuItem>
- <menuItem title="Satellite" tag="5" id="xYa-J8-bLe">
- <modifierMask key="keyEquivalentModifierMask"/>
- </menuItem>
- <menuItem title="Hybrid" tag="6" id="1Uq-Nl-FSZ">
- <modifierMask key="keyEquivalentModifierMask"/>
- </menuItem>
- </items>
- </menu>
- </popUpButtonCell>
- </popUpButton>
- <connections>
- <action selector="setStyle:" target="-1" id="qb2-E8-7pE"/>
- </connections>
- </toolbarItem>
- </allowedToolbarItems>
- <defaultToolbarItems>
- <toolbarItem reference="HtT-hs-jWY"/>
- <toolbarItem reference="SMs-8a-x2h"/>
- <toolbarItem reference="ge5-0H-wyr"/>
- </defaultToolbarItems>
- <connections>
- <outlet property="delegate" destination="Voe-Tx-rLC" id="ciT-Nf-Dtm"/>
- </connections>
- </toolbar>
- <connections>
- <binding destination="Voe-Tx-rLC" name="title" keyPath="mapView.centerCoordinate" id="XFo-sp-PtR">
- <dictionary key="options">
- <string key="NSValueTransformerName">LocationCoordinate2DTransformer</string>
- </dictionary>
- </binding>
- </connections>
- </window>
<window title="Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="Preferences" animationBehavior="default" id="UWc-yQ-qda">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
@@ -700,29 +603,8 @@
<point key="canvasLocation" x="754" y="210"/>
</window>
<userDefaultsController representsSharedInstance="YES" id="45S-yT-WUN"/>
- <menu title="Map View" id="6rZ-M1-uTn">
- <items>
- <menuItem title="Drop Pin" id="fxQ-3P-E7l">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="dropPin:" target="Voe-Tx-rLC" id="eDR-iw-JUG"/>
- </connections>
- </menuItem>
- <menuItem title="Remove Pin" id="rgF-cY-qgP">
- <modifierMask key="keyEquivalentModifierMask"/>
- <connections>
- <action selector="removePin:" target="Voe-Tx-rLC" id="38n-9m-8Wp"/>
- </connections>
- </menuItem>
- </items>
- <connections>
- <outlet property="delegate" destination="Voe-Tx-rLC" id="MsV-YU-f4X"/>
- </connections>
- <point key="canvasLocation" x="820" y="254.5"/>
- </menu>
</objects>
<resources>
<image name="NSFollowLinkFreestandingTemplate" width="14" height="14"/>
- <image name="NSShareTemplate" width="11" height="16"/>
</resources>
</document>
diff --git a/platform/osx/app/MapDocument.h b/platform/osx/app/MapDocument.h
new file mode 100644
index 0000000000..86ad05e6e2
--- /dev/null
+++ b/platform/osx/app/MapDocument.h
@@ -0,0 +1,14 @@
+#import <Cocoa/Cocoa.h>
+
+@class MGLMapView;
+
+@interface MapDocument : NSDocument
+
+@property (weak) IBOutlet MGLMapView *mapView;
+
+- (IBAction)setStyle:(id)sender;
+- (IBAction)chooseCustomStyle:(id)sender;
+
+- (IBAction)reload:(id)sender;
+
+@end
diff --git a/platform/osx/app/MapDocument.m b/platform/osx/app/MapDocument.m
new file mode 100644
index 0000000000..b7d16e5c46
--- /dev/null
+++ b/platform/osx/app/MapDocument.m
@@ -0,0 +1,568 @@
+#import "MapDocument.h"
+
+#import "DroppedPinAnnotation.h"
+
+#import <Mapbox/Mapbox.h>
+
+static NSString * const MGLDroppedPinAnnotationImageIdentifier = @"dropped";
+
+static const CLLocationCoordinate2D WorldTourDestinations[] = {
+ { .latitude = 38.9131982, .longitude = -77.0325453144239 },
+ { .latitude = 37.7757368, .longitude = -122.4135302 },
+ { .latitude = 12.9810816, .longitude = 77.6368034 },
+ { .latitude = -13.15589555, .longitude = -74.2178961777998 },
+};
+
+@interface MapDocument () <NSSharingServicePickerDelegate, NSMenuDelegate, MGLMapViewDelegate>
+
+@property (weak) IBOutlet NSMenu *mapViewContextMenu;
+
+@end
+
+@implementation MapDocument {
+ NSPoint _mouseLocationForMapViewContextMenu;
+ NSUInteger _droppedPinCounter;
+ NSNumberFormatter *_spellOutNumberFormatter;
+
+ BOOL _showsToolTipsOnDroppedPins;
+ BOOL _randomizesCursorsOnDroppedPins;
+ BOOL _isTouringWorld;
+}
+
+#pragma mark Lifecycle
+
+- (NSString *)windowNibName {
+ return @"MapDocument";
+}
+
+- (void)windowControllerDidLoadNib:(NSWindowController *)aController {
+ [super windowControllerDidLoadNib:aController];
+
+ NSURL *savedURL = [[NSUserDefaults standardUserDefaults] URLForKey:@"MBXCurrentStyleURL"];
+ if (savedURL) {
+ self.mapView.styleURL = savedURL;
+ }
+
+ _spellOutNumberFormatter = [[NSNumberFormatter alloc] init];
+
+ NSPressGestureRecognizer *pressGestureRecognizer = [[NSPressGestureRecognizer alloc] initWithTarget:self action:@selector(handlePressGesture:)];
+ [self.mapView addGestureRecognizer:pressGestureRecognizer];
+}
+
+- (NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError {
+ // Insert code here to write your document to data of the specified type. If outError != NULL, ensure that you create and set an appropriate error when returning nil.
+ // You can also choose to override -fileWrapperOfType:error:, -writeToURL:ofType:error:, or -writeToURL:ofType:forSaveOperation:originalContentsURL:error: instead.
+ if (outError) {
+ *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:nil];
+ }
+ return nil;
+}
+
+- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError {
+ // Insert code here to read your document from the given data of the specified type. If outError != NULL, ensure that you create and set an appropriate error when returning NO.
+ // You can also choose to override -readFromFileWrapper:ofType:error: or -readFromURL:ofType:error: instead.
+ // If you override either of these, you should also override -isEntireFileLoaded to return NO if the contents are lazily loaded.
+ if (outError) {
+ *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:nil];
+ }
+ return NO;
+}
+
++ (BOOL)autosavesInPlace {
+ return YES;
+}
+
+- (NSWindow *)window {
+ return self.windowControllers.firstObject.window;
+}
+
+#pragma mark Services
+
+- (IBAction)showShareMenu:(id)sender {
+ NSSharingServicePicker *picker = [[NSSharingServicePicker alloc] initWithItems:@[self.shareURL]];
+ picker.delegate = self;
+ [picker showRelativeToRect:[sender bounds] ofView:sender preferredEdge:NSMinYEdge];
+}
+
+- (NSURL *)shareURL {
+ NSArray *components = self.mapView.styleURL.pathComponents;
+ CLLocationCoordinate2D centerCoordinate = self.mapView.centerCoordinate;
+ return [NSURL URLWithString:
+ [NSString stringWithFormat:@"https://api.mapbox.com/styles/v1/%@/%@.html?access_token=%@#%.2f/%.5f/%.5f/%.f",
+ components[1], components[2], [MGLAccountManager accessToken],
+ self.mapView.zoomLevel, centerCoordinate.latitude, centerCoordinate.longitude, self.mapView.direction]];
+}
+
+#pragma mark View methods
+
+- (IBAction)setStyle:(id)sender {
+ NSInteger tag;
+ if ([sender isKindOfClass:[NSMenuItem class]]) {
+ tag = [sender tag];
+ } else if ([sender isKindOfClass:[NSPopUpButton class]]) {
+ tag = [sender selectedTag];
+ }
+ NSURL *styleURL;
+ switch (tag) {
+ case 1:
+ styleURL = [MGLStyle streetsStyleURL];
+ break;
+ case 2:
+ styleURL = [MGLStyle emeraldStyleURL];
+ break;
+ case 3:
+ styleURL = [MGLStyle lightStyleURL];
+ break;
+ case 4:
+ styleURL = [MGLStyle darkStyleURL];
+ break;
+ case 5:
+ styleURL = [MGLStyle satelliteStyleURL];
+ break;
+ case 6:
+ styleURL = [MGLStyle hybridStyleURL];
+ break;
+ default:
+ NSAssert(NO, @"Cannot set style from control with tag %li", (long)tag);
+ break;
+ }
+ self.mapView.styleURL = styleURL;
+ [[NSUserDefaults standardUserDefaults] setURL:self.mapView.styleURL forKey:@"MBXCurrentStyleURL"];
+ [self.window.toolbar validateVisibleItems];
+}
+
+- (IBAction)chooseCustomStyle:(id)sender {
+ NSAlert *alert = [[NSAlert alloc] init];
+ alert.messageText = @"Apply custom style";
+ alert.informativeText = @"Enter the URL to a JSON file that conforms to the Mapbox GL style specification, such as a style designed in Mapbox Studio:";
+ NSTextField *textField = [[NSTextField alloc] initWithFrame:NSZeroRect];
+ [textField sizeToFit];
+ NSRect textFieldFrame = textField.frame;
+ textFieldFrame.size.width = 300;
+ textField.frame = textFieldFrame;
+ NSURL *savedURL = [[NSUserDefaults standardUserDefaults] URLForKey:@"MBXCustomStyleURL"];
+ if (savedURL) {
+ textField.stringValue = savedURL.absoluteString;
+ }
+ alert.accessoryView = textField;
+ [alert addButtonWithTitle:@"Apply"];
+ [alert addButtonWithTitle:@"Cancel"];
+ if ([alert runModal] == NSAlertFirstButtonReturn) {
+ self.mapView.styleURL = [NSURL URLWithString:textField.stringValue];
+ [[NSUserDefaults standardUserDefaults] setURL:self.mapView.styleURL forKey:@"MBXCustomStyleURL"];
+ [[NSUserDefaults standardUserDefaults] setURL:self.mapView.styleURL forKey:@"MBXCurrentStyleURL"];
+ [self.window.toolbar validateVisibleItems];
+ }
+}
+
+- (IBAction)zoomIn:(id)sender {
+ [self.mapView setZoomLevel:self.mapView.zoomLevel + 1 animated:YES];
+}
+
+- (IBAction)zoomOut:(id)sender {
+ [self.mapView setZoomLevel:self.mapView.zoomLevel - 1 animated:YES];
+}
+
+- (IBAction)snapToNorth:(id)sender {
+ [self.mapView setDirection:0 animated:YES];
+}
+
+- (IBAction)reload:(id)sender {
+ [self.mapView reloadStyle:sender];
+}
+
+#pragma mark Debug methods
+
+- (IBAction)toggleTileBoundaries:(id)sender {
+ self.mapView.debugMask ^= MGLMapDebugTileBoundariesMask;
+}
+
+- (IBAction)toggleTileInfo:(id)sender {
+ self.mapView.debugMask ^= MGLMapDebugTileInfoMask;
+}
+
+- (IBAction)toggleTileTimestamps:(id)sender {
+ self.mapView.debugMask ^= MGLMapDebugTimestampsMask;
+}
+
+- (IBAction)toggleCollisionBoxes:(id)sender {
+ self.mapView.debugMask ^= MGLMapDebugCollisionBoxesMask;
+}
+
+- (IBAction)toggleShowsToolTipsOnDroppedPins:(id)sender {
+ _showsToolTipsOnDroppedPins = !_showsToolTipsOnDroppedPins;
+}
+
+- (IBAction)toggleRandomizesCursorsOnDroppedPins:(id)sender {
+ _randomizesCursorsOnDroppedPins = !_randomizesCursorsOnDroppedPins;
+}
+
+- (IBAction)dropManyPins:(id)sender {
+ [self.mapView removeAnnotations:self.mapView.annotations];
+
+ NSRect bounds = self.mapView.bounds;
+ NSMutableArray *annotations = [NSMutableArray array];
+ for (CGFloat x = NSMinX(bounds); x < NSMaxX(bounds); x += arc4random_uniform(50)) {
+ for (CGFloat y = NSMaxY(bounds); y >= NSMinY(bounds); y -= arc4random_uniform(100)) {
+ [annotations addObject:[self pinAtPoint:NSMakePoint(x, y)]];
+ }
+ }
+
+ [NSTimer scheduledTimerWithTimeInterval:1/60
+ target:self
+ selector:@selector(dropOneOfManyPins:)
+ userInfo:annotations
+ repeats:YES];
+}
+
+- (void)dropOneOfManyPins:(NSTimer *)timer {
+ NSMutableArray *annotations = timer.userInfo;
+ NSUInteger numberOfAnnotationsToAdd = 50;
+ if (annotations.count < numberOfAnnotationsToAdd) {
+ numberOfAnnotationsToAdd = annotations.count;
+ }
+ NSArray *annotationsToAdd = [annotations subarrayWithRange:
+ NSMakeRange(0, numberOfAnnotationsToAdd)];
+ [self.mapView addAnnotations:annotationsToAdd];
+ [annotations removeObjectsInRange:NSMakeRange(0, numberOfAnnotationsToAdd)];
+ if (!annotations.count) {
+ [timer invalidate];
+ }
+}
+
+- (IBAction)removeAllPins:(id)sender {
+ [self.mapView removeAnnotations:self.mapView.annotations];
+}
+
+- (IBAction)startWorldTour:(id)sender {
+ _isTouringWorld = YES;
+
+ [self removeAllPins:sender];
+ NSUInteger numberOfAnnotations = sizeof(WorldTourDestinations) / sizeof(WorldTourDestinations[0]);
+ NSMutableArray *annotations = [NSMutableArray arrayWithCapacity:numberOfAnnotations];
+ for (NSUInteger i = 0; i < numberOfAnnotations; i++) {
+ MGLPointAnnotation *annotation = [[MGLPointAnnotation alloc] init];
+ annotation.coordinate = WorldTourDestinations[i];
+ [annotations addObject:annotation];
+ }
+ [self.mapView addAnnotations:annotations];
+ [self continueWorldTourWithRemainingAnnotations:annotations];
+}
+
+- (void)continueWorldTourWithRemainingAnnotations:(NS_MUTABLE_ARRAY_OF(MGLPointAnnotation *) *)annotations {
+ MGLPointAnnotation *nextAnnotation = annotations.firstObject;
+ if (!nextAnnotation || !_isTouringWorld) {
+ _isTouringWorld = NO;
+ return;
+ }
+
+ [annotations removeObjectAtIndex:0];
+ MGLMapCamera *camera = [MGLMapCamera cameraLookingAtCenterCoordinate:nextAnnotation.coordinate
+ fromDistance:0
+ pitch:arc4random_uniform(60)
+ heading:arc4random_uniform(360)];
+ __weak MapDocument *weakSelf = self;
+ [self.mapView flyToCamera:camera completionHandler:^{
+ MapDocument *strongSelf = weakSelf;
+ [strongSelf performSelector:@selector(continueWorldTourWithRemainingAnnotations:)
+ withObject:annotations
+ afterDelay:2];
+ }];
+}
+
+- (IBAction)stopWorldTour:(id)sender {
+ _isTouringWorld = NO;
+ // Any programmatic viewpoint change cancels outstanding animations.
+ self.mapView.camera = self.mapView.camera;
+}
+
+#pragma mark Help methods
+
+- (IBAction)giveFeedback:(id)sender {
+ CLLocationCoordinate2D centerCoordinate = self.mapView.centerCoordinate;
+ NSURL *feedbackURL = [NSURL URLWithString:[NSString stringWithFormat:@"https://www.mapbox.com/map-feedback/#/%.5f/%.5f/%.0f",
+ centerCoordinate.longitude, centerCoordinate.latitude, round(self.mapView.zoomLevel)]];
+ [[NSWorkspace sharedWorkspace] openURL:feedbackURL];
+}
+
+#pragma mark Mouse events
+
+- (void)handlePressGesture:(NSPressGestureRecognizer *)gestureRecognizer {
+ if (gestureRecognizer.state == NSGestureRecognizerStateBegan) {
+ NSPoint location = [gestureRecognizer locationInView:self.mapView];
+ [self dropPinAtPoint:location];
+ }
+}
+
+- (IBAction)dropPin:(NSMenuItem *)sender {
+ [self dropPinAtPoint:_mouseLocationForMapViewContextMenu];
+}
+
+- (void)dropPinAtPoint:(NSPoint)point {
+ DroppedPinAnnotation *annotation = [self pinAtPoint:point];
+ [self.mapView addAnnotation:annotation];
+ [self.mapView selectAnnotation:annotation];
+}
+
+- (DroppedPinAnnotation *)pinAtPoint:(NSPoint)point {
+ DroppedPinAnnotation *annotation = [[DroppedPinAnnotation alloc] init];
+ annotation.coordinate = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
+ annotation.title = @"Dropped Pin";
+ _spellOutNumberFormatter.numberStyle = NSNumberFormatterSpellOutStyle;
+ if (_showsToolTipsOnDroppedPins) {
+ NSString *formattedNumber = [_spellOutNumberFormatter stringFromNumber:@(++_droppedPinCounter)];
+ annotation.toolTip = formattedNumber;
+ }
+ return annotation;
+}
+
+- (IBAction)removePin:(NSMenuItem *)sender {
+ [self removePinAtPoint:_mouseLocationForMapViewContextMenu];
+}
+
+- (void)removePinAtPoint:(NSPoint)point {
+ [self.mapView removeAnnotation:[self.mapView annotationAtPoint:point]];
+}
+
+#pragma mark User interface validation
+
+- (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
+ if (menuItem.action == @selector(setStyle:)) {
+ NSURL *styleURL = self.mapView.styleURL;
+ NSCellStateValue state;
+ switch (menuItem.tag) {
+ case 1:
+ state = [styleURL isEqual:[MGLStyle streetsStyleURL]];
+ break;
+ case 2:
+ state = [styleURL isEqual:[MGLStyle emeraldStyleURL]];
+ break;
+ case 3:
+ state = [styleURL isEqual:[MGLStyle lightStyleURL]];
+ break;
+ case 4:
+ state = [styleURL isEqual:[MGLStyle darkStyleURL]];
+ break;
+ case 5:
+ state = [styleURL isEqual:[MGLStyle satelliteStyleURL]];
+ break;
+ case 6:
+ state = [styleURL isEqual:[MGLStyle hybridStyleURL]];
+ break;
+ default:
+ return NO;
+ }
+ menuItem.state = state;
+ return YES;
+ }
+ if (menuItem.action == @selector(chooseCustomStyle:)) {
+ menuItem.state = self.indexOfStyleInToolbarItem == NSNotFound;
+ return YES;
+ }
+ if (menuItem.action == @selector(zoomIn:)) {
+ return self.mapView.zoomLevel < self.mapView.maximumZoomLevel;
+ }
+ if (menuItem.action == @selector(zoomOut:)) {
+ return self.mapView.zoomLevel > self.mapView.minimumZoomLevel;
+ }
+ if (menuItem.action == @selector(snapToNorth:)) {
+ return self.mapView.direction != 0;
+ }
+ if (menuItem.action == @selector(reload:)) {
+ return YES;
+ }
+ if (menuItem.action == @selector(dropPin:)) {
+ BOOL isOverAnnotation = [self.mapView annotationAtPoint:_mouseLocationForMapViewContextMenu];
+ menuItem.hidden = isOverAnnotation;
+ return YES;
+ }
+ if (menuItem.action == @selector(removePin:)) {
+ BOOL isOverAnnotation = [self.mapView annotationAtPoint:_mouseLocationForMapViewContextMenu];
+ menuItem.hidden = !isOverAnnotation;
+ return YES;
+ }
+ if (menuItem.action == @selector(toggleTileBoundaries:)) {
+ BOOL isShown = self.mapView.debugMask & MGLMapDebugTileBoundariesMask;
+ menuItem.title = isShown ? @"Hide Tile Boundaries" : @"Show Tile Boundaries";
+ return YES;
+ }
+ if (menuItem.action == @selector(toggleTileInfo:)) {
+ BOOL isShown = self.mapView.debugMask & MGLMapDebugTileInfoMask;
+ menuItem.title = isShown ? @"Hide Tile Info" : @"Show Tile Info";
+ return YES;
+ }
+ if (menuItem.action == @selector(toggleTileTimestamps:)) {
+ BOOL isShown = self.mapView.debugMask & MGLMapDebugTimestampsMask;
+ menuItem.title = isShown ? @"Hide Tile Timestamps" : @"Show Tile Timestamps";
+ return YES;
+ }
+ if (menuItem.action == @selector(toggleCollisionBoxes:)) {
+ BOOL isShown = self.mapView.debugMask & MGLMapDebugCollisionBoxesMask;
+ menuItem.title = isShown ? @"Hide Collision Boxes" : @"Show Collision Boxes";
+ return YES;
+ }
+ if (menuItem.action == @selector(toggleShowsToolTipsOnDroppedPins:)) {
+ BOOL isShown = _showsToolTipsOnDroppedPins;
+ menuItem.title = isShown ? @"Hide Tooltips on Dropped Pins" : @"Show Tooltips on Dropped Pins";
+ return YES;
+ }
+ if (menuItem.action == @selector(toggleRandomizesCursorsOnDroppedPins:)) {
+ BOOL isRandom = _randomizesCursorsOnDroppedPins;
+ menuItem.title = isRandom ? @"Use Default Cursor for Dropped Pins" : @"Use Random Cursors for Dropped Pins";
+ return _showsToolTipsOnDroppedPins;
+ }
+ if (menuItem.action == @selector(dropManyPins:)) {
+ return YES;
+ }
+ if (menuItem.action == @selector(removeAllPins:)) {
+ return self.mapView.annotations.count;
+ }
+ if (menuItem.action == @selector(startWorldTour:)) {
+ return !_isTouringWorld;
+ }
+ if (menuItem.action == @selector(stopWorldTour:)) {
+ return _isTouringWorld;
+ }
+ if (menuItem.action == @selector(giveFeedback:)) {
+ return YES;
+ }
+ return NO;
+}
+
+- (NSUInteger)indexOfStyleInToolbarItem {
+ if (![MGLAccountManager accessToken]) {
+ return NSNotFound;
+ }
+
+ NSArray *styleURLs = @[
+ [MGLStyle streetsStyleURL],
+ [MGLStyle emeraldStyleURL],
+ [MGLStyle lightStyleURL],
+ [MGLStyle darkStyleURL],
+ [MGLStyle satelliteStyleURL],
+ [MGLStyle hybridStyleURL],
+ ];
+ return [styleURLs indexOfObject:self.mapView.styleURL];
+}
+
+- (BOOL)validateToolbarItem:(NSToolbarItem *)toolbarItem {
+ if (!self.mapView) {
+ return NO;
+ }
+
+ if (toolbarItem.action == @selector(showShareMenu:)) {
+ [(NSButton *)toolbarItem.view sendActionOn:NSLeftMouseDownMask];
+ if (![MGLAccountManager accessToken]) {
+ return NO;
+ }
+ NSURL *styleURL = self.mapView.styleURL;
+ return ([styleURL.scheme isEqualToString:@"mapbox"]
+ && [styleURL.pathComponents.firstObject isEqualToString:@"styles"]);
+ }
+ if (toolbarItem.action == @selector(setStyle:)) {
+ NSPopUpButton *popUpButton = (NSPopUpButton *)toolbarItem.view;
+ NSUInteger index = self.indexOfStyleInToolbarItem;
+ if (index == NSNotFound) {
+ [popUpButton addItemWithTitle:@"Custom"];
+ index = [popUpButton numberOfItems] - 1;
+ }
+ [popUpButton selectItemAtIndex:index];
+ }
+ return NO;
+}
+
+#pragma mark NSSharingServicePickerDelegate methods
+
+- (NS_ARRAY_OF(NSSharingService *) *)sharingServicePicker:(NSSharingServicePicker *)sharingServicePicker sharingServicesForItems:(NSArray *)items proposedSharingServices:(NS_ARRAY_OF(NSSharingService *) *)proposedServices {
+ NSURL *shareURL = self.shareURL;
+ NSURL *browserURL = [[NSWorkspace sharedWorkspace] URLForApplicationToOpenURL:shareURL];
+ NSImage *browserIcon = [[NSWorkspace sharedWorkspace] iconForFile:browserURL.path];
+ NSString *browserName = [[NSFileManager defaultManager] displayNameAtPath:browserURL.path];
+ NSString *browserServiceName = [NSString stringWithFormat:@"Open in %@", browserName];
+
+ NSSharingService *browserService = [[NSSharingService alloc] initWithTitle:browserServiceName
+ image:browserIcon
+ alternateImage:nil
+ handler:^{
+ [[NSWorkspace sharedWorkspace] openURL:self.shareURL];
+ }];
+
+ NSMutableArray *sharingServices = [proposedServices mutableCopy];
+ [sharingServices insertObject:browserService atIndex:0];
+ return sharingServices;
+}
+
+#pragma mark NSMenuDelegate methods
+
+- (void)menuWillOpen:(NSMenu *)menu {
+ if (menu == self.mapViewContextMenu) {
+ _mouseLocationForMapViewContextMenu = [self.window.contentView convertPoint:self.window.mouseLocationOutsideOfEventStream
+ toView:self.mapView];
+ }
+}
+
+#pragma mark MGLMapViewDelegate methods
+
+- (BOOL)mapView:(MGLMapView *)mapView annotationCanShowCallout:(id <MGLAnnotation>)annotation {
+ return YES;
+}
+
+- (MGLAnnotationImage *)mapView:(MGLMapView *)mapView imageForAnnotation:(id <MGLAnnotation>)annotation {
+ MGLAnnotationImage *annotationImage = [self.mapView dequeueReusableAnnotationImageWithIdentifier:MGLDroppedPinAnnotationImageIdentifier];
+ if (!annotationImage) {
+ NSString *imagePath = [[NSBundle bundleForClass:[MGLMapView class]]
+ pathForResource:@"default_marker" ofType:@"pdf"];
+ NSImage *image = [[NSImage alloc] initWithContentsOfFile:imagePath];
+ NSRect alignmentRect = image.alignmentRect;
+ alignmentRect.origin.y = NSMidY(alignmentRect);
+ alignmentRect.size.height /= 2;
+ image.alignmentRect = alignmentRect;
+ annotationImage = [MGLAnnotationImage annotationImageWithImage:image
+ reuseIdentifier:MGLDroppedPinAnnotationImageIdentifier];
+ }
+ if (_randomizesCursorsOnDroppedPins) {
+ NSArray *cursors = @[
+ [NSCursor IBeamCursor],
+ [NSCursor crosshairCursor],
+ [NSCursor pointingHandCursor],
+ [NSCursor disappearingItemCursor],
+ [NSCursor IBeamCursorForVerticalLayout],
+ [NSCursor operationNotAllowedCursor],
+ [NSCursor dragLinkCursor],
+ [NSCursor dragCopyCursor],
+ [NSCursor contextualMenuCursor],
+ ];
+ annotationImage.cursor = cursors[arc4random_uniform(cursors.count)];
+ } else {
+ annotationImage.cursor = nil;
+ }
+ return annotationImage;
+}
+
+- (void)mapView:(MGLMapView *)mapView didSelectAnnotation:(id <MGLAnnotation>)annotation {
+ if ([annotation isKindOfClass:[DroppedPinAnnotation class]]) {
+ DroppedPinAnnotation *droppedPin = annotation;
+ [droppedPin resume];
+ }
+}
+
+- (void)mapView:(MGLMapView *)mapView didDeselectAnnotation:(id <MGLAnnotation>)annotation {
+ if ([annotation isKindOfClass:[DroppedPinAnnotation class]]) {
+ DroppedPinAnnotation *droppedPin = annotation;
+ [droppedPin pause];
+ }
+}
+
+@end
+
+@interface ValidatedToolbarItem : NSToolbarItem
+
+@end
+
+@implementation ValidatedToolbarItem
+
+- (void)validate {
+ [(MapDocument *)self.toolbar.delegate validateToolbarItem:self];
+}
+
+@end
diff --git a/platform/osx/app/MapDocument.xib b/platform/osx/app/MapDocument.xib
new file mode 100644
index 0000000000..3adbba8c10
--- /dev/null
+++ b/platform/osx/app/MapDocument.xib
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="9531"/>
+ </dependencies>
+ <objects>
+ <customObject id="-2" userLabel="File's Owner" customClass="MapDocument">
+ <connections>
+ <outlet property="mapView" destination="q4d-kF-8Hi" id="7hI-dS-A5R"/>
+ <outlet property="mapViewContextMenu" destination="XbX-6a-Mgy" id="YD0-1r-5N2"/>
+ <outlet property="window" destination="cSv-fg-MAQ" id="TBu-Mu-79N"/>
+ </connections>
+ </customObject>
+ <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
+ <customObject id="-3" userLabel="Application" customClass="NSObject"/>
+ <window title="Mapbox GL" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" frameAutosaveName="Mapbox" animationBehavior="default" id="cSv-fg-MAQ">
+ <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" fullSizeContentView="YES"/>
+ <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
+ <rect key="contentRect" x="388" y="211" width="512" height="480"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
+ <view key="contentView" id="TuG-C5-zLS">
+ <rect key="frame" x="0.0" y="0.0" width="512" height="480"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <subviews>
+ <customView translatesAutoresizingMaskIntoConstraints="NO" id="q4d-kF-8Hi" customClass="MGLMapView">
+ <rect key="frame" x="0.0" y="0.0" width="512" height="480"/>
+ <connections>
+ <outlet property="delegate" destination="-2" id="dh2-0H-jFZ"/>
+ </connections>
+ </customView>
+ </subviews>
+ <constraints>
+ <constraint firstAttribute="bottom" secondItem="q4d-kF-8Hi" secondAttribute="bottom" id="L2t-Be-qWL"/>
+ <constraint firstItem="q4d-kF-8Hi" firstAttribute="top" secondItem="TuG-C5-zLS" secondAttribute="top" id="T8A-o3-Bhq"/>
+ <constraint firstItem="q4d-kF-8Hi" firstAttribute="leading" secondItem="TuG-C5-zLS" secondAttribute="leading" id="fGH-YW-Qd3"/>
+ <constraint firstAttribute="trailing" secondItem="q4d-kF-8Hi" secondAttribute="trailing" id="yfG-iG-K4C"/>
+ </constraints>
+ </view>
+ <toolbar key="toolbar" implicitIdentifier="A3AC6577-4712-4628-813D-113498171A84" allowsUserCustomization="NO" displayMode="iconOnly" sizeMode="regular" id="DTc-AP-Bah">
+ <allowedToolbarItems>
+ <toolbarItem implicitItemIdentifier="NSToolbarSpaceItem" id="bld-8W-Wgg"/>
+ <toolbarItem implicitItemIdentifier="NSToolbarFlexibleSpaceItem" id="z4l-5x-MzK"/>
+ <toolbarItem implicitItemIdentifier="2CB58C0A-7B95-4233-8DD3-F94BFE7D3061" label="Share" paletteLabel="Share" image="NSShareTemplate" id="XJT-Ho-tuZ" customClass="ValidatedToolbarItem">
+ <nil key="toolTip"/>
+ <size key="minSize" width="40" height="32"/>
+ <size key="maxSize" width="48" height="32"/>
+ <button key="view" verticalHuggingPriority="750" id="y6e-ev-rVL">
+ <rect key="frame" x="0.0" y="14" width="48" height="32"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="NSShareTemplate" imagePosition="only" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="TBK-Ra-XzZ">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ </button>
+ <connections>
+ <action selector="showShareMenu:" target="-1" id="fCB-HP-iou"/>
+ </connections>
+ </toolbarItem>
+ <toolbarItem implicitItemIdentifier="BA3542AF-D63A-4893-9CC7-8F67EF2E82B0" label="Style" paletteLabel="Style" id="u23-0z-Otl" customClass="ValidatedToolbarItem">
+ <nil key="toolTip"/>
+ <size key="minSize" width="100" height="26"/>
+ <size key="maxSize" width="100" height="26"/>
+ <popUpButton key="view" verticalHuggingPriority="750" id="Tzm-Cy-dQg">
+ <rect key="frame" x="0.0" y="14" width="100" height="26"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
+ <popUpButtonCell key="cell" type="roundTextured" title="Streets" bezelStyle="texturedRounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="border" tag="1" imageScaling="proportionallyDown" inset="2" selectedItem="wvt-tP-O3a" id="3PJ-qK-Oh3">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="menu"/>
+ <menu key="menu" id="xf3-qk-IhF">
+ <items>
+ <menuItem title="Streets" state="on" tag="1" id="wvt-tP-O3a"/>
+ <menuItem title="Emerald" tag="2" id="RkE-lp-fL9"/>
+ <menuItem title="Light" tag="3" id="R4X-kt-HHb"/>
+ <menuItem title="Dark" tag="4" id="jUC-5X-0Zx">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem title="Satellite" tag="5" id="CTe-e2-o42">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem title="Hybrid" tag="6" id="7ly-oA-0ND">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ </items>
+ </menu>
+ </popUpButtonCell>
+ </popUpButton>
+ <connections>
+ <action selector="setStyle:" target="-1" id="2Kw-9i-a3G"/>
+ </connections>
+ </toolbarItem>
+ </allowedToolbarItems>
+ <defaultToolbarItems>
+ <toolbarItem reference="XJT-Ho-tuZ"/>
+ <toolbarItem reference="z4l-5x-MzK"/>
+ <toolbarItem reference="u23-0z-Otl"/>
+ </defaultToolbarItems>
+ <connections>
+ <outlet property="delegate" destination="-2" id="V9D-gS-Tvu"/>
+ </connections>
+ </toolbar>
+ <connections>
+ <binding destination="-2" name="displayPatternTitle1" keyPath="mapView.centerCoordinate" id="wtz-AV-bG1">
+ <dictionary key="options">
+ <string key="NSDisplayPattern">%{title1}@</string>
+ <string key="NSValueTransformerName">LocationCoordinate2DTransformer</string>
+ </dictionary>
+ </binding>
+ <outlet property="delegate" destination="-2" id="HEo-Qf-o6o"/>
+ </connections>
+ </window>
+ <menu title="Map View" id="XbX-6a-Mgy">
+ <items>
+ <menuItem title="Drop Pin" id="qZJ-mM-bLj">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ <menuItem title="Remove Pin" id="Zhx-30-VmE">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ </menuItem>
+ </items>
+ <connections>
+ <outlet property="delegate" destination="-2" id="oHe-ZP-lyc"/>
+ </connections>
+ <point key="canvasLocation" x="820" y="254.5"/>
+ </menu>
+ </objects>
+ <resources>
+ <image name="NSShareTemplate" width="11" height="16"/>
+ </resources>
+</document>
diff --git a/platform/osx/app/mapboxgl-app.gypi b/platform/osx/app/mapboxgl-app.gypi
index ca096143c2..7b39f5d9eb 100644
--- a/platform/osx/app/mapboxgl-app.gypi
+++ b/platform/osx/app/mapboxgl-app.gypi
@@ -13,6 +13,7 @@
'Credits.rtf',
'Icon.icns',
'MainMenu.xib',
+ 'MapDocument.xib',
],
'dependencies': [
@@ -26,6 +27,8 @@
'./DroppedPinAnnotation.m',
'./LocationCoordinate2DTransformer.h',
'./LocationCoordinate2DTransformer.m',
+ './MapDocument.h',
+ './MapDocument.m',
'./TimeIntervalTransformer.h',
'./TimeIntervalTransformer.m',
'./NSValue+Additions.h',