00001 import os
00002 import sys
00003 import threading
00004 import Image
00005
00006 import cterm
00007 from cterm.cterm_const import *
00008
00009 import clnch_misc
00010 import clnch_plane3x3
00011 import clnch_ini
00012 import clnch_threadutil
00013 import clnch_resource
00014
00015
00016
00017
00018 class Log:
00019
00020 def __init__(self):
00021 self.lock = threading.Lock()
00022 self.log = [u""]
00023 self.last_line_terminated = False
00024
00025 def write(self,s):
00026 self.lock.acquire()
00027 try:
00028 if type(s)!=type(u''):
00029 s = unicode(s,'mbcs')
00030 while True:
00031 return_pos = s.find("\n")
00032 if return_pos < 0 : break
00033 if self.last_line_terminated:
00034 self.log.append(u"")
00035 self.log[-1] += s[:return_pos]
00036 s = s[return_pos+1:]
00037 self.last_line_terminated = True
00038 if len(s)>0 :
00039 if self.last_line_terminated:
00040 self.log.append(u"")
00041 self.log[-1] += s
00042 self.last_line_terminated = False
00043 if len(self.log)>1000:
00044 self.log = self.log[-1000:]
00045 finally:
00046 self.lock.release()
00047
00048 def numLines(self):
00049 return len(self.log)
00050
00051 def getLine(self,lineno):
00052 try:
00053 return self.log[lineno]
00054 except IndexError:
00055 return u""
00056
00057
00058
00059
00060 class ConsoleWindow(cterm.Window):
00061
00062 def __init__( self, parent_window, debug=False ):
00063
00064 self.initialized = False
00065
00066 self.loadState()
00067
00068 self.parent_window = parent_window
00069
00070 self.active = False
00071
00072 cterm.Window.__init__(
00073 self,
00074 x = self.window_normal_x,
00075 y = self.window_normal_y,
00076 width = self.window_normal_width,
00077 height = self.window_normal_height,
00078 parent_window=parent_window,
00079 bg_color = (0,0,0),
00080 border_size = 2,
00081 title_bar = False,
00082 title = clnch_resource.clnch_appname + u" Console",
00083 show = False,
00084 icon=False,
00085 activate_handler = self._onActivate,
00086 close_handler = self._onClose,
00087 move_handler = self._onMove,
00088 size_handler = self._onSize,
00089 keydown_handler = self._onKeyDown,
00090
00091
00092 lbuttondown_handler = self._onLeftButtonDown,
00093 lbuttonup_handler = self._onLeftButtonUp,
00094 mbuttondown_handler = self._onMiddleButtonDown,
00095 mbuttonup_handler = self._onMiddleButtonUp,
00096 rbuttondown_handler = self._onRightButtonDown,
00097 rbuttonup_handler = self._onRightButtonUp,
00098 lbuttondoubleclick_handler = self._onLeftButtonDoubleClick,
00099 mousemove_handler = self._onMouseMove,
00100 mousewheel_handler= self._onMouseWheel,
00101 )
00102
00103 self.plane_scrollbar0 = clnch_plane3x3.Plane3x3( self, os.path.join(os.path.split(sys.argv[0])[0],'skin/scrollbar0.png'), 2 )
00104 self.plane_scrollbar1 = clnch_plane3x3.Plane3x3( self, os.path.join(os.path.split(sys.argv[0])[0],'skin/scrollbar1.png'), 1 )
00105
00106 self.debug = debug
00107
00108 self.log = Log()
00109 self.scroll_info = clnch_misc.ScrollInfo()
00110
00111 self.mouse_click_info = None
00112 self.selection = [ [ 0, 0 ], [ 0, 0 ] ]
00113
00114 self.auto_show = False
00115
00116 self.initialized = True
00117
00118 self.paint()
00119
00120
00121
00122
00123 def show( self, show, activate=True ):
00124 clnch_misc.adjustWindowPosition( self.parent_window, self, default_up=True )
00125 cterm.Window.show( self, show, activate )
00126
00127 def setAutoShow( self, auto_show ):
00128 self.auto_show = auto_show
00129
00130
00131
00132
00133 def rectText(self):
00134 return [ 0, 0, self.width()-2, self.height() ]
00135
00136 def rectScrollbar(self):
00137 return [ self.width()-2, 0, 2, self.height() ]
00138
00139
00140
00141
00142 def _onActivate( self, active ):
00143 self.active = active
00144
00145 def _onClose(self):
00146 self.show(False)
00147
00148 def _onMove( self, x, y ):
00149
00150 if not self.isMaximized() and not self.isMinimized():
00151 self.window_normal_x = x
00152 self.window_normal_y = y
00153
00154 def _onSize( self, width, height ):
00155
00156 if not self.isMaximized() and not self.isMinimized():
00157 self.window_normal_width = width
00158 self.window_normal_height = height
00159
00160 self.paint()
00161
00162 def _onKeyDown( self, vk, mod ):
00163
00164 if vk==VK_ESCAPE and mod==0:
00165 self.show(False)
00166
00167 elif vk==VK_UP and mod==0:
00168 self._scroll(-1)
00169 self.paint()
00170
00171 elif vk==VK_DOWN and mod==0:
00172 self._scroll(1)
00173 self.paint()
00174
00175 elif ( vk==VK_PRIOR or vk==VK_LEFT ) and mod==0:
00176 rect = self.rectText()
00177 self._scroll( -(rect[3]-rect[1]) )
00178 self.paint()
00179
00180 elif ( vk==VK_NEXT or vk==VK_RIGHT ) and mod==0:
00181 rect = self.rectText()
00182 self._scroll( rect[3]-rect[1] )
00183 self.paint()
00184
00185 elif vk==VK_F1 and mod==0:
00186 def jobHelp(job_item):
00187 help_path = os.path.join( os.path.split(sys.argv[0])[0], 'doc\\index.html' )
00188 clnch_misc.shellExecute( None, None, help_path, u"", u"" )
00189
00190 def jobHelpFinished(job_item):
00191 print u"Helpを開きました"
00192 print u""
00193
00194 job_item = clnch_threadutil.JobItem( jobHelp, jobHelpFinished )
00195 clnch_threadutil.job_queue.enqueue(job_item)
00196
00197 def _mousePosToCharPos( self, x, y ):
00198 client_rect = self.getClientRect()
00199 offset_x, offset_y = self.charToClient( 0, 0 )
00200 char_w, char_h = self.getCharSize()
00201 char_x = (x-offset_x) / char_w
00202 char_y = (y-offset_y) / char_h
00203 return char_x, char_y
00204
00205 def _mousePosToLogPos( self, x, y ):
00206 char_x, char_y = self._mousePosToCharPos(x,y)
00207
00208 char_x = max(char_x,0)
00209 char_x = min(char_x,self.width())
00210
00211 if char_y < 0:
00212 char_x = 0
00213 char_y = 0
00214
00215 if char_y > self.height():
00216 char_x = self.width()
00217 char_y = self.height()
00218
00219 lineno = self.scroll_info.pos+char_y
00220
00221 s = self.log.getLine(lineno)
00222
00223 w = 0
00224 char_index = 0
00225 for char_index in xrange(len(s)):
00226 w += self.getStringWidth(s[char_index])
00227 if w > char_x : break
00228 else:
00229 char_index = len(s)
00230
00231 return lineno, char_index
00232
00233 def _copySelectedRegion(self):
00234
00235 joint_text = u""
00236
00237 selection_left, selection_right = self.selection
00238 if selection_left > selection_right:
00239 selection_left, selection_right = selection_right, selection_left
00240
00241 i = selection_left[0]
00242 while i<=selection_right[0] and i<self.log.numLines():
00243
00244 s = self.log.getLine(i)
00245
00246 if i==selection_left[0]:
00247 left = selection_left[1]
00248 else:
00249 left = 0
00250
00251 if i==selection_right[0]:
00252 right = selection_right[1]
00253 else:
00254 right = len(s)
00255
00256 joint_text += s[left:right]
00257
00258 if i!=selection_right[0]:
00259 joint_text += "\r\n"
00260
00261 i += 1
00262
00263 if joint_text:
00264 clnch_misc.setClipboardText(joint_text)
00265
00266 def _onLeftButtonDown( self, x, y, mod ):
00267
00268 lineno, char_index = self._mousePosToLogPos(x,y)
00269
00270 self.mouse_click_info = [ False, x, y, lineno, char_index ]
00271 self.setCapture()
00272
00273 self.selection = [
00274 [ lineno, char_index ],
00275 [ lineno, char_index ]
00276 ]
00277 self.paint()
00278
00279 def _onLeftButtonUp( self, x, y, mod ):
00280 self.mouse_click_info = None
00281 self.releaseCapture()
00282 self._copySelectedRegion()
00283
00284 def _onMiddleButtonDown( self, x, y, mod ):
00285 self.mouse_click_info = None
00286
00287 def _onMiddleButtonUp( self, x, y, mod ):
00288 self.mouse_click_info = None
00289
00290 def _onRightButtonDown( self, x, y, mod ):
00291 self.mouse_click_info = None
00292
00293 def _onRightButtonUp( self, x, y, mod ):
00294 self.mouse_click_info = None
00295
00296 def _wordbreak( self, s, pos, step ):
00297
00298 word_break_chars1 = "\"!@#$%^&*()+|~-=\`[]{};:',./<>?"
00299 word_break_chars2 = " \t"
00300
00301 while True:
00302 if pos<=0 : return 0
00303 if pos>=len(s) : return len(s)
00304
00305 if s[pos-1] in word_break_chars1:
00306 left_char_type = 1
00307 elif s[pos-1] in word_break_chars2:
00308 left_char_type = 2
00309 else:
00310 left_char_type = 0
00311
00312 if s[pos] in word_break_chars1:
00313 right_char_type = 1
00314 elif s[pos] in word_break_chars2:
00315 right_char_type = 2
00316 else:
00317 right_char_type = 0
00318
00319 if left_char_type!=0:
00320 if right_char_type==0:
00321 return pos
00322
00323 pos += step
00324
00325 def _onLeftButtonDoubleClick( self, x, y, mod ):
00326
00327 lineno, char_index = self._mousePosToLogPos(x,y)
00328
00329 s = self.log.getLine(lineno)
00330
00331 left = max( self._wordbreak( s, char_index, -1 ), 0 )
00332 right = min( self._wordbreak( s, char_index+1, +1 ), len(s) )
00333
00334 self.mouse_click_info = [ True, x, y, lineno, left, lineno, right ]
00335 self.setCapture()
00336
00337 self.selection = [
00338 [ lineno, left ],
00339 [ lineno, right ]
00340 ]
00341
00342 self.paint()
00343
00344 def _onMouseMove( self, x, y, mod ):
00345
00346 if self.mouse_click_info:
00347
00348 char_x, char_y = self._mousePosToCharPos(x,y)
00349
00350 if char_y < 0:
00351 self._scroll(-1)
00352 elif char_y >= self.height():
00353 self._scroll(1)
00354
00355 lineno, char_index = self._mousePosToLogPos(x,y)
00356
00357 double_click = self.mouse_click_info[0]
00358 if double_click:
00359
00360 s = self.log.getLine(lineno)
00361
00362 if [ lineno, char_index ] > self.selection[0]:
00363 right = min( self._wordbreak( s, char_index+1, +1 ), len(s) )
00364 self.selection[0] = self.mouse_click_info[3:5]
00365 self.selection[1] = [ lineno, right ]
00366 else:
00367 left = max( self._wordbreak( s, char_index, -1 ), 0 )
00368 self.selection[0] = self.mouse_click_info[5:7]
00369 self.selection[1] = [ lineno, left ]
00370
00371 else:
00372 self.selection[1] = [ lineno, char_index ]
00373
00374 self.paint()
00375
00376 def _scroll( self, delta ):
00377 self.scroll_info.pos += delta
00378 self.scroll_info.pos = min( self.scroll_info.pos, self.log.numLines()-self.height() )
00379 self.scroll_info.pos = max( self.scroll_info.pos, 0 )
00380
00381 def _onMouseWheel( self, x, y, wheel, mod ):
00382
00383 while wheel>0:
00384 self._scroll(-1)
00385 wheel-=1
00386 while wheel<0:
00387 self._scroll(+1)
00388 wheel+=1
00389
00390 self.paint()
00391
00392
00393
00394
00395 def registerStdio(self):
00396
00397 def writeCommon(s):
00398 make_visible = False
00399 if self.log.numLines()-1 < self.scroll_info.pos + self.height():
00400 make_visible = True
00401
00402 self.log.write(s)
00403
00404 if make_visible:
00405 self.scroll_info.makeVisible( self.log.numLines()-1, self.height() )
00406
00407 self.paint()
00408
00409 class Stdout:
00410 def write( writer_self, s ):
00411 writeCommon(s)
00412 if self.auto_show:
00413 self.show(True,False)
00414
00415 class Stderr:
00416 def write( writer_self, s ):
00417 writeCommon(s)
00418 if self.auto_show:
00419 self.show(True,False)
00420
00421 class DebugStdout:
00422 def write( writer_self, s ):
00423 if type(s)==type(u''):
00424 s = s.encode("mbcs")
00425 sys.__stdout__.write(s)
00426
00427 class DebugStderr:
00428 def write( writer_self, s ):
00429 if type(s)==type(u''):
00430 s = s.encode("mbcs")
00431 sys.__stdout__.write(s)
00432
00433 if self.debug:
00434 sys.stdout = DebugStdout()
00435 sys.stderr = DebugStderr()
00436 else:
00437 sys.stdout = Stdout()
00438 sys.stderr = Stderr()
00439
00440 def unregisterStdio(self):
00441 sys.stdout = sys.__stdout__
00442 sys.stderr = sys.__stderr__
00443
00444
00445 def updateFont(self):
00446 self.setFont( clnch_ini.get( "FONT", "name", "" ), clnch_ini.getint( "FONT", "size", 12 ) )
00447 window_rect = self.getWindowRect()
00448 self.setPosSize( window_rect[0], window_rect[1], self.width(), self.height(), 0 )
00449
00450
00451
00452
00453 def paint(self):
00454
00455
00456 if 1:
00457 x, y, width, height = self.rectText()
00458
00459 attr = cterm.Attribute( fg=(255,255,255) )
00460 attr_selected = cterm.Attribute( fg=(255,255,255), bg=(30,100,150) )
00461
00462 selection_left, selection_right = self.selection
00463 if selection_left > selection_right:
00464 selection_left, selection_right = selection_right, selection_left
00465
00466 for i in xrange(height):
00467
00468 if self.scroll_info.pos+i < self.log.numLines():
00469
00470 if selection_left[0] <= self.scroll_info.pos+i <= selection_right[0]:
00471
00472 s = self.log.getLine( self.scroll_info.pos + i )
00473
00474 if selection_left[0]==self.scroll_info.pos+i:
00475 left = selection_left[1]
00476 else:
00477 left = 0
00478
00479 if selection_right[0]==self.scroll_info.pos+i:
00480 right = selection_right[1]
00481 else:
00482 right = len(s)
00483
00484 s = [ s[0:left], s[left:right], s[right:len(s)] ]
00485
00486 line_x = x
00487
00488 self.putString( line_x, y+i, width-line_x, 1, attr, s[0] )
00489 line_x += self.getStringWidth(s[0])
00490
00491 self.putString( line_x, y+i, width-line_x, 1, attr_selected, s[1] )
00492 line_x += self.getStringWidth(s[1])
00493
00494 self.putString( line_x, y+i, width-line_x, 1, attr, s[2] )
00495 line_x += self.getStringWidth(s[2])
00496
00497 self.putString( line_x, y+i, width-line_x, 1, attr, u" " * (width-line_x) )
00498
00499 else:
00500 s = self.log.getLine( self.scroll_info.pos + i )
00501 self.putString( x, y+i, width, 1, attr, s )
00502 w = self.getStringWidth(s)
00503 space_x = x + w
00504 space_width = width - w
00505 self.putString( space_x, y+i, space_width, 1, attr, u" " * space_width )
00506 else:
00507 self.putString( x, y+i, width, 1, attr, u" " * width )
00508
00509
00510 if 1:
00511 x, y, width, height = self.rectScrollbar()
00512
00513 for i in xrange(height):
00514 self.putString( x, y+i, width, 1, attr, u" " * width )
00515
00516 client_rect = self.getClientRect()
00517 scrollbar_left, tmp = self.charToClient( self.width()-2, 0 )
00518
00519 scrollbar0_rect = [ scrollbar_left, 0, client_rect[2], client_rect[3] ]
00520
00521 scrollbar1_height = client_rect[3] * self.height() / self.log.numLines()
00522 scrollbar1_height = min( scrollbar1_height, client_rect[3] )
00523 scrollbar1_height = max( scrollbar1_height, 12 )
00524
00525 scrollbar1_pos = (client_rect[3]-scrollbar1_height) * self.scroll_info.pos / max((self.log.numLines()-self.height()),1)
00526
00527 scrollbar1_rect = [ scrollbar_left, scrollbar1_pos, client_rect[2], scrollbar1_pos+scrollbar1_height ]
00528
00529 self.plane_scrollbar0.adjust( scrollbar0_rect )
00530 self.plane_scrollbar1.adjust( scrollbar1_rect )
00531
00532
00533
00534
00535 def loadState(self):
00536 self.window_normal_x = clnch_ini.getint( "CONSOLE", "x", 0 )
00537 self.window_normal_y = clnch_ini.getint( "CONSOLE", "y", 0 )
00538 self.window_normal_width = clnch_ini.getint( "CONSOLE", "width", 80 )
00539 self.window_normal_height = clnch_ini.getint( "CONSOLE", "height", 32 )
00540
00541 def saveState(self):
00542 clnch_ini.set( "CONSOLE", "x", str(self.window_normal_x) )
00543 clnch_ini.set( "CONSOLE", "y", str(self.window_normal_y) )
00544 clnch_ini.set( "CONSOLE", "width", str(self.window_normal_width) )
00545 clnch_ini.set( "CONSOLE", "height", str(self.window_normal_height) )
00546
00547
00548
00549
00550 def clearLog(self):
00551 self.log = Log()
00552 self.scroll_info = clnch_misc.ScrollInfo()
00553 self.mouse_click_info = None
00554 self.selection = [ [ 0, 0 ], [ 0, 0 ] ]
00555 self.paint()
00556
00557