Skip to content

Commit

Permalink
* Fully enable export of favorite groups
Browse files Browse the repository at this point in the history
* Favorites can now be imported, sorted even if "Quick Connect" is selected
* Favorite files containing groups will now be imported correctly
* If a favorite and the group containing said favorite are exported, the favorite will no longer be included twice
  • Loading branch information
dmoagx committed May 11, 2015
1 parent 0f11cb7 commit 11b8718
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 20 deletions.
5 changes: 3 additions & 2 deletions Source/SPConnectionController.m
Expand Up @@ -51,6 +51,7 @@
#import "SPFavoritesImporter.h"
#import "SPThreadAdditions.h"
#import "SPFavoriteColorSupport.h"
#import "SPNamedNode.h"

#import <SPMySQL/SPMySQL.h>

Expand Down Expand Up @@ -738,7 +739,7 @@ - (NSMutableDictionary *)selectedFavorite
{
SPTreeNode *node = [self selectedFavoriteNode];

return (![node isGroup]) ? [[node representedObject] nodeFavorite] : nil;
return (![node isGroup]) ? [(SPFavoriteNode *)[node representedObject] nodeFavorite] : nil;
}

/**
Expand Down Expand Up @@ -1045,7 +1046,7 @@ - (IBAction)exportFavorites:(id)sender
NSSavePanel *savePanel = [NSSavePanel savePanel];

// suggest the name of the favorite or a default name for multiple selection
NSString *fileName = ([[self selectedFavoriteNodes] count] == 1)? [[[self selectedFavorite] objectForKey:SPFavoriteNameKey] stringByAppendingPathExtension:@"plist"] : nil;
NSString *fileName = ([[self selectedFavoriteNodes] count] == 1)? [[(id<SPNamedNode>)[[self selectedFavoriteNode] representedObject] nodeName] stringByAppendingPathExtension:@"plist"] : nil;
// This if() is so we can also catch nil due to favorite corruption (NSSavePanel will @throw if nil is passed in)
if(!fileName)
fileName = SPExportFavoritesFilename;
Expand Down
12 changes: 7 additions & 5 deletions Source/SPConnectionControllerDelegate.m
Expand Up @@ -617,8 +617,6 @@ - (BOOL)validateMenuItem:(NSMenuItem *)menuItem

SPTreeNode *node = [self selectedFavoriteNode];
NSInteger selectedRows = [favoritesOutlineView numberOfSelectedRows];

if (node == quickConnectItem) return NO;

if ((action == @selector(sortFavorites:)) || (action == @selector(reverseSortFavorites:))) {

Expand All @@ -634,7 +632,14 @@ - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
if (action == @selector(reverseSortFavorites:)) {
[menuItem setState:reverseFavoritesSort];
}

return YES;
}

// import does not depend on a selection
if(action == @selector(importFavorites:)) return YES;

if (node == quickConnectItem) return NO;

// Remove/rename the selected node
if (action == @selector(removeNode:) || action == @selector(renameNode:)) {
Expand All @@ -659,9 +664,6 @@ - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
if ([[favoritesRoot allChildLeafs] count] == 0 || selectedRows == 0) {
return NO;
}
else if (selectedRows == 1) {
return (![[self selectedFavoriteNode] isGroup]);
}
else if (selectedRows > 1) {
[menuItem setTitle:NSLocalizedString(@"Export Selected...", @"export selected favorites menu item")];
}
Expand Down
3 changes: 2 additions & 1 deletion Source/SPFavoriteNode.h
Expand Up @@ -28,14 +28,15 @@
//
// More info at <https://github.com/sequelpro/sequelpro>

#import "SPNamedNode.h"
/**
* @class SPFavoriteNode SPFavoriteNode.h
*
* @author Stuart Connolly http://stuconnolly.com/
*
* Tree node the represents a connection favorite.
*/
@interface SPFavoriteNode : NSObject <NSCopying, NSCoding>
@interface SPFavoriteNode : NSObject <NSCopying, NSCoding, SPNamedNode>
{
NSMutableDictionary *nodeFavorite;
}
Expand Down
8 changes: 7 additions & 1 deletion Source/SPFavoriteNode.m
Expand Up @@ -81,6 +81,7 @@ - (id)copyWithZone:(NSZone *)zone

