API  1.0.0
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 
52 - (CPArray)rowTemplates
53 {
54  return _allTemplates;
55 }
56 
62 - (void)setRowTemplates:(id)rowTemplates
63 {
64  if (_allTemplates == rowTemplates)
65  return;
66 
67  _allTemplates = rowTemplates;
68 
69  [self _updateItemsByCompoundTemplates];
70  [self _updateItemsBySimpleTemplates];
71 
72  if ([self numberOfRows] > 0)
73  {
74  var predicate = [super predicate];
75  [self _reflectPredicate:predicate];
76  }
77 }
78 
80 + (Class)_binderClassForBinding:(CPString)aBinding
81 {
82  if (aBinding == CPValueBinding)
84 
85  return [super _binderClassForBinding:aBinding];
86 }
87 
88 - (CPString)_replacementKeyPathForBinding:(CPString)aBinding
89 {
90  if (aBinding == CPValueBinding)
91  return @"predicate";
92 
93  return [super _replacementKeyPathForBinding:aBinding];
94 }
95 
96 - (void)_initRuleEditorShared
97 {
98  [super _initRuleEditorShared];
99 
100  _rootTrees = [CPArray array];
101  _rootHeaderTrees = [CPArray array];
102 }
103 
104 - (id)initWithFrame:(CGRect)aFrame
105 {
106  self = [super initWithFrame:aFrame];
107  if (self != nil)
108  {
109  var initialTemplate = [[CPPredicateEditorRowTemplate alloc] initWithCompoundTypes:[CPAndPredicateType, CPOrPredicateType]];
110  _allTemplates = [CPArray arrayWithObject:initialTemplate];
111  }
112 
113  return self;
114 }
115 
116 - (id)objectValue
117 {
118  return [super predicate];
119 }
120 
121 - (void)_updateItemsBySimpleTemplates
122 {
123  var templates = [CPMutableArray array],
124  count = [_allTemplates count],
125  t;
126 
127  while (count--)
128  {
129  var t = _allTemplates[count];
130  if ([t _rowType] == CPRuleEditorRowTypeSimple)
131  [templates insertObject:t atIndex:0];
132  }
133 
134  var trees = [self _constructTreesForTemplates:templates];
135  if ([trees count] > 0)
136  _rootTrees = [self _mergeTree:trees];
137 }
138 
139 - (void)_updateItemsByCompoundTemplates
140 {
141  var templates = [CPMutableArray array],
142  count = [_allTemplates count],
143  t;
144 
145  while (count--)
146  {
147  var t = _allTemplates[count];
148  if ([t _rowType] == CPRuleEditorRowTypeCompound)
149  [templates insertObject:t atIndex:0];
150  }
151 
152  var trees = [self _constructTreesForTemplates:templates];
153  if ([trees count] > 0)
154  _rootHeaderTrees = [self _mergeTree:trees];
155 }
156 
157 - (CPArray)_constructTreesForTemplates:(id)templates
158 {
159  var trees = [CPMutableArray array],
160  count = [templates count];
161 
162  for (var i = 0; i < count; i++)
163  {
164  var tree = [self _constructTreeForTemplate:templates[i]];
165  [trees addObjectsFromArray:tree];
166  }
167 
168  return trees;
169 }
170 
171 - (CPMutableArray)_mergeTree:(CPArray)aTree
172 {
173  var mergedTree = [CPMutableArray array];
174  if (aTree == nil)
175  return mergedTree;
176 
177  var icount = [aTree count];
178  for (var i = 0; i < icount; i++)
179  {
180  var anode = [aTree objectAtIndex:i],
181  jcount = [mergedTree count],
182  merged = NO;
183 
184  for (var j = 0; j < jcount; j++)
185  {
186  var mergednode = [mergedTree objectAtIndex:j];
187 
188  if ([[mergednode title] isEqualToString:[anode title]])
189  {
190  var children1 = [mergednode children],
191  children2 = [anode children],
192  children12 = [children1 arrayByAddingObjectsFromArray:children2],
193  mergedChildren = [self _mergeTree:children12];
194 
195  [mergednode setChildren:mergedChildren];
196  merged = YES;
197  }
198  }
199 
200  if (!merged)
201  [mergedTree addObject:anode];
202  }
203 
204  return mergedTree;
205 }
206 
207 - (id)_constructTreeForTemplate:(CPPredicateEditorRowTemplate)aTemplate
208 {
209  var tree = [CPArray array],
210  templateViews = [aTemplate templateViews],
211  count = [templateViews count];
212 
213  while (count--)
214  {
215  var children = [CPArray array],
216  itemsCount = 0,
217  menuIndex = -1,
218  itemArray,
219 
220  templateView = [templateViews objectAtIndex:count],
221  isPopup = [templateView isKindOfClass:[CPPopUpButton class]];
222 
223  if (isPopup)
224  {
225  itemArray = [[templateView itemArray] valueForKey:@"title"];
226  itemsCount = [itemArray count];
227  menuIndex = 0;
228  }
229 
230  for (; menuIndex < itemsCount; menuIndex++)
231  {
232  var item = [_CPPredicateEditorTree new];
233  [item setIndexIntoTemplate:count];
234  [item setTemplate:aTemplate];
235  [item setMenuItemIndex:menuIndex];
236  if (isPopup)
237  [item setTitle:[itemArray objectAtIndex:menuIndex]];
238 
239  [children addObject:item];
240  }
241 
242  [children makeObjectsPerformSelector:@selector(setChildren:) withObject:tree];
243  tree = children;
244  }
245 
246  return tree;
247 }
248 
249 #pragma mark Set the Predicate
250 
251 - (void)setObjectValue:(id)objectValue
252 {
253  var ov = [self objectValue];
254  if ((ov == nil) != (objectValue == nil) || ![ov isEqual:objectValue])
255  {
256  [self _setPredicate:objectValue];
257  [self _reflectPredicate:objectValue];
258  }
259 }
260 
261 - (void)_reflectPredicate:(id)predicate
262 {
263  var animation = _currentAnimation;
264  _currentAnimation = nil;
265  _sendAction = NO;
266 
267  if (predicate != nil)
268  {
269  if ((_nestingMode == CPRuleEditorNestingModeSimple || _nestingMode == CPRuleEditorNestingModeCompound)
270  && [predicate isKindOfClass:[CPComparisonPredicate class]])
271  predicate = [[CPCompoundPredicate alloc] initWithType:[self _compoundPredicateTypeForRootRows] subpredicates:[CPArray arrayWithObject:predicate]];
272 
273  var row = [self _rowObjectFromPredicate:predicate];
274  if (row != nil)
275  [_boundArrayOwner setValue:[CPArray arrayWithObject:row] forKey:_boundArrayKeyPath];
276  }
277 
278  [self setAnimation:animation];
279 }
280 
281 - (id)_rowObjectFromPredicate:(CPPredicate)predicate
282 {
283  var quality, // TODO: We should use this ref somewhere !
284  type,
285  matchedTemplate = [CPPredicateEditorRowTemplate _bestMatchForPredicate:predicate inTemplates:[self rowTemplates] quality:quality];
286 
287  if (matchedTemplate == nil)
288  return nil;
289 
290  var copyTemplate = [matchedTemplate copy],
291  subpredicates = [matchedTemplate displayableSubpredicatesOfPredicate:predicate];
292 
293  if (subpredicates == nil)
294  {
295  [copyTemplate _setComparisonPredicate:predicate];
297  }
298  else
299  {
300  [copyTemplate _setCompoundPredicate:predicate];
302  }
303 
304  var row = [self _rowFromTemplate:copyTemplate originalTemplate:matchedTemplate withRowType:type];
305 
306  if (subpredicates == nil)
307  return row;
308 
309  var count = [subpredicates count],
310  subrows = [CPMutableArray array];
311 
312  for (var i = 0; i < count; i++)
313  {
314  var subrow = [self _rowObjectFromPredicate:subpredicates[i]];
315  if (subrow != nil)
316  [subrows addObject:subrow];
317  }
318 
319  [row setValue:subrows forKey:[super subrowsKeyPath]];
320 
321  return row;
322 }
323 
324 - (id)_rowFromTemplate:(CPPredicateEditorRowTemplate)aTemplate originalTemplate:(CPPredicateEditorRowTemplate)originalTemplate withRowType:(CPRuleEditorRowType)rowType
325 {
326  var criteria = [CPArray array],
327  values = [CPArray array],
328  templateViews = [aTemplate templateViews],
329  rootItems,
330  count;
331 
332  rootItems = (rowType == CPRuleEditorRowTypeSimple) ? _rootTrees : _rootHeaderTrees;
333 
334  while ((count = [rootItems count]) > 0)
335  {
336  var treeChild;
337  for (var i = 0; i < count; i++)
338  {
339  treeChild = [rootItems objectAtIndex:i];
340 
341  var currentView = [templateViews objectAtIndex:[treeChild indexIntoTemplate]],
342  title = [treeChild title];
343 
344  if (title == nil || [title isEqual:[currentView titleOfSelectedItem]])
345  {
346  var node = [_CPPredicateEditorRowNode rowNodeFromTree:treeChild];
347  [node applyTemplate:aTemplate withViews:templateViews forOriginalTemplate:originalTemplate];
348 
349  [criteria addObject:node];
350  [values addObject:[node displayValue]];
351  break;
352  }
353  }
354 
355  rootItems = [treeChild children];
356  }
357 
358  var row = @{
359  @"criteria": criteria,
360  @"displayValues": values,
361  @"rowType": rowType,
362  };
363 
364  return row;
365 }
366 
367 #pragma mark Get the predicate
368 
369 - (void)_updatePredicate
370 {
371  [self _updatePredicateFromRows];
372 }
373 
374 - (void)_updatePredicateFromRows
375 {
376  var rootRowsArray = [super _rootRowsArray],
377  subpredicates = [CPMutableArray array],
378  count,
379  count2 = count = [rootRowsArray count],
380  predicate;
381 
382  while (count--)
383  {
384  var item = [rootRowsArray objectAtIndex:count],
385  subpredicate = [self _predicateFromRowItem:item];
386 
387  if (subpredicate != nil)
388  [subpredicates insertObject:subpredicate atIndex:0];
389  }
390 
391  if (_nestingMode != CPRuleEditorNestingModeList && count2 == 1)
392  predicate = [subpredicates lastObject];
393  else
394  predicate = [[CPCompoundPredicate alloc] initWithType:[self _compoundPredicateTypeForRootRows] subpredicates:subpredicates];
395 
396  [self _setPredicate:predicate];
397 }
398 
399 - (id)_predicateFromRowItem:(id)rowItem
400 {
401  var subpredicates = [CPArray array],
402  rowType = [rowItem valueForKey:_typeKeyPath];
403 
404  if (rowType == CPRuleEditorRowTypeCompound)
405  {
406  var subrows = [rowItem valueForKey:_subrowsArrayKeyPath],
407  count = [subrows count];
408 
409  for (var i = 0; i < count; i++)
410  {
411  var subrow = [subrows objectAtIndex:i],
412  predicate = [self _predicateFromRowItem:subrow];
413 
414  [subpredicates addObject:predicate];
415  }
416  }
417 
418  var criteria = [rowItem valueForKey:_itemsKeyPath],
419  displayValues = [rowItem valueForKey:_valuesKeyPath],
420  count = [criteria count],
421  lastItem = [criteria lastObject],
422  template = [lastItem templateForRow],
423  templateViews = [template templateViews];
424 
425  for (var j = 0; j < count; j++)
426  {
427  var view = [templateViews objectAtIndex:j],
428  value = [displayValues objectAtIndex:j];
429  [[criteria objectAtIndex:j] setTemplateViews:templateViews];
430 
431  if ([view isKindOfClass:[CPPopUpButton class]])
432  [view selectItemWithTitle:value];
433  else if ([view respondsToSelector:@selector(setObjectValue:)])
434  [view setObjectValue:[value objectValue]];
435  }
436 
437  return [template predicateWithSubpredicates:subpredicates];
438 }
439 
440 - (CPCompoundPredicateType)_compoundPredicateTypeForRootRows
441 {
442  return CPAndPredicateType;
443 }
444 
445 #pragma mark Control delegate
446 
447 - (void)_sendRuleAction
448 {
449  [super _sendRuleAction];
450 }
451 
452 - (BOOL)_sendsActionOnIncompleteTextChange
453 {
454  return NO;
455 }
456 
457 /*
458 - (void)_setDefaultTargetAndActionOnView:(CPView)view
459 {
460  if ([view isKindOfClass:[CPControl class]])
461  {
462  [view setTarget:self];
463  [view setAction:@selector(_templateControlValueDidChange:)];
464  }
465 }
466 - (void)_templateControlValueDidChange:(id)sender
467 {
468 }
469 - (void)controlTextDidBeginEditing:(CPNotification)notification
470 {
471 }
472 - (void)controlTextDidEndEditing:(CPNotification)notification
473 {
474 }
475 - (void)controlTextDidChange:(CPNotification)notification
476 {
477 }
478 */
479 
480 #pragma mark RuleEditor delegate methods
481 
482 - (int)_queryNumberOfChildrenOfItem:(id)rowItem withRowType:(CPRuleEditorRowType)type
483 {
484  if (rowItem == nil)
485  {
486  var trees = (type == CPRuleEditorRowTypeSimple) ? _rootTrees : _rootHeaderTrees;
487  return [trees count];
488  }
489  return [[rowItem children] count];
490 }
491 
492 - (id)_queryChild:(int)childIndex ofItem:(id)rowItem withRowType:(CPRuleEditorRowType)type
493 {
494  if (rowItem == nil)
495  {
496  var trees = (type == CPRuleEditorRowTypeSimple) ? _rootTrees : _rootHeaderTrees;
497  return [_CPPredicateEditorRowNode rowNodeFromTree:trees[childIndex]];
498  }
499 
500  return [[rowItem children] objectAtIndex:childIndex];
501 }
502 
503 - (id)_queryValueForItem:(id)rowItem inRow:(CPInteger)rowIndex
504 {
505  return [rowItem displayValue];
506 }
507 
508 @end
509 
510 var CPPredicateTemplatesKey = @"CPPredicateTemplates";
511 
512 @implementation CPPredicateEditor (CPCoding)
513 
514 - (id)initWithCoder:(CPCoder)aCoder
515 {
516  self = [super initWithCoder:aCoder];
517 
518  if (self != nil)
519  {
520  var nibTemplates = [aCoder decodeObjectForKey:CPPredicateTemplatesKey];
521 
522  if (nibTemplates != nil)
523  [self setRowTemplates:nibTemplates];
524  }
525 
526  return self;
527 }
528 
529 - (void)encodeWithCoder:(CPCoder)aCoder
530 {
531  [super encodeWithCoder:aCoder];
532  [aCoder encodeObject:_allTemplates forKey:CPPredicateTemplatesKey];
533 }
534 
535 @end
536 @implementation CPPredicateEditorValueBinder : CPBinder
537 {
538  id __doxygen__;
539 }
540 
541 - (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
542 {
543  [_source _reflectPredicate:nil];
544 }
545 
546 - (void)setValue:(id)aValue forBinding:(CPString)aBinding
547 {
548  [_source _reflectPredicate:aValue];
549 }
550 
551 @end
554 @implementation CPPredicateEditor (CPSynthesizedAccessors)
555 
556 
559 - (id)target
560 {
561  return _predicateTarget;
562 }
563 
567 - (void)setTarget:(id)aValue
568 {
569  _predicateTarget = aValue;
570 }
571 
575 - (SEL)action
576 {
577  return _predicateAction;
578 }
579 
583 - (void)setAction:(SEL)aValue
584 {
585  _predicateAction = aValue;
586 }
587 
588 @end
id initWithCompoundTypes:(CPArray compoundTypes)
Initializes and returns a row template suitable for displaying compound predicates.
CPRuleEditorNestingModeList
var isEqual
CPRuleEditorRowType CPRuleEditorRowTypeSimple
CPRuleEditorNestingModeCompound
CPRuleEditorNestingModeSimple
CPPredicate predicate()
Returns the predicate for the receiver.
Definition: CPRuleEditor.j:789
CPCompoundPredicate is a subclass of CPPredicate used to represent logical “gate” operations (AND/O...
CPRuleEditorRowTypeCompound
An immutable string (collection of characters).
Definition: CPString.h:2
A view for creating and configuring criteria.
Definition: CPRuleEditor.h:2
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
CPComparisonPredicate is a subclass of CPPredicate used to compare expressions.
Class class()
Definition: CPObject.j:179
CPArray templateViews()
Returns the views for the receiver.
id alloc()
Definition: CPObject.j:130