indexing description: "[ A box that contains (in cells) other boxes aligned in a grid-like table structure. The number of columns and rows has to be specified during creation and can not be changed after that. Columns and rows provide alignment and a minimal size for each cell. Columns and rows will be resized to accommodate the largest child item, if neccessary. The total size of a table is the sum of all row-heights and column-widths. Alignment and size can be uniformly set over all cells but cells might resize during the next update to fit the contents, thus the total size might be larger than the desired size. A table has a colored, arbitrarily wide border that is initially hidden and of width 1. ]" author: "" date: "$Date$" revision: "$Revision$" class EM_VIZ_TABLE_BOX [B -> EM_VIZ_BOX] inherit EM_VIZ_COLLECTION_BOX [B] rename count as cell_count redefine vz_render end EM_VIZ_RESIZEABLE redefine set_width, set_height, set_size end EM_VIZ_ALIGNABLE redefine set_alignment, set_horizontal_alignment, set_vertical_alignment end EM_VIZ_COLORED rename set_color as set_border_color, color as border_color end create make feature -- Initialization make (a_suggested_size: like size; a_dimensions: EM_VECTOR2I) is -- Make new table with `a_suggested_size' and `a_dimensions' columns and rows require positive_dimensions: 1 <= a_dimensions.x and 1 <= a_dimensions.y do make_collection make_alignable make_colored (Viz_options.default_border_color) create_columns (a_dimensions.x, a_suggested_size.x, horizontal_alignment) create_rows (a_dimensions.y, a_suggested_size.y, vertical_alignment) create_cells -- Set_width/set_height requires columns/rows to exist make_resizeable (a_suggested_size) set_border_hidden (False) set_border_width (1.0) end feature -- Access border_hidden: BOOLEAN -- Should cell border be hidden? border_width: DOUBLE -- Width of border if visible column_count: INTEGER -- How many columns row_count: INTEGER -- How many rows infix "@", item (an_index: EM_VECTOR2I): B is -- Item in cell at `an_index' require valid_index: valid_index (an_index) do Result := cells.item (cell_index (an_index)) end as_linear: DS_LINEAR [B] is -- Expose items in linear list do Result := cells end feature -- Measurement cell_count: INTEGER is -- How many cells? do Result := cells.count end cell_size (an_index: EM_VECTOR2I): like size is -- Size of cell at `an_index' require valid_index: valid_index (an_index) do Result.set ( column_width (an_index.x), row_height (an_index.y), depth ) end cell_alignment (an_index: EM_VECTOR2I): like alignment is -- Alignment of cell at `an_index' require valid_index: valid_index (an_index) do Result.set ( column_alignment (an_index.x), row_alignment (an_index.y), depth_alignment ) end column_width (a_column: INTEGER): DOUBLE is -- Width of `a_column' require valid_index: 1 <= a_column and a_column <= column_count do Result := column (a_column).width end column_alignment (a_column: INTEGER): EM_ALIGNMENT is -- Alignment of `a_column' require valid_index: 1 <= a_column and a_column <= column_count do Result := column (a_column).alignment end row_height (a_row: INTEGER): DOUBLE is -- Height of `a_row' require valid_index: 1 <= a_row and a_row <= row_count do Result := row (a_row).height end row_alignment (a_row: INTEGER): EM_ALIGNMENT is -- Alignment of `a_row' require valid_index: 1 <= a_row and a_row <= row_count do Result := row (a_row).alignment end feature -- Status report is_empty: BOOLEAN is -- Table is never empty do Result := false end has (b: B): BOOLEAN is -- Has box `b'? do Result := cells.has (b) end valid_index (a_index: EM_VECTOR2I): BOOLEAN is -- Is `a_index' valid? do Result := 1 <= a_index.x and a_index.x <= column_count and 1 <= a_index.y and a_index.y <= row_count end feature -- Element change set_border_hidden (hide: like border_hidden) is -- Set border hidden or visible do border_hidden := hide expire ensure set: border_hidden = hide expired: needs_update end set_border_width (a_width: like border_width) is -- Set `border_width' to `a_width' require positive_width: 0.0 < a_width do border_width := a_width expire ensure set: border_width = a_width expired: needs_update end put (a_box: like item; a_index: EM_VECTOR2I) is -- Put `a_box' into cell at `a_index' require valid_index: valid_index (a_index) orphan_box: a_box /= Void implies a_box.parent = Void do cast_out (item (a_index)) cells.replace (a_box, cell_index (a_index)) adopt (a_box) expire ensure has: has (a_box) expired: needs_update end set_column_width (a_column: INTEGER; a_width: like column_width) is -- Set width of `a_column' to `a_width' require valid_index: 1 <= a_column and a_column <= column_count do column (a_column).set_width (a_width) expire ensure expired: needs_update end set_column_alignment (a_column: INTEGER; an_alignment: like column_alignment) is -- Set alignment of `a_column' to `an_alignment' require valid_index: 1 <= a_column and a_column <= column_count do column (a_column).set_alignment (an_alignment) if an_alignment /= horizontal_alignment then set_horizontal_alignment (Align_user_specified) end expire ensure expired: needs_update end set_row_height (a_row: INTEGER; a_height: like row_height) is -- Set height of `a_row' to `a_height' require valid_index: 1 <= a_row and a_row <= row_count do row (a_row).set_height (a_height) expire ensure expired: needs_update end set_row_alignment (a_row: INTEGER; an_alignment: like row_alignment) is -- Set alignment of `a_row' to `an_alignment' require valid_index: 1 <= a_row and a_row <= row_count do row (a_row).set_alignment (an_alignment) if an_alignment /= vertical_alignment then set_vertical_alignment (Align_user_specified) end expire ensure expired: needs_update end set_width (a_total_width: like width) is -- Set width of table to `a_total_width' local c: INTEGER do from c := 1 until c > column_count loop set_column_width (c, a_total_width / column_count) c := c + 1 end Precursor (a_total_width) end set_height (a_total_height: like height) is -- Set height of table to `a_total_height' local r: INTEGER do from r := 1 until r > row_count loop set_row_height (r, a_total_height / row_count) r := r + 1 end Precursor (a_total_height) end set_size (a_total_size: like size) is -- Set size of table to `a_total_size' do set_width (a_total_size.x) set_height (a_total_size.y) set_depth (a_total_size.z) end set_horizontal_alignment (an_uniform_alignment: like horizontal_alignment) is -- Set `an_uniform_alignment' for all columns local c: INTEGER do if not an_uniform_alignment.is_user_specified then from c := 1 until c > column_count loop set_column_alignment (c, an_uniform_alignment) c := c + 1 end end Precursor (an_uniform_alignment) end set_vertical_alignment (an_uniform_alignment: like vertical_alignment) is -- Set `an_uniform_alignment' for all rows local r: INTEGER do if not an_uniform_alignment.is_user_specified then from r := 1 until r > row_count loop set_row_alignment (r, an_uniform_alignment) r := r + 1 end end Precursor (an_uniform_alignment) end set_alignment (an_uniform_alignment: like alignment) is -- Set `an_uniform_alignment' for all cells do set_vertical_alignment (an_uniform_alignment.x) set_horizontal_alignment (an_uniform_alignment.y) set_depth_alignment (an_uniform_alignment.z) end feature {NONE} -- Implementation vz_render (viewing_direction: EM_VECTOR3D) is -- Render back-to-front do if viewing_direction.z > 0.0 then setup_local_coordinate_frame render_contents (viewing_direction) revert_local_coordinate_frame if not border_hidden then render_border end else if not border_hidden then render_border end setup_local_coordinate_frame render_contents (viewing_direction) revert_local_coordinate_frame end end render_contents (viewing_direction: EM_VECTOR3D) is -- Render back-to-front local reverse_x, reverse_y: BOOLEAN c, r: INTEGER i: EM_VECTOR2I b: EM_VIZ_BOX do reverse_x := viewing_direction.x > 0.0 reverse_y := viewing_direction.y > 0.0 from c := 1 until c > column_count loop from r := 1 until r > row_count loop if reverse_x then i.set_x (column_count - c + 1) else i.set_x (c) end if reverse_y then i.set_y (row_count - r + 1) else i.set_y (r) end b := item (i) if b /= Void then b.render (viewing_direction) end r := r + 1 end c := c + 1 end end render_border is -- Draw table border local c, r: INTEGER x, y, w, h: DOUBLE bs: like border_width do setup_local_coordinate_frame w := width h := height bs := border_width / 2 vz_push_attributes (Vz_polygon_bit) vz_set_culling (False, Vz_front_and_back) vz_normal3d (0, 0, -1) vz_color (border_color) vz_begin_quads -- Vertical lines from c := 1 x := 0.0 vz_vertex2d (x-bs, -bs) vz_vertex2d (x+bs, -bs) vz_vertex2d (x+bs, h+bs) vz_vertex2d (x-bs, h+bs) until c > column_count loop x := x + column_width (c) vz_vertex2d (x-bs, -bs) vz_vertex2d (x+bs, -bs) vz_vertex2d (x+bs, h+bs) vz_vertex2d (x-bs, h+bs) c := c + 1 end -- Horizontal lines from r := 1 y := 0.0 vz_vertex2d (w+bs, y-bs) vz_vertex2d (w+bs, y+bs) vz_vertex2d ( -bs, y+bs) vz_vertex2d ( -bs, y-bs) until r > row_count loop y := y + row_height (r) vz_vertex2d (w+bs, y-bs) vz_vertex2d (w+bs, y+bs) vz_vertex2d ( -bs, y+bs) vz_vertex2d ( -bs, y-bs) r := r + 1 end vz_end vz_pop_attributes revert_local_coordinate_frame end resize_container is -- Resize container to fit contents local c, r: INTEGER b: B w, h, d: DOUBLE do d := 0.0 from c := 1 until c > column_count loop from r := 1 until r > row_count loop b := item ([c, r]) if b /= Void then row (r).set_height (b.height.max (row_height (r))) column (c).set_width (b.width.max (column_width (c))) d := b.depth.max (d) end r := r + 1 end c := c + 1 end from w := 0.0 c := 1 until c > column_count loop w := w + column_width (c) c := c + 1 end from h := 0.0 r := 1 until r > row_count loop h := h + row_height (r) r := r + 1 end set_size_internal ([w, h, d]) end realign_contents is -- Realign contents after resizing container local c, r: INTEGER b: B bwin: EM_INTERVAL_3D xmin, xmax, ymin, ymax, zmin, zmax: DOUBLE do zmin := 0.0 zmax := depth from r := 1 ymin := 0.0 until r > row_count loop ymax := ymin + row_height (r) from c := 1 xmin := 0.0 until c > column_count loop xmax := xmin + column_width (c) b := item ([c, r]) if b /= Void then bwin.set ([xmin, ymin, zmin], [xmax, ymax, zmax]) b.realign (bwin, cell_alignment ([c, r])) end xmin := xmax c := c + 1 end ymin := ymax r := r + 1 end end frozen cell_index (i: EM_VECTOR2I): INTEGER is -- Cell index from `i' do Result := i.x + (row_count - i.y)*column_count -- column_count + (row_count - 1)*column_count = row_count * column_count -- 1 + (row_count - row_count)*column_count = 1 end frozen column (c: INTEGER): EM_VIZ_TABLE_COLUMN is -- `c'-th column do Result := columns @ c end frozen row (r: INTEGER): EM_VIZ_TABLE_ROW is -- `r'-th row do Result := rows @ r end create_columns (count: INTEGER; total_width: like width; default_alignment: EM_ALIGNMENT) is -- Create `count' columns with `total_width' and `default_alignment' require natural_count: 1 <= count do column_count := count from create columns.make (count) until columns.is_full loop columns.put_last (create {EM_VIZ_TABLE_COLUMN}.make (total_width / count, default_alignment)) end end create_rows (count: INTEGER; total_height: like height; default_alignment: EM_ALIGNMENT) is -- Create `count' rows with `total_height' and `default_alignment' require natural_count: 1 <= count do row_count := count from create rows.make (count) until rows.is_full loop rows.put_last (create {EM_VIZ_TABLE_ROW}.make (total_height / count, default_alignment)) end end create_cells is -- Create internal storage do from create cells.make (row_count * column_count) until cells.is_full loop cells.put_last (Void) end end cells: DS_ARRAYED_LIST [B] -- Table cells (storage) rows: DS_ARRAYED_LIST [EM_VIZ_TABLE_ROW] -- Table rows columns: DS_ARRAYED_LIST [EM_VIZ_TABLE_COLUMN] -- Table columns invariant columns_exist: columns /= Void rows_exist: rows /= Void cells_exist: cells /= Void consistent_counts: rows.count = row_count and columns.count = column_count end