Class: RubyCurses::App

Inherits:
Object show all
Extended by:
Forwardable
Defined in:
lib/rbcurse/app.rb

Overview

This is the Application class which does the job of setting up the environment, and closing it at the end.

Defined Under Namespace

Classes: Stack

Instance Attribute Summary collapse

methods to create widgets easily collapse

positioning of components collapse

Instance Method Summary collapse

Constructor Details

#initialize(config = {}, &block) ⇒ App

TODO: i should be able to pass window coords here in config :title



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/rbcurse/app.rb', line 87

def initialize config={}, &block
  #$log.debug " inside constructor of APP #{config}  "
  @config = config
  @app_row = @app_col = 0
  @stack = [] # stack's coordinates
  @flowstack = []
  @variables = {}
  # if we are creating child objects then we will not use outer form. this object will manage
  @current_object = [] 
  @_system_commands = %w{ bind_global bind_component }

  init_vars
  $log.debug "XXX APP CONFIG: #{@config}  " if $log.debug? 
  run &block
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



75
76
77
# File 'lib/rbcurse/app.rb', line 75

def config
  @config
end

#formObject (readonly)

Returns the value of attribute form.



76
77
78
# File 'lib/rbcurse/app.rb', line 76

def form
  @form
end

#prompt_rowObject

the row on which to prompt user for any inputs



80
81
82
# File 'lib/rbcurse/app.rb', line 80

def prompt_row
  @prompt_row
end

#quit_key=(value) ⇒ Object (writeonly)

Sets the attribute quit_key

Parameters:

  • value

    the value to set the attribute quit_key to.



78
79
80
# File 'lib/rbcurse/app.rb', line 78

def quit_key=(value)
  @quit_key = value
end

#windowObject (readonly)

Returns the value of attribute window.



77
78
79
# File 'lib/rbcurse/app.rb', line 77

def window
  @window
end

Instance Method Details

#app_header(title, config = {}, &block) ⇒ Object



733
734
735
736
# File 'lib/rbcurse/app.rb', line 733

def app_header title, config={}, &block
  require 'rbcurse/applicationheader'
  header = ApplicationHeader.new @form, title, config, &block
end

#basiclist(*args, &block) ⇒ Object

create a readonly list



868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
# File 'lib/rbcurse/app.rb', line 868

def basiclist *args, &block
  require 'rbcurse/rbasiclistbox'
  config = {}
  #TODO check these
  events = [ :LEAVE, :ENTER, :ENTER_ROW, :LEAVE_ROW, :LIST_DATA_EVENT ]
  # TODO how to do this so he gets selected row easily
  block_event = :ENTER_ROW
  _process_args args, config, block_event, events
  # some guesses at a sensible height for listbox
  if !config.has_key? :height
    ll = 0
    ll = config[:list].length + 2 if config.has_key? :list
    config[:height] ||= ll
    config[:height] = 15 if config[:height] > 20
  end
  _position(config)
  # if no width given, expand to flows width
  config[:width] ||= @stack.last.width if @stack.last
  config[:width] ||= longest_in_list(config[:list])+2
  #config.delete :title
  #config[:default_values] = config.delete :choose
  config[:selection_mode] = :single unless config.has_key? :selection_mode
  useform = nil
  useform = @form if @current_object.empty?

  w = BasicListbox.new useform, config # NO BLOCK GIVEN
  if block_given?
    field.bind(block_event, &block)
  end
  return w
end

#bind_componentObject

message “Bound #str to #cmd ”



371
372
373
374
375
376
# File 'lib/rbcurse/app.rb', line 371

def bind_component
  # the idea here is to get the current component
  # and bind some keys to some methods.
  # however, how do we divine the methods we can map to
  # and also in some cases the components itself has multiple components
end

#bind_globalObject

bind a key to a method at global (form) level Note that individual component may be overriding this.



334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/rbcurse/app.rb', line 334

def bind_global
  opts = get_all_commands
  cmd = ask("Select a command (TAB for choices) : ", opts)
  if cmd.nil? || cmd == ""
    raw_message "Aborted."
    return
  end
  key = []
  str = ""
  raw_message "Enter one or 2 keys. Finish with ENTER. Enter first key:"
  #raw_message "Enter first key:"
  ch = @window.getchar()
  if [KEY_ENTER, 10, 13, ?\C-g.getbyte(0)].include? ch
    raw_message "Aborted."
    return
  end
  key << ch
  str << keycode_tos(ch)
  raw_message "Enter second key or hit return:"
  ch = @window.getchar()
  if ch == 3 || ch == ?\C-g.getbyte(0)
    raw_message "Aborted."
    return
  end
  if ch == 10 || ch == KEY_ENTER || ch == 13
  else
    key << ch
    str << keycode_tos(ch)
  end
  if !key.empty?
    raw_message "Binding #{cmd} to #{str} "
    key = key[0] if key.size == 1
    #@form.bind_key(key, cmd.to_sym) # not finding it, getting called by that comp
    @form.bind_key(key){ send(cmd.to_sym) }
  end
  #message "Bound #{str} to #{cmd} "
