######################################################################## #### #### Title: rledit.magik #### Author: Mark Cederholm #### Last Revised: 06-Feb-2004 #### #### Notes: #### #### Provides a multi_list_view for viewing selected rwos or clipboard #### contents and an edit field for updating one or more records. #### #### The "Force geometry update" option forces table update for mapped #### geometries. #### #### Requires the sort_menu and ordered_choice_lister classes. #### ######################################################################## #********************************************************************** # Exemplar #********************************************************************** remex(:rwo_list_editor) $ def_slotted_exemplar(:rwo_list_editor, { {:grs, _unset, :readable}, {:rwos_orig, _unset, :readable}, {:rwos, _unset, :readable}, {:flist, _unset, :readable}, {:slist, _unset, :readable}, {:vlist, _unset, :readable}, {:items, _unset, :readable} }, :engine_model) $ #********************************************************************** # Method to launch #********************************************************************** _method graphics_system.launch_rwo_list_editor() menu_key << :rwo_list_editor _if (menu << .sub_menus[menu_key]) _is _unset _then menu << .sub_menus[menu_key] << rwo_list_editor.new(_self) _endif menu.activate() >> menu _endmethod $ #********************************************************************** # Public methods #********************************************************************** _method rwo_list_editor.new(a_grs) >> _clone.init(a_grs) _endmethod _method rwo_list_editor.activate_in(a_frame) _self.title << "RWO List Editor" .items << hash_table.new() i << .items[:view] << heading_multi_list_view.new(_self,a_frame, :|cell_data()|, :|do_select()|, :yourself, 10,100,:many,_unset,_unset,_unset,_true) w << i.window w.tile_name << :view w.resize_x? << _true w.resize_y? << _true pop << popup_menu.new(w) button_item.new(pop,"Select Column",_self,:|select_column()|) button_item.new(pop,"Clear Selection",_self,:|clear_selection()|) button_item.new(pop,"Update",_self,:|update()|) i.menu << pop p << panel.new(a_frame) label_item.new(p, "Value:") i << .items[:pick] << button_item.new(p,"?",_self,:pick_enum|()|) i.visibility << _false i << .items[:value] << text_item.new(p) i.display_length << 20 # .items[:update_geom] << toggle_item.new(p,"Force geometry update", :value, _false) p.start_row() m << pulldown_menu.new("Get") button_item.new(m,"Selection",_self,:|get_selection()|) button_item.new(m,"Clipboard",_self,:|get_clipboard()|) menu_button_item.new(p,"Get",m) button_item.new(p,"Update",_self,:|update()|) button_item.new(p,"Delete",_self,:|delete()|) button_item.new(p,"Highlight",_self,:|highlight()|) button_item.new(p,"Goto",_self,:|goto()|) button_item.new(p,"Sort...",_self,:|sort()|) button_item.new(p,"Visible...",_self,:|visible()|) button_item.new(p,"Interrupt",_self,:|interrupt_engine()|) button_item.new(p,"Refresh",_self,{:|refresh()|, _true}) button_item.new(p,"Reset",_self,:|reset()|) button_item.new(p,"Clear",_self,:|clear()|) button_item.new(p,"Quit",_self,:|quit()|) .items[:status] << label_item.new(p,"OK") _endmethod _method rwo_list_editor.cell_data() ## Data to populate view _if .rwos _is _unset _then _return {} _endif # Create column for each field values << rope.new() _for f _over .vlist.fast_elements() _loop # heading r << rope.new() r.add(f.external_name) # records _for e _over .rwos.fast_elements() _loop val << e.perform(f.name).write_string r.add(val) _endloop values.add(r) _endloop >> values _endmethod _method rwo_list_editor.do_select(an_index) ## Populate value item with selection _if an_index.size = 0 _then .items[:pick].visibility << _false .items[:value].value << "" _return _endif # Make sure selection is restricted to single column i << an_index.an_element() r << rope.new() _for e _over an_index.fast_elements() _loop _if e.col = i.col _then r.add(e) _endif _endloop _if r.size ~= an_index.size _then s << sorted_collection.new(r.size) s.add_all(r) .items[:view].current_index << s _return _endif # Populate value item f << .vlist[i.col] r << .rwos[i.row - 1] val << r.perform(f.name).default("").write_string .items[:value].value << val # Set pick visibility t << f.type _if t _is _unset _orif t.enumerator _is _unset _then .items[:pick].visibility << _false _else .items[:pick].visibility << _true _endif _endmethod _method rwo_list_editor.select_column() ## Select all cells in current column _if .rwos _is _unset _then _return _endif v << .items[:view] i << v.current_index _if i.size = 0 _then _return _endif nrows << .rwos.size col << i.an_element().col r << rope.new() _for row _over range(2,nrows + 1) _loop r.add(rc_index.new(row, col)) _endloop s << sorted_collection.new(r.size) s.add_all(r) v.current_index << s _endmethod _method rwo_list_editor.clear_selection() ## Clear selected cells .items[:view].current_index << sorted_collection.new() _endmethod _method rwo_list_editor.pick_enum() ## Put up a choice lister of enumeration values to pick i << .items[:view].current_index.an_element() f << .vlist[i.col] enum << f.type.enumerator cl << choice_lister.new() vals << rope.new_from(enum.sorted_values) cl.activate_on(vals, _self, :|select_enum()|) cl.title << "Select " + f.external_name _endmethod _method rwo_list_editor.select_enum(val) ## Set value item to returned enum value .items[:value].value << val.write_string _endmethod _method rwo_list_editor.get_selection() ## Get selected rwos and populate view .rwos << .flist << _unset r << .grs.selected_rwos _if r.size = 0 _then _self.show_alert("No current selection") _self.clear() .items[:status].label << "ERROR" _return _endif # Launch thread _global thread _self.run_engine(thread.interactive_priority,:|do_it()|, :selection) _endmethod _method rwo_list_editor.get_clipboard() ## Get rwos from clipboard and populate view .rwos << .flist << _unset # max << !maximum_collection_size! cb << gis_program_manager.scrapbook().clipboard _if cb _is _unset _orif cb.n_objects = 0 _then _self.show_alert("No features in clipboard") _self.clear() .items[:status].label << "ERROR" _return # _elif cb.n_objects > max # _then # msg << "Greater than " + max.write_string + " objects." # _self.show_alert(msg) # _self.clear() # .items[:status].label << "ERROR" # _return _endif # Launch thread _global thread _self.run_engine(thread.interactive_priority,:|do_it()|, :clipboard) _endmethod _method rwo_list_editor.update() _self.do_update(:update) _endmethod _method rwo_list_editor.delete() _self.do_update(:delete) _endmethod _method rwo_list_editor.highlight() _self.do_update(:highlight) _endmethod _method rwo_list_editor.goto() _self.do_update(:goto) _endmethod _method rwo_list_editor.sort() _if .vlist _is _unset _then _self.show_alert("No fields to sort") .items[:status].label << "ERROR" _return _endif # Create sort_menu for field sort choices sm << sort_menu.new(.vlist, _self, :|set_sort_fields()|) _if .slist _isnt _unset _then sm.set_sort_list(.slist) _endif sm.activate() _endmethod _method rwo_list_editor.set_sort_fields(a_selection) _if a_selection.size = 0 _then .slist << _unset _else .slist << a_selection _endif # Launch thread _global thread _self.run_engine(thread.interactive_priority,:|do_it()|, :sort) _endmethod _method rwo_list_editor.visible() _if .flist _is _unset _then _self.show_alert("No fields to select") .items[:status].label << "ERROR" _return _endif an_index << rope.new() _for f _over .vlist.fast_elements() _loop _if .flist.includes?(f) _then an_index.add(.flist.index_of(f)) _endif _endloop c << ordered_choice_lister.new(.flist, _self, :|set_visible_fields()|, :external_name, "Select Visible Fields:") c.set_selection(an_index) c.activate() _endmethod _method rwo_list_editor.set_visible_fields(a_selection) .items[:status].label << "Working..." .vlist << a_selection v << .items[:view] v.update_list(_self.cell_data(), _unset) _self.do_select(v.current_index) .items[:status].label << "OK" _endmethod _method rwo_list_editor.refresh(_optional clear_selection?) ## Refresh editor record contents ## CLEAR_SELECTION? defaults to _false clear? << clear_selection?.default(_false) .items[:status].label << "Working..." v << .items[:view] _if clear? _then update_option << _unset s << sorted_collection.new() _self.int!sort() _else update_option << :update s << .items[:view].current_index _endif v.update_list(_self.cell_data(), update_option) _self.do_select(s) .items[:status].label << "OK" _endmethod _method rwo_list_editor.reset() ## Reset editor, clearing any sort or visibility options .rwos << .rwos_orig .vlist << .flist .slist << _unset _self.refresh() _endmethod _method rwo_list_editor.clear() ## Clear out editor .rwos << .vlist << _unset .rwos_orig << .flist << _unset .slist << _unset .items[:view].current_index << sorted_collection.new() _self.refresh() _endmethod _method rwo_list_editor.quit() .grs.remove_dependent(_self) _super.quit() _endmethod _method rwo_list_editor.do_it(mode) ok? << _false _protect .items[:status].label << "Working..." _self.busy? << _true n << _unset _if mode _is :selection _then n << _self.int!get_selection() _elif mode _is :clipboard _then n << _self.int!get_clipboard() _elif mode _is :update _then _self.int!update() _elif mode _is :delete _then _self.int!delete() _elif mode _is :highlight _then _self.int!highlight() _elif mode _is :goto _then _self.int!goto() _elif mode _is :sort _then _self.int!sort() _endif ok? << _true _protection _self.refresh(_false) _self.busy? << _false _if ~ ok? _then .items[:status].label << "Interrupted" _elif n _isnt _unset _then .items[:status].label << n.write_string + " records." _else .items[:status].label << "OK" _endif _endprotect _endmethod #********************************************************************** # Private methods #********************************************************************** _private _method rwo_list_editor.init(a_grs) .grs << a_grs >> _super.init() _endmethod _private _method rwo_list_editor.int!get_selection() # Get rwo list r << .grs.selected_rwos an_e << r.an_element() rwolist << rope.new() _for e _over r.fast_elements() _loop _if ~ e.is_class_of?(an_e) _then _self.show_alert("Ambiguous selection") _self.clear() _return _endif rwolist.add(e.detached()) _endloop _self.get_fields(rwolist) >> rwolist.size _endmethod _private _method rwo_list_editor.int!get_clipboard() cb << gis_program_manager.scrapbook().clipboard rwolist << rope.new() rset << cb.record_read_stream() r0 << rset.get() rwolist.add(r0.detached()) _loop r << rset.get() _if r _is _unset _then _leave _endif _if ~ r.is_class_of?(r0) _then _self.show_alert("Ambiguous selection") _self.clear() _return _endif rwolist.add(r.detached()) _endloop _self.get_fields(rwolist) >> rwolist.size _endmethod _private _method rwo_list_editor.get_fields(rwolist) # Get visible physical fields e << rwolist.an_element() fldlist << rope.new() _for f _over e.visible_fields.fast_elements() _loop _if f.is_physical? _then t << f.type _if t _is _unset _orif t.is_string? _orif ~ t.is_vector? _then fldlist.add(f) _endif _endif _endloop _if fldlist.size = 0 _then _self.show_alert("No displayable fields") _self.clear() _return _endif .rwos << .rwos_orig << rwolist .flist << .vlist << fldlist .slist << _unset _self.clear_selection() _endmethod _private _method rwo_list_editor.do_update(mode) ## Update item(s) with new value sel << .items[:view].current_index _if sel.size = 0 _then _self.show_alert("Nothing selected.") .items[:status].label << "ERROR" _return _endif _if mode _is :update _or mode _is :delete _then # Check for writable alternative v << .rwos.an_element().source_view _if ~ v.writable? _then _self.show_alert("ERROR: Alternative not writable") .items[:status].label << "ERROR" _return _endif # **** optional code for authorisation environments **** # Check for authorisation t << .rwos.an_element().source_collection _if ~ t.write_authorised? _then _self.show_alert("ERROR: You are not authorised to edit this table") .items[:status].label << "ERROR" _return _endif _endif # Launch thread _global thread _self.run_engine(thread.interactive_priority,:|do_it()|, mode) _endmethod _private _method rwo_list_editor.int!update() # If value is unset, check if user really wants to clear fields val << .items[:value].value.trim_spaces() _if val = "" _then msg << "No value has been entered. Do you wish to erase values?" erase? << _self.show_question("Yes", "No", msg) _if ~ erase? _then .items[:status].label << "Cancelled" _return _endif val << _unset _endif # Check that value is valid for field sel << .items[:view].current_index f << .vlist[sel.an_element().col] fname << f.name t << f.type _if val _is _unset _or t _is _unset _then the_value << val _elif t.enumerator _isnt _unset _then valid? << _false enums << rope.new_from(f.type.enumerator.sorted_values) _for e _over enums.fast_elements() _loop the_value << e _if the_value.write_string = val _then valid? << _true _leave _endif _endloop _else ftype << t.phys_type valid? << _true _if ftype _is :ds_int _or ftype _is :ds_uint _or ftype _is :ds_byte _then the_value << val.as_integer() valid? << (the_value _isnt _unset) _elif ftype _is :ds_float _or ftype _is :ds_double _then the_value << val.as_number() valid? << (the_value _isnt _unset) _elif ftype _is :ds_bool _then val << val.uppercase _if val = "TRUE" _or val = "T" _then the_value << _true _elif val = "FALSE" _or val = "F" _then the_value << _false _else valid? << _false _endif _elif ftype _is :ds_date _then _try _with the_condition the_value << date.new_from_string(val) _when error valid? << _false _endtry _elif ftype _is :ds_time _then _try _with the_condition the_value << date_time.new_from_string(val) _when error valid? << _false _endtry _else the_value << val _endif _if ~ valid? _then _self.show_alert("ERROR: Invalid value entered") .items[:status].label << "ERROR" _return _endif _endif # get selected records and loop through them v << .rwos.an_element().source_view _dynamic !current_grs! << .grs _dynamic !current_dsview! << v _dynamic !current_world! << v.world t << .rwos.an_element().source_collection recnum << 0 _for i _over sel.fast_elements() _loop r << .rwos[i.row - 1] _if r.responds_to?(:|has_geometry?|) _then has_geometry? << r.has_geometry? _else has_geometry? << _false _endif recnum +<< 1 _if recnum _mod 100 = 0 _then msg << "Updating record " + recnum.write_string + "..." .items[:status].label << msg _endif _try _with the_condition ok? << v.start_lwt() _protect r.perform(fname.with_chevron, the_value) t.update(r) ok? << _true _protection v.end_lwt(ok?) _endprotect _when error _self.show_alert("ERROR: Could not set value") .items[:status].label << "ERROR" _return _endtry _endloop _endmethod _private _method rwo_list_editor.int!delete() _if ~ _self.show_question("Yes", "No", "Delete records?") _then _return _endif sel << .items[:view].current_index v << .rwos.an_element().source_view _dynamic !current_grs! << .grs _dynamic !current_dsview! << v _dynamic !current_world! << v.world t << .rwos.an_element().source_collection _for i _over sel.elements_in_reverse() _loop r << .rwos[i.row - 1] .rwos.remove(r) ok? << v.start_lwt() _protect t.remove(r) ok? << _true _protection v.end_lwt(ok?) _endprotect _endloop _self.clear_selection() _endmethod _private _method rwo_list_editor.int!highlight() sel << .items[:view].current_index _dynamic !current_grs! << .grs _for i _over sel.fast_elements() _loop r << .rwos[i.row - 1] _if r.responds_to?(:draw|()|) _then r.draw(:highlight) _endif _endloop _endmethod _private _method rwo_list_editor.int!goto() sel << .items[:view].current_index _dynamic !current_grs! << .grs _dynamic !current_world! << !current_grs!.safe_world bounds << _unset _for i _over sel.fast_elements() _loop r << .rwos[i.row - 1] _if r.responds_to?(:goto_bounds) _andif (rwo_bounds << r.goto_bounds) _isnt _unset _then bounds << rwo_bounds.union(bounds) _endif _endloop _if bounds _is _unset _then _self.show_alert("No geometry") _return _endif g.goto(bounds.new_enlarging(1.1)) _endmethod _private _method rwo_list_editor.int!sort() _local sortlist << .slist _if sortlist _is _unset _then .rwos << .rwos_orig _return _endif sortproc << _proc(arg1,arg2) _import sortlist _for e _over sortlist.fast_elements() _loop fname << e[1].name dir << e[2] val1 << arg1.perform(fname) val2 << arg2.perform(fname) _if val1 _is _unset _and val2 _is _unset _then _continue _elif val1 _is _unset _then _return (dir _is :ascending) _elif val2 _is _unset _then _return (dir _isnt :ascending) _elif val1 = val2 _then _continue _endif _if dir _is :ascending _then _return (val1 < val2) _else _return (val1 > val2) _endif _endloop >> _maybe _endproc s << sorted_collection.new(.rwos.size, sortproc) s.add_all(.rwos) .rwos << s _self.clear_selection() _endmethod