# Copyright 2014-2021, Trimble Inc. # # License: The MIT License (MIT) require "sketchup.rb" require "stringio" module Trimble module AttributeHelper PLUGIN = self class << self attr_reader :app_observer attr_reader :model_observer attr_reader :selection_observer end def self.visualize_selected content = self.traverse_selected html = self.wrap_content(content) options = { :dialog_title => "Attribute Visualizer", :preferences_key => 'AttributeVisualizer', :scrollable => true, :resizable => true, :height => 300, :width => 400, :left => 200, :top => 200 } @window ||= UI::WebDialog.new(options) @window.set_html(html) @window.set_on_close { @window = nil self.detach_observers } unless @window.visible? @window.show self.attach_observers end end def self.attach_observers @app_observer ||= AppObserver.new @model_observer ||= ModelObserver.new @selection_observer ||= SelectionObserver.new model = Sketchup.active_model Sketchup.remove_observer(@app_observer) model.remove_observer(@model_observer) model.selection.remove_observer(@selection_observer) Sketchup.add_observer(@app_observer) model.add_observer(@model_observer) model.selection.add_observer(@selection_observer) end def self.detach_observers Sketchup.remove_observer(@app_observer) Sketchup.active_model.remove_observer(@model_observer) Sketchup.active_model.selection.remove_observer(@selection_observer) end def self.traverse_selected html = StringIO.new model = Sketchup.active_model selection = model.selection if selection.empty? if model.active_path.nil? entity = model else entity = model.active_path.last end else return "Invalid selection size" unless selection.size == 1 entity = selection[0] end html.puts "

#{self.escape_html(entity)}

" if entity.respond_to?(:name) html.puts "

#{self.escape_html(entity.name)}

" end if entity.attribute_dictionaries entity.attribute_dictionaries.each { |dictionary| html.puts self.format_dictionary(dictionary) } else html.puts "No dictionaries" end if entity.is_a?(Sketchup::Group) definition = entity.entities.parent elsif entity.is_a?(Sketchup::ComponentInstance) definition = entity.definition else definition = nil end if definition && definition.attribute_dictionaries html.puts "

#{self.escape_html(definition)}

" html.puts "

#{self.escape_html(definition.name)}

" definition.attribute_dictionaries.each { |dictionary| html.puts self.format_dictionary(dictionary) } end html.string end def self.format_dictionary(dictionary, path = "") html_name = self.escape_html(dictionary.name) path = "#{path}:#{html_name}" html = StringIO.new html.puts "" html.puts "" html.puts "" dictionary.each { |key, value| html_key = self.escape_html(key) html_value = self.escape_html(value) node_path = "#{path}:#{html_key}" html.puts "" } if dictionary.attribute_dictionaries dictionary.attribute_dictionaries.each { |sub_dic| html.puts "" } end html.puts "" html.puts "
#{html_name}
#{html_key}#{html_value}#{value.class}
" html.puts self.format_dictionary(sub_dic, path) html.puts "
" html.string end def self.escape_html(data) data.to_s.gsub("&", "&").gsub("<", "<").gsub(">", ">") end def self.wrap_content(content) html = <<-EOT #{content} EOT end class SelectionObserver < Sketchup::SelectionObserver def onSelectionAdded(selection, element) selection_changed() end def onSelectionBulkChange(selection) selection_changed() end def onSelectionCleared(selection) selection_changed() end def onSelectionRemoved(selection, element) selection_changed() end private def selection_changed PLUGIN.visualize_selected end end # class SelectionObserver class ModelObserver < Sketchup::ModelObserver def onActivePathChanged(model) PLUGIN.visualize_selected end def onTransactionCommit(model) model_changed(model) end def onTransactionEmpty(model) model_changed(model) end def onTransactionRedo(model) model_changed(model) end def onTransactionUndo(model) model_changed(model) end private def model_changed(model) if @timer.nil? @timer = UI.start_timer(0.0, false) { @timer = nil PLUGIN.visualize_selected } end end end # class ModelObserver class AppObserver < Sketchup::AppObserver def onNewModel(model) observe_model(model) end def onOpenModel(model) observe_model(model) end private def observe_model(model) model.add_observer(PLUGIN.model_observer) model.selection.add_observer(PLUGIN.selection_observer) PLUGIN.visualize_selected end end # class AppObserver unless file_loaded?(__FILE__) command = UI::Command.new("Attribute Helper") { self.visualize_selected } command.status_bar_text = "Inspect and edit the attributes of a selection." menu_name = Sketchup.version.to_f < 21.1 ? 'Plugins' : 'Developer' menu = UI.menu(menu_name) menu.add_item(command) file_loaded(__FILE__) end end # module AttributeHelper end # module Sketchup