end

#blank(rows = 1, config = {}) ⇒ Object

creates a blank row



711
712
713
# File 'lib/rbcurse/app.rb', line 711

def blank rows=1, config={}
  @app_row += rows
end

#button(*args, &block) ⇒ Object



469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
# File 'lib/rbcurse/app.rb', line 469

def button *args, &block
  config = {}
  events = [ :PRESS,  :LEAVE, :ENTER ]
  block_event = :PRESS

  _process_args args, config, block_event, events
  config[:text] ||= config[:name]
  config.delete(:title)
  # flow gets precedence over stack
  _position(config)
  button = Button.new @form, config
  # shooz uses CHANGED, which is equivalent to our CHANGE. Our CHANGED means modified and exited
  if block
    button.bind(block_event, &block)
  end
  return button
end

#check(*args, &block) ⇒ Object

check button



558
559
560
561
562
563
564
565
566
567
568
569
570
# File 'lib/rbcurse/app.rb', line 558

def check *args, &block
  config = {}
  # TODO confirm events
  events = [ :PRESS,  :LEAVE, :ENTER ]
  block_event = :PRESS
  _process_args args, config, block_event, events
  _position(config)
  toggle = CheckBox.new @form, config
  if block
    toggle.bind(block_event, &block)
  end
  return toggle
end

#closeObject



128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/rbcurse/app.rb', line 128

def close
  $log.debug " INSIDE CLOSE, #{@stop_ncurses_on_close} "
  @window.destroy if !@window.nil?
  $log.debug " INSIDE CLOSE, #{@stop_ncurses_on_close} "
  if @stop_ncurses_on_close
    $tt.destroy  # added on 2011-10-9 since we created a window, but only hid it after use
    VER::stop_ncurses
    $log.debug " CLOSING NCURSES"
  end
  #p $error_message.value unless $error_message.value.nil?
  $log.debug " CLOSING APP"
  #end
end

#combo(*args, &block) ⇒ Object

creates a simple readonly table, that allows users to click on rows and also on the header. Header clicking is for column-sorting.



970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
# File 'lib/rbcurse/app.rb', line 970

def combo *args, &block
  require 'rbcurse/rcombo'
  config = {}
  events = [:PROPERTY_CHANGE, :LEAVE, :ENTER, :CHANGE, :ENTER_ROW, :PRESS ] # XXX
  block_event = nil
  _process_args args, config, block_event, events
  _position(config)
  # if no width given, expand to flows width
  config[:width] ||= @stack.last.width if @stack.last
  #config.delete :title
  useform = nil
  useform = @form if @current_object.empty?

  w = ComboBox.new useform, config # NO BLOCK GIVEN
  if block_given?
    @current_object << w
    yield_or_eval &block
    @current_object.pop
  end
  return w
end

#display_app_helpObject



302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
# File 'lib/rbcurse/app.rb', line 302

def display_app_help
  if respond_to? :help_text
    arr = help_text
  else
    arr = []
    arr << "    NO HELP SPECIFIED FOR APP "
    arr << "    "
    arr << "     --- General help ---          "
    arr << "    F10         -  exit application "
    arr << "    Alt-x       -  select commands  "
    arr << "    :           -  select commands  "
    arr << "    "
  end
  case arr
  when String
    arr = arr.split("\n")
  when Array
  end
  w = arr.max_by(&:length).length

  require 'rbcurse/extras/viewer'
  RubyCurses::Viewer.view(arr, :layout => [2, 10, [4+arr.size, 24].min, w+2],:close_key => KEY_RETURN, :title => "<Enter> to close", :print_footer => true) do |t|
  # you may configure textview further here.
  #t.suppress_borders true
  #t.color = :black
  #t.bgcolor = :white
  # or
  t.attr = :reverse
  end
end

#divider(*args, &block) ⇒ Object

divider used to resize neighbouring components TOTEST XXX



958
959
960
961
962
963
964
965
966
967
# File 'lib/rbcurse/app.rb', line 958

def divider *args, &block
  require 'rbcurse/extras/divider'
  config = {}
  events = [:PROPERTY_CHANGE, :LEAVE, :ENTER, :DRAG_EVENT  ] # # none really at present
  block_event = nil
  _process_args args, config, block_event, events
  useform = nil
  useform = @form if @current_object.empty?
  sb = Divider.new useform, config
end

#dock(labels, config = {}, &block) ⇒ Object

prints pine-like key labels



