API  0.9.6
 All Classes Files Functions Variables Macros Groups Pages
CPPredicateEditor.j
Go to the documentation of this file.
1 /*
2  * CPPredicateEditor.j
3  * AppKit
4  *
5  * Created by cacaodev.
6  * Copyright 2011, cacaodev.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 @implementation CPPredicateEditor : CPRuleEditor
25 {
26  CPArray _allTemplates;
27  CPArray _rootTrees;
28  CPArray _rootHeaderTrees;
29  id _predicateTarget;
30  SEL _predicateAction;
31 }
32 
33 #pragma mark public methods
34 
53 - (CPArray)rowTemplates
54 {
55  return _allTemplates;
56 }
57 
63 - (void)setRowTemplates:(id)rowTemplates
64 {
65  if (_allTemplates == rowTemplates)
66  return;
67 
68  _allTemplates = rowTemplates;
69 
70  [self _updateItemsByCompoundTemplates];
71  [self _updateItemsBySimpleTemplates];
72 
73  if ([self numberOfRows] > 0)
74  {
75  var predicate = [super predicate];
76  [self _reflectPredicate:predicate];
77  }
78 }
79 
81 + (Class)_binderClassForBinding:(CPString)theBinding
82 {
83  if (theBinding == CPValueBinding)
85 
86  return [super _binderClassForBinding:theBinding];
87 }
88 
89 - (id)_replacementKeyPathForBinding:(CPString)aBinding
90 {
91  if (aBinding == CPValueBinding)
92  return @"predicate";
93 
94  return [super _replacementKeyPathForBinding:aBinding];
95 }
96 
97 - (void)_initRuleEditorShared
98 {
99  [super _initRuleEditorShared];
100 
101  _rootTrees = [CPArray array];
102  _rootHeaderTrees = [CPArray array];
103 }
104 
105 - (id)initWithFrame:(CGRect)aFrame
106 {
107  self = [super initWithFrame:aFrame];
108  if (self != nil)
109  {
110  var initialTemplate = [[CPPredicateEditorRowTemplate alloc] initWithCompoundTypes:[CPAndPredicateType, CPOrPredicateType]];
111  _allTemplates = [CPArray arrayWithObject:initialTemplate];
112  }
113 
114  return self;
115 }
116 
117 - (id)objectValue
118 {
119  return [super predicate];
120 }
121 
122 - (void)_updateItemsBySimpleTemplates
123 {
124  var templates = [CPMutableArray array],
125  count = [_allTemplates count],
126  t;
127 
128  while (count--)
129  {
130  var t = _allTemplates[count];
131  if ([t _rowType] == CPRuleEditorRowTypeSimple)
132  [templates insertObject:t atIndex:0];
133  }
134 
135  var trees = [self _constructTreesForTemplates:templates];
136  if ([trees count] > 0)
137  _rootTrees = [self _mergeTree:trees];
138 }
139 
140 - (void)_updateItemsByCompoundTemplates
141 {
142  var templates = [CPMutableArray array],
143  count = [_allTemplates count],
144  t;
145 
146  while (count--)
147  {
148  var t = _allTemplates[count];
149  if ([t _rowType] == CPRuleEditorRowTypeCompound)
150  [templates insertObject:t atIndex:0];
151  }
152 
153  var trees = [self _constructTreesForTemplates:templates];
154  if ([trees count] > 0)
155  _rootHeaderTrees = [self _mergeTree:trees];
156 }
157 
158 - (CPArray)_constructTreesForTemplates:(id)templates
159 {
160  var trees = [CPMutableArray array],
161  count = [templates count];
162 
163  for (var i = 0; i < count; i++)
164  {
165  var tree = [self _constructTreeForTemplate:templates[i]];
166  [trees addObjectsFromArray:tree];
167  }
168 
169  return trees;
170 }
171 
172 - (CPMutableArray)_mergeTree:(CPArray)aTree
173 {
174  var mergedTree = [CPMutableArray array];
175  if (aTree == nil)
176  return mergedTree;
177 
178  var icount = [aTree count];
179  for (var i = 0; i < icount; i++)
180  {
181  var anode = [aTree objectAtIndex:i],
182  jcount = [mergedTree count],
183  merged = NO;
184 
185  for (var j = 0; j < jcount; j++)
186  {
187  var mergednode = [mergedTree objectAtIndex:j];
188 
189  if ([[mergednode title] isEqualToString:[anode title]])
190  {
191  var children1 = [mergednode children],
192  children2 = [anode children],
193  children12 = [children1 arrayByAddingObjectsFromArray:children2],
194  mergedChildren = [self _mergeTree:children12];
195 
196  [mergednode setChildren:mergedChildren];
197  merged = YES;
198  }
199  }
200 
201  if (!merged)
202  [mergedTree addObject:anode];
203  }
204 
205  return mergedTree;
206 }
207 
208 - (id)_constructTreeForTemplate:(CPPredicateEditorRowTemplate)aTemplate
209 {
210  var tree = [CPArray array],
211  templateViews = [aTemplate templateViews],
212  count = [templateViews count];
213 
214  while (count--)
215  {
216  var children = [CPArray array],
217  itemsCount = 0,
218  menuIndex = -1,
219  itemsArray,
220 
221  templateView = [templateViews objectAtIndex:count],
222  isPopup = [templateView isKindOfClass:[CPPopUpButton class]];
223 
224  if (isPopup)
225  {
226  itemArray = [[templateView itemArray] valueForKey:@"title"];
227  itemsCount = [itemArray count];
228  menuIndex = 0;
229  }
230 
231  for (; menuIndex < itemsCount; menuIndex++)
232  {
233  var item = [_CPPredicateEditorTree new];
234  [item setIndexIntoTemplate:count];
235  [item setTemplate:aTemplate];
236  [item setMenuItemIndex:menuIndex];
237  if (isPopup)
238  [item setTitle:[itemArray objectAtIndex:menuIndex]];
239 
240  [children addObject:item];
241  }
242 
243  [children makeObjectsPerformSelector:@selector(setChildren:) withObject:tree];
244  tree = children;
245  }
246 
247  return tree;
248 }
249 
250 #pragma mark Set the Predicate
251 
252 - (void)setObjectValue:(id)objectValue
253 {
254  var ov = [self objectValue];
255  if ((ov == nil) != (objectValue == nil) || ![ov isEqual:objectValue])
256  {
257  [self _setPredicate:objectValue];
258  [self _reflectPredicate:objectValue];
259  }
260 }
261 
262 - (void)_reflectPredicate:(id)predicate
263 {
264  var animation = _currentAnimation;
265  _currentAnimation = nil;
266  _sendAction = NO;
267 
268  if (predicate != nil)
269  {
270  if ((_nestingMode == CPRuleEditorNestingModeSimple || _nestingMode == CPRuleEditorNestingModeCompound)
271  && [predicate isKindOfClass:[CPComparisonPredicate class]])
272  predicate = [[CPCompoundPredicate alloc] initWithType:[self _compoundPredicateTypeForRootRows] subpredicates:[CPArray arrayWithObject:predicate]];
273 
274  var row = [self _rowObjectFromPredicate:predicate];
275  if (row != nil)
276  [_boundArrayOwner setValue:[CPArray arrayWithObject:row] forKey:_boundArrayKeyPath];
277  }
278 
279  [self setAnimation:animation];
280 }
281 
282 - (id)_rowObjectFromPredicate:(CPPredicate)predicate
283 {
284  var quality, // TODO: We should use this ref somewhere !
285  type,
286  matchedTemplate = [CPPredicateEditorRowTemplate _bestMatchForPredicate:predicate inTemplates:[self rowTemplates] quality:quality];
287 
288  if (matchedTemplate == nil)
289  return nil;
290 
291  var copyTemplate = [matchedTemplate copy],
292  subpredicates = [matchedTemplate displayableSubpredicatesOfPredicate:predicate];
293 
294  if (subpredicates == nil)
295  {
296  [copyTemplate _setComparisonPredicate:predicate];
298  }
299  else
300  {
301  [copyTemplate _setCompoundPredicate:predicate];
303  }
304 
305  var row = [self _rowFromTemplate:copyTemplate originalTemplate:matchedTemplate withRowType:type];
306 
307  if (subpredicates == nil)
308  return row;
309 
310  var count = [subpredicates count],
311  subrows = [CPMutableArray array];
312 
313  for (var i = 0; i < count; i++)
314  {
315  var subrow = [self _rowObjectFromPredicate:subpredicates[i]];
316  if (subrow != nil)
317  [subrows addObject:subrow];
318  }
319 
320  [row setValue:subrows forKey:[super subrowsKeyPath]];
321 
322  return row;
323 }
324 
325 - (id)_rowFromTemplate:(CPPredicateEditorRowTemplate)aTemplate originalTemplate:(CPPredicateEditorRowTemplate)originalTemplate withRowType:(CPRuleEditorRowType)rowType
326 {
327  var criteria = [CPArray array],
328  values = [CPArray array],
329  templateViews = [aTemplate templateViews],
330  rootItems,
331  count;
332 
333  rootItems = (rowType == CPRuleEditorRowTypeSimple) ? _rootTrees : _rootHeaderTrees;
334 
335  while ((count = [rootItems count]) > 0)
336  {
337  var treeChild;
338  for (var i = 0; i < count; i++)
339  {
340  treeChild = [rootItems objectAtIndex:i];
341 
342  var currentView = [templateViews objectAtIndex:[treeChild indexIntoTemplate]],
343  title = [treeChild title];
344 
345  if (title == nil || [title isEqual:[currentView titleOfSelectedItem]])
346  {
347  var node = [_CPPredicateEditorRowNode rowNodeFromTree:treeChild];
348  [node applyTemplate:aTemplate withViews:templateViews forOriginalTemplate:originalTemplate];
349 
350  [criteria addObject:node];
351  [values addObject:[node displayValue]];
352  break;
353  }
354  }
355 
356  rootItems = [treeChild children];
357  }
358 
359  var row = [CPDictionary dictionaryWithObjectsAndKeys:criteria, @"criteria", values, @"displayValues", rowType, @"rowType"];
360 
361  return row;
362 }
363 
364 #pragma mark Get the predicate
365 
366 - (void)_updatePredicate
367 {
368  [self _updatePredicateFromRows];
369 }
370 
371 - (void)_updatePredicateFromRows
372 {
373  var rootRowsArray = [super _rootRowsArray],
374  subpredicates = [CPMutableArray array],
375  count,
376  count2 = count = [rootRowsArray count],
377  predicate;
378 
379  while (count--)
380  {
381  var item = [rootRowsArray objectAtIndex:count],
382  subpredicate = [self _predicateFromRowItem:item];
383 
384  if (subpredicate != nil)
385  [subpredicates insertObject:subpredicate atIndex:0];
386  }
387 
388  if (_nestingMode != CPRuleEditorNestingModeList && count2 == 1)
389  predicate = [subpredicates lastObject];
390  else
391  predicate = [[CPCompoundPredicate alloc] initWithType:[self _compoundPredicateTypeForRootRows] subpredicates:subpredicates];
392 
393  [self _setPredicate:predicate];
394 }
395 
396 - (id)_predicateFromRowItem:(id)rowItem
397 {
398  var subpredicates = [CPArray array],
399  rowType = [rowItem valueForKey:_typeKeyPath];
400 
401  if (rowType == CPRuleEditorRowTypeCompound)
402  {
403  var subrows = [rowItem valueForKey:_subrowsArrayKeyPath],
404  count = [subrows count];
405 
406  for (var i = 0; i < count; i++)
407  {
408  var subrow = [subrows objectAtIndex:i],
409  predicate = [self _predicateFromRowItem:subrow];
410 
411  [subpredicates addObject:predicate];
412  }
413  }
414 
415  var criteria = [rowItem valueForKey:_itemsKeyPath],
416  displayValues = [rowItem valueForKey:_valuesKeyPath],
417  count = [criteria count],
418  lastItem = [criteria lastObject],
419  template = [lastItem templateForRow],
420  templateViews = [template templateViews];
421 
422  for (var j = 0; j < count; j++)
423  {
424  var view = [templateViews objectAtIndex:j],
425  value = [displayValues objectAtIndex:j];
426  [[criteria objectAtIndex:j] setTemplateViews:templateViews];
427 
428  if ([view isKindOfClass:[CPPopUpButton class]])
429  [view selectItemWithTitle:value];
430  else if ([view respondsToSelector:@selector(setObjectValue:)])
431  [view setObjectValue:[value objectValue]];
432  }
433 
434  return [template predicateWithSubpredicates:subpredicates];
435 }
436 
437 - (CPCompoundPredicateType)_compoundPredicateTypeForRootRows
438 {
439  return CPAndPredicateType;
440 }
441 
442 #pragma mark Control delegate
443 
444 - (void)_sendRuleAction
445 {
446  [super _sendRuleAction];
447 }
448 
449 - (BOOL)_sendsActionOnIncompleteTextChange
450 {
451  return NO;
452 }
453 
454 /*
455 - (void)_setDefaultTargetAndActionOnView:(CPView)view
456 {
457  if ([view isKindOfClass:[CPControl class]])
458  {
459  [view setTarget:self];
460  [view setAction:@selector(_templateControlValueDidChange:)];
461  }
462 }
463 - (void)_templateControlValueDidChange:(id)sender
464 {
465 }
466 - (void)controlTextDidBeginEditing:(CPNotification)notification
467 {
468 }
469 - (void)controlTextDidEndEditing:(CPNotification)notification
470 {
471 }
472 - (void)controlTextDidChange:(CPNotification)notification
473 {
474 }
475 */
476 
477 #pragma mark RuleEditor delegate methods
478 
479 - (int)_queryNumberOfChildrenOfItem:(id)rowItem withRowType:(int)type
480 {
481  if (rowItem == nil)
482  {
483  var trees = (type == CPRuleEditorRowTypeSimple) ? _rootTrees : _rootHeaderTrees;
484  return [trees count];
485  }
486  return [[rowItem children] count];
487 }
488 
489 - (id)_queryChild:(int)childIndex ofItem:(id)rowItem withRowType:(int)type
490 {
491  if (rowItem == nil)
492  {
493  var trees = (type == CPRuleEditorRowTypeSimple) ? _rootTrees : _rootHeaderTrees;
494  return [_CPPredicateEditorRowNode rowNodeFromTree:trees[childIndex]];
495  }
496 
497  return [[rowItem children] objectAtIndex:childIndex];
498 }
499 
500 - (id)_queryValueForItem:(id)rowItem inRow:(int)rowIndex
501 {
502  return [rowItem displayValue];
503 }
504 
505 @end
506 
507 var CPPredicateTemplatesKey = @"CPPredicateTemplates";
508 
509 @implementation CPPredicateEditor (CPCoding)
510 
511 - (id)initWithCoder:(id)aCoder
512 {
513  self = [super initWithCoder:aCoder];
514 
515  if (self != nil)
516  {
517  var nibTemplates = [aCoder decodeObjectForKey:CPPredicateTemplatesKey];
518 
519  if (nibTemplates != nil)
520  [self setRowTemplates:nibTemplates];
521  }
522 
523  return self;
524 }
525 
526 - (void)encodeWithCoder:(id)aCoder
527 {
528  [super encodeWithCoder:aCoder];
529  [aCoder encodeObject:_allTemplates forKey:CPPredicateTemplatesKey];
530 }
531 
532 @end
533 @implementation CPPredicateEditorValueBinder : CPBinder
534 {
535  id __doxygen__;
536 }
537 
538 - (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
539 {
540  [_source _reflectPredicate:nil];
541 }
542 
543 - (void)setValue:(id)aValue forBinding:(CPString)aBinding
544 {
545  [_source _reflectPredicate:aValue];
546 }
547 
548 @end
551 @implementation CPPredicateEditor (CPSynthesizedAccessors)
552 
553 
556 - (id)target
557 {
558  return _predicateTarget;
559 }
560 
564 - (void)setTarget:(id)aValue
565 {
566  _predicateTarget = aValue;
567 }
568 
572 - (SEL)action
573 {
574  return _predicateAction;
575 }
576 
580 - (void)setAction:(SEL)aValue
581 {
582  _predicateAction = aValue;
583 }
584 
585 @end