Revert "Fix percent-escape URI issue (hopefully)."
[lilypad-macos.git] / ProcessLog.py
blob15f5833520b76ae31ad6ade63332f0ddd7cb7f0b
1 import objc
2 from Foundation import *
3 from AppKit import *
4 from PyObjCTools import AppHelper
6 import subprocess
7 import os
8 import signal
10 debug = 1
12 # class defined in ProcessLog.nib
13 class ProcessLog (NSObject):
15 def init (self):
16 self = super (ProcessLog, self).init ()
18 self.process = None
19 self.out_str = ''
21 return self
23 # the actual base class is NSObject
24 def setProcess (self, process):
25 self.out_str = ''
26 if self.isLive ():
27 self.killProcess ()
29 self.process = process
31 def getNewOutput (self):
32 out = self.process.stdout
33 fd = out.fileno ()
34 size = 1024
35 str = ''
36 while True:
37 s = unicode (os.read (fd, size), "utf-8")
38 str += s
39 if (len (s) == size):
40 size *= 2
41 else:
42 break
44 self.out_str += str
45 return str
47 def killProcess (self):
48 if self.process:
49 os.kill (self.process.pid, signal.SIGINT)
51 def isLive (self):
52 if not self.process:
53 return False
55 return self.process.poll () == None
57 # class defined in ProcessLog.nib
58 class ProcessLogWindowController (NSWindowController):
59 # the actual base class is NSWindowController
60 # The following outlets are added to the class:
62 # window
63 # textView
64 # processLog
65 # cancelButton
66 # throbber
67 textView = objc.IBOutlet ()
68 cancelButton = objc.IBOutlet ()
69 throbber = objc.IBOutlet ()
71 def __new__ (cls):
72 # "Pythonic" constructor
73 return cls.alloc ().initEmpty ()
75 def initEmpty (self):
76 self = self.initWithWindowNibName_ ("ProcessLog")
77 self.setWindowTitle_ ('Process')
78 self.close_callback = None
79 self.processLog = ProcessLog.alloc ().init ()
80 self.window ().makeFirstResponder_ (self.textView)
81 self.showWindow_ (self)
83 # The window controller doesn't need to be retained (referenced)
84 # anywhere, so we pretend to have a reference to ourselves to avoid
85 # being garbage collected before the window is closed. The extra
86 # reference will be released in self.windowWillClose_ ()
87 self.retain ()
88 return self
90 def windowWillClose_ (self, notification):
92 ## UGH.
93 if self.close_callback:
94 self.close_callback (self)
95 self.cancelProcess_ (self)
96 self.suicide ()
98 def suicide (self):
99 # see comment in self.initWithObject_ ()
100 self.autorelease ()
102 def setWindowTitle_ (self, title):
103 self.window ().setTitle_ (title)
105 def cancelProcess_ (self, sender):
106 if self.processLog.isLive ():
107 self.processLog.killProcess ()
108 # rest is handled by timer.
110 def runProcessWithCallback (self, process, finish_callback):
111 self.finish_callback = finish_callback
112 self.processLog.setProcess (process)
114 self.cancelButton.setEnabled_ (True)
115 self.throbber.setUsesThreadedAnimation_ (True)
116 self.throbber.startAnimation_ (self)
117 self.setTimer ()
119 def setTimer (self):
120 self.timer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_ \
121 (1.0/10.0, self, "timerCallback:", 0, 0)
123 def timerCallback_ (self, userinfo):
124 self.updateLog_ (None)
126 if self.processLog.isLive ():
127 self.setTimer ()
128 else:
129 self.finish ()
132 def finish (self):
133 self.updateLog_ (None)
134 self.cancelButton.setEnabled_ (False)
135 self.throbber.stopAnimation_ (self)
137 cb = self.finish_callback
138 if cb <> None:
139 cb (self)
142 def clearLog_ (self, sender):
143 tv = self.textView
144 ts_len = tv.textStorage ().length ()
145 range = NSRange ()
146 range.location = 0
147 range.length = ts_len
148 tv.replaceCharactersInRange_withString_ (range, '')
150 def addText (self, str):
151 tv = self.textView
152 ts_len = tv.textStorage ().length ()
154 range = NSRange ()
155 range.location = ts_len
156 range.length = 0
157 tv.replaceCharactersInRange_withString_ (range, str)
158 range.length = len (str)
159 tv.scrollRangeToVisible_ (range)
161 def updateLog_ (self, sender):
162 str = self.processLog.getNewOutput ()
163 self.addText (str)
166 if __name__ == "__main__":
167 AppHelper.runEventLoop ()