API  0.9.7
 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)aBinding
82 {
83  if (aBinding == CPValueBinding)
84  return [CPPredicateEditorValueBinder class];
85 
86  return [super _binderClassForBinding:aBinding];
87 }
88 
89 - (CPString)_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  itemArray,
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 = @{
360  @"criteria": criteria,
361  @"displayValues": values,
362  @"rowType": rowType,
363  };
364 
365  return row;
366 }
367 
368 #pragma mark Get the predicate
369 
370 - (void)_updatePredicate
371 {
372  [self _updatePredicateFromRows];
373 }
374 
375 - (void)_updatePredicateFromRows
376 {
377  var rootRowsArray = [super _rootRowsArray],
378  subpredicates = [CPMutableArray array],
379  count,
380  count2 = count = [rootRowsArray count],
381  predicate;
382 
383  while (count--)
384  {
385  var item = [rootRowsArray objectAtIndex:count],
386  subpredicate = [self _predicateFromRowItem:item];
387 
388  if (subpredicate != nil)
389  [subpredicates insertObject:subpredicate atIndex:0];
390  }
391 
392  if (_nestingMode != CPRuleEditorNestingModeList && count2 == 1)
393  predicate = [subpredicates lastObject];
394  else
395  predicate = [[CPCompoundPredicate alloc] initWithType:[self _compoundPredicateTypeForRootRows] subpredicates:subpredicates];
396 
397  [self _setPredicate:predicate];
398 }
399 
400 - (id)_predicateFromRowItem:(id)rowItem
401 {
402  var subpredicates = [CPArray array],
403  rowType = [rowItem valueForKey:_typeKeyPath];
404 
405  if (rowType == CPRuleEditorRowTypeCompound)
406  {
407  var subrows = [rowItem valueForKey:_subrowsArrayKeyPath],
408  count = [subrows count];
409 
410  for (var i = 0; i < count; i++)
411  {
412  var subrow = [subrows objectAtIndex:i],
413  predicate = [self _predicateFromRowItem:subrow];
414 
415  [subpredicates addObject:predicate];
416  }
417  }
418 
419  var criteria = [rowItem valueForKey:_itemsKeyPath],
420  displayValues = [rowItem valueForKey:_valuesKeyPath],
421  count = [criteria count],
422  lastItem = [criteria lastObject],
423  template = [lastItem templateForRow],
424  templateViews = [template templateViews];
425 
426  for (var j = 0; j < count; j++)
427  {
428  var view = [templateViews objectAtIndex:j],
429  value = [displayValues objectAtIndex:j];
430  [[criteria objectAtIndex:j] setTemplateViews:templateViews];
431 
432  if ([view isKindOfClass:[CPPopUpButton class]])
433  [view selectItemWithTitle:value];
434  else if ([view respondsToSelector:@selector(setObjectValue:)])
435  [view setObjectValue:[value objectValue]];
436  }
437 
438  return [template predicateWithSubpredicates:subpredicates];
439 }
440 
441 - (CPCompoundPredicateType)_compoundPredicateTypeForRootRows
442 {
443  return CPAndPredicateType;
444 }
445 
446 #pragma mark Control delegate
447 
448 - (void)_sendRuleAction
449 {
450  [super _sendRuleAction];
451 }
452 
453 - (BOOL)_sendsActionOnIncompleteTextChange
454 {
455  return NO;
456 }
457 
458 /*
459 - (void)_setDefaultTargetAndActionOnView:(CPView)view
460 {
461  if ([view isKindOfClass:[CPControl class]])
462  {
463  [view setTarget:self];
464  [view setAction:@selector(_templateControlValueDidChange:)];
465  }
466 }
467 - (void)_templateControlValueDidChange:(id)sender
468 {
469 }
470 - (void)controlTextDidBeginEditing:(CPNotification)notification
471 {
472 }
473 - (void)controlTextDidEndEditing:(CPNotification)notification
474 {
475 }
476 - (void)controlTextDidChange:(CPNotification)notification
477 {
478 }
479 */
480 
481 #pragma mark RuleEditor delegate methods
482 
483 - (int)_queryNumberOfChildrenOfItem:(id)rowItem withRowType:(CPRuleEditorRowType)type
484 {
485  if (rowItem == nil)
486  {
487  var trees = (type == CPRuleEditorRowTypeSimple) ? _rootTrees : _rootHeaderTrees;
488  return [trees count];
489  }
490  return [[rowItem children] count];
491 }
492 
493 - (id)_queryChild:(int)childIndex ofItem:(id)rowItem withRowType:(CPRuleEditorRowType)type
494 {
495  if (rowItem == nil)
496  {
497  var trees = (type == CPRuleEditorRowTypeSimple) ? _rootTrees : _rootHeaderTrees;
498  return [_CPPredicateEditorRowNode rowNodeFromTree:trees[childIndex]];
499  }
500 
501  return [[rowItem children] objectAtIndex:childIndex];
502 }
503 
504 - (id)_queryValueForItem:(id)rowItem inRow:(CPInteger)rowIndex
505 {
506  return [rowItem displayValue];
507 }
508 
509 @end
510 
511 var CPPredicateTemplatesKey = @"CPPredicateTemplates";
512 
513 @implementation CPPredicateEditor (CPCoding)
514 
515 - (id)initWithCoder:(CPCoder)aCoder
516 {
517  self = [super initWithCoder:aCoder];
518 
519  if (self != nil)
520  {
521  var nibTemplates = [aCoder decodeObjectForKey:CPPredicateTemplatesKey];
522 
523  if (nibTemplates != nil)
524  [self setRowTemplates:nibTemplates];
525  }
526 
527  return self;
528 }
529 
530 - (void)encodeWithCoder:(CPCoder)aCoder
531 {
532  [super encodeWithCoder:aCoder];
533  [aCoder encodeObject:_allTemplates forKey:CPPredicateTemplatesKey];
534 }
535 
536 @end
537 @implementation CPPredicateEditorValueBinder : CPBinder
538 {
539  id __doxygen__;
540 }
541 
542 - (void)setPlaceholderValue:(id)aValue withMarker:(CPString)aMarker forBinding:(CPString)aBinding
543 {
544  [_source _reflectPredicate:nil];
545 }
546 
547 - (void)setValue:(id)aValue forBinding:(CPString)aBinding
548 {
549  [_source _reflectPredicate:aValue];
550 }
551 
552 @end
555 @implementation CPPredicateEditor (CPSynthesizedAccessors)
556 
557 
560 - (id)target
561 {
562  return _predicateTarget;
563 }
564 
568 - (void)setTarget:(id)aValue
569 {
570  _predicateTarget = aValue;
571 }
572 
576 - (SEL)action
577 {
578  return _predicateAction;
579 }
580 
584 - (void)setAction:(SEL)aValue
585 {
586  _predicateAction = aValue;
587 }
588 
589 @end