739
740
741
742
# File 'lib/rbcurse/app.rb', line 739

def dock labels, config={}, &block
  require 'rbcurse/keylabelprinter'
  klp = RubyCurses::KeyLabelPrinter.new @form, labels, config, &block
end

#field(*args, &block) ⇒ Object

process arguments based on datatype, perhaps making configuration of some components easier for caller avoiding too much boiler plate code

create a field



433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
# File 'lib/rbcurse/app.rb', line 433

def field *args, &block
  config = {}
  events = [ :CHANGED,  :LEAVE, :ENTER, :CHANGE ]
  block_event = :CHANGED # LEAVE, ENTER, CHANGE

  _process_args args, config, block_event, events
  config.delete(:title)
  _position config
  # hope next line doesn't bonk anything
  config[:display_length] ||= @stack.last.width if @stack.last # added here not sure 2010-11-17 18:43 
  field = Field.new @form, config
  # shooz uses CHANGED, which is equivalent to our CHANGE. Our CHANGED means modified and exited
  if block
    field.bind(block_event, &block)
  end
  return field
end

#flow(config = {}, &block) ⇒ Object

keep adding to right of previous and when no more space move down and continue fitting in. Useful for button positioning. Currently, we can use a second flow to get another row. TODO: move down when row filled TODO: align right, center



1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
# File 'lib/rbcurse/app.rb', line 1029

def flow config={}, &block
  @inflow = true
  mt =  config[:margin_top] || 0
  @app_row += mt
  col = @flowstack.last || @stack.last.margin || @app_col
  col += config[:margin] || 0
  @flowstack << col
  @flowcol = col
  #instance_eval &block if block_given?
  yield_or_eval &block if block_given? # modified 2010-11-17 20:36 
  @flowstack.pop
  @inflow = false if @flowstack.empty?
end

#get_all_commandsObject



295
296
297
298
299
300
301
# File 'lib/rbcurse/app.rb', line 295

def get_all_commands
  opts = @_system_commands.dup
  if respond_to? :get_commands
    opts.push(*get_commands())
  end
  opts
end

#get_bindingObject

used only by LiveConsole, if enables in an app, usually only during testing.



258
259
260
# File 'lib/rbcurse/app.rb', line 258

def get_binding
  return binding()
end

#get_command_from_user(choices = ["quit"]) ⇒ Object

prompts user for a command. we need to get this back to the calling app or have some block stuff TODO Actually, this is naive, you would want to pass some values in like current data value or lines ?? Also may want command completion, or help so all commands can be displayed



382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
# File 'lib/rbcurse/app.rb', line 382

def get_command_from_user choices=["quit"]
  #code, str = rbgetstr(@window, $lastline, 0, "", 80, :default => ":")
  #return unless code == 0
        @_command_history ||= Array.new
  str = ask("Cmd: ", choices) { |q| q.default = @_previous_command; q.history = @_command_history }
          @_command_history << str unless @_command_history.include? str
  # shell the command
  if str =~ /^!/
    str = str[1..-1]
    suspend(false) { 
      #system(str); 
      $log.debug "XXX STR #{str}  " if $log.debug? 

      output=`#{str}`
      system("echo ' ' ");
      $log.debug "XXX output #{output} " if $log.debug? 
      system("echo '#{output}' ");
      system("echo Press Enter to continue.");
      system("read"); 
    }
    return nil # i think
  else
    # TODO
    # here's where we can take internal commands
    #alert "[#{str}] string did not match :!"
    str = str.to_s #= str[1..-1]
    cmdline = str.split
    cmd = cmdline.shift #.to_sym
    return unless cmd # added 2011-09-11 FFI
    if respond_to?(cmd, true)
      if cmd == "close"
        throw :close # other seg faults in del_panel window.destroy executes 2x
      else
        send cmd, *cmdline
      end
    else
      alert "#{self.class} does not respond to #{cmd} "
      ret = false
      ret = execute_this(cmd, *cmdline) if respond_to?(:execute_this, true)
      say_with_pause("#{self.class} does not respond to #{cmd} ", :color_pair => $promptcolor) unless ret
      # should be able to say in red as error
    end
  end
end

#hline(config = {}) ⇒ Object

displays a horizontal line takes col (column to start from) from current stack take row from app_row

requires width to be passed in config, else defaults to 20

Examples:

hline :width => 55  


721
722
723
724
725
726
727
728
729
730
731
732
# File 'lib/rbcurse/app.rb', line 721

def hline config={}
  row = config[:row] || @app_row
  width = config[:width] || 20
  _position config
  col = config[:col] || 1
  @color_pair = config[:color_pair] || $datacolor
  @attrib = config[:attrib] || Ncurses::A_NORMAL
  @window.attron(Ncurses.COLOR_PAIR(@color_pair) | @attrib)
  @window.mvwhline( row, col, FFI::NCurses::ACS_HLINE, width)
  @window.attron(Ncurses.COLOR_PAIR(@color_pair) | @attrib)
  @app_row += 1