- (id)initWithCoder:(NSCoder *)coder
{
#warning This is not a valid initializer.
[self setNodeFavorite:[coder decodeObjectForKey:SPFavoriteNodeKey]];

return self;
Expand All @@ -96,7 +97,12 @@ - (void)encodeWithCoder:(NSCoder *)coder

- (NSString *)description
{
return [NSString stringWithFormat:@"<%@: %p ('%@')>", [self className], self, [[self nodeFavorite] objectForKey:SPFavoriteNameKey]];
return [NSString stringWithFormat:@"<%@: %p ('%@')>", [self className], self, [self nodeName]];
}

- (NSString *)nodeName
{
return [[self nodeFavorite] objectForKey:SPFavoriteNameKey];
}

#pragma mark -
Expand Down
40 changes: 34 additions & 6 deletions Source/SPFavoritesController.m
Expand Up @@ -45,7 +45,7 @@ - (void)_saveFavoritesData:(NSDictionary *)data;
- (void)_addNode:(SPTreeNode *)node asChildOfNode:(SPTreeNode *)parent;

- (SPTreeNode *)_constructBranchForNodeData:(NSDictionary *)nodeData;

- (SPTreeNode *)_addFavoriteNodeWithData:(NSMutableDictionary *)data asChildOfNode:(SPTreeNode *)parent;
@end

@implementation SPFavoritesController
Expand Down Expand Up @@ -164,6 +164,7 @@ - (SPTreeNode *)addGroupNodeWithName:(NSString *)name asChildOfNode:(SPTreeNode

[self _addNode:node asChildOfNode:parent];

[self saveFavorites];
[[NSNotificationCenter defaultCenter] postNotificationName:SPConnectionFavoritesChangedNotification object:self];

return node;
Expand All @@ -179,11 +180,40 @@ - (SPTreeNode *)addGroupNodeWithName:(NSString *)name asChildOfNode:(SPTreeNode
*/
- (SPTreeNode *)addFavoriteNodeWithData:(NSMutableDictionary *)data asChildOfNode:(SPTreeNode *)parent
{
SPTreeNode *node = [SPTreeNode treeNodeWithRepresentedObject:[SPFavoriteNode favoriteNodeWithDictionary:data]];

SPTreeNode *node = [self _addFavoriteNodeWithData:data asChildOfNode:parent];

[self saveFavorites];
[[NSNotificationCenter defaultCenter] postNotificationName:SPConnectionFavoritesChangedNotification object:self];

return node;
}

/**
* Inner recursive variant of the method above
*/
- (SPTreeNode *)_addFavoriteNodeWithData:(NSMutableDictionary *)data asChildOfNode:(SPTreeNode *)parent
{
id object;
NSArray *childs = nil;
//if it has "Children" it must be a group node, otherwise assume favorite node
if ([data objectForKey:SPFavoriteChildrenKey]) {
object = [SPGroupNode groupNodeWithDictionary:data];
childs = [data objectForKey:SPFavoriteChildrenKey];
}
else {
object = [SPFavoriteNode favoriteNodeWithDictionary:data];
}

SPTreeNode *node = [SPTreeNode treeNodeWithRepresentedObject:object];

[self _addNode:node asChildOfNode:parent];

[[NSNotificationCenter defaultCenter] postNotificationName:SPConnectionFavoritesChangedNotification object:self];
//also add the children
if(childs) {
for (NSMutableDictionary *childData in childs) {
[self _addFavoriteNodeWithData:childData asChildOfNode:node];
}
}

return node;
}
Expand Down Expand Up @@ -460,8 +490,6 @@ - (void)_addNode:(SPTreeNode *)node asChildOfNode:(SPTreeNode *)parent
else {
[[[[favoritesTree mutableChildNodes] objectAtIndex:0] mutableChildNodes] addObject:node];
}

[self saveFavorites];
}

#pragma mark -
Expand Down
5 changes: 4 additions & 1 deletion Source/SPFavoritesExporter.m
Expand Up @@ -74,7 +74,10 @@ - (void)_writeFavoritesInBackground
// Get a dictionary representation of all favorites
for (SPTreeNode *node in [self exportFavorites])
{
[favorites addObject:[node dictionaryRepresentation]];
// The selection could contain a group as well as items in that group.
// So we skip those items, as their group will already export them.
if(![node isDescendantOfNodes:[self exportFavorites]])
[favorites addObject:[node dictionaryRepresentation]];
}

NSDictionary *dictionary = @{SPFavoritesDataRootKey : favorites};
Expand Down
5 changes: 4 additions & 1 deletion Source/SPGroupNode.h
Expand Up @@ -28,14 +28,15 @@
//
// More info at <https://github.com/sequelpro/sequelpro>

#import "SPNamedNode.h"
/**
* @class SPGroupNode SPGroupNode.h
*
* @author Stuart Connolly http://stuconnolly.com/
*
* Tree node that represents a group.
*/
@interface SPGroupNode : NSObject <NSCopying, NSCoding>
@interface SPGroupNode : NSObject <NSCopying, NSCoding, SPNamedNode>
{
BOOL nodeIsExpanded;

Expand All @@ -53,7 +54,9 @@
@property (readwrite, assign) BOOL nodeIsExpanded;

- (id)initWithName:(NSString *)name;
- (id)initWithDictionary:(NSDictionary *)dict;

+ (SPGroupNode *)groupNodeWithName:(NSString *)name;
+ (SPGroupNode *)groupNodeWithDictionary:(NSDictionary *)dict;

@end
15 changes: 15 additions & 0 deletions Source/SPGroupNode.m
Expand Up @@ -61,11 +61,25 @@ - (id)initWithName:(NSString *)name
return self;
}

- (id)initWithDictionary:(NSDictionary *)dict
{
if ((self = [self initWithName:[dict objectForKey:SPFavoritesGroupNameKey]])) {
[self setNodeIsExpanded:[(NSNumber *)[dict objectForKey:SPFavoritesGroupIsExpandedKey] boolValue]];
}

return self;
}

+ (SPGroupNode *)groupNodeWithName:(NSString *)name
{
return [[[self alloc] initWithName:name] autorelease];
}

+ (SPGroupNode *)groupNodeWithDictionary:(NSDictionary *)dict
{
return [[[self alloc] initWithDictionary:dict] autorelease];
}

#pragma mark -
#pragma mark Copying protocol methods

Expand All @@ -84,6 +98,7 @@ - (id)copyWithZone:(NSZone *)zone

- (id)initWithCoder:(NSCoder *)coder
{
#warning This is not a valid initializer.
[self setNodeName:[coder decodeObjectForKey:SPGroupNodeNameKey]];
[self setNodeIsExpanded:[[coder decodeObjectForKey:SPGroupNodeIsExpandedKey] boolValue]];

Expand Down
37 changes: 37 additions & 0 deletions Source/SPNamedNode.h
@@ -0,0 +1,37 @@
//
// SPNamedNode.h
// sequel-pro
//
// Created by Max Lohrmann on 11.05.15.
// Copyright (c) 2015 Max Lohrmann. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// More info at <https://github.com/sequelpro/sequelpro>

#import <Foundation/Foundation.h>

@protocol SPNamedNode <NSObject>

- (NSString *)nodeName;

@end
1 change: 1 addition & 0 deletions Source/SPTreeNode.h
Expand Up @@ -55,6 +55,7 @@
- (SPTreeNode *)parentFromArray:(NSArray *)array;

- (BOOL)isDescendantOfOrOneOfNodes:(NSArray *)nodes;
- (BOOL)isDescendantOfNodes:(NSArray *)nodes;

- (NSDictionary *)dictionaryRepresentation;

Expand Down
36 changes: 33 additions & 3 deletions Source/SPTreeNode.m
Expand Up @@ -50,7 +50,7 @@ + (id)treeNodeWithRepresentedObject:(id)object
- (id)initWithRepresentedObject:(id)object
{
if ((self = [super initWithRepresentedObject:object])) {
[self setIsGroup:NO];
[self setIsGroup:[object isKindOfClass:[SPGroupNode class]]];
}

return self;
Expand Down Expand Up @@ -197,11 +197,12 @@ - (SPTreeNode *)parentFromArray:(NSArray *)array

/**
* Returns YES if self is contained anywhere inside the children or children of
* sub-nodes of the nodes contained inside the supplied array.
* sub-nodes of the nodes contained inside the supplied array or is itself a
* member of the array.
*
* @param nodes The array of nodes to search
*
* @return A BOOL indicating whether or not it's a descendent
* @return A BOOL indicating whether or not it's a descendent or array member
*/
- (BOOL)isDescendantOfOrOneOfNodes:(NSArray *)nodes
{
Expand All @@ -220,6 +221,34 @@ - (BOOL)isDescendantOfOrOneOfNodes:(NSArray *)nodes
return NO;
}

/**
* Returns YES if self is contained anywhere inside the children or children of
* sub-nodes of the nodes contained inside the supplied array, but NOT the given
* array itself.
* This means, if self is a member of nodes but not a child of any
* other node in nodes it will still return NO.
*
* @param nodes The array of nodes to search
*
* @return A BOOL indicating whether or not it's a descendent
*/
- (BOOL)isDescendantOfNodes:(NSArray *)nodes
{
for (SPTreeNode *node in nodes)
{
if (node == self) continue;

// Check all the sub-nodes
if ([node isGroup]) {
if ([self isDescendantOfOrOneOfNodes:[node childNodes]]) {
return YES;
}
}
}

return NO;
}

/**
* Constructs a dictionary representation of the favorite.
*
Expand Down Expand Up @@ -265,6 +294,7 @@ - (NSDictionary *)dictionaryRepresentation

- (id)initWithCoder:(NSCoder *)coder
{
#warning This is not a valid initializer.
[self setIsGroup:[[coder decodeObjectForKey:SPTreeNodeIsGroupKey] boolValue]];

return self;
Expand Down
2 changes: 2 additions & 0 deletions sequel-pro.xcodeproj/project.pbxproj
Expand Up @@ -891,6 +891,7 @@
4DECC3340EC2A170008D359E /* Growl.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Growl.framework; path = Frameworks/Growl.framework; sourceTree = "<group>"; };
501B1D161728A3DA0017C92E /* SPCharsetCollationHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPCharsetCollationHelper.h; sourceTree = "<group>"; };
501B1D171728A3DA0017C92E /* SPCharsetCollationHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPCharsetCollationHelper.m; sourceTree = "<group>"; };
5037F79A1B00148000733564 /* SPNamedNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SPNamedNode.h; sourceTree = "<group>"; };
503B02C81AE82C5E0060CAB1 /* SPTableFilterParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTableFilterParser.h; sourceTree = "<group>"; };
503B02C91AE82C5E0060CAB1 /* SPTableFilterParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTableFilterParser.m; sourceTree = "<group>"; };
503B02CE1AE95C2C0060CAB1 /* SPTableFilterParserTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPTableFilterParserTest.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1756,6 +1757,7 @@
1798F1941550181B004B0AB8 /* SPGroupNode.m */,
17D3C22012859E070047709F /* SPFavoriteNode.h */,
17D3C22112859E070047709F /* SPFavoriteNode.m */,
5037F79A1B00148000733564 /* SPNamedNode.h */,
);
name = "Tree Nodes";
sourceTree = "<group>";
Expand Down

0 comments on commit 11b8718

Please sign in to comment.