from kivy.uix.label import Label from kivy.core.window import Window from kivy.graphics import Color,Rectangle import glob import os.path import filecheck class Command_prompt(Label): def __init__(self,app,**kwargs): # app is used to share information between widgets self.app=app # insert mode self.insert=False # width of a letter self.char_width=9 # cursor position self.cursor=0 # the text, with no color information self.raw_text="" # a one-time message that shows up over the bar self.message="" # array of command with arguments self.argv=[] # display characters from window_offset onwards (for text wrapping) self.window_offset=0 # init Label super(Command_prompt,self).__init__(**kwargs) self.keyboard = Window.request_keyboard(None,self,"text") self.keyboard.bind(on_textinput=self.on_textinput,on_key_down=self.on_key_down) self.draw() def draw(self): self.canvas.before.clear() # background with self.canvas.before: Color(0,0,0) Rectangle(pos=self.pos,size=self.size) # if message is not empty, draw message instead if self.message!="": # do not wrap self.text=self.message[:min(len(self.message),int(self.width/self.char_width))] self.message="" return # wrap text window_size=int(self.width/self.char_width) if self.cursor>=self.window_offset+window_size: self.window_offset=self.cursor-window_size+1 elif self.cursor0 and self.cursor==len(self.raw_text)) or (n<0 and self.cursor==0): return self.cursor=max(1,min(len(self.raw_text),self.cursor+n)) self.draw() # move cursor to absolute position def move_cursor(self,n): self.cursor=max(1,min(len(self.raw_text),n)) self.draw() # parse text as argv def parse_argv(self): # init self.argv=[""] # return position of cursor cursor=(0,0) # whether inside quotes single_quoted=False double_quoted=False # whether after '\' backslashed=False # start after ':' # proceed one character at a time for i in range(1,len(self.raw_text)): char=self.raw_text[i] # split argument if single_quoted==False and double_quoted==False and backslashed==False and char==' ': # new argv self.argv.append("") # quotes or '\' elif double_quoted==False and backslashed==False and char=='\'': single_quoted=not single_quoted elif single_quoted==False and backslashed==False and char=='"': double_quoted=not double_quoted elif single_quoted==False and backslashed==False and char=='\\': backslashed=True # write character else: self.argv[len(self.argv)-1]+=char # reset backslash backslashed=False # record position of cursor if self.cursor==i+1: cursor=(len(self.argv)-1,len(self.argv[len(self.argv)-1])) return cursor # tab completion def tab_complete(self): # parse argv cursor=self.parse_argv() # write and edit commands if self.argv[0]=="w" or self.argv[0]=="e": # check that cursor is in first argument if cursor[0]==1: # complete filesystem path self.append_text_at_cursor(self.complete_path(self.argv[cursor[0]],cursor[1])) # complete filesystem path def complete_path(self,base,end): paths=glob.glob(glob.escape(base[:end])+"*") print(glob.escape(base[:end])+"*", paths) if len(paths)==0: return "" elif len(paths)==1: # append '/' to directories if os.path.isdir(paths[0]): return paths[0][end:]+"/" else: return paths[0][end:] else: # display in status bar self.app.status_bar.message="" for path in paths: name=os.path.basename(path) # add quotes if needed if " " in name: name="\""+name+"\"" self.app.status_bar.message+=name+" " self.app.status_bar.draw() return os.path.commonprefix(paths)[end:] # run command def run_command(self): # parse command line self.parse_argv() # single letter commands (which can be combined) # write if self.argv[0]=="w" or self.argv[0]=="w!" or self.argv[0]=="wq": self.run_write(self.argv) # edit file if self.argv[0]=="e": self.run_edit(self.argv) # quit if self.argv[0]=="q" or self.argv=="wq": self.app.stop() # wrie to file def run_write(self,argv): if len(argv)>2: self.message="error: could not write to file: too many arguments -- usage: ':w [path to file]'" return elif len(argv)==1: if self.app.openfile!="": if self.app.readonly: self.message="error: open file is readonly" return self.app.painter.write(self.app.openfile) return else: self.message="error: no file is open for editing, specify a path" return (ret,self.message)=filecheck.check_write(argv[1],argv[0]=="w!") # add comment if no overwrite if ret==-2: self.message+=" (use ':w!' to overwrite)" if ret<0: return self.app.openfile=argv[1] self.app.readonly=False self.app.painter.write(argv[1]) # update status bar self.app.status_bar.draw() def run_edit(self,argv): if len(argv)>2: self.message="error: could not open file: too many arguments -- usage: ':e '" return elif len(argv)==1: self.message="error: could not open file: no argument found -- usage: ':e '" return # check that the file can be edited (ret,self.message)=filecheck.check_edit(argv[1]) # error if ret<0: return if os.path.isfile(argv[1]): # read the file self.app.painter.read(argv[1]) # set readonly mode self.app.readonly=not os.access(argv[1],os.W_OK) else: # new file, reset painter self.app.painter.reset() self.app.readonly=False # select openfile in app self.app.openfile=argv[1] # update status bar self.app.status_bar.draw()