end

#init_varsObject



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/rbcurse/app.rb', line 102

def init_vars
  @quit_key ||= FFI::NCurses::KEY_F10
  # actually this should be maintained inside ncurses pack, so not loaded 2 times.
  # this way if we call an app from existing program, App won't start ncurses.
  unless $ncurses_started
    init_ncurses
  end
  $lastline = Ncurses.LINES - 1
  @message_row = Ncurses.LINES-1
  @prompt_row = @message_row # hope to use for ask etc
  unless $log
    path = File.join(ENV["LOGDIR"] || "./" ,"rbc13.log")
    file   = File.open(path, File::WRONLY|File::TRUNC|File::CREAT) 
    $log = Logger.new(path)
    $log.level = Logger::DEBUG # change to warn when you've tested your app.
    colors = Ncurses.COLORS
    $log.debug "START #{colors} colors  --------- #{$0} win: #{@window} "
  end
=begin
# trying without 2011-10-8 
    require 'rbcurse/extras/stdscrwindow'
    awin = StdscrWindow.new
    $tt.window = awin; $tt.message_row = @message_row
=end

end

#keypress(&block) ⇒ Object

returns a symbol of the key pressed e.g. :C_c for Ctrl-C :Space, :bs, :M_d etc



181
182
183
# File 'lib/rbcurse/app.rb', line 181

def keypress &block
 @keyblock = block
end

#label(*args) ⇒ Object Also known as: text

instance_eval &block if block_given? or colorlabel = Label.new @form, => “Select a color:”, “row” => row, “col” => col, “color”=>“cyan”, “mnemonic” => ‘S’ var = RubyCurses::Label.new @form, => $results, “row” => r, “col” => fc



456
457
458
459
460
461
462
463
464
465
466
467
# File 'lib/rbcurse/app.rb', line 456

def label *args
  events = block_event = nil
  config = {}
  _process_args args, config, block_event, events
  config[:text] ||= config[:name]
  config[:height] ||= 1
  config.delete(:title)
  _position(config)
  label = Label.new @form, config
  # shooz uses CHANGED, which is equivalent to our CHANGE. Our CHANGED means modified and exited
  return label
end


744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
# File 'lib/rbcurse/app.rb', line 744

def link *args, &block
  require 'rbcurse/extras/rlink'
  config = {}
  events = [ :PRESS,  :LEAVE, :ENTER ]
  block_event = :PRESS
  _process_args args, config, block_event, events
  _position(config)
  config[:text] ||= config.delete :title
  config[:highlight_foreground] = "yellow"
  config[:highlight_background] = "red"
  toggle = Link.new @form, config
  if block
    toggle.bind(block_event, toggle, &block)
  end
  return toggle
end

#list_box(*args, &block) ⇒ Object

create a list Since we are mouseless, one can traverse without selection. So we have a different way of selecting row/s and traversal. XXX this aspect of LB’s has always troubled me hugely.



490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
# File 'lib/rbcurse/app.rb', line 490

def list_box *args, &block
  config = {}
  # TODO confirm events
  # listdataevent has interval added and interval removed, due to multiple
  # selection, we have to make that simple for user here.
  events = [ :LEAVE, :ENTER, :ENTER_ROW, :LEAVE_ROW, :LIST_DATA_EVENT ]
  # TODO how to do this so he gets selected row easily
  block_event = :ENTER_ROW

  _process_args args, config, block_event, events
  # naive defaults, since list could be large or have very long items
  # usually user will provide
  if !config.has_key? :height
    ll = 0
    ll = config[:list].length + 2 if config.has_key? :list
    config[:height] ||= ll
    config[:height] = 15 if config[:height] > 20
  end
  if @current_object.empty?
    $log.debug "1 APP LB w: #{config[:width]} ,#{config[:name]} "
    config[:width] ||= @stack.last.width if @stack.last
    $log.debug "2 APP LB w: #{config[:width]} "
    config[:width] ||= longest_in_list(config[:list])+2
    $log.debug "3 APP LB w: #{config[:width]} "
  end
  # if no width given, expand to flows width XXX SHOULD BE NOT EXPAND ?
  #config[:width] ||= @stack.last.width if @stack.last
  #if config.has_key? :choose
  config[:default_values] = config.delete :choose
  # we make the default single unless specified
  config[:selection_mode] = :single unless config.has_key? :selection_mode
  if @current_object.empty?
  if @instack
    # most likely you won't have row and col. should we check or just go ahead
    col = @stack.last.margin
    config[:row] = @app_row
    config[:col] = col
    @app_row += config[:height] # this needs to take into account height of prev object
  end
  end
  useform = nil
  useform = @form if @current_object.empty?
  field = Listbox.new useform, config
  # shooz uses CHANGED, which is equivalent to our CHANGE. Our CHANGED means modified and exited
  if block
    # this way you can't pass params to the block
    field.bind(block_event, &block)
  end
  return field
end

#loggerObject

# trying without 2011-10-8

require 'rbcurse/extras/stdscrwindow'
awin = StdscrWindow.new
$tt.window = awin; $tt.message_row = @message_row


127
# File 'lib/rbcurse/app.rb', line 127

def logger; return $log; end

#loop(&block) ⇒ Object

not sure, but user shuld be able to trap keystrokes if he wants but do i still call handle_key if he does, or give him total control. But loop is already called by framework



144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/rbcurse/app.rb', line 144

def loop &block
  @form.repaint
  @window.wrefresh
  Ncurses::Panel.update_panels
  while((ch = @window.getchar()) != @quit_key )
    str = keycode_tos ch
    @keyblock.call(str.gsub(/-/, "_").to_sym) if @keyblock
    $log.debug  "#{ch} got (#{str})"
    yield ch if block # <<<----
    @form.handle_key ch
    @form.repaint
    @window.wrefresh
  end
end

#master_detail(*args, &block) ⇒ Object



899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
# File 'lib/rbcurse/app.rb', line 899

def master_detail *args, &block
  require 'rbcurse/extras/masterdetail'
  config = {}
  events = [:PROPERTY_CHANGE, :LEAVE, :ENTER ]
  block_event = nil
  _process_args args, config, block_event, events
  #config[:height] ||= 10
  _position(config)
  # if no width given, expand to flows width
  config[:width] ||= @stack.last.width if @stack.last
  #config.delete :title
  useform = nil
  useform = @form if @current_object.empty?

  w = MasterDetail.new useform, config # NO BLOCK GIVEN
  if block_given?
    @current_object << w
    yield_or_eval &block
    @current_object.pop
  end
  return w
end

menu bar



705
706
707
708
# File 'lib/rbcurse/app.rb', line 705

def menubar &block
  require 'rbcurse/rmenu'
  RubyCurses::MenuBar.new &block
end


760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
# File 'lib/rbcurse/app.rb', line 760

def menulink *args, &block
  require 'rbcurse/extras/rmenulink'
  config = {}
  events = [ :PRESS,  :LEAVE, :ENTER ]
  block_event = :PRESS
  _process_args args, config, block_event, events
  _position(config)
  config[:text] ||= config.delete :title
  config[:highlight_foreground] = "yellow"
  config[:highlight_background] = "red"
  toggle = MenuLink.new @form, config
  if block
    toggle.bind(block_event, toggle, &block)
  end
  return toggle
end

#message(text) ⇒ Object

updates a global var with text. Calling app has to set up a Variable with that name and attach to a label so it can be printed.



186
187
188
189
# File 'lib/rbcurse/app.rb', line 186

def message text
  $status_message.value = text # trying out 2011-10-9 
  @message.value = text
end

#message_immediate(text) ⇒ Object

during a process, when you wish to update status, since ordinarily the thread is busy and form does not get control back, so the window won’t refresh. NOTE: use this only if message is not working XXX Not sure if this is working after move to ffi-ncurses, check the demos



198
199
200
201
202
203
204
205
# File 'lib/rbcurse/app.rb', line 198

def message_immediate text
  $status_message.value = text # trying out 2011-10-9 user needs to use in statusline command
  message text
  if @message_label
    @message_label.repaint
    @window.refresh
  end
end

#message_row(row) ⇒ Object



190
191
192
193
# File 'lib/rbcurse/app.rb', line 190

def message_row row
  raise "Please use create_message_label first as message_label is no longer default behaviour" unless @message_label
  @message_label.row = row 
end

#multisplit(*args, &block) ⇒ Object



801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
# File 'lib/rbcurse/app.rb', line 801

def multisplit *args, &block
  require 'rbcurse/rmultisplit'
  config = {}
  events = [ :PROPERTY_CHANGE,  :LEAVE, :ENTER ]
  block_event = events[0]
  _process_args args, config, block_event, events
  _position(config)
  # if no width given, expand to flows width
  config[:width] ||= @stack.last.width if @stack.last
  config.delete :title
  useform = nil
  useform = @form if @current_object.empty?

  w = MultiSplit.new useform, config
  #if block
    #w.bind(block_event, w, &block)
  #end
  if block_given?
    @current_object << w
    #instance_eval &block if block_given?
    yield w
    @current_object.pop
  end
  return w
end

#progress(*args, &block) ⇒ Object

progress bar



637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
# File 'lib/rbcurse/app.rb', line 637

def progress *args, &block
  require 'rbcurse/rprogress'
  config = {}
  # TODO confirm events many more
  events = [ :CHANGE,  :LEAVE, :ENTER ]
  block_event = nil
  _process_args args, config, block_event, events
  config[:width] = config[:display_length] || 10 unless config.has_key? :width
  _position(config)
  w = Progress.new @form, config
  #if block
    #w.bind(block_event, &block)
  #end
  return w
end

#radio(*args, &block) ⇒ Object

radio button



572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
# File 'lib/rbcurse/app.rb', line 572

def radio *args, &block
  config = {}
  # TODO confirm events
  events = [ :PRESS,  :LEAVE, :ENTER ]
  block_event = :PRESS
  _process_args args, config, block_event, events
  a = config[:group]
  # FIXME we should check if user has set a varialbe in :variable.
  # we should create a variable, so he can use it if he wants.
  if @variables.has_key? a
    v = @variables[a]
  else
    v = Variable.new
    @variables[a] = v
  end
  config[:variable] = v
  config.delete(:group)
  _position(config)
  radio = RadioButton.new @form, config
  if block
    radio.bind(block_event, &block)
  end
  return radio
end

#raw_message(text) ⇒ Object

Deprecated.

since it uses stdscr. Use say_with_pause or use rdialogs status_window, see test2.rb

NOTE XXX using stdscr results in the screen going black if a dialog or other window is popped up, this was great but has not worked out. print directly onto stdscr so that form or window does not require repainting and cursor not messed. however, once form paints then this will be overwritten so at end of printing raw_messages, use message() for final status. Usage: application is inside a long processing loop and wishes to print ongoing status (similar to message_immediate) but faster and less involved



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/rbcurse/app.rb', line 214

def raw_message text
  $log.warn "WARNING: don't use this method as it uses stdscr. Use rdialogs statuswindow."
  row = @message_label ? @message_label.row : Ncurses.LINES-1
  # experimentally trying stdscr instead of label
  scr = FFI::NCurses.stdscr
  text = "%-80s" % text
  Ncurses.mvprintw row ,0, text
  #@_stext ||= ""
  #@_stext <<  text
  ## appending is quite a pain, maybe we should make it separate.
  #stext = "%-80s" % @_stext
  #Ncurses.mvprintw row ,0, stext[-80..-1]
  #scr.refresh() # NW w FFI XXX
  #FFI::NCurses.refresh
end

#raw_progress(arg) ⇒ Object

Deprecated.
  • don’t use stdscr at all, use rdialogs status_window (see test2.rb)

shows a simple progress bar on last row, using stdscr If Array of two numbers is given then also print part/total on left of bar

Parameters:

  • percentage, (Float, Array<Fixnum,Fixnum>)

    or part/total



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/rbcurse/app.rb', line 233

def raw_progress arg
  $log.warning "WARNING: don't use this method as it uses stdscr"
  row = @message_label ? @message_label.row : Ncurses.LINES-1
  s = nil
  case arg
  when Array
    #calculate percentage
    pc = (arg[0]*1.0)/arg[1]
    # print items/total also
    s = "%-10s" % "(#{arg[0]}/#{arg[1]})"
  when
    Float
    pc = arg
  end
  scr = Ncurses.stdscr
  endcol = Ncurses.COLS-1
  startcol = endcol - 12
  stext = ("=" * (pc*10).to_i) 
  text = "[" + "%-10s" % stext + "]"
  Ncurses.mvprintw( row ,startcol-10, s) if s
  Ncurses.mvprintw row ,startcol, text
  #scr.refresh() # XXX FFI NW

end

#safe_loop(&block) ⇒ Object

if calling loop separately better to call this, since it will shut off ncurses and print error on screen.



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/rbcurse/app.rb', line 160

def safe_loop &block
  begin
    loop &block
  rescue => ex
    $log.debug( "APP.rb rescue reached ")
    $log.debug( ex) if ex
    $log.debug(ex.backtrace.join("\n")) if ex
  ensure
    close
    # putting it here allows it to be printed on screen, otherwise it was not showing at all.
    if ex
      puts "========== EXCEPTION =========="
      p ex 
      puts "==============================="
      puts(ex.backtrace.join("\n")) 
    end
  end
end

#scrollbar(*args, &block) ⇒ Object

scrollbar attached to the right of a parent object



946
947
948
949
950
951
952
953
954
955
956
# File 'lib/rbcurse/app.rb', line 946

def scrollbar *args, &block
  require 'rbcurse/extras/scrollbar'
  config = {}
  events = [:PROPERTY_CHANGE, :LEAVE, :ENTER  ] # # none really at present
  block_event = nil
  _process_args args, config, block_event, events
  raise "parent needed for scrollbar" if !config.has_key? :parent
  useform = nil
  useform = @form if @current_object.empty?
  sb = Scrollbar.new useform, config
end

#splitpane(*args, &block) ⇒ Object



776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
# File 'lib/rbcurse/app.rb', line 776

def splitpane *args, &block
  require 'rbcurse/rsplitpane2'
  config = {}
  events = [ :PROPERTY_CHANGE,  :LEAVE, :ENTER ]
  block_event = events[0]
  _process_args args, config, block_event, events
  _position(config)
  # if no width given, expand to flows width
  config[:width] ||= @stack.last.width if @stack.last
  config.delete :title
  useform = nil
  useform = @form if @current_object.empty?

  w = SplitPane.new useform, config
  #if block
    #w.bind(block_event, w, &block)
  #end
  if block_given?
    @current_object << w
    #instance_eval &block if block_given?
    yield w
    @current_object.pop
  end
  return w
end

#stack(config = {}, &block) ⇒ Object



1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
# File 'lib/rbcurse/app.rb', line 1004

def stack config={}, &block
  @instack = true
  mt =  config[:margin_top] || 1
  mr =  config[:margin] || 0
  # must take into account margin
  defw = Ncurses.COLS - mr
  config[:width] = defw if config[:width] == :EXPAND
  w =   config[:width] || [50, defw].min
  s = Stack.new(mt, mr, w)
  @app_row += mt
  mr += @stack.last.margin if @stack.last
  #@stack << mr
  @stack << s
  #instance_eval &block if block_given?
          yield_or_eval &block if block_given? # modified 2010-11-17 20:36 
  @stack.pop
  @instack = false if @stack.empty?
  @app_row = 0 if @stack.empty?
end

#subtitle(string, config = {}) ⇒ Object

print a sutitle on second row



701
702
703
# File 'lib/rbcurse/app.rb', line 701

def subtitle string, config={}
  @window.printstring 2, 30, string, $datacolor, 'normal'
end

#suspend(clear = true) ⇒ Object

suspends curses so you can play around on the shell or in cooked mode like Vim does. Expects a block to be passed. Purpose: you can print some stuff without creating a window, or just run shell commands without coming out. NOTE: if you pass clear as true, then the screen will be cleared and you can use puts or print to print. You may have to flush. However, with clear as false, the screen will not be cleared. You will have to print using printw, and if you expect user input you must do a “system /bin/stty sane” If you print stuff, you will have to put a getch() or system(“read”) to pause the screen.



273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/rbcurse/app.rb', line 273

def suspend clear=true
  return unless block_given?
  Ncurses.def_prog_mode
  if clear
    Ncurses.endwin 
    # NOTE: avoid false since screen remains half off
    # too many issues
  else
    system "/bin/stty sane"
  end
  yield if block_given?
  Ncurses.reset_prog_mode
  if !clear
    # Hope we don't screw your terminal up with this constantly.
    VER::stop_ncurses
    VER::start_ncurses  
    #@form.reset_all # not required
  end
  @form.repaint
  @window.wrefresh
  Ncurses::Panel.update_panels
end

#table(*args, &block) ⇒ Object

table widget

Examples:

data = [["Roger",16,"SWI"], ["Phillip",1, "DEU"]]
colnames = ["Name", "Wins", "Place"]
t = table :width => 40, :height => 10, :columns => colnames, :data => data, :estimate_widths => true
  other options are :column_widths => [12,4,12]
  :size_to_fit => true


660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
# File 'lib/rbcurse/app.rb', line 660

def table *args, &block
  require 'rbcurse/rtable'
  config = {}
  # TODO confirm events many more
  events = [ :ENTER_ROW,  :LEAVE, :ENTER ]
  block_event = events[0]
  _process_args args, config, block_event, events
  # if user is leaving out width, then we don't want it in config
  # else Widget will put a value of 10 as default, overriding what we've calculated
  if config.has_key? :display_length
    config[:width] = config[:display_length] unless config.has_key? :width
  end
  ext = config.delete :extended_keys

  model = nil
  _position(config)
  # if no width given, expand to flows width
  config[:width] ||= @stack.last.width if @stack.last
  w = Table.new @form, config
  if ext
    require 'rbcurse/extras/tableextended' 
      # so we can increase and decrease column width using keys
    w.extend TableExtended
    w.bind_key(?w){ w.next_column }
    w.bind_key(?b){ w.previous_column }
    w.bind_key(?+) { w.increase_column }
    w.bind_key(?-) { w.decrease_column }
    w.bind_key([?d, ?d]) { w.table_model.delete_at w.current_index }
    w.bind_key(?u) { w.table_model.undo w.current_index}
  end
  if block
    w.bind(block_event, &block)
  end
  return w
end

