Commit 1fc824db authored by ElenaSubbotina's avatar ElenaSubbotina

OdfFormatReader - fix custom oox shape

parent c18d5a82
...@@ -51,28 +51,28 @@ namespace cpdoccore ...@@ -51,28 +51,28 @@ namespace cpdoccore
{ {
struct _property struct _property
{ {
_property(std::wstring n, std::wstring s){name_ = n; val_ = s;} _property(std::wstring n, std::wstring s) { name_ = n; val_ = s;}
_property(std::wstring n, bool b) {name_ = n; val_ = b;} _property(std::wstring n, bool b) { name_ = n; val_ = b;}
_property(std::wstring n, int i){name_ = n; val_ = i;} _property(std::wstring n, int i) { name_ = n; val_ = i;}
_property(std::wstring n, double d){name_ = n;val_ = d;} _property(std::wstring n, double d) {name_ = n;val_ = d;}
std::wstring name_; std::wstring name_;
boost::variant<bool,std::wstring,double,int> val_; boost::variant<bool,std::wstring,double,int> val_;
}; };
template <class T> template <class T>
bool GetProperty(std::vector<_property> Heap, const std::wstring Name, T & Val) bool GetProperty(const std::vector<_property> & Heap, const std::wstring Name, T & Val)
{ {
typedef typename T::value_type T_value_type; typedef typename T::value_type T_value_type;
Val.reset(); Val.reset();
BOOST_FOREACH(_property const & p, Heap) for (int i = 0 ; i < Heap.size(); i++)
{ {
if (p.name_ == Name ) if (Heap[i].name_ == Name )
{ {
try try
{ {
Val = boost::get<T_value_type>(p.val_); Val = boost::get<T_value_type>(Heap[i].val_);
} }
catch(...) catch(...)
{ {
......
...@@ -439,20 +439,20 @@ void docx_serialize_wps(std::wostream & strm, _docx_drawing & val) ...@@ -439,20 +439,20 @@ void docx_serialize_wps(std::wostream & strm, _docx_drawing & val)
CP_XML_NODE(L"wp:positionH") CP_XML_NODE(L"wp:positionH")
{ {
std::wstring relativeFrom = L"margin"; std::wstring relativeFrom = L"margin";
if (val.styleHorizontalRel)relativeFrom =val.styleHorizontalRel->get_type_str(); if (val.styleHorizontalRel) relativeFrom =val.styleHorizontalRel->get_type_str();
CP_XML_ATTR(L"relativeFrom",relativeFrom); CP_XML_ATTR(L"relativeFrom", relativeFrom);
if (val.styleHorizontalPos && if (val.styleHorizontalPos &&
val.styleHorizontalPos->get_type() != odf_types::horizontal_pos::FromLeft && val.styleHorizontalPos->get_type() != odf_types::horizontal_pos::FromLeft &&
val.styleHorizontalPos->get_type() != odf_types::horizontal_pos::Outside) val.styleHorizontalPos->get_type() != odf_types::horizontal_pos::Outside)
{ {
CP_XML_NODE(L"wp:align"){CP_XML_STREAM() << boost::lexical_cast<std::wstring>(*val.styleHorizontalPos);} CP_XML_NODE(L"wp:align") {CP_XML_STREAM() << boost::lexical_cast<std::wstring>(*val.styleHorizontalPos);}
} }
else else
{ {
CP_XML_NODE(L"wp:posOffset"){CP_XML_STREAM() << val.posOffsetH;} CP_XML_NODE(L"wp:posOffset") {CP_XML_STREAM() << val.posOffsetH;}
} }
} }
......
...@@ -332,7 +332,11 @@ void oox_serialize_shape(std::wostream & strm, _oox_drawing & val) ...@@ -332,7 +332,11 @@ void oox_serialize_shape(std::wostream & strm, _oox_drawing & val)
{ {
_CP_OPT(int) iVal; _CP_OPT(int) iVal;
odf_reader::GetProperty(val.additional, L"odf-custom-draw-index",iVal); odf_reader::GetProperty(val.additional, L"odf-custom-draw-index",iVal);
if (iVal)shapeType = _OO_OOX_custom_shapes[*iVal].oox;
if (iVal)
shapeType = _OO_OOX_custom_shapes[*iVal].oox;
else
val.sub_type = 6; //path
if (shapeType == L"textBox") if (shapeType == L"textBox")
{ {
...@@ -340,16 +344,11 @@ void oox_serialize_shape(std::wostream & strm, _oox_drawing & val) ...@@ -340,16 +344,11 @@ void oox_serialize_shape(std::wostream & strm, _oox_drawing & val)
shapeType = L"rect"; shapeType = L"rect";
} }
} }
else if (val.sub_type<9 && val.sub_type>=0) else if (val.sub_type < 9 && val.sub_type >= 0)
{ {
shapeType = _ooxShapeType[val.sub_type]; shapeType = _ooxShapeType[val.sub_type];
} }
if (shapeType.length()<1)
{
shapeType = L"rect";
val.sub_type = 2;
}
if (bWordArt) val.sub_type = 1; if (bWordArt) val.sub_type = 1;
CP_XML_WRITER(strm) CP_XML_WRITER(strm)
...@@ -358,7 +357,8 @@ void oox_serialize_shape(std::wostream & strm, _oox_drawing & val) ...@@ -358,7 +357,8 @@ void oox_serialize_shape(std::wostream & strm, _oox_drawing & val)
{ {
CP_XML_NODE(L"a:custGeom") CP_XML_NODE(L"a:custGeom")
{ {
oox_serialize_aLst(CP_XML_STREAM(),val.additional); oox_serialize_aLst(CP_XML_STREAM(), val.additional);
CP_XML_NODE(L"a:ahLst"); CP_XML_NODE(L"a:ahLst");
CP_XML_NODE(L"a:gdLst"); CP_XML_NODE(L"a:gdLst");
CP_XML_NODE(L"a:rect") CP_XML_NODE(L"a:rect")
...@@ -369,14 +369,18 @@ void oox_serialize_shape(std::wostream & strm, _oox_drawing & val) ...@@ -369,14 +369,18 @@ void oox_serialize_shape(std::wostream & strm, _oox_drawing & val)
CP_XML_ATTR(L"t",0); CP_XML_ATTR(L"t",0);
} }
//<a:rect b="b" l="0" r="r" t="0"/> //<a:rect b="b" l="0" r="r" t="0"/>
if (odf_reader::GetProperty(val.additional,L"custom_path",strVal)) if (odf_reader::GetProperty(val.additional, L"custom_path", strVal))
{ {
_CP_OPT(int) w, h;
odf_reader::GetProperty(val.additional, L"custom_path_w", w);
odf_reader::GetProperty(val.additional, L"custom_path_h", h);
CP_XML_NODE(L"a:pathLst") CP_XML_NODE(L"a:pathLst")
{ {
CP_XML_NODE(L"a:path") CP_XML_NODE(L"a:path")
{ {
CP_XML_ATTR(L"w", val.cx); CP_XML_ATTR(L"w", w ? *w : val.cx);
CP_XML_ATTR(L"h", val.cy); CP_XML_ATTR(L"h", h ? *h : val.cy);
CP_XML_STREAM() << strVal.get(); CP_XML_STREAM() << strVal.get();
} }
} }
...@@ -385,6 +389,11 @@ void oox_serialize_shape(std::wostream & strm, _oox_drawing & val) ...@@ -385,6 +389,11 @@ void oox_serialize_shape(std::wostream & strm, _oox_drawing & val)
} }
else else
{ {
if (shapeType.length() < 1)
{
shapeType = L"rect";
val.sub_type = 2;
}
CP_XML_NODE(L"a:prstGeom")//автофигура CP_XML_NODE(L"a:prstGeom")//автофигура
{ {
CP_XML_ATTR(L"prst", shapeType); CP_XML_ATTR(L"prst", shapeType);
......
...@@ -519,10 +519,10 @@ void draw_a::add_attributes( const xml::attributes_wc_ptr & Attributes ) ...@@ -519,10 +519,10 @@ void draw_a::add_attributes( const xml::attributes_wc_ptr & Attributes )
{ {
common_xlink_attlist_.add_attributes(Attributes); common_xlink_attlist_.add_attributes(Attributes);
CP_APPLY_ATTR(L"office:name", office_name_, std::wstring(L"")); CP_APPLY_ATTR(L"office:name" , office_name_ , std::wstring(L""));
CP_APPLY_ATTR(L"office:target-frame-name", office_target_frame_name_); CP_APPLY_ATTR(L"office:target-frame-name" , office_target_frame_name_);
CP_APPLY_ATTR(L"text:style-name", text_style_name_, style_ref(L"")); CP_APPLY_ATTR(L"text:style-name" , text_style_name_ , style_ref(L""));
CP_APPLY_ATTR(L"text:visited-style-name", text_visited_style_name_, style_ref(L"")); CP_APPLY_ATTR(L"text:visited-style-name" , text_visited_style_name_ , style_ref(L""));
} }
...@@ -582,10 +582,10 @@ void oox_convert_transforms(std::wstring transformStr,std::vector<odf_reader::_p ...@@ -582,10 +582,10 @@ void oox_convert_transforms(std::wstring transformStr,std::vector<odf_reader::_p
boost::algorithm::split(transform,t, boost::algorithm::is_any_of(L"("), boost::algorithm::token_compress_on); boost::algorithm::split(transform,t, boost::algorithm::is_any_of(L"("), boost::algorithm::token_compress_on);
if (transform.size()>1)//тока с аргументами if (transform.size() > 1)//тока с аргументами
{ {
int res=0; int res=0;
if ((res = transform[0].find(L"translate"))>=0)//перемещение if ((res = transform[0].find(L"translate")) >= 0) //перемещение
{ {
std::vector<length> Points ; std::vector<length> Points ;
parse_string_to_points(transform[1], Points); parse_string_to_points(transform[1], Points);
...@@ -594,11 +594,12 @@ void oox_convert_transforms(std::wstring transformStr,std::vector<odf_reader::_p ...@@ -594,11 +594,12 @@ void oox_convert_transforms(std::wstring transformStr,std::vector<odf_reader::_p
{ {
double x_pt = Points[0].get_value_unit(length::pt); double x_pt = Points[0].get_value_unit(length::pt);
double y_pt = 0; double y_pt = 0;
if (Points.size()>1)y_pt = Points[1].get_value_unit(length::pt);//ее может не быть
if (Points.size()>1) y_pt = Points[1].get_value_unit(length::pt); //ее может не быть
//Context.get_drawing_context().set_translate(x_pt,y_pt); //Context.get_drawing_context().set_translate(x_pt,y_pt);
additional.push_back(_property(L"svg:translate_x",x_pt)); additional.push_back(_property(L"svg:translate_x", x_pt));
additional.push_back(_property(L"svg:translate_y",y_pt)); additional.push_back(_property(L"svg:translate_y", y_pt));
} }
} }
else if ((res = transform[0].find(L"scale"))>=0)//масштабирование else if ((res = transform[0].find(L"scale"))>=0)//масштабирование
...@@ -612,14 +613,14 @@ void oox_convert_transforms(std::wstring transformStr,std::vector<odf_reader::_p ...@@ -612,14 +613,14 @@ void oox_convert_transforms(std::wstring transformStr,std::vector<odf_reader::_p
if (Points.size()>1)y_pt = Points[1].get_value_unit(length::pt);//ее может не быть if (Points.size()>1)y_pt = Points[1].get_value_unit(length::pt);//ее может не быть
//Context.get_drawing_context().set_scale(x_pt,y_pt); //Context.get_drawing_context().set_scale(x_pt,y_pt);
additional.push_back(_property(L"svg:scale_x",x_pt)); additional.push_back(_property(L"svg:scale_x", x_pt));
additional.push_back(_property(L"svg:scale_y",y_pt)); additional.push_back(_property(L"svg:scale_y", y_pt));
} }
} }
else if ((res = transform[0].find(L"rotate"))>=0)//вращение else if ((res = transform[0].find(L"rotate"))>=0)//вращение
{ {
double angle = boost::lexical_cast<double>(transform[1]); double angle = boost::lexical_cast<double>(transform[1]);
additional.push_back(_property(L"svg:rotate",angle)); additional.push_back(_property(L"svg:rotate", angle));
} }
} }
} }
......
...@@ -434,6 +434,8 @@ void draw_enhanced_geometry_attlist::add_attributes( const xml::attributes_wc_pt ...@@ -434,6 +434,8 @@ void draw_enhanced_geometry_attlist::add_attributes( const xml::attributes_wc_pt
CP_APPLY_ATTR(L"draw:type" , draw_type_); CP_APPLY_ATTR(L"draw:type" , draw_type_);
CP_APPLY_ATTR(L"draw:modifiers" , draw_modifiers_); CP_APPLY_ATTR(L"draw:modifiers" , draw_modifiers_);
CP_APPLY_ATTR(L"draw:text-path" , draw_text_path_); CP_APPLY_ATTR(L"draw:text-path" , draw_text_path_);
CP_APPLY_ATTR(L"draw:enhanced-path" , draw_enhanced_path_);
CP_APPLY_ATTR(L"drawooo:sub-view-size" , drawooo_sub_view_size_);
} }
// draw:enhanced_geometry // draw:enhanced_geometry
const wchar_t * draw_enhanced_geometry::ns = L"draw"; const wchar_t * draw_enhanced_geometry::ns = L"draw";
...@@ -490,13 +492,15 @@ void draw_enhanced_geometry::find_draw_type_oox() ...@@ -490,13 +492,15 @@ void draw_enhanced_geometry::find_draw_type_oox()
draw_type_oox_index_ = i; draw_type_oox_index_ = i;
break; break;
} }
} }
} }
else else
{ {
int count = sizeof(_OO_OOX_custom_shapes) / sizeof(_shape_converter); int count = sizeof(_OO_OOX_custom_shapes) / sizeof(_shape_converter);
int pos = odf_type.find(L"ooxml-");
if (pos < 0)
{
for (long i=0; i< count; i++) for (long i=0; i< count; i++)
{ {
if (_OO_OOX_custom_shapes[i].odf_reader == odf_type) if (_OO_OOX_custom_shapes[i].odf_reader == odf_type)
...@@ -504,17 +508,26 @@ void draw_enhanced_geometry::find_draw_type_oox() ...@@ -504,17 +508,26 @@ void draw_enhanced_geometry::find_draw_type_oox()
draw_type_oox_index_ = i; draw_type_oox_index_ = i;
break; break;
} }
}
}
else
{
std::wstring oox_type = odf_type.substr(pos + 6);
for (long i=0; i< count; i++)
{
if (_OO_OOX_custom_shapes[i].oox == oox_type)
{
draw_type_oox_index_ = i;
break;
}
}
} }
if ((draw_type_oox_index_) && (*draw_type_oox_index_== 179))//L"textBox" if ((draw_type_oox_index_) && (*draw_type_oox_index_== 179))//L"textBox"
{ {
sub_type_ = 1;//textBox sub_type_ = 1;//textBox
} }
} }
} }
std::wstringstream str; std::wstringstream str;
BOOST_FOREACH(const office_element_ptr & parElement, draw_handle_) BOOST_FOREACH(const office_element_ptr & parElement, draw_handle_)
...@@ -527,19 +540,19 @@ void draw_enhanced_geometry::find_draw_type_oox() ...@@ -527,19 +540,19 @@ void draw_enhanced_geometry::find_draw_type_oox()
try try
{ {
min = parsing(handle->draw_handle_attlist_.draw_handle_range_y_minimum_);//пока статик .. и выдается только цыфровое значение min = parsing(handle->draw_handle_attlist_.draw_handle_range_y_minimum_);//пока статик .. и выдается только цыфровое значение
if (min<0)min =parsing(handle->draw_handle_attlist_.draw_handle_range_x_minimum_); if (min<0)min = parsing(handle->draw_handle_attlist_.draw_handle_range_x_minimum_);
if (min<0)min = parsing(handle->draw_handle_attlist_.draw_handle_radius_range_minimum_); if (min<0)min = parsing(handle->draw_handle_attlist_.draw_handle_radius_range_minimum_);
} }
catch(...) catch(...)
{ {
} }
if (min<0)min=0; if (min <0 ) min=0;
try try
{ {
max = parsing(handle->draw_handle_attlist_.draw_handle_range_y_maximum_); max = parsing(handle->draw_handle_attlist_.draw_handle_range_y_maximum_);
if (max<0)max = parsing(handle->draw_handle_attlist_.draw_handle_range_x_maximum_); if (max < 0) max = parsing(handle->draw_handle_attlist_.draw_handle_range_x_maximum_);
if (max<0)max = parsing(handle->draw_handle_attlist_.draw_handle_radius_range_maximum_); if (max < 0) max = parsing(handle->draw_handle_attlist_.draw_handle_radius_range_maximum_);
} }
catch(...) catch(...)
{ {
...@@ -605,11 +618,11 @@ void draw_connector::reset_svg_path() ...@@ -605,11 +618,11 @@ void draw_connector::reset_svg_path()
{ {
if (poly.points[i].x) if (poly.points[i].x)
{ {
poly.points[i].x = length(poly.points[i].x.get()/1000.,length::cm).get_value_unit(length::emu)-x1; poly.points[i].x = length(poly.points[i].x.get()/1000.,length::cm).get_value_unit(length::emu) - x1;
} }
if (poly.points[i].y) if (poly.points[i].y)
{ {
poly.points[i].y = length(poly.points[i].y.get()/1000.,length::cm).get_value_unit(length::emu)-y1; poly.points[i].y = length(poly.points[i].y.get()/1000.,length::cm).get_value_unit(length::emu) - y1;
} }
} }
o_Polyline_pt.push_back(poly); o_Polyline_pt.push_back(poly);
......
...@@ -436,6 +436,8 @@ public: ...@@ -436,6 +436,8 @@ public:
_CP_OPT(std::wstring) draw_type_; _CP_OPT(std::wstring) draw_type_;
_CP_OPT(std::wstring) draw_modifiers_; _CP_OPT(std::wstring) draw_modifiers_;
_CP_OPT(bool) draw_text_path_; _CP_OPT(bool) draw_text_path_;
_CP_OPT(std::wstring) draw_enhanced_path_;
_CP_OPT(std::wstring) drawooo_sub_view_size_;
}; };
///////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////
class draw_enhanced_geometry : public office_element_impl<draw_enhanced_geometry> class draw_enhanced_geometry : public office_element_impl<draw_enhanced_geometry>
...@@ -466,11 +468,11 @@ public: ...@@ -466,11 +468,11 @@ public:
typedef std::pair<std::wstring,std::wstring> pair_string_value; typedef std::pair<std::wstring,std::wstring> pair_string_value;
std::vector<draw_handle_geometry> draw_handle_geometry_; //параметры в удобноваримом виде std::vector<draw_handle_geometry> draw_handle_geometry_;
std::vector<pair_string_value>draw_equation_array_; std::vector<pair_string_value> draw_equation_array_;
office_element_ptr_array draw_handle_; office_element_ptr_array draw_handle_;
office_element_ptr_array draw_equation_;//некоторые заданные параметры отрисовки которые используются в draw_handle - автозамена общих частей office_element_ptr_array draw_equation_;
static int parsing(_CP_OPT(std::wstring) val); static int parsing(_CP_OPT(std::wstring) val);
......
...@@ -245,10 +245,6 @@ void draw_enhanced_geometry::docx_convert(oox::docx_conversion_context & Context ...@@ -245,10 +245,6 @@ void draw_enhanced_geometry::docx_convert(oox::docx_conversion_context & Context
shape->word_art_ = word_art_; shape->word_art_ = word_art_;
if (sub_type_)
{
shape->sub_type_ = sub_type_.get();
}
if (draw_type_oox_index_) if (draw_type_oox_index_)
{ {
shape->additional_.push_back(_property(L"odf-custom-draw-index", draw_type_oox_index_.get())); shape->additional_.push_back(_property(L"odf-custom-draw-index", draw_type_oox_index_.get()));
...@@ -257,7 +253,39 @@ void draw_enhanced_geometry::docx_convert(oox::docx_conversion_context & Context ...@@ -257,7 +253,39 @@ void draw_enhanced_geometry::docx_convert(oox::docx_conversion_context & Context
shape->additional_.push_back(_property(L"wordArt", true)); shape->additional_.push_back(_property(L"wordArt", true));
} }
if (draw_enhanced_geometry_attlist_.draw_modifiers_) if (sub_type_)
{
shape->sub_type_ = sub_type_.get();
}
if (draw_enhanced_geometry_attlist_.draw_enhanced_path_)
{
std::vector<svg_path::_polyline> o_Polyline;
bool res = svg_path::parseSvgD(o_Polyline, draw_enhanced_geometry_attlist_.draw_enhanced_path_.get(), true);
if (o_Polyline.size()>0)
{
//сформируем xml-oox сдесь ... а то придется плодить массивы в drawing .. хоть и не красиво..
std::wstringstream output_;
svg_path::oox_serialize(output_, o_Polyline);
shape->additional_.push_back(odf_reader::_property(L"custom_path", output_.str()));
if (draw_enhanced_geometry_attlist_.drawooo_sub_view_size_)
{
int pos = draw_enhanced_geometry_attlist_.drawooo_sub_view_size_->find(L" ");
if (pos >= 0)
{
int w = boost::lexical_cast<int>(draw_enhanced_geometry_attlist_.drawooo_sub_view_size_->substr(0, pos));
int h = boost::lexical_cast<int>(draw_enhanced_geometry_attlist_.drawooo_sub_view_size_->substr(pos + 1));
shape->additional_.push_back(odf_reader::_property(L"custom_path_w", w));
shape->additional_.push_back(odf_reader::_property(L"custom_path_h", h));
}
}
}
}
else if (draw_enhanced_geometry_attlist_.draw_modifiers_)
{ {
shape->additional_.push_back(_property(L"draw-modifiers",draw_enhanced_geometry_attlist_.draw_modifiers_.get())); shape->additional_.push_back(_property(L"draw-modifiers",draw_enhanced_geometry_attlist_.draw_modifiers_.get()));
if (draw_handle_geometry_.size()>0) if (draw_handle_geometry_.size()>0)
...@@ -268,7 +296,6 @@ void draw_enhanced_geometry::docx_convert(oox::docx_conversion_context & Context ...@@ -268,7 +296,6 @@ void draw_enhanced_geometry::docx_convert(oox::docx_conversion_context & Context
shape->additional_.push_back(_property(L"draw-modifiers-max",draw_handle_geometry_[0].max)); shape->additional_.push_back(_property(L"draw-modifiers-max",draw_handle_geometry_[0].max));
} }
} }
} }
} }
} }
......
...@@ -224,9 +224,8 @@ namespace svg_path ...@@ -224,9 +224,8 @@ namespace svg_path
nLastX = aFirst.x.get(); nLastX = aFirst.x.get();
nLastY = aFirst.y.get(); nLastY = aFirst.y.get();
} }
break; aCurrPoly.command=L"a:close";
} } break;
case 'm' : case 'm' :
case 'M' : case 'M' :
{ {
...@@ -260,7 +259,6 @@ namespace svg_path ...@@ -260,7 +259,6 @@ namespace svg_path
aCurrPoly.points.clear(); aCurrPoly.points.clear();
} }
} }
nPos++; nPos++;
skipSpaces(nPos, rSvgDStatement, nLen); skipSpaces(nPos, rSvgDStatement, nLen);
...@@ -286,9 +284,7 @@ namespace svg_path ...@@ -286,9 +284,7 @@ namespace svg_path
Polyline.push_back(aCurrPoly); Polyline.push_back(aCurrPoly);
aCurrPoly.points.clear(); aCurrPoly.points.clear();
} }
break; }break;
}
case 'h' : case 'h' :
{ {
bRelative = true; bRelative = true;
...@@ -405,8 +401,7 @@ namespace svg_path ...@@ -405,8 +401,7 @@ namespace svg_path
nLastControlX = nX2; nLastControlX = nX2;
nLastControlY = nY2; nLastControlY = nY2;
} }
break; }break;
}
case 'c' : case 'c' :
{ {
...@@ -458,8 +453,7 @@ namespace svg_path ...@@ -458,8 +453,7 @@ namespace svg_path
nLastControlX = nX2; nLastControlX = nX2;
nLastControlY = nY2; nLastControlY = nY2;
} }
break; }break;
}
// #100617# quadratic beziers are imported as cubic ones // #100617# quadratic beziers are imported as cubic ones
//case 'q' : //case 'q' :
...@@ -760,7 +754,7 @@ namespace svg_path ...@@ -760,7 +754,7 @@ namespace svg_path
} }
} }
if(aCurrPoly.points.size()>0) if(aCurrPoly.points.size() > 0 || bIsClosed)
{ {
// end-process last poly // end-process last poly
if(bIsClosed) if(bIsClosed)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment