| Class | JSONTreeView |
| In: |
lib/json/editor.rb
|
| Parent: | Gtk::TreeView |
This class inherits from Gtk::TreeView, to configure it and to add a lot of behaviour to it.
| expanded | [RW] | Returns true, if nodes are autoexpanding, false otherwise. |
| window | [R] | Returns the MainWindow instance of this JSONTreeView. |
Creates a JSONTreeView instance, the parameter window is a MainWindow instance and used for self delegation.
# File lib/json/editor.rb, line 701
701: def initialize(window)
702: @window = window
703: super(TreeStore.new(Gdk::Pixbuf, String, String))
704: self.selection.mode = SELECTION_BROWSE
705:
706: @expanded = false
707: self.headers_visible = false
708: add_columns
709: add_popup_menu
710: end
Ask for an element to be appended parent.
# File lib/json/editor.rb, line 897
897: def ask_for_element(parent = nil, default_type = nil, value_text = @content)
898: type_input = value_input = nil
899:
900: dialog = Dialog.new(
901: "New element into #{parent ? parent.type : 'root'}",
902: nil, nil,
903: [ Stock::OK, Dialog::RESPONSE_ACCEPT ],
904: [ Stock::CANCEL, Dialog::RESPONSE_REJECT ]
905: )
906: hbox = HBox.new(false, 5)
907: hbox.pack_start(Label.new("Type:"), false)
908: hbox.pack_start(type_input = ComboBox.new(true))
909: default_active = 0
910: types = parent ? ALL_TYPES : CONTAINER_TYPES
911: types.each_with_index do |t, i|
912: type_input.append_text(t)
913: if t == default_type
914: default_active = i
915: end
916: end
917: type_input.active = default_active
918: dialog.vbox.pack_start(hbox, false)
919: type_input.signal_connect(:changed) do
920: configure_value(value_input, types[type_input.active])
921: end
922:
923: hbox = HBox.new(false, 5)
924: hbox.pack_start(Label.new("Value:"), false)
925: hbox.pack_start(value_input = Entry.new)
926: value_input.width_chars = 60
927: value_input.text = value_text if value_text
928: configure_value(value_input, types[type_input.active])
929:
930: dialog.vbox.pack_start(hbox, false)
931:
932: dialog.signal_connect('key-press-event''key-press-event', &DEFAULT_DIALOG_KEY_PRESS_HANDLER)
933: dialog.show_all
934: self.focus = dialog
935: dialog.run do |response|
936: if response == Dialog::RESPONSE_ACCEPT
937: type = types[type_input.active]
938: @content = case type
939: when 'Numeric'
940: Integer(value_input.text) rescue Float(value_input.text) rescue 0
941: else
942: value_input.text
943: end.to_s
944: return type, @content
945: end
946: end
947: return
948: ensure
949: dialog.destroy if dialog
950: end
Ask for a find term to search for in the tree. Returns the term as a string.
# File lib/json/editor.rb, line 988
988: def ask_for_find_term(search = nil)
989: dialog = Dialog.new(
990: "Find a node matching regex in tree.",
991: nil, nil,
992: [ Stock::OK, Dialog::RESPONSE_ACCEPT ],
993: [ Stock::CANCEL, Dialog::RESPONSE_REJECT ]
994: )
995: hbox = HBox.new(false, 5)
996:
997: hbox.pack_start(Label.new("Regex:"), false)
998: hbox.pack_start(regex_input = Entry.new)
999: hbox.pack_start(icase_checkbox = CheckButton.new('Icase'), false)
1000: regex_input.width_chars = 60
1001: if search
1002: regex_input.text = search.source
1003: icase_checkbox.active = search.casefold?
1004: end
1005:
1006: dialog.vbox.pack_start(hbox, false)
1007:
1008: dialog.signal_connect('key-press-event''key-press-event', &DEFAULT_DIALOG_KEY_PRESS_HANDLER)
1009: dialog.show_all
1010: self.focus = dialog
1011: dialog.run do |response|
1012: if response == Dialog::RESPONSE_ACCEPT
1013: begin
1014: return Regexp.new(regex_input.text, icase_checkbox.active? ? Regexp::IGNORECASE : 0)
1015: rescue => e
1016: Editor.error_dialog(self, "Evaluation of regex /#{regex_input.text}/ failed: #{e}!")
1017: return
1018: end
1019: end
1020: end
1021: return
1022: ensure
1023: dialog.destroy if dialog
1024: end
Ask for a hash key, value pair to be added to the Hash node parent.
# File lib/json/editor.rb, line 826
826: def ask_for_hash_pair(parent)
827: key_input = type_input = value_input = nil
828:
829: dialog = Dialog.new("New (key, value) pair for Hash", nil, nil,
830: [ Stock::OK, Dialog::RESPONSE_ACCEPT ],
831: [ Stock::CANCEL, Dialog::RESPONSE_REJECT ]
832: )
833: dialog.width_request = 640
834:
835: hbox = HBox.new(false, 5)
836: hbox.pack_start(Label.new("Key:"), false)
837: hbox.pack_start(key_input = Entry.new)
838: key_input.text = @key || ''
839: dialog.vbox.pack_start(hbox, false)
840: key_input.signal_connect(:activate) do
841: if parent.any? { |c| c.content == key_input.text }
842: toplevel.display_status('Key already exists in Hash!')
843: key_input.text = ''
844: else
845: toplevel.display_status('Key has been changed.')
846: end
847: end
848:
849: hbox = HBox.new(false, 5)
850: hbox.pack_start(Label.new("Type:"), false)
851: hbox.pack_start(type_input = ComboBox.new(true))
852: ALL_TYPES.each { |t| type_input.append_text(t) }
853: type_input.active = @type || 0
854: dialog.vbox.pack_start(hbox, false)
855:
856: type_input.signal_connect(:changed) do
857: value_input.editable = false
858: case ALL_TYPES[type_input.active]
859: when 'Array', 'Hash'
860: value_input.text = ''
861: when 'TrueClass'
862: value_input.text = 'true'
863: when 'FalseClass'
864: value_input.text = 'false'
865: when 'NilClass'
866: value_input.text = 'null'
867: else
868: value_input.text = ''
869: value_input.editable = true
870: end
871: end
872:
873: hbox = HBox.new(false, 5)
874: hbox.pack_start(Label.new("Value:"), false)
875: hbox.pack_start(value_input = Entry.new)
876: value_input.width_chars = 60
877: value_input.text = @value || ''
878: dialog.vbox.pack_start(hbox, false)
879:
880: dialog.signal_connect('key-press-event''key-press-event', &DEFAULT_DIALOG_KEY_PRESS_HANDLER)
881: dialog.show_all
882: self.focus = dialog
883: dialog.run do |response|
884: if response == Dialog::RESPONSE_ACCEPT
885: @key = key_input.text
886: type = ALL_TYPES[@type = type_input.active]
887: content = value_input.text
888: return @key, type, content
889: end
890: end
891: return
892: ensure
893: dialog.destroy
894: end
Ask for an order criteria for sorting, using x for the element in question. Returns the order criterium, and true/false for reverse sorting.
# File lib/json/editor.rb, line 955
955: def ask_for_order
956: dialog = Dialog.new(
957: "Give an order criterium for 'x'.",
958: nil, nil,
959: [ Stock::OK, Dialog::RESPONSE_ACCEPT ],
960: [ Stock::CANCEL, Dialog::RESPONSE_REJECT ]
961: )
962: hbox = HBox.new(false, 5)
963:
964: hbox.pack_start(Label.new("Order:"), false)
965: hbox.pack_start(order_input = Entry.new)
966: order_input.text = @order || 'x'
967: order_input.width_chars = 60
968:
969: hbox.pack_start(reverse_checkbox = CheckButton.new('Reverse'), false)
970:
971: dialog.vbox.pack_start(hbox, false)
972:
973: dialog.signal_connect('key-press-event''key-press-event', &DEFAULT_DIALOG_KEY_PRESS_HANDLER)
974: dialog.show_all
975: self.focus = dialog
976: dialog.run do |response|
977: if response == Dialog::RESPONSE_ACCEPT
978: return @order = order_input.text, reverse_checkbox.active?
979: end
980: end
981: return
982: ensure
983: dialog.destroy if dialog
984: end
Create a type node with content content, and add it to parent in the model. If parent is nil, create a new model and put it into the editor treeview.
# File lib/json/editor.rb, line 812
812: def create_node(parent, type, content)
813: iter = if parent
814: model.append(parent)
815: else
816: new_model = Editor.data2model(nil)
817: toplevel.view_new_model(new_model)
818: new_model.iter_first
819: end
820: iter.type, iter.content = type, content
821: expand_collapse(parent) if parent
822: iter
823: end
Expand or collapse row pointed to by iter according to the expanded attribute.
# File lib/json/editor.rb, line 1028
1028: def expand_collapse(iter)
1029: if expanded
1030: expand_row(iter.path, true)
1031: else
1032: collapse_row(iter.path)
1033: end
1034: end
# File lib/json/editor.rb, line 720
720: def add_columns
721: cell = CellRendererPixbuf.new
722: column = TreeViewColumn.new('Icon', cell,
723: 'pixbuf' => ICON_COL
724: )
725: append_column(column)
726:
727: cell = CellRendererText.new
728: column = TreeViewColumn.new('Type', cell,
729: 'text' => TYPE_COL
730: )
731: append_column(column)
732:
733: cell = CellRendererText.new
734: cell.editable = true
735: column = TreeViewColumn.new('Content', cell,
736: 'text' => CONTENT_COL
737: )
738: cell.signal_connect(:edited, &method(:cell_edited))
739: append_column(column)
740: end
# File lib/json/editor.rb, line 802
802: def add_popup_menu
803: menu = PopUpMenu.new(self)
804: menu.create
805: end
# File lib/json/editor.rb, line 755
755: def cell_edited(cell, path, value)
756: iter = model.get_iter(path)
757: case iter.type
758: when 'Key'
759: unify_key(iter, value)
760: toplevel.display_status('Key has been changed.')
761: when 'FalseClass'
762: value.downcase!
763: if value == 'true'
764: iter.type, iter.content = 'TrueClass', 'true'
765: end
766: when 'TrueClass'
767: value.downcase!
768: if value == 'false'
769: iter.type, iter.content = 'FalseClass', 'false'
770: end
771: when 'Numeric'
772: iter.content = (Integer(value) rescue Float(value) rescue 0).to_s
773: when 'String'
774: iter.content = value
775: when 'Hash', 'Array'
776: return
777: else
778: fail "Unknown type found in model: #{iter.type}"
779: end
780: window.change
781: end
# File lib/json/editor.rb, line 783
783: def configure_value(value, type)
784: value.editable = false
785: case type
786: when 'Array', 'Hash'
787: value.text = ''
788: when 'TrueClass'
789: value.text = 'true'
790: when 'FalseClass'
791: value.text = 'false'
792: when 'NilClass'
793: value.text = 'null'
794: when 'Numeric', 'String'
795: value.text ||= ''
796: value.editable = true
797: else
798: raise ArgumentError, "unknown type '#{type}' encountered"
799: end
800: end
# File lib/json/editor.rb, line 742
742: def unify_key(iter, key)
743: return unless iter.type == 'Key'
744: parent = iter.parent
745: if parent.any? { |c| c != iter and c.content == key }
746: old_key = key
747: i = 0
748: begin
749: key = sprintf("%s.%d", old_key, i += 1)
750: end while parent.any? { |c| c != iter and c.content == key }
751: end
752: iter.content = key
753: end