• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /macosx-10.9.5/pyobjc-42/pyobjc/pyobjc-framework-Cocoa/Examples/AppKit/PredicateEditorSample/
1from Cocoa import *
2
3import math
4
5searchIndex = 0
6
7class MyWindowController (NSWindowController):
8    query = objc.ivar()
9    previousRowCount = objc.ivar(objc._C_INT)
10
11    myTableView = objc.IBOutlet()
12    mySearchResults = objc.IBOutlet()
13    predicateEditor = objc.IBOutlet()
14    progressView = objc.IBOutlet()	  # the progress search view
15    progressSearch = objc.IBOutlet()	  # spinning gear
16    progressSearchLabel = objc.IBOutlet() # search result #
17
18
19    def dealloc(self):
20	NSNotificationCenter.defaultCenter().removeObserver_(self)
21
22    def awakeFromNib(self):
23        # no vertical scrolling, we always want to show all rows
24        self.predicateEditor.enclosingScrollView().setHasVerticalScroller_(False)
25
26        self.previousRowCount = 3
27        self.predicateEditor.addRow_(self)
28
29        # put the focus in the first text field
30        displayValue = self.predicateEditor.displayValuesForRow_(1).lastObject()
31        if isinstance(displayValue,  NSControl):
32            self.window().makeFirstResponder_(displayValue)
33
34        # create and initalize our query
35        self.query = NSMetadataQuery.alloc().init()
36
37        # setup our Spotlight notifications
38        nf = NSNotificationCenter.defaultCenter()
39        nf.addObserver_selector_name_object_(self, 'queryNotification:', None, self.query)
40
41        # initialize our Spotlight query, sort by contact name
42
43        # XXX: this framework isn't wrapped yet!
44        self.query.setSortDescriptors_([NSSortDescriptor.alloc().initWithKey_ascending_(
45            'kMDItemContactKeywords', True)])
46        self.query.setDelegate_(self)
47
48        # start with our progress search label empty
49        self.progressSearchLabel.setStringValue_("")
50
51        return
52
53    def applicationShouldTerminateAfterLastWindowClosed_(self, sender):
54        return True
55
56    def loadResultsFromQuery_(self, notif):
57        results = notif.object().results()
58
59        NSLog("search count = %d", len(results))
60        foundResultsStr = "Results found: %d"%(len(results),)
61        self.progressSearchLabel.setStringValue_(foundResultsStr)
62
63        # iterate through the array of results, and match to the existing stores
64        for item in results:
65            cityStr = item.valueForAttribute_('kMDItemCity')
66            nameStr = item.valueForAttribute_('kMDItemDisplayName')
67            stateStr = item.valueForAttribute_('kMDItemStateOrProvince')
68            phoneNumbers = item.valueForAttribute_('kMDItemPhoneNumbers')
69            phoneStr = None
70            if phoneNumbers:
71                phoneStr = phoneNumbers[0]
72
73            storePath = item.valueForAttribute_('kMDItemPath').stringByResolvingSymlinksInPath()
74
75            # create a dictionary entry to be added to our search results array
76            emptyStr = ""
77            dict = {
78                    'name': nameStr or "",
79                    'phone': phoneStr or "",
80                    'city': cityStr or "",
81                    'state': stateStr or "",
82                    'url': NSURL.fileURLWithPath_(storePath),
83            }
84            self.mySearchResults.append(dict)
85
86    def queryNotification_(self, note):
87        # the NSMetadataQuery will send back a note when updates are happening.
88        # By looking at the [note name], we can tell what is happening
89        if note.name() == NSMetadataQueryDidStartGatheringNotification:
90            # the query has just started
91            NSLog("search: started gathering")
92
93            self.progressSearch.setHidden_(False)
94            self.progressSearch.startAnimation_(self)
95            self.progressSearch.animate_(self)
96            self.progressSearchLabel.setStringValue_("Searching...")
97
98        elif note.name() == NSMetadataQueryDidFinishGatheringNotification:
99            # at this point, the query will be done. You may recieve an update
100            # later on.
101            NSLog("search: finished gathering");
102
103            self.progressSearch.setHidden_(True)
104            self.progressSearch.stopAnimation_(self)
105
106            self.loadResultsFromQuery_(note)
107
108        elif note.name() == NSMetadataQueryGatheringProgressNotification:
109            # the query is still gathering results...
110            NSLog("search: progressing...")
111
112            self.progressSearch.animate_(self)
113
114        elif note.name() == NSMetadataQueryDidUpdateNotification:
115            # an update will happen when Spotlight notices that a file as
116            # added, removed, or modified that affected the search results.
117            NSLog("search: an update happened.")
118
119    # -------------------------------------------------------------------------
120    #	inspect:selectedObjects
121    #
122    #	This method obtains the selected object (in our case for single selection,
123    #	it's the first item), and opens its URL.
124    # -------------------------------------------------------------------------
125    def inspect_(self, selectedObjects):
126        objectDict = selectedObjects[0]
127        if objectDict is not None:
128            url = objectDict['url']
129            NSWorkspace.sharedWorkspace().openURL_(url)
130
131    # ------------------------------------------------------------------------
132    #	spotlightFriendlyPredicate:predicate
133    #
134    #	This method will "clean up" an NSPredicate to make it ready for Spotlight, or return nil if the predicate can't be cleaned.
135    #
136    #	Foundation's Spotlight support in NSMetdataQuery places the following requirements on an NSPredicate:
137    #		- Value-type (always YES or NO) predicates are not allowed
138    #		- Any compound predicate (other than NOT) must have at least two subpredicates
139    # -------------------------------------------------------------------------
140    def spotlightFriendlyPredicate_(self, predicate):
141        if predicate == NSPredicate.predicateWithValue_(True) or predicate == NSPredicate.predicateWithValue_(False):
142            return False
143
144        elif isinstance(predicate, NSCompoundPredicate):
145
146            type = predicate.compoundPredicateType()
147            cleanSubpredicates = []
148            for dirtySubpredicate in predicate.subpredicates():
149                cleanSubpredicate = self.spotlightFriendlyPredicate_(
150                    dirtySubpredicate)
151                if cleanSubpredicate:
152                    cleanSubpredicates.append(cleanSubpredicate)
153
154            if len(cleanSubpredicates) == 0:
155                return None
156
157            else:
158                if len(cleanSubpredicates) == 1 and type != NSNotPredicateType:
159                    return cleanSubpredicates[0]
160
161                else:
162                    return NSCompoundPredicate.alloc().initWithType_subpredicates_(type, cleanSubpredicates)
163
164        else:
165            return predicate
166
167    # -------------------------------------------------------------------------
168    #	createNewSearchForPredicate:predicate:withTitle
169    #
170    # -------------------------------------------------------------------------
171    def createNewSearchForPredicate_withTitle_(self, predicate, title):
172        if predicate is not None:
173            self.mySearchResults.removeObjects_(
174                self.mySearchResults.arrangedObjects());	# remove the old search results
175
176            # always search for items in the Address Book
177            addrBookPredicate = NSPredicate.predicateWithFormat_(
178                "(kMDItemKind = 'Address Book Person Data')")
179            predicate = NSCompoundPredicate.andPredicateWithSubpredicates_(
180                [addrBookPredicate, predicate])
181
182            self.query.setPredicate_(predicate)
183            self.query.startQuery()
184
185    # --------------------------------------------------------------------------
186    #	predicateEditorChanged:sender
187    #
188    #  This method gets called whenever the predicate editor changes.
189    #	It is the action of our predicate editor and the single plate for all our updates.
190    #
191    #	We need to do potentially three things:
192    #		1) Fire off a search if the user hits enter.
193    #		2) Add some rows if the user deleted all of them, so the user isn't left without any rows.
194    #		3) Resize the window if the number of rows changed (the user hit + or -).
195    # --------------------------------------------------------------------------
196    @objc.IBAction
197    def predicateEditorChanged_(self, sender):
198        # check NSApp currentEvent for the return key
199        event = NSApp.currentEvent()
200        if event is None:
201            return
202
203        if event.type() == NSKeyDown:
204            characters = event.characters()
205            if len(characters) > 0 and characters[0] == u'\r':
206                # get the predicat, which is the object value of our view
207                predicate = self.predicateEditor.objectValue()
208
209                # make it Spotlight friendly
210                predicate = self.spotlightFriendlyPredicate_(predicate)
211                if predicate is not None:
212                    global searchIndex
213                    title = NSLocalizedString("Search #%ld", "Search title");
214                    self.createNewSearchForPredicate_withTitle_(
215                            predicate, title % searchIndex)
216                    searchIndex += 1
217
218        # if the user deleted the first row, then add it again - no sense leaving the user with no rows
219        if self.predicateEditor.numberOfRows() == 0:
220            self.predicateEditor.addRow_(self)
221
222        # resize the window vertically to accomodate our views:
223
224        # get the new number of rows, which tells us the needed change in height,
225        # note that we can't just get the view frame, because it's currently animating - this method is called before the animation is finished.
226        newRowCount = self.predicateEditor.numberOfRows()
227
228        # if there's no change in row count, there's no need to resize anything
229        if newRowCount == self.previousRowCount:
230            return
231
232        # The autoresizing masks, by default, allows the NSTableView to grow and keeps the predicate editor fixed.
233        # We need to temporarily grow the predicate editor, and keep the NSTableView fixed, so we have to change the autoresizing masks.
234        # Save off the old ones; we'll restore them after changing the window frame.
235        tableScrollView = self.myTableView.enclosingScrollView()
236        oldOutlineViewMask = tableScrollView.autoresizingMask()
237
238        predicateEditorScrollView = self.predicateEditor.enclosingScrollView()
239        oldPredicateEditorViewMask = predicateEditorScrollView.autoresizingMask()
240
241        tableScrollView.setAutoresizingMask_(
242                NSViewWidthSizable | NSViewMaxYMargin)
243        predicateEditorScrollView.setAutoresizingMask_(
244                NSViewWidthSizable | NSViewHeightSizable)
245
246        # determine if we need to grow or shrink the window
247        growing = (newRowCount > self.previousRowCount)
248
249        # if growing, figure out by how much.  Sizes must contain nonnegative values, which is why we avoid negative floats here.
250        heightDifference = abs(self.predicateEditor.rowHeight() * (newRowCount - self.previousRowCount))
251
252        # convert the size to window coordinates -
253        # if we didn't do this, we would break under scale factors other than 1.
254        # We don't care about the horizontal dimension, so leave that as 0.
255        #
256        sizeChange = self.predicateEditor.convertSize_toView_(
257                NSMakeSize(0, heightDifference), None)
258
259        # offset our status view
260        frame = self.progressView.frame()
261        self.progressView.setFrameOrigin_(NSMakePoint(
262            frame.origin.x,
263            frame.origin.y - self.predicateEditor.rowHeight() * (newRowCount - self.previousRowCount)))
264
265        # change the window frame size:
266        # - if we're growing, the height goes up and the origin goes down (corresponding to growing down).
267        # - if we're shrinking, the height goes down and the origin goes up.
268        windowFrame = self.window().frame()
269        if growing:
270            windowFrame.size.height += sizeChange.height
271            windowFrame.origin.y -= sizeChange.height
272        else:
273            windowFrame.size.height -= sizeChange.height
274            windowFrame.origin.y += sizeChange.height
275
276        self.window().setFrame_display_animate_(windowFrame, True, True)
277
278        # restore the autoresizing mask
279        tableScrollView.setAutoresizingMask_(oldOutlineViewMask)
280        predicateEditorScrollView.setAutoresizingMask_(oldPredicateEditorViewMask)
281
282        self.previousRowCount = newRowCount	# save our new row count
283