dotar/vim/autoload/railmoon/widget/base.vim
2011-11-17 16:00:49 -06:00

572 lines
15 KiB
VimL
Executable File

" Author: Mykola Golubyev ( Nickolay Golubev )
" Email: golubev.nikolay@gmail.com
" Site: www.railmoon.com
" Module: railmoon#widget#base
" Purpose: base widget functionality and vim windows triggers handler
" -
" [ internal usage ]
" Purpose: collect widget that will be closed
" -
let s:widget_for_close = []
function! s:clear_widget_for_close()
let s:widget_for_close = []
endfunction
function! s:widget_present(list, widget)
for widget in a:list
if widget.id == a:widget.id
return 1
endif
endfor
return 0
endfunction
function! s:add_widget_for_close(widget)
if s:widget_present(s:widget_for_close, a:widget)
return
endif
call add(s:widget_for_close, a:widget)
endfunction
function! s:remove_widget_from_delete(widget)
if ! s:widget_present(s:widget_for_close, a:widget)
return
endif
call filter(s:widget_for_close, 'v:val.id != '.a:widget.id)
endfunction
let s:buffer_name_prefix = 'rmwidget'
let s:in_create_widget_state = 0
" -
" [ public for extend library function ]
" Name: railmoon#widget#base#create
" Purpose: create base widget
" [ parameters ]
" name widget window name
" titlename name that will on title bar
" child widget that inherit base
" child_callback_object call back object with following methods that child can
" react on
" on_close
" on_close_with_tab_page
" on_focus
" on_focus_lost
" on_setup
" callback_object user defined call back object
" -
function! railmoon#widget#base#create(name, titlename, child, child_callback_object, callback_object)
call s:clear_widget_for_close()
let new_object = extend( deepcopy(s:base), deepcopy(a:child) )
call railmoon#trace#debug('create base:'.a:name)
call railmoon#trace#debug(string(new_object))
" if bufexists(escaped_name)
" throw 'widget:base:buffer_exists'
" endif
let new_object.id = railmoon#id#acquire('railmoon_widget_id')
let new_object.name = a:name
let new_object.titlename = a:titlename
let new_object.child_callback_object = a:child_callback_object
let new_object.callback_object = a:callback_object
let new_object.is_closed = 0
let buffer_name = s:buffer_name_prefix.new_object.id
let s:in_create_widget_state = 1
let buffer_number = bufnr( buffer_name )
if -1 == buffer_number
exec 'silent edit '.buffer_name
else
exec 'silent buffer '.buffer_number
endif
exec 'setlocal statusline='.escape(new_object.name, ' ')
setlocal noreadonly
let w:widget_id = new_object.id
let w:widget = new_object
let b:widget_id = new_object.id
let b:widget = new_object
call s:buffer_auto_command_setup()
call s:buffer_setup()
let s:in_create_widget_state = 0
return new_object
endfunction
" -
" [ internal usage ]
" Name: railmoon#widget#base#call_back
" Purpose: call back method for handlers
" -
function! railmoon#widget#base#call_back(widget, method_name, ... )
let arguments = join(a:000, ';')
call railmoon#trace#debug('call back for widget. name:'. a:widget.name. '; method:'.a:method_name.' ; arguments:'.arguments)
let exec_line = 'call object.'.a:method_name.'('.arguments.')'
for object in [ a:widget.child_callback_object, a:widget.callback_object ]
if has_key(object, a:method_name)
call railmoon#trace#debug(exec_line)
exec exec_line
endif
endfor
endfunction
" -
" [ internal usage ]
" Name: buffer_option_setup
" Purpose: setup common local options for widget buffer
" -
function! s:buffer_option_setup()
setlocal noswapfile
setlocal nomodifiable
setlocal bufhidden=delete
setlocal buftype=nofile
setlocal nobuflisted
setlocal nonumber
setlocal nowrap
setlocal nocursorline
endfunction
function! s:redraw_widget()
if has_key(b:widget, 'draw')
call b:widget.draw()
endif
endfunction
" -
" [ internal usage ]
" Name: buffer_setup
" Purpose: when buffer needs to be resetup. after reopen in widget window
" -
function! s:buffer_setup()
call s:buffer_option_setup()
let b:widget = w:widget
let b:widget_id = w:widget_id
call s:set_widget_title()
if s:in_create_widget_state
return
endif
call railmoon#widget#base#call_back(b:widget, 'on_setup')
call s:redraw_widget()
endfunction
" -
" [ internal usage ]
" Name: buffer_auto_command_setup
" Purpose: setup common handlers for window triggers
" -
function! s:buffer_auto_command_setup()
autocmd! * <buffer>
autocmd BufWinLeave <buffer> call s:on_buffer_win_leave()
autocmd WinEnter <buffer> call s:on_window_enter()
autocmd WinLeave <buffer> call s:on_window_leave()
autocmd TabLeave <buffer> call s:on_tab_leave()
endfunction
" -
" [ internal usage ]
" Name: auto_command_setup
" Purpose: setup handlers for any window or buffer to resolve conflicts
" -
function! s:auto_command_setup()
augroup base_widget_autocommands
autocmd!
autocmd WinEnter * call s:on_any_window_enter()
autocmd BufEnter * call s:on_any_buffer_enter()
autocmd BufWinEnter * call s:on_any_buffer_window_enter()
" autocmd BufWinLeave * call s:on_any_buffer_win_leave()
augroup END
endfunction
function! s:widget_in_auto_command()
let buffer_name = expand('<afile>')
let widget = getbufvar(buffer_name, 'widget')
if empty(widget)
throw 'widget not found!!!! TODO'
endif
return widget
endfunction
function! s:on_any_buffer_window_enter()
call railmoon#trace#push('on_any_buffer_window_enter')
try
let buffer_name = expand('<afile>')
call railmoon#trace#debug('name: '.buffer_name)
if empty(buffer_name) " TODO
return
endif
if ! railmoon#widget#handle_autocommands()
call railmoon#trace#debug('handle autocommands stoped')
return
endif
if exists('w:widget_id') && !exists('b:widget_id')
call railmoon#trace#debug('w:widget_id present but b:widget_id not')
call s:remove_widget_from_delete(w:widget)
let buffer_number = bufnr(s:buffer_name_prefix.w:widget_id)
let buffer_change_cmd = 'buffer '.buffer_number
call railmoon#trace#debug(buffer_change_cmd)
exec buffer_change_cmd
call s:buffer_setup()
return
endif
if exists('b:widget_id') && !exists('w:widget_id')
call railmoon#trace#debug('b:widget exists but w:widget not')
close
return
endif
catch /.*/
echo v:exception
call railmoon#trace#debug(v:exception)
finally
if exists('w:widget')
call s:remove_widget_from_delete(w:widget)
endif
call railmoon#trace#debug('...')
call railmoon#trace#pop()
endtry
endfunction
" -
" [ internal usage ]
" Name: on_buffer_win_leave
" Purpose: handle widget close event
" -
function! s:on_buffer_win_leave()
call railmoon#trace#push('on_buffer_win_leave')
try
let widget = s:widget_in_auto_command()
call railmoon#trace#debug('widget for close set up')
call s:add_widget_for_close(widget)
if widget.is_closed
call railmoon#trace#debug('already closed')
return
endif
let buffer_name = expand('<afile>')
" closed by close tab page
" TODO find out another cases
" when <afile> != %
if buffer_name != bufname('%')
let s:close_with_tab_page = 1
" TODO s:close_with_tab_page
else
let s:close_with_tab_page = 0
endif
finally
call s:close_ready_for_close_widgets()
call railmoon#trace#debug('...')
call railmoon#trace#pop()
endtry
endfunction
function! railmoon#widget#base#gui_tab_label()
if v:lnum == s:current_tab_page_number
return s:current_widget_name
endif
" let old_line = s:old_gui_tab_label
" let old_line = substitute(old_line, '%{\(.\{-}\)}', '\1', 'g')
" return eval(old_line)
return ''
endfunction
function! railmoon#widget#base#tab_line()
return s:current_widget_name
endfunction
function! s:set_widget_title()
let s:current_tab_page_number = tabpagenr()
let s:current_widget_name = b:widget.name
let s:current_widget_title_name = b:widget.titlename
if ! exists('s:old_title_string')
let s:old_title_string = &titlestring
let s:old_gui_tab_label = &guitablabel
let s:old_tab_line = &tabline
endif
let &titlestring = s:current_widget_title_name
set guitablabel=%{railmoon#widget#base#gui_tab_label()}
set tabline=%!railmoon#widget#base#tab_line()
exec 'setlocal statusline='.escape(s:current_widget_name, ' ')
endfunction
function! s:restore_original_title()
if exists('s:old_title_string')
let &titlestring = s:old_title_string
let &guitablabel = s:old_gui_tab_label
let &tabline = s:old_tab_line
unlet s:old_title_string
unlet s:old_gui_tab_label
endif
endfunction
" -
" [ internal usage ]
" Name: on_window_enter
" Purpose: handle gain focus event
" -
function! s:on_window_enter()
call railmoon#trace#push('on_window_enter')
try
let widget = s:widget_in_auto_command()
call s:set_widget_title()
call railmoon#widget#base#call_back(widget, 'on_focus')
catch /widget not found/
call railmoon#trace#debug(v:exception)
finally
call railmoon#trace#debug('...')
call railmoon#trace#pop()
endtry
endfunction
function! s:on_any_buffer_enter()
call railmoon#trace#push('on_any_buffer_enter')
try
if ! railmoon#widget#handle_autocommands()
call railmoon#trace#debug('handle autocommands stoped')
return
endif
let buffer_name = expand('<afile>')
call railmoon#trace#debug('buffer name:'.buffer_name)
call railmoon#trace#debug('in_create_widget_state:'.s:in_create_widget_state)
" attempt to edit file with name reserved to widget buffers
"
if !exists('b:widget_id') && ! s:in_create_widget_state
call railmoon#trace#debug('b:widget not found')
if buffer_name =~ s:buffer_name_prefix
call railmoon#trace#debug('name of widget buffer')
if buffer_name == expand('%')
call railmoon#trace#debug('open alternate window')
buffer #
endif
endif
"
" attempt to re open widget buffer
elseif exists('b:widget_id') && ! s:in_create_widget_state
call railmoon#widget#base#call_back(b:widget, 'on_setup')
call s:redraw_widget()
endif
finally
call railmoon#trace#debug('...')
call railmoon#trace#pop()
endtry
endfunction
function! s:close_ready_for_close_widgets()
call railmoon#trace#push('s:close_ready_for_close_widgets()')
try
for widget in s:widget_for_close
call railmoon#trace#debug('s:widget_for_close exists')
if widget.is_closed
call railmoon#trace#debug('already closed')
else
if s:close_with_tab_page
call railmoon#widget#base#call_back(widget, 'on_close_with_tab_page')
else
call railmoon#widget#base#call_back(widget, 'on_close')
endif
let widget.is_closed = 1
call railmoon#id#release('railmoon_widget_id', widget.id)
endif
endfor
call s:clear_widget_for_close()
finally
call railmoon#trace#debug('...')
call railmoon#trace#pop()
endtry
endfunction
function! s:on_any_window_enter()
call railmoon#trace#push('on_any_window_enter')
try
call railmoon#trace#debug('name: '.expand('<afile>'))
if ! railmoon#widget#handle_autocommands()
call railmoon#trace#debug('handle autocommands stoped')
return
endif
call s:close_ready_for_close_widgets()
" split or something like that
if exists('b:widget_id') && !exists('w:widget_id') && ! s:in_create_widget_state
call railmoon#trace#debug('b:widget_id present but w:widget_id not')
call railmoon#trace#debug('closing')
close
endif
if exists('w:widget_id') && !exists('b:widget_id')
call railmoon#trace#debug('w:widget_id present but b:widget_id not')
close
endif
finally
call railmoon#trace#debug('...')
call railmoon#trace#pop()
endtry
endfunction
" -
" [ internal usage ]
" Name: on_tab_leave
" Purpose: handle tab page lost focus event
" -
function! s:on_tab_leave()
call railmoon#trace#push('on_window_leave')
try
call railmoon#widget#base#call_back(b:widget, 'on_tab_leave')
finally
call railmoon#trace#debug('...')
call railmoon#trace#pop()
endtry
endfunction
" -
" [ internal usage ]
" Name: on_window_leave
" Purpose: handle lost focus event
" -
function! s:on_window_leave()
call railmoon#trace#push('on_window_leave')
try
if !exists('b:widget')
call railmoon#trace#debug('b:widget not present')
return
endif
if b:widget.is_closed
call railmoon#trace#debug('already closed')
return
endif
call railmoon#widget#base#call_back(b:widget, 'on_focus_lost')
catch /.*/
call railmoon#trace#debug(v:exception)
call railmoon#trace#debug(v:throwpoint)
finally
call s:restore_original_title()
call railmoon#trace#debug('...')
call railmoon#trace#pop()
endtry
endfunction
" -
" [ internal usage ]
" Name: base
" Purpose: base widget object
" -
let s:base = {}
function! s:base.select()
call railmoon#trace#push('base.select')
try
let id = self.id
call railmoon#trace#debug('selecting...')
call railmoon#widget#window#select(id)
finally
call railmoon#trace#debug('id = '.id)
call railmoon#trace#pop()
endtry
endfunction
function! s:base.close()
call railmoon#trace#push('base.close')
try
if self.is_closed
call railmoon#trace#debug('already closed')
return
endif
let not_active = (w:widget_id != self.id)
if not_active
let selected = railmoon#widget#window#save_selected()
endif
call self.select()
call railmoon#trace#debug('closing.. id = '.self.id)
let window_numbers = winnr('$')
let self.is_closed = 1
close
if not_active
call railmoon#widget#window#load_selected(selected)
endif
call railmoon#id#release('railmoon_widget_id', self.id)
if window_numbers > 1
call railmoon#widget#base#call_back(self, 'on_close')
else
call railmoon#widget#base#call_back(self, 'on_close_with_tab_page')
endif
finally
call s:close_ready_for_close_widgets()
call railmoon#trace#debug('...')
call railmoon#trace#pop()
endtry
endfunction
call s:auto_command_setup()