forked from albertz/music-player
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathguiCocoaCommon.py
253 lines (235 loc) · 7.97 KB
/
guiCocoaCommon.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# MusicPlayer, https://github.com/albertz/music-player
# Copyright (c) 2012, Albert Zeyer, www.az2000.de
# All rights reserved.
# This code is under the 2-clause BSD license, see License.txt in the root directory of this project.
import objc
import AppKit
import utils
if "pools" not in globals():
from collections import deque
pools = deque()
# just in case that we are not the main thread
pools.append(AppKit.NSAutoreleasePool.alloc().init())
# The native types are defined in _gui.so.
try:
_NSFlippedView = objc.lookUpClass("_NSFlippedView")
class NSFlippedView(_NSFlippedView):
control = None
onBecomeFirstResponder = None
onResignFirstResponder = None
onKeyDown = None
onKeyUp = None
onMouseDown = None
onMouseDragged = None
onMouseUp = None
onDraggingEntered = None
onDraggingUpdated = None
onDraggingExited = None
onPerformDragOperation = None
def acceptsFirstResponder(self):
return utils.attrChain(self, "control", "attr", "canHaveFocus", default=False)
def becomeFirstResponder(self):
if AppKit.NSView.becomeFirstResponder(self):
if self.onBecomeFirstResponder: self.onBecomeFirstResponder()
return True
else:
return False
def resignFirstResponder(self):
if AppKit.NSView.resignFirstResponder(self):
if self.onResignFirstResponder: self.onResignFirstResponder()
return True
else:
return False
def keyDown_(self, ev):
if not self.onKeyDown or not self.onKeyDown(ev):
AppKit.NSView.keyDown_(self, ev)
def keyUp_(self, ev):
if not self.onKeyUp or not self.onKeyUp(ev):
AppKit.NSView.keyUp_(self, ev)
def mouseDown_(self, ev):
if not self.onMouseDown or not self.onMouseDown(ev):
AppKit.NSView.mouseDown_(self, ev)
def mouseDragged_(self, ev):
if not self.onMouseDragged or not self.onMouseDragged(ev):
AppKit.NSView.mouseDragged_(self, ev)
def mouseUp_(self, ev):
if not self.onMouseUp or not self.onMouseUp(ev):
AppKit.NSView.mouseUp_(self, ev)
def draggingEntered_(self, sender):
if self.onDraggingEntered: self.onDraggingEntered(sender)
return self.draggingUpdated_(sender)
def draggingUpdated_(self, sender):
if self.onDraggingUpdated: self.onDraggingUpdated(sender)
return AppKit.NSDragOperationGeneric
def draggingExited_(self, sender):
if self.onDraggingExited: self.onDraggingExited(sender)
def prepareForDragOperation_(self, sender):
return True
def performDragOperation_(self, sender):
if self.onPerformDragOperation and self.onPerformDragOperation(sender):
return True
return False
except Exception:
NSFlippedView = objc.lookUpClass("NSFlippedView")
try:
class NSExtendedTextField(AppKit.NSTextField):
onMouseEntered = None
onMouseExited = None
onMouseDown = None
onMouseDragged = None
onMouseUp = None
onTextChange = None
def mouseEntered_(self, ev):
if self.onMouseEntered: self.onMouseEntered(ev)
else: AppKit.NSTextField.mouseEntered_(self, ev)
def mouseExited_(self, ev):
if self.onMouseExited: self.onMouseExited(ev)
else: AppKit.NSTextField.mouseExited_(self, ev)
def mouseDown_(self, ev):
if not self.onMouseDown or not self.onMouseDown(ev):
AppKit.NSView.mouseDown_(self, ev)
def mouseDragged_(self, ev):
if not self.onMouseDragged or not self.onMouseDragged(ev):
AppKit.NSView.mouseDragged_(self, ev)
def mouseUp_(self, ev):
if not self.onMouseUp or not self.onMouseUp(ev):
AppKit.NSView.mouseUp_(self, ev)
def textDidChange_(self, notif):
AppKit.NSTextField.textDidChange_(self, notif)
if self.onTextChange:
self.onTextChange()
except Exception:
NSExtendedTextField = objc.lookUpClass("NSExtendedTextField")
try:
class NSExtendedSlider(AppKit.NSSlider):
onValueChange = None
def initWithFrame_(self, frame):
AppKit.NSSlider.initWithFrame_(self, frame)
self.setTarget_(self)
self.setAction_("valueChange")
return self
def valueChange(self, sender):
if self.onValueChange:
self.onValueChange(self.doubleValue())
except Exception:
NSExtendedSlider = objc.lookUpClass("NSExtendedSlider")
try:
class TableViewDataSource(AppKit.NSObject):
data = ()
formaters = {}
lock = None
def init(self):
import threading
self.lock = threading.RLock()
return self
def numberOfRowsInTableView_(self, tableView):
try:
with self.lock:
return len(self.data)
except Exception:
import sys
sys.excepthook(*sys.exc_info())
return 0
def tableView_objectValueForTableColumn_row_(self, tableView, tableColumn, rowIndex):
try:
with self.lock:
if rowIndex >= len(self.data):
# This can happen if the data has changed in the middle
# of a tableView.redraw().
# Note that wrapping tableView.reloadData() doesn't work
# because that doesn't reload it internally - it just delays
# a redraw and inside the redraw, it reloads the data.
# So, overriding redraw with locking might work.
# But anyway, it doesn't really matter as we should have delayed
# a further reloadData().
# Also, in guiCocoa, there is another further workaround
# which probably makes this obsolete.
return None
key = str(tableColumn.identifier())
v = self.data[rowIndex].get(key, None)
if key in self.formaters: v = self.formaters[key](v)
if isinstance(v, str): v = utils.convertToUnicode(v)
return v
except Exception:
import sys
sys.excepthook(*sys.exc_info())
return None
def resort(self, tableView):
sortDescs = list(tableView.sortDescriptors())
def itemIter(item):
for d in sortDescs:
value = item.get(d.key(), None)
if isinstance(value, (str,unicode)):
value = value.lower()
yield value
def key(item):
item = tuple(itemIter(item))
return item
if sortDescs:
firstAsc = sortDescs[0].ascending()
else:
# sort descriptors hasn't been set yet
firstAsc = True
with self.lock:
self.data.sort(key=key, reverse=not firstAsc)
def tableView_sortDescriptorsDidChange_(self, tableView, oldDescriptors):
try:
with self.lock:
self.resort(tableView)
tableView.reloadData()
except Exception:
import sys
sys.excepthook(*sys.exc_info())
def tableView_writeRowsWithIndexes_toPasteboard_(self, tableView, rowIndexes, pboard):
possibleSources = []
def handleRowIndex(index, stop):
try:
url = self.data[index].get("url", None)
if url:
url = utils.convertToUnicode(url)
possibleSources.append(url)
except Exception:
import sys
sys.excepthook(*sys.exc_info())
with self.lock:
rowIndexes.enumerateIndexesUsingBlock_(handleRowIndex)
if not possibleSources: return False
pboard.declareTypes_owner_([AppKit.NSFilenamesPboardType], None)
pboard.setPropertyList_forType_(possibleSources, AppKit.NSFilenamesPboardType)
return True
except Exception:
TableViewDataSource = objc.lookUpClass("TableViewDataSource")
try:
class TableViewDelegate(AppKit.NSObject):
onSelectionChange = None
def tableViewSelectionDidChange_(self, notif):
if self.onSelectionChange:
tableView = notif.object()
selection = []
def handleRowIndex(index, stop):
try:
selection.append(tableView.dataSource().data[index])
except Exception:
import sys
sys.excepthook(*sys.exc_info())
tableView.selectedRowIndexes().enumerateIndexesUsingBlock_(handleRowIndex)
try:
self.onSelectionChange(selection)
except Exception:
import sys
sys.excepthook(*sys.exc_info())
except Exception:
TableViewDelegate = objc.lookUpClass("TableViewDelegate")
try:
class ButtonActionHandler(AppKit.NSObject):
def initWithArgs(self, userAttr, inst):
self.init()
self.userAttr = userAttr
self.inst = inst
return self
def click(self, sender):
attr = self.userAttr.__get__(self.inst)
utils.daemonThreadCall(attr, name="%r click handler" % (self.userAttr))
except Exception:
ButtonActionHandler = objc.lookUpClass("ButtonActionHandler") # already defined earlier
# keep old pools. there is no real safe way to know whether we still have some refs to objects