diff --git a/release/ui/space_console.py b/release/ui/space_console.py new file mode 100644 index 00000000000..497f34a28d0 --- /dev/null +++ b/release/ui/space_console.py @@ -0,0 +1,417 @@ + +import bpy + +class CONSOLE_HT_header(bpy.types.Header): + __space_type__ = "CONSOLE" + __idname__ = "CONSOLE_HT_header" + + def draw(self, context): + sc = context.space_data + # text = sc.text + layout = self.layout + + layout.template_header() + + if context.area.show_menus: + row = layout.row() + row.itemM("CONSOLE_MT_console") + + row = layout.row() + row.scale_x = 0.9 + row.itemR(sc, "type", expand=True) + if sc.type == 'REPORT': + row.itemR(sc, "show_report_debug") + row.itemR(sc, "show_report_info") + row.itemR(sc, "show_report_operator") + row.itemR(sc, "show_report_warn") + row.itemR(sc, "show_report_error") + + +class CONSOLE_MT_console(bpy.types.Menu): + __space_type__ = "CONSOLE" + __label__ = "Console" + + def draw(self, context): + layout = self.layout + sc = context.space_data + + layout.column() + layout.itemO("CONSOLE_OT_clear") + +def add_scrollback(text, text_type): + for l in text.split('\n'): + bpy.ops.CONSOLE_OT_scrollback_append(text=l.replace('\t', ' '), type=text_type) + +def get_console(console_id): + ''' + helper function for console operators + currently each text datablock gets its own console - code.InteractiveConsole() + ...which is stored in this function. + + console_id can be any hashable type + ''' + import sys, code, io + + try: consoles = get_console.consoles + except:consoles = get_console.consoles = {} + + # clear all dead consoles, use text names as IDs + # TODO, find a way to clear IDs + ''' + for console_id in list(consoles.keys()): + if console_id not in bpy.data.texts: + del consoles[id] + ''' + + try: + namespace, console, stdout, stderr = consoles[console_id] + except: + namespace = {'__builtins__':__builtins__} # locals() + namespace['bpy'] = bpy + + console = code.InteractiveConsole(namespace) + + if sys.version.startswith('2'): + stdout = io.BytesIO() # Py2x support + stderr = io.BytesIO() + else: + stdout = io.StringIO() + stderr = io.StringIO() + + consoles[console_id]= namespace, console, stdout, stderr + + return namespace, console, stdout, stderr + +class CONSOLE_OT_exec(bpy.types.Operator): + ''' + Operator documentatuon text, will be used for the operator tooltip and python docs. + ''' + __label__ = "Console Execute" + + # Both prompts must be the same length + PROMPT = '>>> ' + PROMPT_MULTI = '... ' + + # is this working??? + ''' + def poll(self, context): + return (context.space_data.type == 'PYTHON') + ''' # its not :| + + def execute(self, context): + import sys + + sc = context.space_data + + try: + line = sc.history[-1].line + except: + return ('CANCELLED',) + + if sc.type != 'PYTHON': + return ('CANCELLED',) + + namespace, console, stdout, stderr = get_console(hash(context.region)) + + # redirect output + sys.stdout = stdout + sys.stderr = stderr + + # run the console + if not line.strip(): + line_exec = '\n' # executes a multiline statement + else: + line_exec = line + + is_multiline = console.push(line_exec) + + stdout.seek(0) + stderr.seek(0) + + output = stdout.read() + output_err = stderr.read() + + # cleanup + sys.stdout = sys.__stdout__ + sys.stderr = sys.__stderr__ + sys.last_traceback = None + + # So we can reuse, clear all data + stdout.truncate(0) + stderr.truncate(0) + + bpy.ops.CONSOLE_OT_scrollback_append(text = sc.prompt+line, type='INPUT') + + if is_multiline: sc.prompt = self.PROMPT_MULTI + else: sc.prompt = self.PROMPT + + # insert a new blank line + bpy.ops.CONSOLE_OT_history_append(text="", current_character=0) + + # Insert the output into the editor + # not quite correct because the order might have changed, but ok 99% of the time. + if output: add_scrollback(output, 'OUTPUT') + if output_err: add_scrollback(output_err, 'ERROR') + + + return ('FINISHED',) + + +def autocomp(bcon): + ''' + This function has been taken from a BGE console autocomp I wrote a while ago + the dictionaty bcon is not needed but it means I can copy and paste from the old func + which works ok for now. + + could be moved into its own module. + ''' + + + def is_delimiter(ch): + ''' + For skipping words + ''' + if ch == '_': + return False + if ch.isalnum(): + return False + + return True + + def is_delimiter_autocomp(ch): + ''' + When autocompleteing will earch back and + ''' + if ch in '._[] "\'': + return False + if ch.isalnum(): + return False + + return True + + + def do_autocomp(autocomp_prefix, autocomp_members): + ''' + return text to insert and a list of options + ''' + autocomp_members = [v for v in autocomp_members if v.startswith(autocomp_prefix)] + + print("AUTO: '%s'" % autocomp_prefix) + print("MEMBERS: '%s'" % str(autocomp_members)) + + if not autocomp_prefix: + return '', autocomp_members + elif len(autocomp_members) > 1: + # find a common string between all members after the prefix + # 'ge' [getA, getB, getC] --> 'get' + + # get the shortest member + min_len = min([len(v) for v in autocomp_members]) + + autocomp_prefix_ret = '' + + for i in range(len(autocomp_prefix), min_len): + char_soup = set() + for v in autocomp_members: + char_soup.add(v[i]) + + if len(char_soup) > 1: + break + else: + autocomp_prefix_ret += char_soup.pop() + + print(autocomp_prefix_ret) + return autocomp_prefix_ret, autocomp_members + elif len(autocomp_members) == 1: + return autocomp_members[0][len(autocomp_prefix):], [] + else: + return '', [] + + + def BCon_PrevChar(bcon): + cursor = bcon['cursor']-1 + if cursor<0: + return None + + try: + return bcon['edit_text'][cursor] + except: + return None + + + def BCon_NextChar(bcon): + try: + return bcon['edit_text'][bcon['cursor']] + except: + return None + + def BCon_cursorLeft(bcon): + bcon['cursor'] -= 1 + if bcon['cursor'] < 0: + bcon['cursor'] = 0 + + def BCon_cursorRight(bcon): + bcon['cursor'] += 1 + if bcon['cursor'] > len(bcon['edit_text']): + bcon['cursor'] = len(bcon['edit_text']) + + def BCon_AddScrollback(bcon, text): + + bcon['scrollback'] = bcon['scrollback'] + text + + + def BCon_cursorInsertChar(bcon, ch): + if bcon['cursor']==0: + bcon['edit_text'] = ch + bcon['edit_text'] + elif bcon['cursor']==len(bcon['edit_text']): + bcon['edit_text'] = bcon['edit_text'] + ch + else: + bcon['edit_text'] = bcon['edit_text'][:bcon['cursor']] + ch + bcon['edit_text'][bcon['cursor']:] + + bcon['cursor'] + if bcon['cursor'] > len(bcon['edit_text']): + bcon['cursor'] = len(bcon['edit_text']) + BCon_cursorRight(bcon) + + + TEMP_NAME = '___tempname___' + + cursor_orig = bcon['cursor'] + + ch = BCon_PrevChar(bcon) + while ch != None and (not is_delimiter(ch)): + ch = BCon_PrevChar(bcon) + BCon_cursorLeft(bcon) + + if ch != None: + BCon_cursorRight(bcon) + + #print (cursor_orig, bcon['cursor']) + + cursor_base = bcon['cursor'] + + autocomp_prefix = bcon['edit_text'][cursor_base:cursor_orig] + + print("PREFIX:'%s'" % autocomp_prefix) + + # Get the previous word + if BCon_PrevChar(bcon)=='.': + BCon_cursorLeft(bcon) + ch = BCon_PrevChar(bcon) + while ch != None and is_delimiter_autocomp(ch)==False: + ch = BCon_PrevChar(bcon) + BCon_cursorLeft(bcon) + + cursor_new = bcon['cursor'] + + if ch != None: + cursor_new+=1 + + pytxt = bcon['edit_text'][cursor_new:cursor_base-1].strip() + print("AUTOCOMP EVAL: '%s'" % pytxt) + #try: + if pytxt: + bcon['console'].runsource(TEMP_NAME + '=' + pytxt, '', 'single') + # print val + else: ##except: + val = None + + try: + val = bcon['namespace'][TEMP_NAME] + del bcon['namespace'][TEMP_NAME] + except: + val = None + + if val: + autocomp_members = dir(val) + + autocomp_prefix_ret, autocomp_members = do_autocomp(autocomp_prefix, autocomp_members) + + bcon['cursor'] = cursor_orig + for v in autocomp_prefix_ret: + BCon_cursorInsertChar(bcon, v) + cursor_orig = bcon['cursor'] + + if autocomp_members: + BCon_AddScrollback(bcon, ', '.join(autocomp_members)) + + del val + + else: + # Autocomp global namespace + autocomp_members = bcon['namespace'].keys() + + if autocomp_prefix: + autocomp_members = [v for v in autocomp_members if v.startswith(autocomp_prefix)] + + autocomp_prefix_ret, autocomp_members = do_autocomp(autocomp_prefix, autocomp_members) + + bcon['cursor'] = cursor_orig + for v in autocomp_prefix_ret: + BCon_cursorInsertChar(bcon, v) + cursor_orig = bcon['cursor'] + + if autocomp_members: + BCon_AddScrollback(bcon, ', '.join(autocomp_members)) + + bcon['cursor'] = cursor_orig + + +class CONSOLE_OT_autocomplete(bpy.types.Operator): + ''' + Operator documentatuon text, will be used for the operator tooltip and python docs. + ''' + __label__ = "Console Autocomplete" + + def poll(self, context): + return context.space_data.type == 'PYTHON' + + def execute(self, context): + + sc = context.space_data + + namespace, console, stdout, stderr = get_console(hash(context.region)) + + current_line = sc.history[-1] + line = current_line.line + + if not console: + return ('CANCELLED',) + + if sc.type != 'PYTHON': + return ('CANCELLED',) + + # fake cursor, use for autocomp func. + bcon = {} + bcon['cursor'] = current_line.current_character + bcon['console'] = console + bcon['edit_text'] = line + bcon['namespace'] = namespace + bcon['scrollback'] = '' # nor from the BGE console + + + # This function isnt aware of the text editor or being an operator + # just does the autocomp then copy its results back + autocomp(bcon) + + # Now we need to copy back the line from blender back into the text editor. + # This will change when we dont use the text editor anymore + if bcon['scrollback']: + add_scrollback(bcon['scrollback'], 'INFO') + + # copy back + current_line.line = bcon['edit_text'] + current_line.current_character = bcon['cursor'] + + context.area.tag_redraw() + + return ('FINISHED',) + + + +bpy.types.register(CONSOLE_HT_header) +bpy.types.register(CONSOLE_MT_console) + +bpy.ops.add(CONSOLE_OT_exec) +bpy.ops.add(CONSOLE_OT_autocomplete) + diff --git a/release/ui/space_text.py b/release/ui/space_text.py index c6ce1cb71d6..07e43f32054 100644 --- a/release/ui/space_text.py +++ b/release/ui/space_text.py @@ -226,376 +226,6 @@ class TEXT_MT_edit(bpy.types.Menu): layout.itemM("TEXT_MT_edit_to3d") - -def get_console(text): - ''' - helper function for console operators - currently each text datablock gets its own console - code.InteractiveConsole() - ...which is stored in this function. - ''' - import sys, code, io - - try: consoles = get_console.consoles - except:consoles = get_console.consoles = {} - - # clear all dead consoles, use text names as IDs - for id in list(consoles.keys()): - if id not in bpy.data.texts: - del consoles[id] - - if not text: - return None, None, None - - id = text.name - - try: - namespace, console, stdout = consoles[id] - except: - namespace = locals() - namespace['bpy'] = bpy - - console = code.InteractiveConsole(namespace) - - if sys.version.startswith('2'): stdout = io.BytesIO() # Py2x support - else: stdout = io.StringIO() - - consoles[id]= namespace, console, stdout - - return namespace, console, stdout - -class TEXT_OT_console_exec(bpy.types.Operator): - ''' - Operator documentatuon text, will be used for the operator tooltip and python docs. - ''' - __label__ = "Console Execute" - - # Each text block gets its own console info. - console = {} - - # Both prompts must be the same length - PROMPT = '>>> ' - PROMPT_MULTI = '... ' - - def execute(self, context): - import sys - - st = context.space_data - text = st.text - - if not text: - return ('CANCELLED',) - - namespace, console, stdout = get_console(text) - - line = text.current_line.line - - # redirect output - sys.stdout = stdout - sys.stderr = stdout - - # run the console - if not line.strip(): - line = '\n' # executes a multiline statement - - if line.startswith(self.PROMPT_MULTI) or line.startswith(self.PROMPT): - line = line[len(self.PROMPT):] - was_prefix = True - else: - was_prefix = False - - - is_multiline = console.push(line) - - stdout.seek(0) - output = stdout.read() - - # cleanup - sys.stdout = sys.__stdout__ - sys.stderr = sys.__stderr__ - sys.last_traceback = None - - # So we can reuse, clear all data - stdout.truncate(0) - - if is_multiline: - prefix = self.PROMPT_MULTI - else: - prefix = self.PROMPT - - # Kindof odd, add the prefix if we didnt have one. makes it easier to re-read. - if not was_prefix: - bpy.ops.TEXT_OT_move(type='LINE_BEGIN') - bpy.ops.TEXT_OT_insert(text = prefix) - - bpy.ops.TEXT_OT_move(type='LINE_END') - - # Insert the output into the editor - bpy.ops.TEXT_OT_insert(text= '\n' + output + prefix) - - return ('FINISHED',) - - -def autocomp(bcon): - ''' - This function has been taken from a BGE console autocomp I wrote a while ago - the dictionaty bcon is not needed but it means I can copy and paste from the old func - which works ok for now. - - could be moved into its own module. - ''' - - - def is_delimiter(ch): - ''' - For skipping words - ''' - if ch == '_': - return False - if ch.isalnum(): - return False - - return True - - def is_delimiter_autocomp(ch): - ''' - When autocompleteing will earch back and - ''' - if ch in '._[] "\'': - return False - if ch.isalnum(): - return False - - return True - - - def do_autocomp(autocomp_prefix, autocomp_members): - ''' - return text to insert and a list of options - ''' - autocomp_members = [v for v in autocomp_members if v.startswith(autocomp_prefix)] - - print("AUTO: '%s'" % autocomp_prefix) - print("MEMBERS: '%s'" % str(autocomp_members)) - - if not autocomp_prefix: - return '', autocomp_members - elif len(autocomp_members) > 1: - # find a common string between all members after the prefix - # 'ge' [getA, getB, getC] --> 'get' - - # get the shortest member - min_len = min([len(v) for v in autocomp_members]) - - autocomp_prefix_ret = '' - - for i in range(len(autocomp_prefix), min_len): - char_soup = set() - for v in autocomp_members: - char_soup.add(v[i]) - - if len(char_soup) > 1: - break - else: - autocomp_prefix_ret += char_soup.pop() - - print(autocomp_prefix_ret) - return autocomp_prefix_ret, autocomp_members - elif len(autocomp_members) == 1: - return autocomp_members[0][len(autocomp_prefix):], [] - else: - return '', [] - - - def BCon_PrevChar(bcon): - cursor = bcon['cursor']-1 - if cursor<0: - return None - - try: - return bcon['edit_text'][cursor] - except: - return None - - - def BCon_NextChar(bcon): - try: - return bcon['edit_text'][bcon['cursor']] - except: - return None - - def BCon_cursorLeft(bcon): - bcon['cursor'] -= 1 - if bcon['cursor'] < 0: - bcon['cursor'] = 0 - - def BCon_cursorRight(bcon): - bcon['cursor'] += 1 - if bcon['cursor'] > len(bcon['edit_text']): - bcon['cursor'] = len(bcon['edit_text']) - - def BCon_AddScrollback(bcon, text): - - bcon['scrollback'] = bcon['scrollback'] + text - - - def BCon_cursorInsertChar(bcon, ch): - if bcon['cursor']==0: - bcon['edit_text'] = ch + bcon['edit_text'] - elif bcon['cursor']==len(bcon['edit_text']): - bcon['edit_text'] = bcon['edit_text'] + ch - else: - bcon['edit_text'] = bcon['edit_text'][:bcon['cursor']] + ch + bcon['edit_text'][bcon['cursor']:] - - bcon['cursor'] - if bcon['cursor'] > len(bcon['edit_text']): - bcon['cursor'] = len(bcon['edit_text']) - BCon_cursorRight(bcon) - - - TEMP_NAME = '___tempname___' - - cursor_orig = bcon['cursor'] - - ch = BCon_PrevChar(bcon) - while ch != None and (not is_delimiter(ch)): - ch = BCon_PrevChar(bcon) - BCon_cursorLeft(bcon) - - if ch != None: - BCon_cursorRight(bcon) - - #print (cursor_orig, bcon['cursor']) - - cursor_base = bcon['cursor'] - - autocomp_prefix = bcon['edit_text'][cursor_base:cursor_orig] - - print("PREFIX:'%s'" % autocomp_prefix) - - # Get the previous word - if BCon_PrevChar(bcon)=='.': - BCon_cursorLeft(bcon) - ch = BCon_PrevChar(bcon) - while ch != None and is_delimiter_autocomp(ch)==False: - ch = BCon_PrevChar(bcon) - BCon_cursorLeft(bcon) - - cursor_new = bcon['cursor'] - - if ch != None: - cursor_new+=1 - - pytxt = bcon['edit_text'][cursor_new:cursor_base-1].strip() - print("AUTOCOMP EVAL: '%s'" % pytxt) - #try: - if pytxt: - bcon['console'].runsource(TEMP_NAME + '=' + pytxt, '', 'single') - # print val - else: ##except: - val = None - - try: - val = bcon['namespace'][TEMP_NAME] - del bcon['namespace'][TEMP_NAME] - except: - val = None - - if val: - autocomp_members = dir(val) - - autocomp_prefix_ret, autocomp_members = do_autocomp(autocomp_prefix, autocomp_members) - - bcon['cursor'] = cursor_orig - for v in autocomp_prefix_ret: - BCon_cursorInsertChar(bcon, v) - cursor_orig = bcon['cursor'] - - if autocomp_members: - BCon_AddScrollback(bcon, ', '.join(autocomp_members)) - - del val - - else: - # Autocomp global namespace - autocomp_members = bcon['namespace'].keys() - - if autocomp_prefix: - autocomp_members = [v for v in autocomp_members if v.startswith(autocomp_prefix)] - - autocomp_prefix_ret, autocomp_members = do_autocomp(autocomp_prefix, autocomp_members) - - bcon['cursor'] = cursor_orig - for v in autocomp_prefix_ret: - BCon_cursorInsertChar(bcon, v) - cursor_orig = bcon['cursor'] - - if autocomp_members: - BCon_AddScrollback(bcon, ', '.join(autocomp_members)) - - bcon['cursor'] = cursor_orig - - -class TEXT_OT_console_autocomplete(bpy.types.Operator): - ''' - Operator documentatuon text, will be used for the operator tooltip and python docs. - ''' - __label__ = "Console Autocomplete" - - def execute(self, context): - - st = context.space_data - text = st.text - - namespace, console, stdout = get_console(text) - - line = text.current_line.line - - if not console: - return ('CANCELLED',) - - - # fake cursor, use for autocomp func. - bcon = {} - bcon['cursor'] = text.current_character - bcon['console'] = console - bcon['edit_text'] = line - bcon['namespace'] = namespace - bcon['scrollback'] = '' # nor from the BGE console - - - # This function isnt aware of the text editor or being an operator - # just does the autocomp then copy its results back - autocomp(bcon) - - # Now we need to copy back the line from blender back into the text editor. - # This will change when we dont use the text editor anymore - - # clear the line - bpy.ops.TEXT_OT_move(type='LINE_END') - bpy.ops.TEXT_OT_move_select(type = 'LINE_BEGIN') - bpy.ops.TEXT_OT_delete(type = 'PREVIOUS_CHARACTER') - - if bcon['scrollback']: - bpy.ops.TEXT_OT_move_select(type = 'LINE_BEGIN') - bpy.ops.TEXT_OT_insert(text = bcon['scrollback'].strip() + '\n') - bpy.ops.TEXT_OT_move_select(type='LINE_BEGIN') - - bpy.ops.TEXT_OT_insert(text = bcon['edit_text']) - - # Read only - if 0: - text.current_character = bcon['cursor'] - else: - bpy.ops.TEXT_OT_move(type = 'LINE_BEGIN') - - for i in range(bcon['cursor']): - bpy.ops.TEXT_OT_move(type='NEXT_CHARACTER') - - - return ('FINISHED',) - - - bpy.types.register(TEXT_HT_header) bpy.types.register(TEXT_PT_properties) bpy.types.register(TEXT_PT_find) @@ -607,6 +237,3 @@ bpy.types.register(TEXT_MT_edit_select) bpy.types.register(TEXT_MT_edit_markers) bpy.types.register(TEXT_MT_edit_to3d) -bpy.ops.add(TEXT_OT_console_exec) -bpy.ops.add(TEXT_OT_console_autocomplete) - diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index 92c79ff757f..5baf5af81d1 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -111,11 +111,13 @@ struct SpaceLink *CTX_wm_space_data(const bContext *C); struct ARegion *CTX_wm_region(const bContext *C); void *CTX_wm_region_data(const bContext *C); struct ARegion *CTX_wm_menu(const bContext *C); +struct ReportList *CTX_wm_reports(const bContext *C); struct View3D *CTX_wm_view3d(const bContext *C); struct RegionView3D *CTX_wm_region_view3d(const bContext *C); struct SpaceText *CTX_wm_space_text(const bContext *C); struct SpaceImage *CTX_wm_space_image(const bContext *C); +struct SpaceConsole *CTX_wm_space_console(const bContext *C); void CTX_wm_manager_set(bContext *C, struct wmWindowManager *wm); void CTX_wm_window_set(bContext *C, struct wmWindow *win); diff --git a/source/blender/blenkernel/BKE_report.h b/source/blender/blenkernel/BKE_report.h index 1bb7152fbf3..26853866ebb 100644 --- a/source/blender/blenkernel/BKE_report.h +++ b/source/blender/blenkernel/BKE_report.h @@ -40,15 +40,22 @@ extern "C" { * is needed. */ typedef enum ReportType { - RPT_DEBUG = 0, - RPT_INFO = 1000, - RPT_WARNING = 2000, - RPT_ERROR = 3000, - RPT_ERROR_INVALID_INPUT = 3001, - RPT_ERROR_INVALID_CONTEXT = 3002, - RPT_ERROR_OUT_OF_MEMORY = 3003 + RPT_DEBUG = 1<<0, + RPT_INFO = 1<<1, + RPT_OPERATOR = 1<<2, + RPT_WARNING = 1<<3, + RPT_ERROR = 1<<4, + RPT_ERROR_INVALID_INPUT = 1<<5, + RPT_ERROR_INVALID_CONTEXT = 1<<6, + RPT_ERROR_OUT_OF_MEMORY = 1<<7 } ReportType; +#define RPT_DEBUG_ALL (RPT_DEBUG) +#define RPT_INFO_ALL (RPT_INFO) +#define RPT_OPERATOR_ALL (RPT_OPERATOR) +#define RPT_WARNING_ALL (RPT_WARNING) +#define RPT_ERROR_ALL (RPT_ERROR|RPT_ERROR_INVALID_INPUT|RPT_ERROR_INVALID_CONTEXT|RPT_ERROR_OUT_OF_MEMORY) + enum ReportListFlags { RPT_PRINT = 1, RPT_STORE = 2, diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index 1b499384886..bbf3ceb01e8 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -204,6 +204,11 @@ struct ARegion *CTX_wm_menu(const bContext *C) return C->wm.menu; } +struct ReportList *CTX_wm_reports(const bContext *C) +{ + return C->wm.manager->reports; +} + View3D *CTX_wm_view3d(const bContext *C) { if(C->wm.area && C->wm.area->spacetype==SPACE_VIEW3D) @@ -226,6 +231,13 @@ struct SpaceText *CTX_wm_space_text(const bContext *C) return NULL; } +struct SpaceConsole *CTX_wm_space_console(const bContext *C) +{ + if(C->wm.area && C->wm.area->spacetype==SPACE_CONSOLE) + return C->wm.area->spacedata.first; + return NULL; +} + struct SpaceImage *CTX_wm_space_image(const bContext *C) { if(C->wm.area && C->wm.area->spacetype==SPACE_IMAGE) diff --git a/source/blender/blenkernel/intern/report.c b/source/blender/blenkernel/intern/report.c index 8de8cf8d0f4..6564329ef82 100644 --- a/source/blender/blenkernel/intern/report.c +++ b/source/blender/blenkernel/intern/report.c @@ -49,6 +49,7 @@ static char *report_type_str(int type) switch(type) { case RPT_DEBUG: return "Debug"; case RPT_INFO: return "Info"; + case RPT_OPERATOR: return "Operator"; case RPT_WARNING: return "Warning"; case RPT_ERROR: return "Error"; case RPT_ERROR_INVALID_INPUT: return "Invalid Input Error"; diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 37dda0e41f4..5489c55f789 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4221,7 +4221,7 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm) wm->keymaps.first= wm->keymaps.last= NULL; wm->paintcursors.first= wm->paintcursors.last= NULL; wm->queue.first= wm->queue.last= NULL; - wm->reports.first= wm->reports.last= NULL; + wm->reports= NULL; wm->jobs.first= wm->jobs.last= NULL; wm->windrawable= NULL; @@ -4856,6 +4856,20 @@ static void direct_link_screen(FileData *fd, bScreen *sc) SpaceButs *sbuts= (SpaceButs *)sl; sbuts->path= NULL; } + else if(sl->spacetype==SPACE_CONSOLE) { + SpaceConsole *sconsole= (SpaceConsole *)sl; + ConsoleLine *cl; + + link_list(fd, &sconsole->scrollback); + link_list(fd, &sconsole->history); + + //for(cl= sconsole->scrollback.first; cl; cl= cl->next) + // cl->line= newdataadr(fd, cl->line); + + //for(cl= sconsole->history.first; cl; cl= cl->next) + // cl->line= newdataadr(fd, cl->line); + + } } sa->actionzones.first= sa->actionzones.last= NULL; diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index ebec409ddf4..958e8bb874b 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -1960,6 +1960,9 @@ static void write_screens(WriteData *wd, ListBase *scrbase) else if(sl->spacetype==SPACE_LOGIC){ writestruct(wd, DATA, "SpaceLogic", 1, sl); } + else if(sl->spacetype==SPACE_CONSOLE) { + writestruct(wd, DATA, "SpaceConsole", 1, sl); + } sl= sl->next; } } diff --git a/source/blender/editors/SConscript b/source/blender/editors/SConscript index 9baaf7ae7a5..d7bb567e3eb 100644 --- a/source/blender/editors/SConscript +++ b/source/blender/editors/SConscript @@ -30,6 +30,7 @@ SConscript(['datafiles/SConscript', 'space_text/SConscript', 'space_sequencer/SConscript', 'space_logic/SConscript', + 'space_console/SConscript', 'transform/SConscript', 'screen/SConscript', 'sculpt_paint/SConscript', diff --git a/source/blender/editors/include/ED_space_api.h b/source/blender/editors/include/ED_space_api.h index f2b46369d13..efaf0f56f92 100644 --- a/source/blender/editors/include/ED_space_api.h +++ b/source/blender/editors/include/ED_space_api.h @@ -51,6 +51,7 @@ void ED_spacetype_script(void); void ED_spacetype_text(void); void ED_spacetype_sequencer(void); void ED_spacetype_logic(void); +void ED_spacetype_console(void); /* calls for instancing and freeing spacetype static data called in WM_init_exit */ diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index addda6e02ee..9617a1fbea2 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1059,6 +1059,7 @@ static char *windowtype_pup(void) "|%l" //293 "|Scripts Window %x14"//313 + "|Console %x18" ); } diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index c8df9bb9741..5a55c5fb717 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -75,6 +75,7 @@ void ED_spacetypes_init(void) ED_spacetype_text(); ED_spacetype_sequencer(); ED_spacetype_logic(); + ED_spacetype_console(); // ... /* register operator types for screen and all spaces */ diff --git a/source/blender/editors/space_console/SConscript b/source/blender/editors/space_console/SConscript new file mode 100644 index 00000000000..a29c17f6937 --- /dev/null +++ b/source/blender/editors/space_console/SConscript @@ -0,0 +1,13 @@ +#!/usr/bin/python +Import ('env') + +sources = env.Glob('*.c') +defs = [] +incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf' +incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include' +incs += ' ../../python ../../makesrna ../../blenfont' + +if not env['WITH_BF_PYTHON']: + defs.append('DISABLE_PYTHON') + +env.BlenderLib ( 'bf_editors_space_console', sources, Split(incs), defs, libtype=['core'], priority=[95] ) diff --git a/source/blender/editors/space_console/console_draw.c b/source/blender/editors/space_console/console_draw.c new file mode 100644 index 00000000000..042c84041d0 --- /dev/null +++ b/source/blender/editors/space_console/console_draw.c @@ -0,0 +1,214 @@ +/** + * $Id: text_draw.c 21558 2009-07-13 11:41:24Z campbellbarton $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "BLF_api.h" + +#include "BLI_blenlib.h" + +#include "DNA_space_types.h" +#include "DNA_screen_types.h" +#include "DNA_userdef_types.h" + +#include "BKE_global.h" +#include "BKE_main.h" +// #include "BKE_suggestions.h" +#include "BKE_text.h" +#include "BKE_report.h" +#include "BKE_utildefines.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "ED_datafiles.h" +#include "UI_interface.h" +#include "UI_resources.h" + +//#include "console_intern.h" + +static void console_font_begin(SpaceConsole *sc) +{ + static int mono= -1; // XXX needs proper storage + + if(mono == -1) + mono= BLF_load_mem("monospace", (unsigned char*)datatoc_bmonofont_ttf, datatoc_bmonofont_ttf_size); + + BLF_set(mono); + BLF_aspect(1.0); + + BLF_size(sc->lheight, 72); +} + +static void console_line_color(int type) +{ + switch(type){ + case CONSOLE_LINE_OUTPUT: + glColor4ub(96, 128, 255, 255); + break; + case CONSOLE_LINE_INPUT: + glColor4ub(255, 255, 255, 255); + break; + case CONSOLE_LINE_INFO: + glColor4ub(0, 170, 0, 255); + break; + case CONSOLE_LINE_ERROR: + glColor4ub(220, 96, 96, 255); + break; + } +} + +static void console_report_color(int type) +{ + if(type & RPT_ERROR_ALL) return glColor4ub(220, 0, 0, 255); + if(type & RPT_WARNING_ALL) return glColor4ub(220, 96, 96, 255); + if(type & RPT_OPERATOR_ALL) return glColor4ub(96, 128, 255, 255); + if(type & RPT_INFO_ALL) return glColor4ub(0, 170, 0, 255); + if(type & RPT_DEBUG_ALL) return glColor4ub(196, 196, 196, 255); + return glColor4ub(196, 196, 196, 255); /* unknown */ +} + + +/* return 0 if the last line is off the screen + * should be able to use this for any string type */ +static int console_draw_string(char *str, int str_len, int console_width, int lheight, int ymax, int *x, int *y) +{ + if(str_len > console_width) { /* wrap? */ + int tot_lines = (str_len/console_width)+1; /* total number of lines for wrapping */ + char *line_stride= str + ((tot_lines-1) * console_width); /* advance to the last line and draw it first */ + char eol; /* baclup the end of wrapping */ + + /* last part needs no clipping */ + BLF_position(*x, *y, 0); (*y) += lheight; + BLF_draw(line_stride); + line_stride -= console_width; + + for(; line_stride >= str; line_stride -= console_width) { + eol = line_stride[console_width]; + line_stride[console_width]= '\0'; + + BLF_position(*x, *y, 0); (*y) += lheight; + BLF_draw(line_stride); + + line_stride[console_width] = eol; /* restore */ + + /* check if were out of view bounds */ + if(*y > ymax) + return 0; + } + } + else { /* simple, no wrap */ + BLF_position(*x, *y, 0); (*y) += lheight; + BLF_draw(str); + + if(*y > ymax) + return 0; + } + + return 1; +} + +#define CONSOLE_DRAW_MARGIN 8 + +void console_text_main(struct SpaceConsole *sc, struct ARegion *ar, ReportList *reports) +{ + ConsoleLine *cl= sc->history.last; + + int x_orig=CONSOLE_DRAW_MARGIN, y_orig=CONSOLE_DRAW_MARGIN; + int x,y; + int cwidth; + int console_width; /* number of characters that fit into the width of the console (fixed width) */ + + + console_font_begin(sc); + cwidth = BLF_fixed_width(); + + console_width= (ar->winx - CONSOLE_DRAW_MARGIN*2)/cwidth; + if (console_width < 8) console_width= 8; + + x= x_orig; y= y_orig; + + if(sc->type==CONSOLE_TYPE_PYTHON) { + int prompt_len= strlen(sc->prompt); + + /* text */ + console_line_color(CONSOLE_LINE_INPUT); + + /* command line */ + if(prompt_len) { + BLF_position(x, y, 0); x += cwidth * prompt_len; + BLF_draw(sc->prompt); + } + BLF_position(x, y, 0); + BLF_draw(cl->line); + + /* cursor */ + console_line_color(CONSOLE_LINE_ERROR); /* lazy */ + glRecti(x+(cwidth*cl->cursor) -1, y-2, x+(cwidth*cl->cursor) +1, y+sc->lheight-2); + + x= x_orig; /* remove prompt offset */ + + y += sc->lheight; + + for(cl= sc->scrollback.last; cl; cl= cl->prev) { + console_line_color(cl->type); + + if(!console_draw_string(cl->line, cl->len, console_width, sc->lheight, ar->winy + sc->lheight, &x, &y)) + break; /* past the y limits */ + + } + } + else { + Report *report; + int report_mask= 0; + + /* convert our display toggles into a flag compatible with BKE_report flags */ + if(sc->rpt_mask & CONSOLE_RPT_DEBUG) report_mask |= RPT_DEBUG_ALL; + if(sc->rpt_mask & CONSOLE_RPT_INFO) report_mask |= RPT_INFO_ALL; + if(sc->rpt_mask & CONSOLE_RPT_OP) report_mask |= RPT_OPERATOR_ALL; + if(sc->rpt_mask & CONSOLE_RPT_WARN) report_mask |= RPT_WARNING_ALL; + if(sc->rpt_mask & CONSOLE_RPT_ERR) report_mask |= RPT_ERROR_ALL; + + for(report=reports->list.last; report; report=report->prev) { + + if(report->type & report_mask) { + console_report_color(report->type); + if(!console_draw_string(report->message, strlen(report->message), console_width, sc->lheight, ar->winy + sc->lheight, &x, &y)) + break; /* past the y limits */ + } + + } + } + +} diff --git a/source/blender/editors/space_console/console_intern.h b/source/blender/editors/space_console/console_intern.h new file mode 100644 index 00000000000..55474844d87 --- /dev/null +++ b/source/blender/editors/space_console/console_intern.h @@ -0,0 +1,75 @@ +/** + * $Id: + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ +#ifndef ED_CONSOLE_INTERN_H +#define ED_CONSOLE_INTERN_H + +/* internal exports only */ + +struct ConsoleLine; +struct wmOperatorType; +struct ReportList; + +/* TODO, make into a pref */ +#define CONSOLE_SCROLLBACK_LIMIT 128 + +/* console_draw.c */ +void console_text_main(struct SpaceConsole *sc, struct ARegion *ar, struct ReportList *reports); + +/* console_ops.c */ +void console_history_free(SpaceConsole *sc, ConsoleLine *cl); +void console_scrollback_free(SpaceConsole *sc, ConsoleLine *cl); +ConsoleLine *console_history_add_str(const bContext *C, char *str, int own); +ConsoleLine *console_scrollback_add_str(const bContext *C, char *str, int own); + +ConsoleLine *console_history_verify(const bContext *C); + + +void CONSOLE_OT_move(wmOperatorType *ot); +void CONSOLE_OT_delete(wmOperatorType *ot); +void CONSOLE_OT_insert(wmOperatorType *ot); + +void CONSOLE_OT_history_append(wmOperatorType *ot); +void CONSOLE_OT_scrollback_append(wmOperatorType *ot); + +void CONSOLE_OT_clear(wmOperatorType *ot); +void CONSOLE_OT_history_cycle(wmOperatorType *ot); +void CONSOLE_OT_zoom(wmOperatorType *ot); + +enum { LINE_BEGIN, LINE_END, PREV_CHAR, NEXT_CHAR, PREV_WORD, NEXT_WORD }; +enum { DEL_ALL, DEL_NEXT_CHAR, DEL_PREV_CHAR, DEL_SELECTION, DEL_NEXT_SEL, DEL_PREV_SEL }; + +/* defined in DNA_space_types.h */ +static EnumPropertyItem console_line_type_items[] = { + {CONSOLE_LINE_OUTPUT, "OUTPUT", 0, "Output", ""}, + {CONSOLE_LINE_INPUT, "INPUT", 0, "Input", ""}, + {CONSOLE_LINE_INFO, "INFO", 0, "Information", ""}, + {CONSOLE_LINE_ERROR, "ERROR", 0, "Error", ""}, + {0, NULL, 0, NULL, NULL}}; + +#endif /* ED_CONSOLE_INTERN_H */ + diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c new file mode 100644 index 00000000000..cb883d2d450 --- /dev/null +++ b/source/blender/editors/space_console/console_ops.c @@ -0,0 +1,570 @@ +/** + * $Id: text_ops.c 21549 2009-07-12 12:47:34Z campbellbarton $ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include +#include +#include /* ispunct */ +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_windowmanager_types.h" + +#include "BLI_blenlib.h" +#include "PIL_time.h" + +#include "BKE_utildefines.h" +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_report.h" +// #include "BKE_suggestions.h" +//#include "BKE_text.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_screen.h" +#include "UI_interface.h" +#include "UI_resources.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "console_intern.h" + +void console_history_free(SpaceConsole *sc, ConsoleLine *cl) +{ + BLI_remlink(&sc->history, cl); + MEM_freeN(cl->line); + MEM_freeN(cl); +} +void console_scrollback_free(SpaceConsole *sc, ConsoleLine *cl) +{ + BLI_remlink(&sc->scrollback, cl); + MEM_freeN(cl->line); + MEM_freeN(cl); +} + +void console_scrollback_limit(SpaceConsole *sc) +{ + int tot; + for(tot= BLI_countlist(&sc->scrollback); tot > CONSOLE_SCROLLBACK_LIMIT; tot--) + console_scrollback_free(sc, sc->scrollback.first); +} + +/* return 0 if no change made, clamps the range */ +static int console_line_cursor_set(ConsoleLine *cl, int cursor) +{ + int cursor_new; + + if(cursor < 0) cursor_new= 0; + else if(cursor > cl->len) cursor_new= cl->len; + else cursor_new= cursor; + + if(cursor_new == cl->cursor) + return 0; + + cl->cursor= cursor_new; + return 1; +} + +static ConsoleLine *console_lb_add__internal(ListBase *lb, ConsoleLine *from) +{ + ConsoleLine *ci= MEM_callocN(sizeof(ConsoleLine), "ConsoleLine Add"); + + if(from) { + ci->line= BLI_strdup(from->line); + ci->len= strlen(ci->line); + ci->len_alloc= ci->len; + + ci->cursor= from->cursor; + ci->type= from->type; + } else { + ci->line= MEM_callocN(64, "console-in-line"); + ci->len_alloc= 64; + ci->len= 0; + } + + BLI_addtail(lb, ci); + return ci; +} + +static ConsoleLine *console_history_add(const bContext *C, ConsoleLine *from) +{ + SpaceConsole *sc= CTX_wm_space_console(C); + + return console_lb_add__internal(&sc->history, from); +} + +static ConsoleLine *console_scrollback_add(const bContext *C, ConsoleLine *from) +{ + SpaceConsole *sc= CTX_wm_space_console(C); + + return console_lb_add__internal(&sc->scrollback, from); +} + +static ConsoleLine *console_lb_add_str__internal(ListBase *lb, const bContext *C, char *str, int own) +{ + ConsoleLine *ci= MEM_callocN(sizeof(ConsoleLine), "ConsoleLine Add"); + if(own) ci->line= str; + else ci->line= BLI_strdup(str); + + ci->len = ci->len_alloc = strlen(str); + + BLI_addtail(lb, ci); + return ci; +} +ConsoleLine *console_history_add_str(const bContext *C, char *str, int own) +{ + return console_lb_add_str__internal(&CTX_wm_space_console(C)->history, C, str, own); +} +ConsoleLine *console_scrollback_add_str(const bContext *C, char *str, int own) +{ + return console_lb_add_str__internal(&CTX_wm_space_console(C)->scrollback, C, str, own); +} + +ConsoleLine *console_history_verify(const bContext *C) +{ + SpaceConsole *sc= CTX_wm_space_console(C); + ConsoleLine *ci= sc->history.last; + if(ci==NULL) + ci= console_history_add(C, NULL); + + return ci; +} + + +static void console_line_verify_length(ConsoleLine *ci, int len) +{ + /* resize the buffer if needed */ + if(len > ci->len_alloc) { + int new_len= len * 2; /* new length */ + char *new_line= MEM_callocN(new_len, "console line"); + memcpy(new_line, ci->line, ci->len); + MEM_freeN(ci->line); + + ci->line= new_line; + ci->len_alloc= new_len; + } +} + +static int console_line_insert(ConsoleLine *ci, char *str) +{ + int len = strlen(str); + + if(len==0) + return 0; + + console_line_verify_length(ci, len + ci->len); + + memmove(ci->line+ci->cursor+len, ci->line+ci->cursor, (ci->len - ci->cursor)+1); + memcpy(ci->line+ci->cursor, str, len); + + ci->len += len; + ci->cursor += len; + + return len; +} + +static int console_edit_poll(const bContext *C) +{ + SpaceConsole *sc= CTX_wm_space_console(C); + + if(!sc || sc->type != CONSOLE_TYPE_PYTHON) + return 0; + + return 1; +} + +/* static funcs for text editing */ + + +/* similar to the text editor, with some not used. keep compatible */ +static EnumPropertyItem move_type_items[]= { + {LINE_BEGIN, "LINE_BEGIN", 0, "Line Begin", ""}, + {LINE_END, "LINE_END", 0, "Line End", ""}, + {PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""}, + {NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""}, + {PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""}, + {NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""}, + {0, NULL, 0, NULL, NULL}}; + +static int move_exec(const bContext *C, wmOperator *op) +{ + ConsoleLine *ci= console_history_verify(C); + + int type= RNA_enum_get(op->ptr, "type"); + int done= 0; + + switch(type) { + case LINE_BEGIN: + done= console_line_cursor_set(ci, 0); + break; + case LINE_END: + done= console_line_cursor_set(ci, INT_MAX); + break; + case PREV_CHAR: + done= console_line_cursor_set(ci, ci->cursor-1); + break; + case NEXT_CHAR: + done= console_line_cursor_set(ci, ci->cursor+1); + break; + } + + if(done) { + ED_area_tag_redraw(CTX_wm_area(C)); + } + + return OPERATOR_FINISHED; +} + +void CONSOLE_OT_move(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Move Cursor"; + ot->idname= "CONSOLE_OT_move"; + + /* api callbacks */ + ot->exec= move_exec; + ot->poll= console_edit_poll; + + /* flags */ + ot->flag= OPTYPE_REGISTER; + + /* properties */ + RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to."); +} + + +static int insert_exec(const bContext *C, wmOperator *op) +{ + ConsoleLine *ci= console_history_verify(C); + char *str= RNA_string_get_alloc(op->ptr, "text", NULL, 0); + + int len= console_line_insert(ci, str); + + MEM_freeN(str); + + if(len==0) + return OPERATOR_CANCELLED; + + ED_area_tag_redraw(CTX_wm_area(C)); + + return OPERATOR_FINISHED; +} + +static int insert_invoke(const bContext *C, wmOperator *op, wmEvent *event) +{ + char str[2] = {event->ascii, '\0'}; + RNA_string_set(op->ptr, "text", str); + return insert_exec(C, op); +} + +void CONSOLE_OT_insert(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Insert"; + ot->idname= "CONSOLE_OT_insert"; + + /* api callbacks */ + ot->exec= insert_exec; + ot->invoke= insert_invoke; + ot->poll= console_edit_poll; + + /* flags */ + ot->flag= OPTYPE_REGISTER; + + /* properties */ + RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position."); +} + + +static EnumPropertyItem delete_type_items[]= { + {DEL_NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""}, + {DEL_PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""}, +// {DEL_NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""}, +// {DEL_PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""}, + {0, NULL, 0, NULL, NULL}}; + +static int delete_exec(const bContext *C, wmOperator *op) +{ + + ConsoleLine *ci= console_history_verify(C); + + + int done = 0; + + int type= RNA_enum_get(op->ptr, "type"); + + if(ci->len==0) { + return OPERATOR_CANCELLED; + } + + switch(type) { + case DEL_NEXT_CHAR: + if(ci->cursor < ci->len) { + memmove(ci->line + ci->cursor, ci->line + ci->cursor+1, (ci->len - ci->cursor)+1); + ci->len--; + done= 1; + } + break; + case DEL_PREV_CHAR: + if(ci->cursor > 0) { + ci->cursor--; /* same as above */ + memmove(ci->line + ci->cursor, ci->line + ci->cursor+1, (ci->len - ci->cursor)+1); + ci->len--; + done= 1; + } + break; + } + + if(!done) + return OPERATOR_CANCELLED; + + ED_area_tag_redraw(CTX_wm_area(C)); + + return OPERATOR_FINISHED; +} + + +void CONSOLE_OT_delete(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Delete"; + ot->idname= "CONSOLE_OT_delete"; + + /* api callbacks */ + ot->exec= delete_exec; + ot->poll= console_edit_poll; + + /* flags */ + ot->flag= OPTYPE_REGISTER; + + /* properties */ + RNA_def_enum(ot->srna, "type", delete_type_items, DEL_NEXT_CHAR, "Type", "Which part of the text to delete."); +} + + +/* the python exec operator uses this */ +static int clear_exec(const bContext *C, wmOperator *op) +{ + SpaceConsole *sc= CTX_wm_space_console(C); + + short scrollback= RNA_boolean_get(op->ptr, "scrollback"); + short history= RNA_boolean_get(op->ptr, "history"); + + /*ConsoleLine *ci= */ console_history_verify(C); + + if(scrollback) { /* last item in mistory */ + while(sc->scrollback.first) + console_scrollback_free(sc, sc->scrollback.first); + } + + if(history) { + while(sc->history.first) + console_history_free(sc, sc->history.first); + } + + ED_area_tag_redraw(CTX_wm_area(C)); + + return OPERATOR_FINISHED; +} + +void CONSOLE_OT_clear(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Clear"; + ot->idname= "CONSOLE_OT_clear"; + + /* api callbacks */ + ot->exec= clear_exec; + ot->poll= console_edit_poll; + + /* flags */ + ot->flag= OPTYPE_REGISTER; + + /* properties */ + RNA_def_boolean(ot->srna, "scrollback", 1, "Scrollback", "Clear the scrollback history"); + RNA_def_boolean(ot->srna, "history", 0, "History", "Clear the command history"); +} + + + +/* the python exec operator uses this */ +static int history_cycle_exec(const bContext *C, wmOperator *op) +{ + SpaceConsole *sc= CTX_wm_space_console(C); + ConsoleLine *ci= console_history_verify(C); /* TODO - stupid, just prevernts crashes when no command line */ + + short reverse= RNA_boolean_get(op->ptr, "reverse"); /* assumes down, reverse is up */ + + if(reverse) { /* last item in mistory */ + ci= sc->history.last; + BLI_remlink(&sc->history, ci); + BLI_addhead(&sc->history, ci); + } + else { + ci= sc->history.first; + BLI_remlink(&sc->history, ci); + BLI_addtail(&sc->history, ci); + } + + ED_area_tag_redraw(CTX_wm_area(C)); + + return OPERATOR_FINISHED; +} + +void CONSOLE_OT_history_cycle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "History Cycle"; + ot->idname= "CONSOLE_OT_history_cycle"; + + /* api callbacks */ + ot->exec= history_cycle_exec; + ot->poll= console_edit_poll; + + /* flags */ + ot->flag= OPTYPE_REGISTER; + + /* properties */ + RNA_def_boolean(ot->srna, "reverse", 0, "Reverse", "reverse cycle history"); +} + + +/* the python exec operator uses this */ +static int history_append_exec(const bContext *C, wmOperator *op) +{ + ConsoleLine *ci= console_history_verify(C); + + + char *str= RNA_string_get_alloc(op->ptr, "text", NULL, 0); /* own this text in the new line, dont free */ + int cursor= RNA_int_get(op->ptr, "current_character"); + + ci= console_history_add_str(C, str, 1); /* own the string */ + console_line_cursor_set(ci, cursor); + + ED_area_tag_redraw(CTX_wm_area(C)); + + return OPERATOR_FINISHED; +} + +void CONSOLE_OT_history_append(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "History Append"; + ot->idname= "CONSOLE_OT_history_append"; + + /* api callbacks */ + ot->exec= history_append_exec; + ot->poll= console_edit_poll; + + /* flags */ + ot->flag= OPTYPE_REGISTER; + + /* properties */ + RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position."); + RNA_def_int(ot->srna, "current_character", 0, 0, INT_MAX, "Cursor", "The index of the cursor.", 0, 10000); +} + + +/* the python exec operator uses this */ +static int scrollback_append_exec(const bContext *C, wmOperator *op) +{ + SpaceConsole *sc= CTX_wm_space_console(C); + ConsoleLine *ci= console_history_verify(C); + + char *str= RNA_string_get_alloc(op->ptr, "text", NULL, 0); /* own this text in the new line, dont free */ + int type= RNA_enum_get(op->ptr, "type"); + + ci= console_scrollback_add_str(C, str, 1); /* own the string */ + ci->type= type; + + console_scrollback_limit(sc); + + ED_area_tag_redraw(CTX_wm_area(C)); + + return OPERATOR_FINISHED; +} + +void CONSOLE_OT_scrollback_append(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Scrollback Append"; + ot->idname= "CONSOLE_OT_scrollback_append"; + + /* api callbacks */ + ot->exec= scrollback_append_exec; + ot->poll= console_edit_poll; + + /* flags */ + ot->flag= OPTYPE_REGISTER; + + /* properties */ + RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position."); + RNA_def_enum(ot->srna, "type", console_line_type_items, CONSOLE_LINE_OUTPUT, "Type", "Console output type."); +} + +static int zoom_exec(const bContext *C, wmOperator *op) +{ + SpaceConsole *sc= CTX_wm_space_console(C); + + int delta= RNA_int_get(op->ptr, "delta"); + + sc->lheight += delta; + CLAMP(sc->lheight, 8, 32); + + ED_area_tag_redraw(CTX_wm_area(C)); + + return OPERATOR_FINISHED; +} + + +void CONSOLE_OT_zoom(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Console Zoom"; + ot->idname= "CONSOLE_OT_zoom"; + + /* api callbacks */ + ot->exec= zoom_exec; + + /* flags */ + ot->flag= OPTYPE_REGISTER; + + /* properties */ + RNA_def_int(ot->srna, "delta", 0, 0, INT_MAX, "Delta", "Scale the view font.", 0, 1000); +} + diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c new file mode 100644 index 00000000000..ee80fe3d2d9 --- /dev/null +++ b/source/blender/editors/space_console/space_console.c @@ -0,0 +1,336 @@ +/** + * $Id: + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + + #include +#include + +#include "DNA_space_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_arithb.h" + +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_screen.h" + +#include "ED_space_api.h" +#include "ED_screen.h" + +#include "BIF_gl.h" + + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" +#include "UI_view2d.h" + +#include "console_intern.h" // own include + + +/* ******************** default callbacks for console space ***************** */ + +static SpaceLink *console_new(const bContext *C) +{ + ARegion *ar; + SpaceConsole *sconsole; + + sconsole= MEM_callocN(sizeof(SpaceConsole), "initconsole"); + sconsole->spacetype= SPACE_CONSOLE; + + sconsole->lheight= 14; + sconsole->type= CONSOLE_TYPE_PYTHON; + sconsole->rpt_mask= CONSOLE_RPT_OP; /* ? - not sure whats a good default here?*/ + + /* header */ + ar= MEM_callocN(sizeof(ARegion), "header for console"); + + BLI_addtail(&sconsole->regionbase, ar); + ar->regiontype= RGN_TYPE_HEADER; + ar->alignment= RGN_ALIGN_BOTTOM; + + + /* main area */ + ar= MEM_callocN(sizeof(ARegion), "main area for text"); + + BLI_addtail(&sconsole->regionbase, ar); + ar->regiontype= RGN_TYPE_WINDOW; + + ar->v2d.scroll = (V2D_SCROLL_RIGHT | V2D_SCROLL_BOTTOM_O); + ar->v2d.align = (V2D_ALIGN_NO_NEG_X|V2D_ALIGN_NO_POS_Y); + ar->v2d.keepzoom = (V2D_LOCKZOOM_X|V2D_LOCKZOOM_Y|V2D_KEEPZOOM|V2D_KEEPASPECT); + ar->v2d.keeptot= V2D_KEEPTOT_STRICT; + ar->v2d.minzoom= ar->v2d.maxzoom= 1.0f; + + return (SpaceLink *)sconsole; +} + +/* not spacelink itself */ +static void console_free(SpaceLink *sl) +{ + SpaceConsole *sc= (SpaceConsole*) sl; + + while(sc->scrollback.first) + console_scrollback_free(sc, sc->scrollback.first); + + while(sc->history.first) + console_history_free(sc, sc->history.first); +} + + +/* spacetype; init callback */ +static void console_init(struct wmWindowManager *wm, ScrArea *sa) +{ + +} + +static SpaceLink *console_duplicate(SpaceLink *sl) +{ + SpaceConsole *sconsolen= MEM_dupallocN(sl); + + /* clear or remove stuff from old */ + + /* TODO - duplicate?, then we also need to duplicate the py namespace */ + sconsolen->scrollback.first= sconsolen->scrollback.last= NULL; + sconsolen->history.first= sconsolen->history.last= NULL; + + return (SpaceLink *)sconsolen; +} + + + +/* add handlers, stuff you only do once or on area/region changes */ +static void console_main_area_init(wmWindowManager *wm, ARegion *ar) +{ + ListBase *keymap; + + UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_STANDARD, ar->winx, ar->winy); + + /* own keymap */ + keymap= WM_keymap_listbase(wm, "Console", SPACE_CONSOLE, 0); /* XXX weak? */ + WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); +} + +static void console_main_area_draw(const bContext *C, ARegion *ar) +{ + /* draw entirely, view changes should be handled here */ + SpaceConsole *sc= CTX_wm_space_console(C); + //View2D *v2d= &ar->v2d; + //float col[3]; + + /* clear and setup matrix */ + //UI_GetThemeColor3fv(TH_BACK, col); + //glClearColor(col[0], col[1], col[2], 0.0); + glClearColor(0, 0, 0, 1.0); + + glClear(GL_COLOR_BUFFER_BIT); + + /* worlks best with no view2d matrix set */ + /*UI_view2d_view_ortho(C, v2d);*/ + + /* data... */ + + /* add helper text, why not? */ + if(sc->scrollback.first==NULL) { + console_scrollback_add_str(C, " * Python Interactive Console *", 0); + console_scrollback_add_str(C, "Command History: Up/Down Arrow", 0); + console_scrollback_add_str(C, "Cursor: Left/Right Home/End", 0); + console_scrollback_add_str(C, "Remove: Backspace/Delete", 0); + console_scrollback_add_str(C, "Execute: Enter", 0); + console_scrollback_add_str(C, "Autocomplete: Tab", 0); + console_scrollback_add_str(C, "Ctrl +/- Wheel: Zoom", 0); + console_scrollback_add_str(C, "Builtin Modules: bpy, bpy.data, bpy.ops, bpy.props, bpy.types, bpy.ui", 0); + } + + console_history_verify(C); /* make sure we have some command line */ + console_text_main(sc, ar, CTX_wm_reports(C)); + + /* reset view matrix */ + /* UI_view2d_view_restore(C); */ + + /* scrollers */ + /* + scrollers= UI_view2d_scrollers_calc(C, v2d, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY, V2D_ARG_DUMMY); + UI_view2d_scrollers_draw(C, v2d, scrollers); + UI_view2d_scrollers_free(scrollers); + */ +} + +void console_operatortypes(void) +{ + WM_operatortype_append(CONSOLE_OT_move); + WM_operatortype_append(CONSOLE_OT_delete); + WM_operatortype_append(CONSOLE_OT_insert); + + /* for use by python only */ + WM_operatortype_append(CONSOLE_OT_history_append); + WM_operatortype_append(CONSOLE_OT_scrollback_append); + + + WM_operatortype_append(CONSOLE_OT_clear); + WM_operatortype_append(CONSOLE_OT_history_cycle); + WM_operatortype_append(CONSOLE_OT_zoom); +} + +void console_keymap(struct wmWindowManager *wm) +{ + ListBase *keymap= WM_keymap_listbase(wm, "Console", SPACE_CONSOLE, 0); + + /* + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", LEFTARROWKEY, KM_PRESS, KM_OSKEY, 0)->ptr, "type", LINE_BEGIN); + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", ENDKEY, KM_PRESS, 0, 0)->ptr, "type", LINE_END); + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", RIGHTARROWKEY, KM_PRESS, KM_OSKEY, 0)->ptr, "type", LINE_BEGIN); + + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", EKEY, KM_PRESS, KM_CTRL, 0)->ptr, "type", LINE_END); + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", EKEY, KM_PRESS, KM_CTRL|KM_SHIFT, 0)->ptr, "type", LINE_END); + */ + + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", HOMEKEY, KM_PRESS, 0, 0)->ptr, "type", LINE_BEGIN); + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", ENDKEY, KM_PRESS, 0, 0)->ptr, "type", LINE_END); + + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_zoom", WHEELUPMOUSE, KM_PRESS, KM_CTRL, 0)->ptr, "delta", 1); + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_zoom", WHEELDOWNMOUSE, KM_PRESS, KM_CTRL, 0)->ptr, "delta", -1); + + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_zoom", PADPLUSKEY, KM_PRESS, KM_CTRL, 0)->ptr, "delta", 1); + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_zoom", PADMINUS, KM_PRESS, KM_CTRL, 0)->ptr, "delta", -1); + + + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", LEFTARROWKEY, KM_PRESS, 0, 0)->ptr, "type", PREV_CHAR); + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", RIGHTARROWKEY, KM_PRESS, 0, 0)->ptr, "type", NEXT_CHAR); + + RNA_boolean_set(WM_keymap_add_item(keymap, "CONSOLE_OT_history_cycle", UPARROWKEY, KM_PRESS, 0, 0)->ptr, "reverse", 1); + WM_keymap_add_item(keymap, "CONSOLE_OT_history_cycle", DOWNARROWKEY, KM_PRESS, 0, 0); + + /* + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", LEFTARROWKEY, KM_PRESS, KM_CTRL, 0)->ptr, "type", PREV_WORD); + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", RIGHTARROWKEY, KM_PRESS, KM_CTRL, 0)->ptr, "type", NEXT_WORD); + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", UPARROWKEY, KM_PRESS, 0, 0)->ptr, "type", PREV_LINE); + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", DOWNARROWKEY, KM_PRESS, 0, 0)->ptr, "type", NEXT_LINE); + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", PAGEUPKEY, KM_PRESS, 0, 0)->ptr, "type", PREV_PAGE); + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_move", PAGEDOWNKEY, KM_PRESS, 0, 0)->ptr, "type", NEXT_PAGE); + + + //RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_delete", DELKEY, KM_PRESS, 0, 0)->ptr, "type", DEL_NEXT_CHAR); + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_delete", DKEY, KM_PRESS, KM_CTRL, 0)->ptr, "type", DEL_NEXT_CHAR); + //RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_delete", BACKSPACEKEY, KM_PRESS, 0, 0)->ptr, "type", DEL_PREV_CHAR); + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_delete", DELKEY, KM_PRESS, KM_CTRL, 0)->ptr, "type", DEL_NEXT_WORD); + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_delete", BACKSPACEKEY, KM_PRESS, KM_CTRL, 0)->ptr, "type", DEL_PREV_WORD); + */ + + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_delete", DELKEY, KM_PRESS, 0, 0)->ptr, "type", DEL_NEXT_CHAR); + RNA_enum_set(WM_keymap_add_item(keymap, "CONSOLE_OT_delete", BACKSPACEKEY, KM_PRESS, 0, 0)->ptr, "type", DEL_PREV_CHAR); + +#ifndef DISABLE_PYTHON + WM_keymap_add_item(keymap, "CONSOLE_OT_exec", RETKEY, KM_PRESS, 0, 0); /* python operator - space_text.py */ + WM_keymap_add_item(keymap, "CONSOLE_OT_autocomplete", TABKEY, KM_PRESS, 0, 0); /* python operator - space_text.py */ +#endif + WM_keymap_add_item(keymap, "CONSOLE_OT_insert", KM_TEXTINPUT, KM_PRESS, KM_ANY, 0); // last! +} + +/****************** header region ******************/ + +/* add handlers, stuff you only do once or on area/region changes */ +static void console_header_area_init(wmWindowManager *wm, ARegion *ar) +{ + ED_region_header_init(ar); +} + +static void console_header_area_draw(const bContext *C, ARegion *ar) +{ + ED_region_header(C, ar); +} + +static void console_main_area_listener(ScrArea *sa, wmNotifier *wmn) +{ + SpaceConsole *sc= sa->spacedata.first; + + /* context changes */ + switch(wmn->category) { + case NC_CONSOLE: + if(wmn->data == ND_CONSOLE) { /* generic redraw request */ + ED_area_tag_redraw(sa); + } + else if(wmn->data == ND_CONSOLE_REPORT && sc->type==CONSOLE_TYPE_REPORT) { + /* redraw also but only for report view, could do less redraws by checking the type */ + ED_area_tag_redraw(sa); + } + break; + } +} + +/* only called once, from space/spacetypes.c */ +void ED_spacetype_console(void) +{ + SpaceType *sc= MEM_callocN(sizeof(SpaceType), "spacetype console"); + ARegionType *art; + + sc->spaceid= SPACE_CONSOLE; + + sc->new= console_new; + sc->free= console_free; + sc->init= console_init; + sc->duplicate= console_duplicate; + sc->operatortypes= console_operatortypes; + sc->keymap= console_keymap; + sc->listener= console_main_area_listener; + + /* regions: main window */ + art= MEM_callocN(sizeof(ARegionType), "spacetype console region"); + art->regionid = RGN_TYPE_WINDOW; + art->init= console_main_area_init; + art->draw= console_main_area_draw; + + + + BLI_addhead(&sc->regiontypes, art); + + /* regions: header */ + art= MEM_callocN(sizeof(ARegionType), "spacetype console region"); + art->regionid = RGN_TYPE_HEADER; + art->minsizey= HEADERY; + art->keymapflag= ED_KEYMAP_UI|ED_KEYMAP_VIEW2D; + + art->init= console_header_area_init; + art->draw= console_header_area_draw; + + BLI_addhead(&sc->regiontypes, art); + + + + BKE_spacetype_register(sc); +} + + diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index cc6987084d0..ca50d8494c0 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -464,6 +464,61 @@ typedef struct SpaceImaSel { } SpaceImaSel; +typedef struct ConsoleLine { + struct ConsoleLine *next, *prev; + + /* keep these 3 vars so as to share free, realloc funcs */ + int len_alloc; /* allocated length */ + int len; /* real len - strlen() */ + char *line; + + int cursor; + int type; /* only for use when in the 'scrollback' listbase */ +} ConsoleLine; + +/* ConsoleLine.type */ +enum { + CONSOLE_LINE_OUTPUT=0, + CONSOLE_LINE_INPUT, + CONSOLE_LINE_INFO, /* autocomp feedback */ + CONSOLE_LINE_ERROR +}; + +/* SpaceConsole.rpt_mask */ +enum { + CONSOLE_TYPE_PYTHON=0, + CONSOLE_TYPE_REPORT, +}; + +/* SpaceConsole.type see BKE_report.h */ +enum { + CONSOLE_RPT_DEBUG = 1<<0, + CONSOLE_RPT_INFO = 1<<1, + CONSOLE_RPT_OP = 1<<2, + CONSOLE_RPT_WARN = 1<<3, + CONSOLE_RPT_ERR = 1<<4, +}; + +typedef struct SpaceConsole { + SpaceLink *next, *prev; + ListBase regionbase; /* storage of regions for inactive spaces */ + int spacetype; + float blockscale; // XXX are these needed? + + short blockhandler[8]; // XXX are these needed? + + /* space vars */ + int type; /* console/report/..? */ + int rpt_mask; /* which reports to display */ + int flag, lheight; + + ListBase scrollback; /* ConsoleLine; output */ + ListBase history; /* ConsoleLine; command history, current edited line is the first */ + char prompt[8]; + +} SpaceConsole; + + /* view3d Now in DNA_view3d_types.h */ @@ -810,7 +865,8 @@ enum { SPACE_TIME, SPACE_NODE, SPACE_LOGIC, - SPACEICONMAX = SPACE_LOGIC + SPACE_CONSOLE, + SPACEICONMAX = SPACE_CONSOLE }; #endif diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index fcf3d0aec23..9a60e3c92b8 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -68,7 +68,7 @@ typedef struct wmWindowManager { ListBase queue; /* refresh/redraw wmNotifier structs */ - ListBase reports; /* information and error reports */ + struct ReportList *reports; /* information and error reports */ ListBase jobs; /* threaded jobs manager */ diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index ed1a8052acd..a04a09d4d11 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -371,6 +371,7 @@ extern StructRNA RNA_SoundSequence; extern StructRNA RNA_Space; extern StructRNA RNA_Space3DView; extern StructRNA RNA_SpaceButtonsWindow; +extern StructRNA RNA_SpaceConsole; extern StructRNA RNA_SpaceDopeSheetEditor; extern StructRNA RNA_SpaceGraphEditor; extern StructRNA RNA_SpaceImageEditor; diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c index fb836a98a52..e3171d38932 100644 --- a/source/blender/makesrna/intern/rna_screen.c +++ b/source/blender/makesrna/intern/rna_screen.c @@ -43,6 +43,9 @@ EnumPropertyItem region_type_items[] = { #ifdef RNA_RUNTIME +#include "ED_screen.h" + + #include "WM_api.h" #include "WM_types.h" @@ -97,15 +100,23 @@ static void rna_def_scrarea(BlenderRNA *brna) prop= RNA_def_property(srna, "show_menus", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", HEADER_NO_PULLDOWN); RNA_def_property_ui_text(prop, "Show Menus", "Show menus in the header."); + + RNA_def_function(srna, "tag_redraw", "ED_area_tag_redraw"); } static void rna_def_region(BlenderRNA *brna) { StructRNA *srna; + PropertyRNA *prop; srna= RNA_def_struct(brna, "Region", NULL); RNA_def_struct_ui_text(srna, "Region", "Region in a subdivided screen area."); RNA_def_struct_sdna(srna, "ARegion"); + + prop= RNA_def_property(srna, "id", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "swinid"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Region ID", "Uniqute ID for this region."); } static void rna_def_bscreen(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 697548de817..fb41262b812 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -24,6 +24,8 @@ #include +#include "MEM_guardedalloc.h" + #include "RNA_access.h" #include "RNA_define.h" #include "RNA_types.h" @@ -56,6 +58,7 @@ EnumPropertyItem space_type_items[] = { {SPACE_TIME, "TIMELINE", 0, "Timeline", ""}, {SPACE_NODE, "NODE_EDITOR", 0, "Node Editor", ""}, {SPACE_LOGIC, "LOGIC_EDITOR", 0, "Logic Editor", ""}, + {SPACE_CONSOLE, "CONSOLE", 0, "Console", ""}, {0, NULL, 0, NULL, NULL}}; #define DC_RGB {0, "COLOR", ICON_IMAGE_RGB, "Color", "Draw image with RGB colors."} @@ -122,6 +125,8 @@ static StructRNA* rna_Space_refine(struct PointerRNA *ptr) return &RNA_SpaceNodeEditor; case SPACE_LOGIC: return &RNA_SpaceLogicEditor;*/ + case SPACE_CONSOLE: + return &RNA_SpaceConsole; default: return &RNA_Space; } @@ -234,6 +239,46 @@ StructRNA *rna_SpaceButtonsWindow_pin_id_typef(PointerRNA *ptr) return &RNA_ID; } +/* Space Console */ +static void rna_ConsoleLine_line_get(PointerRNA *ptr, char *value) +{ + ConsoleLine *ci= (ConsoleLine*)ptr->data; + strcpy(value, ci->line); +} + +static int rna_ConsoleLine_line_length(PointerRNA *ptr) +{ + ConsoleLine *ci= (ConsoleLine*)ptr->data; + return ci->len; +} + +static void rna_ConsoleLine_line_set(PointerRNA *ptr, const char *value) +{ + ConsoleLine *ci= (ConsoleLine*)ptr->data; + int len= strlen(value); + + if(len < ci->len_alloc) { /* allocated size is enough? */ + strcpy(ci->line, value); + } + else { /* allocate a new strnig */ + MEM_freeN(ci->line); + ci->line= BLI_strdup(value); + ci->len_alloc= len; + } + ci->len= len; + + if(ci->cursor > len) /* clamp the cursor */ + ci->cursor= len; +} + +static void rna_ConsoleLine_cursor_index_range(PointerRNA *ptr, int *min, int *max) +{ + ConsoleLine *ci= (ConsoleLine*)ptr->data; + + *min= 0; + *max= ci->len; +} + #else static void rna_def_space(BlenderRNA *brna) @@ -987,6 +1032,93 @@ static void rna_def_space_nla(BlenderRNA *brna) // TODO... autosnap, dopesheet? } +static void rna_def_console_line(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ConsoleLine", NULL); + RNA_def_struct_ui_text(srna, "Console Input", "Input line for the interactive console."); + + prop= RNA_def_property(srna, "line", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, "rna_ConsoleLine_line_get", "rna_ConsoleLine_line_length", "rna_ConsoleLine_line_set"); + RNA_def_property_ui_text(prop, "Line", "Text in the line."); + + prop= RNA_def_property(srna, "current_character", PROP_INT, PROP_NONE); /* copied from text editor */ + RNA_def_property_int_sdna(prop, NULL, "cursor"); + RNA_def_property_int_funcs(prop, NULL, NULL, "rna_ConsoleLine_cursor_index_range"); + +} + +static EnumPropertyItem console_type_items[] = { + {CONSOLE_TYPE_PYTHON, "PYTHON", 0, "Python", ""}, + {CONSOLE_TYPE_REPORT, "REPORT", 0, "Report", ""}, + {0, NULL, 0, NULL, NULL}}; + +static void rna_def_space_console(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna= RNA_def_struct(brna, "SpaceConsole", "Space"); + RNA_def_struct_sdna(srna, "SpaceConsole"); + RNA_def_struct_ui_text(srna, "Space Console", "Interactive python console."); + + /* display */ + prop= RNA_def_property(srna, "font_size", PROP_INT, PROP_NONE); /* copied from text editor */ + RNA_def_property_int_sdna(prop, NULL, "lheight"); + RNA_def_property_range(prop, 8, 32); + RNA_def_property_ui_text(prop, "Font Size", "Font size to use for displaying the text."); + RNA_def_property_update(prop, NC_CONSOLE | ND_CONSOLE, NULL); + + prop= RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, console_type_items); + RNA_def_property_ui_text(prop, "Type", "Console type."); + RNA_def_property_update(prop, NC_CONSOLE | ND_CONSOLE, NULL); + + /* reporting display */ + prop= RNA_def_property(srna, "show_report_debug", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "rpt_mask", CONSOLE_RPT_DEBUG); + RNA_def_property_ui_text(prop, "Show Debug", "Display debug reporting info."); + RNA_def_property_update(prop, NC_CONSOLE | ND_CONSOLE_REPORT, NULL); + + prop= RNA_def_property(srna, "show_report_info", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "rpt_mask", CONSOLE_RPT_INFO); + RNA_def_property_ui_text(prop, "Show Info", "Display general information."); + RNA_def_property_update(prop, NC_CONSOLE | ND_CONSOLE_REPORT, NULL); + + prop= RNA_def_property(srna, "show_report_operator", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "rpt_mask", CONSOLE_RPT_OP); + RNA_def_property_ui_text(prop, "Show Operator", "Display the operator log."); + RNA_def_property_update(prop, NC_CONSOLE | ND_CONSOLE_REPORT, NULL); + + prop= RNA_def_property(srna, "show_report_warn", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "rpt_mask", CONSOLE_RPT_WARN); + RNA_def_property_ui_text(prop, "Show Warn", "Display warnings."); + RNA_def_property_update(prop, NC_CONSOLE | ND_CONSOLE_REPORT, NULL); + + prop= RNA_def_property(srna, "show_report_error", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "rpt_mask", CONSOLE_RPT_ERR); + RNA_def_property_ui_text(prop, "Show Error", "Display error text."); + RNA_def_property_update(prop, NC_CONSOLE | ND_CONSOLE_REPORT, NULL); + + + + prop= RNA_def_property(srna, "prompt", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Prompt", "Command line prompt."); + RNA_def_struct_name_property(srna, prop); + + prop= RNA_def_property(srna, "history", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "history", NULL); + RNA_def_property_struct_type(prop, "ConsoleLine"); + RNA_def_property_ui_text(prop, "History", "Command history."); + + prop= RNA_def_property(srna, "scrollback", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "scrollback", NULL); + RNA_def_property_struct_type(prop, "ConsoleLine"); + RNA_def_property_ui_text(prop, "Output", "Command output."); +} + static void rna_def_fileselect_params(BlenderRNA *brna) { StructRNA *srna; @@ -1127,6 +1259,8 @@ void RNA_def_space(BlenderRNA *brna) rna_def_space_dopesheet(brna); rna_def_space_graph(brna); rna_def_space_nla(brna); + rna_def_space_console(brna); + rna_def_console_line(brna); } #endif diff --git a/source/blender/python/intern/bpy_ui.c b/source/blender/python/intern/bpy_ui.c index 088fe436c69..64b8a33fd66 100644 --- a/source/blender/python/intern/bpy_ui.c +++ b/source/blender/python/intern/bpy_ui.c @@ -557,6 +557,7 @@ PyObject *BPY_ui_module( void ) PyModule_AddObject( mod, "SCRIPT", PyLong_FromSsize_t(SPACE_SCRIPT) ); PyModule_AddObject( mod, "TIME", PyLong_FromSsize_t(SPACE_TIME) ); PyModule_AddObject( mod, "NODE", PyLong_FromSsize_t(SPACE_NODE) ); + PyModule_AddObject( mod, "CONSOLE", PyLong_FromSsize_t(SPACE_CONSOLE) ); /* INCREF since its its assumed that all these functions return the * module with a new ref like PyDict_New, since they are passed to diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index ab55f8a4459..3a646c5e799 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -131,6 +131,7 @@ typedef struct wmNotifier { #define NC_WORLD (13<<24) #define NC_FILE (14<<24) #define NC_ANIMATION (15<<24) +#define NC_CONSOLE (16<<24) /* data type, 256 entries is enough, it can overlap */ #define NOTE_DATA 0x00FF0000 @@ -200,6 +201,10 @@ typedef struct wmNotifier { #define ND_NLA_EDIT (76<<16) #define ND_NLA_ACTCHANGE (77<<16) + /* console */ +#define ND_CONSOLE (78<<16) /* general redraw */ +#define ND_CONSOLE_REPORT (79<<16) /* update for reports, could spesify type */ + /* subtype, 256 entries too */ #define NOTE_SUBTYPE 0x0000FF00 diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 7dec14664ae..6b3b128d34b 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -79,9 +79,11 @@ void WM_operator_free(wmOperator *op) /* all operations get registered in the windowmanager here */ /* called on event handling by event_system.c */ -void wm_operator_register(wmWindowManager *wm, wmOperator *op) +void wm_operator_register(bContext *C, wmOperator *op) { + wmWindowManager *wm= CTX_wm_manager(C); int tot; + char *buf; BLI_addtail(&wm->operators, op); tot= BLI_countlist(&wm->operators); @@ -92,6 +94,15 @@ void wm_operator_register(wmWindowManager *wm, wmOperator *op) WM_operator_free(opt); tot--; } + + + /* Report the string representation of the operator */ + buf = WM_operator_pystring(op); + BKE_report(wm->reports, RPT_OPERATOR, buf); + MEM_freeN(buf); + + /* so the console is redrawn */ + WM_event_add_notifier(C, NC_CONSOLE|ND_CONSOLE_REPORT, NULL); } diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 3ef6e545dda..ffd1054d954 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -268,7 +268,7 @@ static int wm_operator_exec(bContext *C, wmOperator *op, int repeat) if(repeat==0) { if(op->type->flag & OPTYPE_REGISTER) - wm_operator_register(CTX_wm_manager(C), op); + wm_operator_register(C, op); else WM_operator_free(op); } @@ -374,7 +374,7 @@ static int wm_operator_invoke(bContext *C, wmOperatorType *ot, wmEvent *event, P ED_undo_push_op(C, op); if(ot->flag & OPTYPE_REGISTER) - wm_operator_register(wm, op); + wm_operator_register(C, op); else WM_operator_free(op); } @@ -697,7 +697,7 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand ED_undo_push_op(C, op); if(ot->flag & OPTYPE_REGISTER) - wm_operator_register(CTX_wm_manager(C), op); + wm_operator_register(C, op); else WM_operator_free(op); handler->op= NULL; diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 0bc35ffa9b2..5938677afe7 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -51,6 +51,7 @@ #include "BKE_global.h" #include "BKE_library.h" #include "BKE_mball.h" +#include "BKE_report.h" #include "BKE_utildefines.h" #include "BKE_packedFile.h" @@ -100,6 +101,22 @@ static void sound_init_listener(void) G.listener->dopplervelocity = 340.29f; } + +static void wm_init_reports(bContext *C) +{ + wmWindowManager *wm= CTX_wm_manager(C); + wm->reports= MEM_callocN(sizeof(ReportList), "wmReportList"); + BKE_reports_init(wm->reports, RPT_STORE); +} +static void wm_free_reports(bContext *C) +{ + wmWindowManager *wm= CTX_wm_manager(C); + BKE_reports_clear(wm->reports); + MEM_freeN(wm->reports); +} + + + /* only called once, for startup */ void WM_init(bContext *C) { @@ -124,6 +141,8 @@ void WM_init(bContext *C) /* get the default database, plus a wm */ WM_read_homefile(C, NULL); + wm_init_reports(C); /* reports cant be initialized before the wm */ + UI_init(); // clear_matcopybuf(); /* XXX */ @@ -256,6 +275,8 @@ void WM_exit(bContext *C) RNA_exit(); + wm_free_reports(C); + CTX_free(C); if(MEM_get_memory_blocks_in_use()!=0) { diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index d7cac82ef90..4dbe26bb79f 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -165,7 +165,7 @@ char *WM_operator_pystring(wmOperator *op) char *cstring, *buf; int first_iter=1; - BLI_dynstr_appendf(dynstr, "%s(", op->idname); + BLI_dynstr_appendf(dynstr, "bpy.ops.%s(", op->idname); iterprop= RNA_struct_iterator_property(op->ptr->type); diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h index e91cbe6b204..36219cf3743 100644 --- a/source/blender/windowmanager/wm.h +++ b/source/blender/windowmanager/wm.h @@ -47,7 +47,7 @@ extern void wm_check(bContext *C); extern void wm_clear_default_size(bContext *C); /* register to windowmanager for redo or macro */ -void wm_operator_register(wmWindowManager *wm, wmOperator *op); +void wm_operator_register(bContext *C, wmOperator *op); extern void wm_report_free(wmReport *report);