| 1 | /* Copyright (C) 2008-2009 Peter Speck |
|---|
| 2 | * |
|---|
| 3 | * This program is free software: you can redistribute it and/or modify |
|---|
| 4 | * it under the terms of the GNU General Public License as published by |
|---|
| 5 | * the Free Software Foundation, either version 3 of the License, or |
|---|
| 6 | * (at your option) any later version. |
|---|
| 7 | * |
|---|
| 8 | * This program is distributed in the hope that it will be useful, |
|---|
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 11 | * GNU General Public License for more details. |
|---|
| 12 | * |
|---|
| 13 | * You should have received a copy of the GNU General Public License |
|---|
| 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 15 | */ |
|---|
| 16 | |
|---|
| 17 | #import "FilterListView.h" |
|---|
| 18 | #import "FilterTabController.h" |
|---|
| 19 | #import "Prefs.h" |
|---|
| 20 | #import "Filter.h" |
|---|
| 21 | #import "ServerCommunication.h" |
|---|
| 22 | #import "Xml.h" |
|---|
| 23 | #import "FilterRuleTableView.h" |
|---|
| 24 | |
|---|
| 25 | #ifdef DEBUG |
|---|
| 26 | //#define DEBUG_DD |
|---|
| 27 | #endif |
|---|
| 28 | |
|---|
| 29 | @interface FilterListDropView : NSView |
|---|
| 30 | { |
|---|
| 31 | NSColor* _color; |
|---|
| 32 | } |
|---|
| 33 | |
|---|
| 34 | - (id)initWithFrame:(NSRect)frameRect; |
|---|
| 35 | - (void)dealloc; |
|---|
| 36 | - (void)drawRect:(NSRect)r; |
|---|
| 37 | |
|---|
| 38 | @end |
|---|
| 39 | |
|---|
| 40 | @implementation FilterListDropView |
|---|
| 41 | |
|---|
| 42 | - (id)initWithFrame:(NSRect)frameRect |
|---|
| 43 | { |
|---|
| 44 | if (!(self = [super initWithFrame:frameRect])) |
|---|
| 45 | return nil; |
|---|
| 46 | _color = [[NSColor colorWithDeviceRed:0.23f green:0.33f blue:0.85f alpha:1] retain]; |
|---|
| 47 | return self; |
|---|
| 48 | } |
|---|
| 49 | |
|---|
| 50 | - (void)dealloc |
|---|
| 51 | { |
|---|
| 52 | [_color release]; |
|---|
| 53 | [super dealloc]; |
|---|
| 54 | } |
|---|
| 55 | |
|---|
| 56 | - (void)drawRect:(NSRect)r |
|---|
| 57 | { |
|---|
| 58 | [super drawRect:r]; |
|---|
| 59 | [_color set]; |
|---|
| 60 | NSRectFill(r); |
|---|
| 61 | } |
|---|
| 62 | |
|---|
| 63 | @end |
|---|
| 64 | |
|---|
| 65 | @interface FilterListView() |
|---|
| 66 | |
|---|
| 67 | - (void)releaseDragInfo; |
|---|
| 68 | |
|---|
| 69 | @end |
|---|
| 70 | |
|---|
| 71 | |
|---|
| 72 | @implementation FilterListView |
|---|
| 73 | |
|---|
| 74 | - (id)initWithFrame:(NSRect)frameRect |
|---|
| 75 | { |
|---|
| 76 | if (!(self = [super initWithFrame:frameRect])) |
|---|
| 77 | return nil; |
|---|
| 78 | [self registerForDraggedTypes:[NSArray arrayWithObject:[Filter draggingDataType]]]; |
|---|
| 79 | return self; |
|---|
| 80 | } |
|---|
| 81 | |
|---|
| 82 | - (void)dealloc |
|---|
| 83 | { |
|---|
| 84 | [self releaseDragInfo]; |
|---|
| 85 | [self setController:NULL]; |
|---|
| 86 | [super dealloc]; |
|---|
| 87 | } |
|---|
| 88 | |
|---|
| 89 | - (void)setController:(FilterTabController*)controller |
|---|
| 90 | { |
|---|
| 91 | [_controller release]; |
|---|
| 92 | _controller = [controller retain]; |
|---|
| 93 | [_prefs release]; |
|---|
| 94 | _prefs = [[controller prefs] retain]; |
|---|
| 95 | } |
|---|
| 96 | |
|---|
| 97 | - (void)zapDropViewFrame |
|---|
| 98 | { |
|---|
| 99 | if (!_dropDestinationView) |
|---|
| 100 | return; |
|---|
| 101 | #ifdef DEBUG_DD |
|---|
| 102 | NSLog(@"Zaps _dropDestinationView"); |
|---|
| 103 | #endif |
|---|
| 104 | [_dropDestinationView removeFromSuperview]; |
|---|
| 105 | [_dropDestinationView release]; |
|---|
| 106 | _dropDestinationView = NULL; |
|---|
| 107 | } |
|---|
| 108 | |
|---|
| 109 | - (NSPoint)findDropDestinationFilter:(NSPoint)mousePos withDestinationIndex:(NSUInteger*)dstIndex |
|---|
| 110 | { |
|---|
| 111 | if (dstIndex) |
|---|
| 112 | *dstIndex = NSNotFound; |
|---|
| 113 | NSArray* rows = [self rows]; |
|---|
| 114 | NSInteger num = [rows count]; |
|---|
| 115 | for (int i = 0; i < num; i++) { |
|---|
| 116 | Filter* filter = [rows objectAtIndex:i]; |
|---|
| 117 | NSRect r = [filter frame]; |
|---|
| 118 | if (mousePos.y > r.origin.y + r.size.height && i != num - 1) // always accept pos below last item in list. |
|---|
| 119 | continue; |
|---|
| 120 | CGFloat y = r.origin.y - 1; |
|---|
| 121 | if (mousePos.y > r.origin.y + r.size.height / 2) { |
|---|
| 122 | y += r.size.height; |
|---|
| 123 | i++; |
|---|
| 124 | } else { |
|---|
| 125 | if (i == 0) |
|---|
| 126 | y += 1; |
|---|
| 127 | } |
|---|
| 128 | if (NO) { |
|---|
| 129 | NSLog(@"dst mp = %.0f, idx = %d, r = %.0f to %.0f to %.0f -> %ld", |
|---|
| 130 | mousePos.y, i, r.origin.y, |
|---|
| 131 | r.origin.y + r.size.height / 2, r.origin.y + r.size.height, |
|---|
| 132 | (long)(dstIndex ? *dstIndex : 66666)); |
|---|
| 133 | } |
|---|
| 134 | if (dstIndex) |
|---|
| 135 | *dstIndex = i; |
|---|
| 136 | return NSMakePoint(0, y); |
|---|
| 137 | } |
|---|
| 138 | return NSMakePoint(-1, -1); |
|---|
| 139 | } |
|---|
| 140 | |
|---|
| 141 | - (NSDragOperation)setDropViewFrame:(NSPoint)pos |
|---|
| 142 | { |
|---|
| 143 | pos = [self findDropDestinationFilter:pos withDestinationIndex:NULL]; |
|---|
| 144 | if (pos.x < 0) { |
|---|
| 145 | [self zapDropViewFrame]; |
|---|
| 146 | return NSDragOperationNone; |
|---|
| 147 | } |
|---|
| 148 | if (_dropDestinationView) { |
|---|
| 149 | [_dropDestinationView setFrameOrigin:pos]; |
|---|
| 150 | } else { |
|---|
| 151 | NSRect r = NSMakeRect(0, pos.y, [self frame].size.width, 2); |
|---|
| 152 | #ifdef DEBUG_DD |
|---|
| 153 | NSLog(@"Creates _dropDestinationView: %@", NSStringFromRect(r)); |
|---|
| 154 | #endif |
|---|
| 155 | _dropDestinationView = [[FilterListDropView alloc] initWithFrame:r]; |
|---|
| 156 | [self addSubview:_dropDestinationView positioned:NSWindowAbove relativeTo:NULL]; |
|---|
| 157 | } |
|---|
| 158 | return NSDragOperationMove; |
|---|
| 159 | } |
|---|
| 160 | |
|---|
| 161 | - (void)releaseDragInfo |
|---|
| 162 | { |
|---|
| 163 | [self zapDropViewFrame]; |
|---|
| 164 | [_srcFilter release]; |
|---|
| 165 | _srcFilter = NULL; |
|---|
| 166 | } |
|---|
| 167 | |
|---|
| 168 | - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender |
|---|
| 169 | { |
|---|
| 170 | NSPasteboard* pboard = [sender draggingPasteboard]; |
|---|
| 171 | #ifdef DEBUG_DD |
|---|
| 172 | NSLog(@"draggingEntered: %@", [pboard types]); |
|---|
| 173 | #endif |
|---|
| 174 | if (![[pboard types] containsObject:[Filter draggingDataType]]) |
|---|
| 175 | return NSDragOperationNone; |
|---|
| 176 | NSPoint pos = [self convertPointFromBase:[sender draggingLocation]]; |
|---|
| 177 | return [self setDropViewFrame:pos]; |
|---|
| 178 | } |
|---|
| 179 | |
|---|
| 180 | - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender |
|---|
| 181 | { |
|---|
| 182 | #ifdef DEBUG_DD |
|---|
| 183 | NSLog(@"performDragOperation: %@", sender); |
|---|
| 184 | #endif |
|---|
| 185 | NSPoint draggingLocationLocal = [self convertPointFromBase:[sender draggingLocation]]; |
|---|
| 186 | NSUInteger srcIndex = [[self rows] indexOfObject:_srcFilter]; |
|---|
| 187 | NSUInteger dstIndex = NSNotFound; |
|---|
| 188 | [self findDropDestinationFilter:draggingLocationLocal withDestinationIndex:&dstIndex]; |
|---|
| 189 | #ifdef DEBUG_DD |
|---|
| 190 | NSLog(@"index: %d -> %d", srcIndex, dstIndex); |
|---|
| 191 | #endif |
|---|
| 192 | if (dstIndex == NSNotFound || srcIndex == NSNotFound || dstIndex == srcIndex || dstIndex == srcIndex + 1) { |
|---|
| 193 | [self releaseDragInfo]; |
|---|
| 194 | return NO; |
|---|
| 195 | } |
|---|
| 196 | ServerCommunication* comm = [_srcFilter comm]; |
|---|
| 197 | [_srcFilter retain]; |
|---|
| 198 | [self removeRow:_srcFilter]; |
|---|
| 199 | [self addRow:_srcFilter atIndex:dstIndex - (dstIndex > srcIndex)]; |
|---|
| 200 | [_srcFilter release]; |
|---|
| 201 | [self releaseDragInfo]; |
|---|
| 202 | NSMutableArray* a = [NSMutableArray arrayWithCapacity:10]; |
|---|
| 203 | for (Filter* filter in [self rows]) |
|---|
| 204 | [a addObject:[filter filterE]]; |
|---|
| 205 | [Xml reorderChilds:a]; |
|---|
| 206 | [_prefs markAsDirty]; |
|---|
| 207 | [comm saveDirtyPrefsAndNotify]; |
|---|
| 208 | return YES; |
|---|
| 209 | } |
|---|
| 210 | |
|---|
| 211 | - (BOOL)prepareForDragOperation:(id < NSDraggingInfo >)sender |
|---|
| 212 | { |
|---|
| 213 | #ifdef DEBUG_DD |
|---|
| 214 | NSLog(@"prepareForDragOperation: %@", sender); |
|---|
| 215 | #endif |
|---|
| 216 | return YES; |
|---|
| 217 | } |
|---|
| 218 | |
|---|
| 219 | - (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender |
|---|
| 220 | { |
|---|
| 221 | #ifdef DEBUG_DD |
|---|
| 222 | // NSLog(@"draggingUpdated: %@", sender); |
|---|
| 223 | #endif |
|---|
| 224 | NSPoint pos = [self convertPointFromBase:[sender draggingLocation]]; |
|---|
| 225 | return [self setDropViewFrame:pos]; |
|---|
| 226 | } |
|---|
| 227 | |
|---|
| 228 | - (void)draggingExited:(id < NSDraggingInfo >)sender |
|---|
| 229 | { |
|---|
| 230 | #ifdef DEBUG_DD |
|---|
| 231 | NSLog(@"draggingUpdated: %@", sender); |
|---|
| 232 | #endif |
|---|
| 233 | [self zapDropViewFrame]; |
|---|
| 234 | } |
|---|
| 235 | |
|---|
| 236 | - (void)startDrag:(Filter*)filter withImage:(NSImage*)image withEvent:(NSEvent*)event |
|---|
| 237 | { |
|---|
| 238 | [self releaseDragInfo]; |
|---|
| 239 | _srcFilter = [filter retain]; |
|---|
| 240 | NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard]; |
|---|
| 241 | [pboard declareTypes:[NSArray arrayWithObject:[Filter draggingDataType]] owner:self]; |
|---|
| 242 | NSString* s = [NSString stringWithFormat:@"%d", [filter filterId]]; |
|---|
| 243 | NSData* data = [s dataUsingEncoding:NSUTF8StringEncoding]; |
|---|
| 244 | [pboard setData:data forType:[Filter draggingDataType]]; |
|---|
| 245 | NSPoint dragPoint = [filter convertPoint:NSMakePoint(0, 0) toView:self]; |
|---|
| 246 | [NSApp preventWindowOrdering]; |
|---|
| 247 | [self dragImage:image |
|---|
| 248 | at:dragPoint |
|---|
| 249 | offset:NSMakeSize(0, 0) |
|---|
| 250 | event:event |
|---|
| 251 | pasteboard:pboard |
|---|
| 252 | source:self |
|---|
| 253 | slideBack:YES]; |
|---|
| 254 | } |
|---|
| 255 | |
|---|
| 256 | - (void)draggedImage:(NSImage*)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation |
|---|
| 257 | { |
|---|
| 258 | #ifdef DEBUG_DD |
|---|
| 259 | NSLog(@"draggedImage:endedAt = %d", operation); |
|---|
| 260 | #endif |
|---|
| 261 | [self releaseDragInfo]; |
|---|
| 262 | } |
|---|
| 263 | |
|---|
| 264 | #pragma mark ------------------- Paste of rules |
|---|
| 265 | |
|---|
| 266 | - (BOOL)validateMenuItem:(NSMenuItem*)item |
|---|
| 267 | { |
|---|
| 268 | if ([item action] != @selector(paste:)) |
|---|
| 269 | return [super validateMenuItem:item]; |
|---|
| 270 | return [[_controller rulesTableView] validateMenuItem:item]; |
|---|
| 271 | } |
|---|
| 272 | |
|---|
| 273 | - (void)paste:(id)sender |
|---|
| 274 | { |
|---|
| 275 | [[_controller rulesTableView] paste:sender]; |
|---|
| 276 | } |
|---|
| 277 | |
|---|
| 278 | @end |
|---|