summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMinh Nguyễn <mxn@1ec5.org>2016-03-28 00:05:58 -0700
committerMinh Nguyễn <mxn@1ec5.org>2016-03-29 11:46:27 -0700
commit78f93708d66d99da71e3292c32f34cc4e46ecc69 (patch)
tree24a620b9fb483ca3ab835e594107d512587fb912
parent8c8b0580388460c0addafb49c852ea8986b47ac4 (diff)
downloadqtlocation-mapboxgl-78f93708d66d99da71e3292c32f34cc4e46ecc69.tar.gz
[osx] Offline pack manager in osxapp
Added an offline pack download manager to osxapp to demonstrate the use of MGLOfflineStorage with Cocoa Bindings. Like the Offline Packs view controller in iosapp, new packs are added based on the foremost map view’s current state. Double-clicking a complete offline pack opens a new map window to the pack’s bounds.
-rw-r--r--platform/osx/app/AppDelegate.h2
-rw-r--r--platform/osx/app/AppDelegate.m96
-rw-r--r--platform/osx/app/MainMenu.xib202
-rw-r--r--platform/osx/app/MapDocument.m38
-rw-r--r--platform/osx/app/OfflinePackNameValueTransformer.h5
-rw-r--r--platform/osx/app/OfflinePackNameValueTransformer.m33
-rw-r--r--platform/osx/app/mapboxgl-app.gypi2
7 files changed, 374 insertions, 4 deletions
diff --git a/platform/osx/app/AppDelegate.h b/platform/osx/app/AppDelegate.h
index 45d389f546..ca9edab773 100644
--- a/platform/osx/app/AppDelegate.h
+++ b/platform/osx/app/AppDelegate.h
@@ -15,8 +15,8 @@ extern NSString * const MGLMapboxAccessTokenDefaultsKey;
@property (assign) double pendingZoomLevel;
@property (copy) MGLMapCamera *pendingCamera;
+@property (assign) MGLCoordinateBounds pendingVisibleCoordinateBounds;
@property (copy) NSURL *pendingStyleURL;
@property (assign) MGLMapDebugMaskOptions pendingDebugMask;
@end
-
diff --git a/platform/osx/app/AppDelegate.m b/platform/osx/app/AppDelegate.m
index b54ab4fe74..186a3a1ab3 100644
--- a/platform/osx/app/AppDelegate.m
+++ b/platform/osx/app/AppDelegate.m
@@ -7,8 +7,58 @@ NSString * const MGLLastMapCameraDefaultsKey = @"MGLLastMapCamera";
NSString * const MGLLastMapStyleURLDefaultsKey = @"MGLLastMapStyleURL";
NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask";
+/**
+ Some convenience methods to make offline pack properties easier to bind to.
+ */
+@implementation MGLOfflinePack (Additions)
+
++ (NSSet *)keyPathsForValuesAffectingStateImage {
+ return [NSSet setWithObjects:@"state", nil];
+}
+
+- (NSImage *)stateImage {
+ switch (self.state) {
+ case MGLOfflinePackStateComplete:
+ return [NSImage imageNamed:@"NSMenuOnStateTemplate"];
+
+ case MGLOfflinePackStateActive:
+ return [NSImage imageNamed:@"NSFollowLinkFreestandingTemplate"];
+
+ default:
+ return nil;
+ }
+}
+
++ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfResourcesCompleted {
+ return [NSSet setWithObjects:@"progress", nil];
+}
+
+- (uint64_t)countOfResourcesCompleted {
+ return self.progress.countOfResourcesCompleted;
+}
+
++ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfResourcesExpected {
+ return [NSSet setWithObjects:@"progress", nil];
+}
+
+- (uint64_t)countOfResourcesExpected {
+ return self.progress.countOfResourcesExpected;
+}
+
++ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingCountOfBytesCompleted {
+ return [NSSet setWithObjects:@"progress", nil];
+}
+
+- (uint64_t)countOfBytesCompleted {
+ return self.progress.countOfBytesCompleted;
+}
+
+@end
+
@interface AppDelegate ()
+@property (weak) IBOutlet NSArrayController *offlinePacksArrayController;
+
@end
@implementation AppDelegate
@@ -63,6 +113,8 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask";
[alert runModal];
[self showPreferences:nil];
}
+
+ [self.offlinePacksArrayController bind:@"content" toObject:[MGLOfflineStorage sharedOfflineStorage] withKeyPath:@"packs" options:nil];
}
- (void)applicationWillTerminate:(NSNotification *)notification {
@@ -79,6 +131,8 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask";
[[NSUserDefaults standardUserDefaults] setInteger:mapView.debugMask forKey:MGLLastMapDebugMaskDefaultsKey];
}
}
+
+ [self.offlinePacksArrayController unbind:@"content"];
}
#pragma mark Services
@@ -121,6 +175,45 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask";
[[NSDocumentController sharedDocumentController] openUntitledDocumentAndDisplay:YES error:NULL];
}
+#pragma mark Offline pack management
+
+- (IBAction)delete:(id)sender {
+ for (MGLOfflinePack *pack in self.offlinePacksArrayController.selectedObjects) {
+ [[MGLOfflineStorage sharedOfflineStorage] removePack:pack withCompletionHandler:^(NSError * _Nullable error) {
+ if (error) {
+ [[NSAlert alertWithError:error] runModal];
+ }
+ }];
+ }
+}
+
+- (IBAction)chooseOfflinePack:(id)sender {
+ for (MGLOfflinePack *pack in self.offlinePacksArrayController.selectedObjects) {
+ switch (pack.state) {
+ case MGLOfflinePackStateComplete:
+ {
+ if ([pack.region isKindOfClass:[MGLTilePyramidOfflineRegion class]]) {
+ MGLTilePyramidOfflineRegion *region = (MGLTilePyramidOfflineRegion *)pack.region;
+ self.pendingVisibleCoordinateBounds = region.bounds;
+ [[NSDocumentController sharedDocumentController] openUntitledDocumentAndDisplay:YES error:NULL];
+ }
+ break;
+ }
+
+ case MGLOfflinePackStateInactive:
+ [pack resume];
+ break;
+
+ case MGLOfflinePackStateActive:
+ [pack suspend];
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
#pragma mark Help methods
- (IBAction)showShortcuts:(id)sender {
@@ -154,6 +247,9 @@ NSString * const MGLLastMapDebugMaskDefaultsKey = @"MGLLastMapDebugMask";
if (menuItem.action == @selector(showPreferences:)) {
return YES;
}
+ if (menuItem.action == @selector(delete:)) {
+ return self.offlinePacksArrayController.selectedObjects.count;
+ }
return NO;
}
diff --git a/platform/osx/app/MainMenu.xib b/platform/osx/app/MainMenu.xib
index 64ff4e550d..c408bdc568 100644
--- a/platform/osx/app/MainMenu.xib
+++ b/platform/osx/app/MainMenu.xib
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10109" systemVersion="15E39d" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10116" systemVersion="15E65" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
- <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10109"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10116"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@@ -17,6 +17,7 @@
</customObject>
<customObject id="Voe-Tx-rLC" customClass="AppDelegate">
<connections>
+ <outlet property="offlinePacksArrayController" destination="dWe-R6-sRz" id="Ar5-xu-ABm"/>
<outlet property="preferencesWindow" destination="UWc-yQ-qda" id="Ota-aT-Mz2"/>
</connections>
</customObject>
@@ -113,6 +114,12 @@
<action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
</connections>
</menuItem>
+ <menuItem title="Save Offline Pack…" id="UXB-sj-C7i">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="addOfflinePack:" target="-1" id="Usu-xO-QEx"/>
+ </connections>
+ </menuItem>
<menuItem title="Revert to Saved" id="KaW-ft-85H">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
@@ -516,6 +523,13 @@
<action selector="performZoom:" target="-1" id="DIl-cC-cCs"/>
</connections>
</menuItem>
+ <menuItem isSeparatorItem="YES" id="Uix-g7-fAt"/>
+ <menuItem title="Offline Packs" id="YW3-jR-knj">
+ <modifierMask key="keyEquivalentModifierMask"/>
+ <connections>
+ <action selector="makeKeyAndOrderFront:" target="Jjv-gs-Tx6" id="cJK-pv-fl5"/>
+ </connections>
+ </menuItem>
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
<modifierMask key="keyEquivalentModifierMask"/>
@@ -551,7 +565,7 @@
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="109" y="131" width="350" height="62"/>
- <rect key="screenRect" x="0.0" y="0.0" width="1680" height="1050"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
<view key="contentView" id="eA4-n3-qPe">
<rect key="frame" x="0.0" y="0.0" width="350" height="62"/>
<autoresizingMask key="autoresizingMask"/>
@@ -609,8 +623,190 @@
<point key="canvasLocation" x="754" y="210"/>
</window>
<userDefaultsController representsSharedInstance="YES" id="45S-yT-WUN"/>
+ <window title="Offline Packs" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" hidesOnDeactivate="YES" oneShot="NO" releasedWhenClosed="NO" showsToolbarButton="NO" visibleAtLaunch="NO" frameAutosaveName="MBXOfflinePacksPanel" animationBehavior="default" id="Jjv-gs-Tx6" customClass="NSPanel">
+ <windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES" utility="YES"/>
+ <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
+ <rect key="contentRect" x="830" y="430" width="400" height="300"/>
+ <rect key="screenRect" x="0.0" y="0.0" width="1280" height="777"/>
+ <view key="contentView" id="8ha-hw-zOD">
+ <rect key="frame" x="0.0" y="0.0" width="400" height="300"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <subviews>
+ <scrollView autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Q8b-0e-dLv">
+ <rect key="frame" x="-1" y="20" width="402" height="281"/>
+ <clipView key="contentView" id="J9U-Yx-o2S">
+ <rect key="frame" x="1" y="0.0" width="400" height="280"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" autosaveColumns="NO" headerView="MAZ-Iq-hBi" id="Ato-Vu-HYT">
+ <rect key="frame" x="0.0" y="0.0" width="400" height="257"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <size key="intercellSpacing" width="3" height="2"/>
+ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+ <color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
+ <tableColumns>
+ <tableColumn identifier="" editable="NO" width="16" minWidth="10" maxWidth="3.4028234663852886e+38" id="xtw-hQ-8C5" userLabel="State">
+ <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+ </tableHeaderCell>
+ <imageCell key="dataCell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" id="edU-Yw-20f"/>
+ <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
+ <connections>
+ <binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.stateImage" id="2wd-1J-TZt"/>
+ </connections>
+ </tableColumn>
+ <tableColumn editable="NO" width="116" minWidth="40" maxWidth="1000" id="2hD-LN-h0L">
+ <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Name">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
+ </tableHeaderCell>
+ <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="oys-QZ-34I">
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
+ <connections>
+ <binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.context" id="NtD-s5-ZUq">
+ <dictionary key="options">
+ <string key="NSValueTransformerName">OfflinePackNameValueTransformer</string>
+ </dictionary>
+ </binding>
+ </connections>
+ </tableColumn>
+ <tableColumn editable="NO" width="50" minWidth="40" maxWidth="1000" id="pkI-c7-xoD">
+ <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Downloaded">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
+ </tableHeaderCell>
+ <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="WfC-qb-HsW">
+ <numberFormatter key="formatter" formatterBehavior="default10_4" numberStyle="decimal" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="sNm-Qn-ne6"/>
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
+ <connections>
+ <binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.countOfResourcesCompleted" id="mu6-Jg-GiU"/>
+ </connections>
+ </tableColumn>
+ <tableColumn identifier="" editable="NO" width="50" minWidth="10" maxWidth="3.4028234663852886e+38" id="Rrd-A9-jqc">
+ <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Total">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+ </tableHeaderCell>
+ <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="mHy-qJ-rOA">
+ <numberFormatter key="formatter" formatterBehavior="default10_4" numberStyle="decimal" usesGroupingSeparator="NO" groupingSize="0" minimumIntegerDigits="0" maximumIntegerDigits="42" id="kyx-ZP-OBH"/>
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
+ <connections>
+ <binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.countOfResourcesExpected" id="mh2-k0-vvB"/>
+ </connections>
+ </tableColumn>
+ <tableColumn identifier="" editable="NO" width="60" minWidth="10" maxWidth="3.4028234663852886e+38" id="h7m-6l-KaS">
+ <tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Size">
+ <font key="font" metaFont="smallSystem"/>
+ <color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+ </tableHeaderCell>
+ <textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="701-bg-k6L">
+ <byteCountFormatter key="formatter" allowsNonnumericFormatting="NO" id="IXV-J9-sP3"/>
+ <font key="font" metaFont="system"/>
+ <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
+ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </textFieldCell>
+ <tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
+ <connections>
+ <binding destination="dWe-R6-sRz" name="value" keyPath="arrangedObjects.countOfBytesCompleted" id="Zsa-Na-yFN"/>
+ </connections>
+ </tableColumn>
+ </tableColumns>
+ <connections>
+ <action trigger="doubleAction" selector="chooseOfflinePack:" target="-1" id="pUN-eT-zRT"/>
+ </connections>
+ </tableView>
+ </subviews>
+ <color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
+ </clipView>
+ <scroller key="horizontalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="YES" id="QLr-6P-Ogs">
+ <rect key="frame" x="1" y="7" width="0.0" height="16"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </scroller>
+ <scroller key="verticalScroller" hidden="YES" verticalHuggingPriority="750" horizontal="NO" id="q0K-eE-mzL">
+ <rect key="frame" x="224" y="17" width="15" height="102"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </scroller>
+ <tableHeaderView key="headerView" id="MAZ-Iq-hBi">
+ <rect key="frame" x="0.0" y="0.0" width="400" height="23"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </tableHeaderView>
+ </scrollView>
+ <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wzf-ce-Spm">
+ <rect key="frame" x="0.0" y="-1" width="21" height="21"/>
+ <constraints>
+ <constraint firstAttribute="width" constant="21" id="5ST-tY-8Ph"/>
+ </constraints>
+ <buttonCell key="cell" type="smallSquare" bezelStyle="smallSquare" image="NSAddTemplate" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" state="on" imageScaling="proportionallyDown" inset="2" id="sew-F7-i5T">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ </buttonCell>
+ <connections>
+ <action selector="addOfflinePack:" target="-1" id="SN0-PM-HoU"/>
+ </connections>
+ </button>
+ <button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7L7-hr-zId">
+ <rect key="frame" x="20" y="0.0" width="21" height="19"/>
+ <constraints>
+ <constraint firstAttribute="width" constant="21" id="JYb-AF-8gZ"/>
+ </constraints>
+ <buttonCell key="cell" type="smallSquare" bezelStyle="smallSquare" image="NSRemoveTemplate" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" state="on" imageScaling="proportionallyDown" inset="2" id="oTF-3m-6qT">
+ <behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
+ <font key="font" metaFont="system"/>
+ <string key="keyEquivalent" base64-UTF8="YES">
+CA
+</string>
+ </buttonCell>
+ <connections>
+ <action selector="delete:" target="-1" id="EGL-bf-yUD"/>
+ </connections>
+ </button>
+ </subviews>
+ <constraints>
+ <constraint firstItem="7L7-hr-zId" firstAttribute="centerY" secondItem="wzf-ce-Spm" secondAttribute="centerY" id="7TI-6w-bf1"/>
+ <constraint firstAttribute="bottom" secondItem="Q8b-0e-dLv" secondAttribute="bottom" constant="20" symbolic="YES" id="DZa-ly-bhV"/>
+ <constraint firstItem="wzf-ce-Spm" firstAttribute="top" secondItem="Q8b-0e-dLv" secondAttribute="bottom" id="LhK-5z-CQA"/>
+ <constraint firstItem="Q8b-0e-dLv" firstAttribute="leading" secondItem="8ha-hw-zOD" secondAttribute="leading" constant="-1" id="Oyo-ch-rZo"/>
+ <constraint firstAttribute="bottom" secondItem="7L7-hr-zId" secondAttribute="bottom" id="TtY-j1-T5h"/>
+ <constraint firstItem="Q8b-0e-dLv" firstAttribute="top" secondItem="8ha-hw-zOD" secondAttribute="top" constant="-1" id="WDk-Ig-Grr"/>
+ <constraint firstAttribute="trailing" secondItem="Q8b-0e-dLv" secondAttribute="trailing" constant="-1" id="hHf-rd-Wcv"/>
+ <constraint firstItem="7L7-hr-zId" firstAttribute="leading" secondItem="8ha-hw-zOD" secondAttribute="leading" constant="20" symbolic="YES" id="iKJ-ph-ACS"/>
+ <constraint firstAttribute="bottom" secondItem="wzf-ce-Spm" secondAttribute="bottom" constant="-1" id="jFV-Xi-fWr"/>
+ <constraint firstItem="wzf-ce-Spm" firstAttribute="leading" secondItem="8ha-hw-zOD" secondAttribute="leading" id="kJt-oJ-72R"/>
+ </constraints>
+ </view>
+ <point key="canvasLocation" x="720" y="317"/>
+ </window>
+ <arrayController objectClassName="MGLOfflinePack" editable="NO" avoidsEmptySelection="NO" id="dWe-R6-sRz" userLabel="Offline Packs Array Controller">
+ <declaredKeys>
+ <string>context</string>
+ <string>countOfResourcesCompleted</string>
+ <string>countOfResourcesExpected</string>
+ <string>countOfBytesCompleted</string>
+ <string>stateImage</string>
+ </declaredKeys>
+ </arrayController>
</objects>
<resources>
+ <image name="NSAddTemplate" width="11" height="11"/>
<image name="NSFollowLinkFreestandingTemplate" width="14" height="14"/>
+ <image name="NSRemoveTemplate" width="11" height="11"/>
</resources>
</document>
diff --git a/platform/osx/app/MapDocument.m b/platform/osx/app/MapDocument.m
index 7c42bc9802..16d15c4ffd 100644
--- a/platform/osx/app/MapDocument.m
+++ b/platform/osx/app/MapDocument.m
@@ -204,6 +204,10 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
appDelegate.pendingZoomLevel = -1;
appDelegate.pendingCamera = nil;
}
+ if (!MGLCoordinateBoundsIsEmpty(appDelegate.pendingVisibleCoordinateBounds)) {
+ self.mapView.visibleCoordinateBounds = appDelegate.pendingVisibleCoordinateBounds;
+ appDelegate.pendingVisibleCoordinateBounds = (MGLCoordinateBounds){ { 0, 0 }, { 0, 0 } };
+ }
if (appDelegate.pendingDebugMask) {
self.mapView.debugMask = appDelegate.pendingDebugMask;
}
@@ -350,6 +354,36 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
[self.mapView addAnnotation:line];
}
+#pragma mark Offline packs
+
+- (IBAction)addOfflinePack:(id)sender {
+ NSAlert *namePrompt = [[NSAlert alloc] init];
+ namePrompt.messageText = @"Add offline pack";
+ namePrompt.informativeText = @"Choose a name for the pack:";
+ NSTextField *nameTextField = [[NSTextField alloc] initWithFrame:NSZeroRect];
+ nameTextField.placeholderString = MGLStringFromCoordinateBounds(self.mapView.visibleCoordinateBounds);
+ [nameTextField sizeToFit];
+ NSRect textFieldFrame = nameTextField.frame;
+ textFieldFrame.size.width = 300;
+ nameTextField.frame = textFieldFrame;
+ namePrompt.accessoryView = nameTextField;
+ [namePrompt addButtonWithTitle:@"Add"];
+ [namePrompt addButtonWithTitle:@"Cancel"];
+ if ([namePrompt runModal] != NSAlertFirstButtonReturn) {
+ return;
+ }
+
+ id <MGLOfflineRegion> region = [[MGLTilePyramidOfflineRegion alloc] initWithStyleURL:self.mapView.styleURL bounds:self.mapView.visibleCoordinateBounds fromZoomLevel:self.mapView.zoomLevel toZoomLevel:self.mapView.maximumZoomLevel];
+ NSData *context = [[NSValueTransformer valueTransformerForName:@"OfflinePackNameValueTransformer"] reverseTransformedValue:nameTextField.stringValue];
+ [[MGLOfflineStorage sharedOfflineStorage] addPackForRegion:region withContext:context completionHandler:^(MGLOfflinePack * _Nullable pack, NSError * _Nullable error) {
+ if (error) {
+ [[NSAlert alertWithError:error] runModal];
+ } else {
+ [pack resume];
+ }
+ }];
+}
+
#pragma mark Help methods
- (IBAction)giveFeedback:(id)sender {
@@ -500,6 +534,10 @@ static const CLLocationCoordinate2D WorldTourDestinations[] = {
if (menuItem.action == @selector(drawPolygonAndPolyLineAnnotations:)) {
return !_isShowingPolygonAndPolylineAnnotations;
}
+ if (menuItem.action == @selector(addOfflinePack:)) {
+ NSURL *styleURL = self.mapView.styleURL;
+ return !styleURL.isFileURL;
+ }
if (menuItem.action == @selector(giveFeedback:)) {
return YES;
}
diff --git a/platform/osx/app/OfflinePackNameValueTransformer.h b/platform/osx/app/OfflinePackNameValueTransformer.h
new file mode 100644
index 0000000000..11fe3ff441
--- /dev/null
+++ b/platform/osx/app/OfflinePackNameValueTransformer.h
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+
+@interface OfflinePackNameValueTransformer : NSValueTransformer
+
+@end
diff --git a/platform/osx/app/OfflinePackNameValueTransformer.m b/platform/osx/app/OfflinePackNameValueTransformer.m
new file mode 100644
index 0000000000..2825e48ed3
--- /dev/null
+++ b/platform/osx/app/OfflinePackNameValueTransformer.m
@@ -0,0 +1,33 @@
+#import "OfflinePackNameValueTransformer.h"
+
+static NSString * const MBXOfflinePackContextNameKey = @"Name";
+
+@implementation OfflinePackNameValueTransformer
+
++ (Class)transformedValueClass {
+ return [NSString class];
+}
+
++ (BOOL)allowsReverseTransformation {
+ return YES;
+}
+
+- (NSString *)transformedValue:(NSData *)context {
+ NSAssert([context isKindOfClass:[NSData class]], @"Context should be NSData.");
+
+ NSDictionary *userInfo = [NSKeyedUnarchiver unarchiveObjectWithData:context];
+ NSAssert([userInfo isKindOfClass:[NSDictionary class]], @"Context of offline pack isn’t a dictionary.");
+ NSString *name = userInfo[MBXOfflinePackContextNameKey];
+ NSAssert([name isKindOfClass:[NSString class]], @"Name of offline pack isn’t a string.");
+ return name;
+}
+
+- (NSData *)reverseTransformedValue:(NSString *)name {
+ NSAssert([name isKindOfClass:[NSString class]], @"Name should be a string.");
+
+ return [NSKeyedArchiver archivedDataWithRootObject:@{
+ MBXOfflinePackContextNameKey: name,
+ }];
+}
+
+@end
diff --git a/platform/osx/app/mapboxgl-app.gypi b/platform/osx/app/mapboxgl-app.gypi
index 7b39f5d9eb..8b8cc17276 100644
--- a/platform/osx/app/mapboxgl-app.gypi
+++ b/platform/osx/app/mapboxgl-app.gypi
@@ -29,6 +29,8 @@
'./LocationCoordinate2DTransformer.m',
'./MapDocument.h',
'./MapDocument.m',
+ './OfflinePackNameValueTransformer.h',
+ './OfflinePackNameValueTransformer.m',
'./TimeIntervalTransformer.h',
'./TimeIntervalTransformer.m',
'./NSValue+Additions.h',