#tabular_widget(*args, &block) ⇒ Object

creates a simple readonly table, that allows users to click on rows and also on the header. Header clicking is for column-sorting.



923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
# File 'lib/rbcurse/app.rb', line 923

def tabular_widget *args, &block
  require 'rbcurse/extras/tabularwidget'
  config = {}
  events = [:PROPERTY_CHANGE, :LEAVE, :ENTER, :CHANGE, :ENTER_ROW, :PRESS ]
  block_event = nil
  _process_args args, config, block_event, events
  config[:height] ||= 10 # not sure if this should be here
  _position(config)
  # if no width given, expand to stack width
  config[:width] ||= @stack.last.width if @stack.last
  #config.delete :title
  useform = nil
  useform = @form if @current_object.empty?

  w = TabularWidget.new useform, config # NO BLOCK GIVEN
  if block_given?
    @current_object << w
    yield_or_eval &block
    @current_object.pop
  end
  return w
end

#textarea(*args, &block) ⇒ Object

editable text area



597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
# File 'lib/rbcurse/app.rb', line 597

def textarea *args, &block
  require 'rbcurse/rtextarea'
  config = {}
  # TODO confirm events many more
  events = [ :CHANGE,  :LEAVE, :ENTER ]
  block_event = events[0]
  _process_args args, config, block_event, events
  config[:width] = config[:display_length] unless config.has_key? :width
  _position(config)
  # if no width given, expand to flows width
  config[:width] ||= @stack.last.width if @stack.last
  useform = nil
  useform = @form if @current_object.empty?
  w = TextArea.new useform, config
  if block
    w.bind(block_event, &block)
  end
  return w
end

#textview(*args, &block) ⇒ Object



616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
# File 'lib/rbcurse/app.rb', line 616

def textview *args, &block
  require 'rbcurse/rtextview'
  config = {}
  # TODO confirm events many more
  events = [ :PRESS, :LEAVE, :ENTER ]
  block_event = events[0]
  _process_args args, config, block_event, events
  config[:width] = config[:display_length] unless config.has_key? :width
  _position(config)
  # if no width given, expand to flows width
  config[:width] ||= @stack.last.width if @stack.last
  raise "height needed for textview" if !config.has_key? :height
  useform = nil
  useform = @form if @current_object.empty?
  w = TextView.new useform, config
  if block
    w.bind(block_event, &block)
  end
  return w
end

#title(string, config = {}) ⇒ Object

print a title on first row



696
697
698
699
# File 'lib/rbcurse/app.rb', line 696

def title string, config={}
  ## TODO center it
  @window.printstring 1, 30, string, $normalcolor, 'reverse'
end

#toggle(*args, &block) ⇒ Object

toggle button



542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
# File 'lib/rbcurse/app.rb', line 542

def toggle *args, &block
  config = {}
  # TODO confirm events
  events = [ :PRESS,  :LEAVE, :ENTER ]
  block_event = :PRESS
  _process_args args, config, block_event, events
  config[:text] ||= longest_in_list2( [config[:onvalue], config[:offvalue]])
    #config[:onvalue] # needed for flow, we need a better way FIXME
  _position(config)
  toggle = ToggleButton.new @form, config
  if block
    toggle.bind(block_event, &block)
  end
  return toggle
end

#tree(*args, &block) ⇒ Object



826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
# File 'lib/rbcurse/app.rb', line 826

def tree *args, &block
  require 'rbcurse/rtree'
  config = {}
  events = [:TREE_WILL_EXPAND_EVENT, :TREE_EXPANDED_EVENT, :TREE_SELECTION_EVENT, :PROPERTY_CHANGE, :LEAVE, :ENTER ]
  block_event = nil
  _process_args args, config, block_event, events
  config[:height] ||= 10
  _position(config)
  # if no width given, expand to flows width
  config[:width] ||= @stack.last.width if @stack.last
  #config.delete :title
  useform = nil
  useform = @form if @current_object.empty?

  w = Tree.new useform, config, &block
  return w
end

#vimsplit(*args, &block) ⇒ Object



843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
# File 'lib/rbcurse/app.rb', line 843

def vimsplit *args, &block
  require 'rbcurse/rvimsplit'
  config = {}
  #TODO check these
  events = [:PROPERTY_CHANGE, :LEAVE, :ENTER ]
  block_event = nil
  _process_args args, config, block_event, events
  config[:height] ||= 10
  _position(config)
  # if no width given, expand to flows width
  config[:width] ||= @stack.last.width if @stack.last
  #config.delete :title
  useform = nil
  useform = @form if @current_object.empty?

  w = VimSplit.new useform, config # NO BLOCK GIVEN
  if block_given?
    @current_object << w
    #instance_eval &block if block_given?
    yield w
    @current_object.pop
  end
  return w
end