mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
Delete old range, s/MutableRange/Range/g;
This commit is contained in:
parent
da17edee92
commit
2a1e97c80f
12 changed files with 60 additions and 110 deletions
|
@ -3,7 +3,7 @@ use geometry::Au;
|
|||
use image::base::Image;
|
||||
use render_context::RenderContext;
|
||||
use text::SendableTextRun;
|
||||
use util::range::MutableRange;
|
||||
use util::range::Range;
|
||||
|
||||
use azure::azure_hl::DrawTarget;
|
||||
use core::dvec::DVec;
|
||||
|
@ -27,7 +27,7 @@ pub enum DisplayItem {
|
|||
// TODO: need to provide spacing data for text run.
|
||||
// (i.e, to support rendering of CSS 'word-spacing' and 'letter-spacing')
|
||||
// TODO: don't copy text runs, ever.
|
||||
Text(DisplayItemData, ~SendableTextRun, MutableRange, Color),
|
||||
Text(DisplayItemData, ~SendableTextRun, Range, Color),
|
||||
Image(DisplayItemData, ARC<~image::base::Image>),
|
||||
Border(DisplayItemData, Au, Color)
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ impl DisplayItem {
|
|||
|
||||
static pure fn new_Text(bounds: &Rect<Au>,
|
||||
run: ~SendableTextRun,
|
||||
range: MutableRange,
|
||||
range: Range,
|
||||
color: Color) -> DisplayItem {
|
||||
Text(DisplayItemData::new(bounds), move run, move range, color)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use color::Color;
|
|||
use font_context::FontContext;
|
||||
use geometry::Au;
|
||||
use render_context::RenderContext;
|
||||
use util::range::{Range, MutableRange};
|
||||
use util::range::Range;
|
||||
use text::glyph::{GlyphStore, GlyphIndex};
|
||||
use text::{Shaper, TextRun};
|
||||
|
||||
|
@ -372,10 +372,10 @@ impl Font {
|
|||
pub trait FontMethods {
|
||||
fn draw_text_into_context(rctx: &RenderContext,
|
||||
run: &TextRun,
|
||||
range: &MutableRange,
|
||||
range: &Range,
|
||||
baseline_origin: Point2D<Au>,
|
||||
color: Color);
|
||||
fn measure_text(&TextRun, &const MutableRange) -> RunMetrics;
|
||||
fn measure_text(&TextRun, &const Range) -> RunMetrics;
|
||||
fn shape_text(@self, &str) -> GlyphStore;
|
||||
fn get_descriptor() -> FontDescriptor;
|
||||
|
||||
|
@ -388,7 +388,7 @@ pub trait FontMethods {
|
|||
pub impl Font : FontMethods {
|
||||
fn draw_text_into_context(rctx: &RenderContext,
|
||||
run: &TextRun,
|
||||
range: &const MutableRange,
|
||||
range: &const Range,
|
||||
baseline_origin: Point2D<Au>,
|
||||
color: Color) {
|
||||
use libc::types::common::c99::{uint16_t, uint32_t};
|
||||
|
@ -445,7 +445,7 @@ pub impl Font : FontMethods {
|
|||
ptr::null());
|
||||
}
|
||||
|
||||
fn measure_text(run: &TextRun, range: &const MutableRange) -> RunMetrics {
|
||||
fn measure_text(run: &TextRun, range: &const Range) -> RunMetrics {
|
||||
assert range.is_valid_for_string(run.text);
|
||||
|
||||
// TODO(Issue #199): alter advance direction for RTL
|
||||
|
|
|
@ -4,7 +4,6 @@ use geometry::Au;
|
|||
use image::base::Image;
|
||||
use opts::Opts;
|
||||
use text::TextRun;
|
||||
use util::range::Range;
|
||||
|
||||
use azure::azure_hl::{AsAzureRect, B8G8R8A8, Color, ColorPattern, DrawOptions};
|
||||
use azure::azure_hl::{DrawSurfaceOptions, DrawTarget, Linear, StrokeOptions};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use au = geometry;
|
||||
use au::Au;
|
||||
use servo_gfx_util::range::MutableRange;
|
||||
use servo_gfx_util::range::Range;
|
||||
use servo_gfx_util::vec::*;
|
||||
|
||||
use core::cmp::{Ord, Eq};
|
||||
|
@ -582,13 +582,13 @@ impl GlyphStore {
|
|||
return true;
|
||||
}
|
||||
|
||||
fn iter_glyphs_for_range(&self, range: &const MutableRange, cb: fn&(uint, GlyphInfo/&) -> bool) {
|
||||
fn iter_glyphs_for_range(&self, range: &const Range, cb: fn&(uint, GlyphInfo/&) -> bool) {
|
||||
assert range.begin() < self.entry_buffer.len();
|
||||
assert range.end() <= self.entry_buffer.len();
|
||||
|
||||
for range.eachi |i| {
|
||||
if !self.iter_glyphs_for_index(i, cb) { break; }
|
||||
}
|
||||
if !self.iter_glyphs_for_index(i, cb) { break; }
|
||||
}
|
||||
}
|
||||
|
||||
fn iter_all_glyphs(cb: fn&(uint, GlyphInfo/&) -> bool) {
|
||||
|
|
|
@ -13,7 +13,7 @@ use font::{
|
|||
|
||||
use glyph::{GlyphStore, GlyphIndex, GlyphData};
|
||||
use servo_util::range;
|
||||
use range::MutableRange;
|
||||
use range::Range;
|
||||
|
||||
use core::libc::types::common::c99::int32_t;
|
||||
use core::libc::{c_uint, c_int, c_void, c_char};
|
||||
|
@ -250,10 +250,10 @@ pub impl HarfbuzzShaper {
|
|||
}
|
||||
|
||||
// some helpers
|
||||
let mut glyph_span : MutableRange = range::empty_mut();
|
||||
let mut glyph_span : Range = Range::empty();
|
||||
// this span contains first byte of first char, to last byte of last char in range.
|
||||
// so, end() points to first byte of last+1 char, if it's less than byte_max.
|
||||
let mut char_byte_span : MutableRange = range::empty_mut();
|
||||
let mut char_byte_span : Range = Range::empty();
|
||||
let mut y_pos = Au(0);
|
||||
|
||||
// main loop over each glyph. each iteration usually processes 1 glyph and 1+ chars.
|
||||
|
|
|
@ -2,7 +2,7 @@ use font_context::FontContext;
|
|||
use geometry::Au;
|
||||
use glyph::GlyphStore;
|
||||
use servo_gfx_font::{Font, FontDescriptor, RunMetrics};
|
||||
use servo_gfx_util::range::{Range, MutableRange};
|
||||
use servo_gfx_util::range::Range;
|
||||
|
||||
use core::libc::{c_void};
|
||||
use geom::point::Point2D;
|
||||
|
@ -60,7 +60,7 @@ impl TextRun {
|
|||
|
||||
pure fn glyphs(&self) -> &self/GlyphStore { &self.glyphs }
|
||||
|
||||
pure fn range_is_trimmable_whitespace(&self, range: &const MutableRange) -> bool {
|
||||
pure fn range_is_trimmable_whitespace(&self, range: &const Range) -> bool {
|
||||
let mut i = range.begin();
|
||||
while i < range.end() {
|
||||
// jump i to each new char
|
||||
|
@ -74,11 +74,11 @@ impl TextRun {
|
|||
return true;
|
||||
}
|
||||
|
||||
fn metrics_for_range(&self, range: &const MutableRange) -> RunMetrics {
|
||||
fn metrics_for_range(&self, range: &const Range) -> RunMetrics {
|
||||
self.font.measure_text(self, range)
|
||||
}
|
||||
|
||||
fn min_width_for_range(&self, range: &const MutableRange) -> Au {
|
||||
fn min_width_for_range(&self, range: &const Range) -> Au {
|
||||
assert range.is_valid_for_string(self.text);
|
||||
|
||||
let mut max_piece_width = Au(0);
|
||||
|
@ -89,10 +89,10 @@ impl TextRun {
|
|||
return max_piece_width;
|
||||
}
|
||||
|
||||
fn iter_natural_lines_for_range(&self, range: &const MutableRange, f: fn(&const MutableRange) -> bool) {
|
||||
fn iter_natural_lines_for_range(&self, range: &const Range, f: fn(&const Range) -> bool) {
|
||||
assert range.is_valid_for_string(self.text);
|
||||
|
||||
let mut clump = MutableRange::new(range.begin(), 0);
|
||||
let mut clump = Range::new(range.begin(), 0);
|
||||
let mut in_clump = false;
|
||||
|
||||
// clump non-linebreaks of nonzero length
|
||||
|
@ -117,10 +117,10 @@ impl TextRun {
|
|||
}
|
||||
}
|
||||
|
||||
fn iter_indivisible_pieces_for_range(&self, range: &const MutableRange, f: fn(&const MutableRange) -> bool) {
|
||||
fn iter_indivisible_pieces_for_range(&self, range: &const Range, f: fn(&const Range) -> bool) {
|
||||
assert range.is_valid_for_string(self.text);
|
||||
|
||||
let mut clump = MutableRange::new(range.begin(), 0);
|
||||
let mut clump = Range::new(range.begin(), 0);
|
||||
loop {
|
||||
// find next non-whitespace byte index, then clump all whitespace before it.
|
||||
match str::find_between(self.text, clump.begin(), range.end(), |c| !char::is_whitespace(c)) {
|
||||
|
|
|
@ -1,20 +1,3 @@
|
|||
pub struct Range {
|
||||
priv off: u16,
|
||||
priv len: u16
|
||||
}
|
||||
|
||||
pub pure fn Range(off: uint, len: uint) -> Range {
|
||||
assert off <= u16::max_value as uint;
|
||||
assert len <= u16::max_value as uint;
|
||||
|
||||
Range {
|
||||
off: off as u16,
|
||||
len: len as u16
|
||||
}
|
||||
}
|
||||
|
||||
pub pure fn empty() -> Range { Range(0,0) }
|
||||
|
||||
enum RangeRelation {
|
||||
OverlapsBegin(/* overlap */ uint),
|
||||
OverlapsEnd(/* overlap */ uint),
|
||||
|
@ -25,53 +8,22 @@ enum RangeRelation {
|
|||
EntirelyAfter
|
||||
}
|
||||
|
||||
pub impl Range {
|
||||
pub pure fn begin() -> uint { self.off as uint }
|
||||
pub pure fn length() -> uint { self.len as uint }
|
||||
pub pure fn end() -> uint { (self.off as uint) + (self.len as uint) }
|
||||
|
||||
pub pure fn eachi(cb: fn&(uint) -> bool) {
|
||||
do uint::range(self.off as uint,
|
||||
(self.off as uint) + (self.len as uint)) |i| {
|
||||
cb(i)
|
||||
}
|
||||
}
|
||||
|
||||
pub pure fn is_valid_for_string(s: &str) -> bool {
|
||||
self.begin() < s.len() && self.end() <= s.len() && self.length() <= s.len()
|
||||
}
|
||||
|
||||
pub pure fn shift_by(i: int) -> Range {
|
||||
Range(((self.off as int) + i) as uint, self.len as uint)
|
||||
}
|
||||
|
||||
pub pure fn extend_by(i: int) -> Range {
|
||||
Range(self.off as uint, ((self.len as int) + i) as uint)
|
||||
}
|
||||
|
||||
pub pure fn adjust_by(off_i: int, len_i: int) -> Range {
|
||||
Range(((self.off as int) + off_i) as uint, ((self.len as int) + len_i) as uint)
|
||||
}
|
||||
}
|
||||
|
||||
pub pure fn empty_mut() -> MutableRange { MutableRange::new(0, 0) }
|
||||
|
||||
pub struct MutableRange {
|
||||
pub struct Range {
|
||||
priv off: uint,
|
||||
priv len: uint
|
||||
}
|
||||
|
||||
pub impl MutableRange {
|
||||
static pub pure fn new(off: uint, len: uint) -> MutableRange {
|
||||
MutableRange { off: off, len: len }
|
||||
pub impl Range {
|
||||
static pub pure fn new(off: uint, len: uint) -> Range {
|
||||
Range { off: off, len: len }
|
||||
}
|
||||
|
||||
static pub pure fn empty() -> MutableRange {
|
||||
MutableRange::new(0, 0)
|
||||
static pub pure fn empty() -> Range {
|
||||
Range::new(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
pub impl MutableRange {
|
||||
pub impl Range {
|
||||
pure fn begin(&const self) -> uint { self.off }
|
||||
pure fn length(&const self) -> uint { self.len }
|
||||
pure fn end(&const self) -> uint { self.off + self.len }
|
||||
|
@ -112,7 +64,7 @@ pub impl MutableRange {
|
|||
/// Computes the relationship between two ranges (`self` and `other`),
|
||||
/// from the point of view of `self`. So, 'EntirelyBefore' means
|
||||
/// that the `self` range is entirely before `other` range.
|
||||
pure fn relation_to_range(&const self, other: &const MutableRange) -> RangeRelation {
|
||||
pure fn relation_to_range(&const self, other: &const Range) -> RangeRelation {
|
||||
if other.begin() > self.end() {
|
||||
return EntirelyBefore;
|
||||
}
|
||||
|
@ -140,7 +92,7 @@ pub impl MutableRange {
|
|||
self, other);
|
||||
}
|
||||
|
||||
fn repair_after_coalesced_range(&mut self, other: &const MutableRange) {
|
||||
fn repair_after_coalesced_range(&mut self, other: &const Range) {
|
||||
let relation = self.relation_to_range(other);
|
||||
debug!("repair_after_coalesced_range: possibly repairing range %?", self);
|
||||
debug!("repair_after_coalesced_range: relation of original range and coalesced range(%?): %?",
|
||||
|
|
|
@ -189,8 +189,8 @@ impl RenderBox : RenderBoxMethods {
|
|||
|
||||
let mut pieces_processed_count : uint = 0;
|
||||
let mut remaining_width : Au = max_width;
|
||||
let mut left_range = MutableRange::new(data.range.begin(), 0);
|
||||
let mut right_range : Option<MutableRange> = None;
|
||||
let mut left_range = Range::new(data.range.begin(), 0);
|
||||
let mut right_range : Option<Range> = None;
|
||||
debug!("split_to_width: splitting text box (strlen=%u, range=%?, avail_width=%?)",
|
||||
data.run.text.len(), data.range, max_width);
|
||||
do data.run.iter_indivisible_pieces_for_range(&const data.range) |piece_range| {
|
||||
|
@ -218,15 +218,15 @@ impl RenderBox : RenderBoxMethods {
|
|||
// if there are still things after the trimmable whitespace, create right chunk
|
||||
if piece_range.end() < data.range.end() {
|
||||
debug!("split_to_width: case=skipping trimmable trailing whitespace, then split remainder");
|
||||
right_range = Some(MutableRange::new(piece_range.end(),
|
||||
data.range.end() - piece_range.end()));
|
||||
right_range = Some(Range::new(piece_range.end(),
|
||||
data.range.end() - piece_range.end()));
|
||||
} else {
|
||||
debug!("split_to_width: case=skipping trimmable trailing whitespace");
|
||||
}
|
||||
} else if piece_range.begin() < data.range.end() {
|
||||
// still things left, create right chunk
|
||||
right_range = Some(MutableRange::new(piece_range.begin(),
|
||||
data.range.end() - piece_range.begin()));
|
||||
right_range = Some(Range::new(piece_range.begin(),
|
||||
data.range.end() - piece_range.begin()));
|
||||
debug!("split_to_width: case=splitting remainder with right range=%?",
|
||||
right_range);
|
||||
}
|
||||
|
@ -239,7 +239,7 @@ impl RenderBox : RenderBoxMethods {
|
|||
Some(layout::text::adapt_textbox_with_range(self.d(), data.run, &const left_range))
|
||||
} else { None };
|
||||
|
||||
let right_box = option::map_default(&right_range, None, |range: &const MutableRange| {
|
||||
let right_box = option::map_default(&right_range, None, |range: &const Range| {
|
||||
Some(layout::text::adapt_textbox_with_range(self.d(), data.run, range))
|
||||
});
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ use util::tree;
|
|||
|
||||
use core::dvec::DVec;
|
||||
use gfx::image::holder::ImageHolder;
|
||||
use gfx::util::range::MutableRange;
|
||||
use gfx::util::range::Range;
|
||||
use newcss::values::{CSSDisplay, CSSDisplayBlock, CSSDisplayInline, CSSDisplayInlineBlock};
|
||||
use newcss::values::{CSSDisplayNone, Inherit, Specified};
|
||||
|
||||
|
@ -151,7 +151,7 @@ impl BoxGenerator {
|
|||
self.flow.inline().boxes.push(*spacer);
|
||||
}
|
||||
}
|
||||
let mut node_range: MutableRange = MutableRange::new(self.range_stack.pop(), 0);
|
||||
let mut node_range: Range = Range::new(self.range_stack.pop(), 0);
|
||||
node_range.extend_to(self.flow.inline().boxes.len());
|
||||
assert node_range.length() > 0;
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ use geom::rect::Rect;
|
|||
use geom::point::Point2D;
|
||||
use gfx::display_list::DisplayList;
|
||||
use gfx::geometry::Au;
|
||||
use gfx::util::range::{Range, MutableRange};
|
||||
|
||||
/** Servo's experimental layout system builds a tree of FlowContexts
|
||||
and RenderBoxes, and figures out positions and display attributes of
|
||||
|
|
|
@ -12,7 +12,7 @@ use geom::{Point2D, Rect, Size2D};
|
|||
use gfx::font::FontStyle;
|
||||
use gfx::geometry::Au;
|
||||
use gfx::text::util::*;
|
||||
use gfx::util::range::{MutableRange, Range};
|
||||
use gfx::util::range::Range;
|
||||
use newcss::values::{CSSTextAlignCenter, CSSTextAlignJustify, CSSTextAlignLeft, CSSTextAlignRight};
|
||||
use newcss::units::{BoxAuto, BoxLength, Px};
|
||||
use std::arc;
|
||||
|
@ -41,11 +41,11 @@ hard to try out that alternative.
|
|||
|
||||
struct NodeRange {
|
||||
node: Node,
|
||||
range: MutableRange,
|
||||
range: Range,
|
||||
}
|
||||
|
||||
impl NodeRange {
|
||||
static pure fn new(node: Node, range: &const MutableRange) -> NodeRange {
|
||||
static pure fn new(node: Node, range: &const Range) -> NodeRange {
|
||||
NodeRange { node: node, range: copy *range }
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ impl ElementMapping {
|
|||
ElementMapping { entries: DVec() }
|
||||
}
|
||||
|
||||
fn add_mapping(node: Node, range: &const MutableRange) {
|
||||
fn add_mapping(node: Node, range: &const Range) {
|
||||
self.entries.push(NodeRange::new(node, range))
|
||||
}
|
||||
|
||||
|
@ -132,8 +132,8 @@ impl ElementMapping {
|
|||
while repair_stack.len() > 0 && old_i == entries[repair_stack.last().entry_idx].range.end() {
|
||||
let item = repair_stack.pop();
|
||||
debug!("repair_for_box_changes: Set range for %u to %?",
|
||||
item.entry_idx, Range(item.begin_idx, new_j - item.begin_idx));
|
||||
entries[item.entry_idx].range = MutableRange::new(item.begin_idx, new_j - item.begin_idx);
|
||||
item.entry_idx, Range::new(item.begin_idx, new_j - item.begin_idx));
|
||||
entries[item.entry_idx].range = Range::new(item.begin_idx, new_j - item.begin_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -148,13 +148,13 @@ impl ElementMapping {
|
|||
// stack-allocated object for scanning an inline flow into
|
||||
// TextRun-containing TextBoxes.
|
||||
priv struct TextRunScanner {
|
||||
clump: MutableRange,
|
||||
clump: Range,
|
||||
}
|
||||
|
||||
priv impl TextRunScanner {
|
||||
static fn new() -> TextRunScanner {
|
||||
TextRunScanner {
|
||||
clump: MutableRange::empty(),
|
||||
clump: Range::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -249,7 +249,7 @@ priv impl TextRunScanner {
|
|||
debug!("TextRunScanner: pushing single text box in range: %?", self.clump);
|
||||
let new_box = layout::text::adapt_textbox_with_range(old_box.d(),
|
||||
run,
|
||||
&const MutableRange::new(0, run.text.len()));
|
||||
&const Range::new(0, run.text.len()));
|
||||
out_boxes.push(new_box);
|
||||
},
|
||||
(false, true) => {
|
||||
|
@ -270,9 +270,9 @@ priv impl TextRunScanner {
|
|||
|
||||
// TODO(Issue #118): use a rope, simply give ownership of nonzero strs to rope
|
||||
let mut run_str : ~str = ~"";
|
||||
let new_ranges : DVec<MutableRange> = DVec();
|
||||
let new_ranges : DVec<Range> = DVec();
|
||||
for uint::range(0, transformed_strs.len()) |i| {
|
||||
new_ranges.push(MutableRange::new(run_str.len(), transformed_strs[i].len()));
|
||||
new_ranges.push(Range::new(run_str.len(), transformed_strs[i].len()));
|
||||
str::push_str(&mut run_str, transformed_strs[i]);
|
||||
}
|
||||
|
||||
|
@ -327,8 +327,8 @@ struct LineboxScanner {
|
|||
flow: @FlowContext,
|
||||
new_boxes: DVec<@RenderBox>,
|
||||
work_list: DList<@RenderBox>,
|
||||
pending_line: {mut range: MutableRange, mut width: Au},
|
||||
line_spans: DVec<MutableRange>,
|
||||
pending_line: {mut range: Range, mut width: Au},
|
||||
line_spans: DVec<Range>,
|
||||
}
|
||||
|
||||
fn LineboxScanner(inline: @FlowContext) -> LineboxScanner {
|
||||
|
@ -338,7 +338,7 @@ fn LineboxScanner(inline: @FlowContext) -> LineboxScanner {
|
|||
flow: inline,
|
||||
new_boxes: DVec(),
|
||||
work_list: DList(),
|
||||
pending_line: {mut range: MutableRange::empty(), mut width: Au(0)},
|
||||
pending_line: {mut range: Range::empty(), mut width: Au(0)},
|
||||
line_spans: DVec()
|
||||
}
|
||||
}
|
||||
|
@ -566,7 +566,7 @@ struct InlineFlowData {
|
|||
boxes: DVec<@RenderBox>,
|
||||
// vec of ranges into boxes that represents line positions.
|
||||
// these ranges are disjoint, and are the result of inline layout.
|
||||
lines: DVec<MutableRange>,
|
||||
lines: DVec<Range>,
|
||||
// vec of ranges into boxes that represent elements. These ranges
|
||||
// must be well-nested, and are only related to the content of
|
||||
// boxes (not lines). Ranges are only kept for non-leaf elements.
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
use layout::box::{TextBox, RenderBox, RenderBoxData, UnscannedTextBox};
|
||||
|
||||
use gfx::text::text_run::TextRun;
|
||||
use gfx::util::range::MutableRange;
|
||||
use gfx::util::range::Range;
|
||||
|
||||
pub struct TextBoxData {
|
||||
run: @TextRun,
|
||||
range: MutableRange,
|
||||
range: Range,
|
||||
}
|
||||
|
||||
pub fn TextBoxData(run: @TextRun, range: &const MutableRange) -> TextBoxData {
|
||||
pub fn TextBoxData(run: @TextRun, range: &const Range) -> TextBoxData {
|
||||
TextBoxData {
|
||||
run: run,
|
||||
range: copy *range,
|
||||
|
@ -18,7 +18,7 @@ pub fn TextBoxData(run: @TextRun, range: &const MutableRange) -> TextBoxData {
|
|||
}
|
||||
|
||||
pub fn adapt_textbox_with_range(box_data: &RenderBoxData, run: @TextRun,
|
||||
range: &const MutableRange) -> @RenderBox {
|
||||
range: &const Range) -> @RenderBox {
|
||||
debug!("Creating textbox with span: (strlen=%u, off=%u, len=%u) of textrun: %s",
|
||||
run.text.len(), range.begin(), range.length(), run.text);
|
||||
let new_box_data = copy *box_data;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue