![]() |
API 0.9.5
|
00001 /* 00002 * CPPredicateEditor.j 00003 * AppKit 00004 * 00005 * Created by cacaodev. 00006 * Copyright 2011, cacaodev. 00007 * 00008 * This library is free software; you can redistribute it and/or 00009 * modify it under the terms of the GNU Lesser General Public 00010 * License as published by the Free Software Foundation; either 00011 * version 2.1 of the License, or (at your option) any later version. 00012 * 00013 * This library is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Lesser General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License along with this library; if not, write to the Free Software 00020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00021 */ 00022 00023 00024 @implementation CPPredicateEditor : CPRuleEditor 00025 { 00026 CPArray _allTemplates; 00027 CPArray _rootTrees; 00028 CPArray _rootHeaderTrees; 00029 id _predicateTarget; 00030 SEL _predicateAction; 00031 } 00032 00033 #pragma mark public methods 00034 00053 - (CPArray)rowTemplates 00054 { 00055 return _allTemplates; 00056 } 00057 00063 - (void)setRowTemplates:(id)rowTemplates 00064 { 00065 if (_allTemplates == rowTemplates) 00066 return; 00067 00068 _allTemplates = rowTemplates; 00069 00070 [self _updateItemsByCompoundTemplates]; 00071 [self _updateItemsBySimpleTemplates]; 00072 00073 if ([self numberOfRows] > 0) 00074 { 00075 var predicate = [super predicate]; 00076 [self _reflectPredicate:predicate]; 00077 } 00078 } 00079 00081 - (void)_initRuleEditorShared 00082 { 00083 [super _initRuleEditorShared]; 00084 00085 _rootTrees = [CPArray array]; 00086 _rootHeaderTrees = [CPArray array]; 00087 } 00088 00089 - (id)initWithFrame:(CGRect)aFrame 00090 { 00091 self = [super initWithFrame:aFrame]; 00092 if (self != nil) 00093 { 00094 var initialTemplate = [[CPPredicateEditorRowTemplate alloc] initWithCompoundTypes:[CPAndPredicateType, CPOrPredicateType]]; 00095 _allTemplates = [CPArray arrayWithObject:initialTemplate]; 00096 } 00097 00098 return self; 00099 } 00100 00101 - (id)objectValue 00102 { 00103 return [super predicate]; 00104 } 00105 00106 - (void)_updateItemsBySimpleTemplates 00107 { 00108 var templates = [CPMutableArray array], 00109 count = [_allTemplates count], 00110 t; 00111 00112 while (count--) 00113 { 00114 var t = _allTemplates[count]; 00115 if ([t _rowType] == CPRuleEditorRowTypeSimple) 00116 [templates insertObject:t atIndex:0]; 00117 } 00118 00119 var trees = [self _constructTreesForTemplates:templates]; 00120 if ([trees count] > 0) 00121 _rootTrees = [self _mergeTree:trees]; 00122 } 00123 00124 - (void)_updateItemsByCompoundTemplates 00125 { 00126 var templates = [CPMutableArray array], 00127 count = [_allTemplates count], 00128 t; 00129 00130 while (count--) 00131 { 00132 var t = _allTemplates[count]; 00133 if ([t _rowType] == CPRuleEditorRowTypeCompound) 00134 [templates insertObject:t atIndex:0]; 00135 } 00136 00137 var trees = [self _constructTreesForTemplates:templates]; 00138 if ([trees count] > 0) 00139 _rootHeaderTrees = [self _mergeTree:trees]; 00140 } 00141 00142 - (CPArray)_constructTreesForTemplates:(id)templates 00143 { 00144 var trees = [CPMutableArray array], 00145 count = [templates count]; 00146 00147 for (var i = 0; i < count; i++) 00148 { 00149 var tree = [self _constructTreeForTemplate:templates[i]]; 00150 [trees addObjectsFromArray:tree]; 00151 } 00152 00153 return trees; 00154 } 00155 00156 - (id)_mergeTree:(id)tree 00157 { 00158 var merged = [CPMutableArray array], 00159 titles = [CPMutableArray array], 00160 count = [tree count]; 00161 00162 for (var i = 0; i < count; i++) 00163 { 00164 var t = tree[i], 00165 title = [CPString stringWithString:[t title]]; 00166 00167 if ([titles containsObject:title]) 00168 { 00169 CPLogConsole("CPPredicateEditor does not support templates merging yet. Ignoring duplicate template: " + [t description]); 00170 continue; 00171 } 00172 00173 [merged addObject:t]; 00174 [titles addObject:title]; 00175 } 00176 00177 return merged; 00178 } 00179 00180 - (id)_constructTreeForTemplate:(CPPredicateEditorRowTemplate)aTemplate 00181 { 00182 var tree = [CPArray array], 00183 templateViews = [aTemplate templateViews], 00184 count = [templateViews count]; 00185 00186 while (count--) 00187 { 00188 var children = [CPArray array], 00189 itemsCount = 0, 00190 menuIndex = -1, 00191 itemsArray, 00192 00193 templateView = [templateViews objectAtIndex:count], 00194 isPopup = [templateView isKindOfClass:[CPPopUpButton class]]; 00195 00196 if (isPopup) 00197 { 00198 itemArray = [[templateView itemArray] valueForKey:@"title"]; 00199 itemsCount = [itemArray count]; 00200 menuIndex = 0; 00201 } 00202 00203 for (; menuIndex < itemsCount; menuIndex++) 00204 { 00205 var item = [_CPPredicateEditorTree new]; 00206 [item setIndexIntoTemplate:count]; 00207 [item setTemplate:aTemplate]; 00208 [item setMenuItemIndex:menuIndex]; 00209 if (isPopup) 00210 [item setTitle:[itemArray objectAtIndex:menuIndex]]; 00211 00212 [children addObject:item]; 00213 } 00214 00215 [children makeObjectsPerformSelector:@selector(setChildren:) withObject:tree]; 00216 tree = children; 00217 } 00218 00219 return tree; 00220 } 00221 00222 #pragma mark Set the Predicate 00223 00224 - (void)setObjectValue:(id)objectValue 00225 { 00226 if (![[objectValue predicateFormat] isEqualToString:[[super predicate] predicateFormat]]) // ?? 00227 [self _reflectPredicate:objectValue]; 00228 } 00229 00230 - (void)_reflectPredicate:(id)predicate 00231 { 00232 var animation = _currentAnimation; 00233 _currentAnimation = nil; 00234 00235 if (predicate != nil) 00236 { 00237 if ((_nestingMode == CPRuleEditorNestingModeSimple || _nestingMode == CPRuleEditorNestingModeCompound) 00238 && [predicate isKindOfClass:[CPComparisonPredicate class]]) 00239 predicate = [[CPCompoundPredicate alloc] initWithType:[self _compoundPredicateTypeForRootRows] subpredicates:[CPArray arrayWithObject:predicate]]; 00240 00241 var row = [self _rowObjectFromPredicate:predicate]; 00242 if (row != nil) 00243 [_boundArrayOwner setValue:[CPArray arrayWithObject:row] forKey:_boundArrayKeyPath]; 00244 } 00245 00246 [self setAnimation:animation]; 00247 } 00248 00249 - (id)_rowObjectFromPredicate:(CPPredicate)predicate 00250 { 00251 var quality, // TODO: We should use this ref somewhere ! 00252 type, 00253 matchedTemplate = [CPPredicateEditorRowTemplate _bestMatchForPredicate:predicate inTemplates:[self rowTemplates] quality:quality]; 00254 00255 if (matchedTemplate == nil) 00256 return nil; 00257 00258 var copyTemplate = [matchedTemplate copy], 00259 subpredicates = [matchedTemplate displayableSubpredicatesOfPredicate:predicate]; 00260 00261 if (subpredicates == nil) 00262 { 00263 [copyTemplate _setComparisonPredicate:predicate]; 00264 type = CPRuleEditorRowTypeSimple; 00265 } 00266 else 00267 { 00268 [copyTemplate _setCompoundPredicate:predicate]; 00269 type = CPRuleEditorRowTypeCompound; 00270 } 00271 00272 var row = [self _rowFromTemplate:copyTemplate originalTemplate:matchedTemplate withRowType:type]; 00273 00274 if (subpredicates == nil) 00275 return row; 00276 00277 var count = [subpredicates count], 00278 subrows = [CPMutableArray array]; 00279 00280 for (var i = 0; i < count; i++) 00281 { 00282 var subrow = [self _rowObjectFromPredicate:subpredicates[i]]; 00283 if (subrow != nil) 00284 [subrows addObject:subrow]; 00285 } 00286 00287 [row setValue:subrows forKey:[super subrowsKeyPath]]; 00288 00289 return row; 00290 } 00291 00292 - (id)_rowFromTemplate:(CPPredicateEditorRowTemplate)aTemplate originalTemplate:(CPPredicateEditorRowTemplate)originalTemplate withRowType:(CPRuleEditorRowType)rowType 00293 { 00294 var criteria = [CPArray array], 00295 values = [CPArray array], 00296 templateViews = [aTemplate templateViews], 00297 rootItems, 00298 count; 00299 00300 rootItems = (rowType == CPRuleEditorRowTypeSimple) ? _rootTrees : _rootHeaderTrees; 00301 00302 while ((count = [rootItems count]) > 0) 00303 { 00304 var treeChild; 00305 for (var i = 0; i < count; i++) 00306 { 00307 treeChild = [rootItems objectAtIndex:i]; 00308 00309 var currentView = [templateViews objectAtIndex:[treeChild indexIntoTemplate]], 00310 menuItemIndex = [treeChild menuItemIndex]; 00311 00312 if (menuItemIndex == -1 || [[treeChild title] isEqual:[currentView titleOfSelectedItem]]) 00313 { 00314 var node = [_CPPredicateEditorRowNode rowNodeFromTree:treeChild]; 00315 [node applyTemplate:aTemplate withViews:templateViews forOriginalTemplate:originalTemplate]; 00316 00317 [criteria addObject:node]; 00318 [values addObject:[node displayValue]]; 00319 break; 00320 } 00321 } 00322 00323 rootItems = [treeChild children]; 00324 } 00325 00326 var row = [CPDictionary dictionaryWithObjectsAndKeys:criteria, @"criteria", values, @"displayValues", rowType, @"rowType"]; 00327 00328 return row; 00329 } 00330 00331 #pragma mark Get the predicate 00332 00333 - (void)_updatePredicate 00334 { 00335 [self willChangeValueForKey:@"objectValue"]; 00336 [self _updatePredicateFromRows]; 00337 [self didChangeValueForKey:@"objectValue"]; 00338 } 00339 00340 - (void)_updatePredicateFromRows 00341 { 00342 var rootRowsArray = [super _rootRowsArray], 00343 subpredicates = [CPMutableArray array], 00344 count = count2 = [rootRowsArray count], 00345 predicate; 00346 00347 while (count--) 00348 { 00349 var item = [rootRowsArray objectAtIndex:count], 00350 subpredicate = [self _predicateFromRowItem:item]; 00351 00352 if (subpredicate != nil) 00353 [subpredicates insertObject:subpredicate atIndex:0]; 00354 } 00355 00356 if (_nestingMode != CPRuleEditorNestingModeList && count2 == 1) 00357 predicate = [subpredicates lastObject]; 00358 else 00359 predicate = [[CPCompoundPredicate alloc] initWithType:[self _compoundPredicateTypeForRootRows] subpredicates:subpredicates]; 00360 00361 [super _setPredicate:predicate]; 00362 } 00363 00364 - (id)_predicateFromRowItem:(id)rowItem 00365 { 00366 var subpredicates = [CPArray array], 00367 rowType = [rowItem valueForKey:_typeKeyPath]; 00368 00369 if (rowType == CPRuleEditorRowTypeCompound) 00370 { 00371 var subrows = [rowItem valueForKey:_subrowsArrayKeyPath], 00372 count = [subrows count]; 00373 00374 for (var i = 0; i < count; i++) 00375 { 00376 var subrow = [subrows objectAtIndex:i]; 00377 var predicate = [self _predicateFromRowItem:subrow]; 00378 [subpredicates addObject:predicate]; 00379 } 00380 } 00381 00382 var criteria = [rowItem valueForKey:_itemsKeyPath], 00383 displayValues = [rowItem valueForKey:_valuesKeyPath], 00384 count = [criteria count], 00385 lastItem = [criteria lastObject], 00386 template = [lastItem templateForRow], 00387 templateViews = [template templateViews]; 00388 00389 for (var j = 0; j < count; j++) 00390 { 00391 var view = [templateViews objectAtIndex:j], 00392 value = [displayValues objectAtIndex:j]; 00393 [[criteria objectAtIndex:j] setTemplateViews:templateViews]; 00394 00395 if ([view isKindOfClass:[CPPopUpButton class]]) 00396 [view selectItemWithTitle:value]; 00397 else if ([view respondsToSelector:@selector(setObjectValue:)]) 00398 [view setObjectValue:[value objectValue]]; 00399 } 00400 00401 return [template predicateWithSubpredicates:subpredicates]; 00402 } 00403 00404 - (CPCompoundPredicateType)_compoundPredicateTypeForRootRows 00405 { 00406 return CPAndPredicateType; 00407 } 00408 00409 #pragma mark Control delegate 00410 00411 - (void)_sendRuleAction 00412 { 00413 [self _updatePredicate]; 00414 [super _sendRuleAction]; 00415 } 00416 00417 - (BOOL)_sendsActionOnIncompleteTextChange 00418 { 00419 return NO; 00420 } 00421 00422 /* 00423 - (void)_setDefaultTargetAndActionOnView:(CPView)view 00424 { 00425 if ([view isKindOfClass:[CPControl class]]) 00426 { 00427 [view setTarget:self]; 00428 [view setAction:@selector(_templateControlValueDidChange:)]; 00429 } 00430 } 00431 - (void)_templateControlValueDidChange:(id)sender 00432 { 00433 } 00434 - (void)controlTextDidBeginEditing:(CPNotification)notification 00435 { 00436 } 00437 - (void)controlTextDidEndEditing:(CPNotification)notification 00438 { 00439 } 00440 - (void)controlTextDidChange:(CPNotification)notification 00441 { 00442 } 00443 */ 00444 00445 #pragma mark RuleEditor delegate methods 00446 00447 - (int)_queryNumberOfChildrenOfItem:(id)rowItem withRowType:(int)type 00448 { 00449 if (rowItem == nil) 00450 { 00451 var trees = (type == CPRuleEditorRowTypeSimple) ? _rootTrees : _rootHeaderTrees; 00452 return [trees count]; 00453 } 00454 return [[rowItem children] count]; 00455 } 00456 00457 - (id)_queryChild:(int)childIndex ofItem:(id)rowItem withRowType:(int)type 00458 { 00459 if (rowItem == nil) 00460 { 00461 var trees = (type == CPRuleEditorRowTypeSimple) ? _rootTrees : _rootHeaderTrees; 00462 return [_CPPredicateEditorRowNode rowNodeFromTree:trees[childIndex]]; 00463 } 00464 00465 return [[rowItem children] objectAtIndex:childIndex]; 00466 } 00467 00468 - (id)_queryValueForItem:(id)rowItem inRow:(int)rowIndex 00469 { 00470 return [rowItem displayValue]; 00471 } 00472 00473 @end 00474 00475 var CPPredicateTemplatesKey = @"CPPredicateTemplates"; 00476 00477 @implementation CPPredicateEditor (CPCoding) 00478 00479 - (id)initWithCoder:(id)aCoder 00480 { 00481 self = [super initWithCoder:aCoder]; 00482 if (self != nil) 00483 { 00484 var nibTemplates = [aCoder decodeObjectForKey:CPPredicateTemplatesKey]; 00485 if (nibTemplates != nil) 00486 [self setRowTemplates:nibTemplates]; 00487 } 00488 00489 return self; 00490 } 00491 00492 - (void)encodeWithCoder:(id)aCoder 00493 { 00494 [super encodeWithCoder:aCoder]; 00495 [aCoder encodeObject:_allTemplates forKey:CPPredicateTemplatesKey]; 00496 } 00497 00498 @end 00499 00502 @implementation CPPredicateEditor (CPSynthesizedAccessors) 00503 00504 00507 - (id)target 00508 { 00509 return _predicateTarget; 00510 } 00511 00515 - (void)setTarget:(id)aValue 00516 { 00517 _predicateTarget = aValue; 00518 } 00519 00523 - (SEL)action 00524 { 00525 return _predicateAction; 00526 } 00527 00531 - (void)setAction:(SEL)aValue 00532 { 00533 _predicateAction = aValue; 00534 } 00535 00536 @end