Unverified Commit 7fb35db6 authored by Nate's avatar Nate Committed by GitHub

Intensive `if` chain refactoring (#145194)

This pull request refactors if-statements into switch expressions, as part of the effort to solve issue #144903.

Making changes beyond just swapping syntax is more difficult (and also more difficult to review, I apologize), but much more satisfying too.
parent 859eb2ed
...@@ -97,7 +97,7 @@ class PaintTest extends CustomPainter { ...@@ -97,7 +97,7 @@ class PaintTest extends CustomPainter {
@override @override
void paint(Canvas canvas, Size size) { void paint(Canvas canvas, Size size) {
final double height = size.height; final double halfHeight = size.height / 2;
double x = 0; double x = 0;
const double strokeSize = .5; const double strokeSize = .5;
const double zoomFactor = .5; const double zoomFactor = .5;
...@@ -125,19 +125,12 @@ class PaintTest extends CustomPainter { ...@@ -125,19 +125,12 @@ class PaintTest extends CustomPainter {
final Float32List offsets = Float32List(consolidate ? waveData.length * 4 : 4); final Float32List offsets = Float32List(consolidate ? waveData.length * 4 : 4);
int used = 0; int used = 0;
for (index = 0; index < waveData.length; index++) { for (index = 0; index < waveData.length; index++) {
Paint curPaint; final (Paint curPaint, Offset p1) = switch (waveData[index]) {
Offset p1; < 0 => (paintPos, Offset(x, halfHeight * (1 - waveData[index] / 32768))),
if (waveData[index].isNegative) { > 0 => (paintNeg, Offset(x, halfHeight * (1 - waveData[index] / 32767))),
curPaint = paintPos; _ => (paintZero, Offset(x, halfHeight + 1)),
p1 = Offset(x, height * 1 / 2 - waveData[index] / 32768 * (height / 2)); };
} else if (waveData[index] == 0) { final Offset p0 = Offset(x, halfHeight);
curPaint = paintZero;
p1 = Offset(x, height * 1 / 2 + 1);
} else {
curPaint = (waveData[index] == 0) ? paintZero : paintNeg;
p1 = Offset(x, height * 1 / 2 - waveData[index] / 32767 * (height / 2));
}
final Offset p0 = Offset(x, height * 1 / 2);
if (consolidate) { if (consolidate) {
if (listPaint != null && listPaint != curPaint) { if (listPaint != null && listPaint != curPaint) {
canvas.drawRawPoints(PointMode.lines, offsets.sublist(0, used), listPaint); canvas.drawRawPoints(PointMode.lines, offsets.sublist(0, used), listPaint);
...@@ -179,7 +172,7 @@ class PaintSomeTest extends CustomPainter { ...@@ -179,7 +172,7 @@ class PaintSomeTest extends CustomPainter {
@override @override
void paint(Canvas canvas, Size size) { void paint(Canvas canvas, Size size) {
final double height = size.height; final double halfHeight = size.height / 2;
double x = 0; double x = 0;
const double strokeSize = .5; const double strokeSize = .5;
const double zoomFactor = .5; const double zoomFactor = .5;
...@@ -203,19 +196,12 @@ class PaintSomeTest extends CustomPainter { ...@@ -203,19 +196,12 @@ class PaintSomeTest extends CustomPainter {
..style = PaintingStyle.stroke; ..style = PaintingStyle.stroke;
for (int index = from; index <= to; index++) { for (int index = from; index <= to; index++) {
Paint curPaint; final (Paint curPaint, Offset p1) = switch (waveData[index]) {
Offset p1; < 0 => (paintPos, Offset(x, halfHeight * (1 - waveData[index] / 32768))),
if (waveData[index].isNegative) { > 0 => (paintNeg, Offset(x, halfHeight * (1 - waveData[index] / 32767))),
curPaint = paintPos; _ => (paintZero, Offset(x, halfHeight + 1)),
p1 = Offset(x, height * 1 / 2 - waveData[index] / 32768 * (height / 2)); };
} else if (waveData[index] == 0) { final Offset p0 = Offset(x, halfHeight);
curPaint = paintZero;
p1 = Offset(x, height * 1 / 2 + 1);
} else {
curPaint = (waveData[index] == 0) ? paintZero : paintNeg;
p1 = Offset(x, height * 1 / 2 - waveData[index] / 32767 * (height / 2));
}
final Offset p0 = Offset(x, height * 1 / 2);
canvas.drawLine(p0, p1, curPaint); canvas.drawLine(p0, p1, curPaint);
x += zoomFactor; x += zoomFactor;
} }
......
...@@ -474,58 +474,13 @@ const Color lerpToColor = Colors.red; ...@@ -474,58 +474,13 @@ const Color lerpToColor = Colors.red;
const BorderSide lerpToBorder = BorderSide(width: 5, color: lerpToColor); const BorderSide lerpToBorder = BorderSide(width: 5, color: lerpToColor);
ShapeBorder? lerpBorder(StarBorder border, LerpTarget target, double t, {bool to = true}) { ShapeBorder? lerpBorder(StarBorder border, LerpTarget target, double t, {bool to = true}) {
switch (target) { final OutlinedBorder targetBorder = switch (target) {
case LerpTarget.circle: LerpTarget.circle => const CircleBorder(side: lerpToBorder, eccentricity: 0.5),
if (to) { LerpTarget.rect => const RoundedRectangleBorder(side: lerpToBorder),
return border.lerpTo(const CircleBorder(side: lerpToBorder, eccentricity: 0.5), t); LerpTarget.stadium => const StadiumBorder(side: lerpToBorder),
} else { LerpTarget.polygon => const StarBorder.polygon(side: lerpToBorder, sides: 4),
return border.lerpFrom(const CircleBorder(side: lerpToBorder, eccentricity: 0.5), t); LerpTarget.star => const StarBorder(side: lerpToBorder, innerRadiusRatio: 0.5),
} LerpTarget.roundedRect => RoundedRectangleBorder(side: lerpToBorder, borderRadius: BorderRadius.circular(10)),
case LerpTarget.roundedRect: };
if (to) { return to ? border.lerpTo(targetBorder, t) : border.lerpFrom(targetBorder, t);
return border.lerpTo(
const RoundedRectangleBorder(
side: lerpToBorder,
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
t,
);
} else {
return border.lerpFrom(
const RoundedRectangleBorder(
side: lerpToBorder,
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
t,
);
}
case LerpTarget.rect:
if (to) {
return border.lerpTo(const RoundedRectangleBorder(side: lerpToBorder), t);
} else {
return border.lerpFrom(const RoundedRectangleBorder(side: lerpToBorder), t);
}
case LerpTarget.stadium:
if (to) {
return border.lerpTo(const StadiumBorder(side: lerpToBorder), t);
} else {
return border.lerpFrom(const StadiumBorder(side: lerpToBorder), t);
}
case LerpTarget.polygon:
if (to) {
return border.lerpTo(const StarBorder.polygon(side: lerpToBorder, sides: 4), t);
} else {
return border.lerpFrom(const StarBorder.polygon(side: lerpToBorder, sides: 4), t);
}
case LerpTarget.star:
if (to) {
return border.lerpTo(const StarBorder(side: lerpToBorder, innerRadiusRatio: .5), t);
} else {
return border.lerpFrom(const StarBorder(side: lerpToBorder, innerRadiusRatio: .5), t);
}
}
} }
...@@ -1080,16 +1080,11 @@ class DataTable extends StatelessWidget { ...@@ -1080,16 +1080,11 @@ class DataTable extends StatelessWidget {
for (int dataColumnIndex = 0; dataColumnIndex < columns.length; dataColumnIndex += 1) { for (int dataColumnIndex = 0; dataColumnIndex < columns.length; dataColumnIndex += 1) {
final DataColumn column = columns[dataColumnIndex]; final DataColumn column = columns[dataColumnIndex];
final double paddingStart; final double paddingStart = switch (dataColumnIndex) {
if (dataColumnIndex == 0 && displayCheckboxColumn && checkboxHorizontalMargin != null) { 0 when displayCheckboxColumn && checkboxHorizontalMargin == null => effectiveHorizontalMargin / 2.0,
paddingStart = effectiveHorizontalMargin; 0 => effectiveHorizontalMargin,
} else if (dataColumnIndex == 0 && displayCheckboxColumn) { _ => effectiveColumnSpacing / 2.0,
paddingStart = effectiveHorizontalMargin / 2.0; };
} else if (dataColumnIndex == 0 && !displayCheckboxColumn) {
paddingStart = effectiveHorizontalMargin;
} else {
paddingStart = effectiveColumnSpacing / 2.0;
}
final double paddingEnd; final double paddingEnd;
if (dataColumnIndex == columns.length - 1) { if (dataColumnIndex == columns.length - 1) {
......
...@@ -2427,130 +2427,35 @@ class _MenuDirectionalFocusAction extends DirectionalFocusAction { ...@@ -2427,130 +2427,35 @@ class _MenuDirectionalFocusAction extends DirectionalFocusAction {
return; return;
} }
final bool buttonIsFocused = anchor.widget.childFocusNode?.hasPrimaryFocus ?? false; final bool buttonIsFocused = anchor.widget.childFocusNode?.hasPrimaryFocus ?? false;
Axis orientation; final Axis? parentOrientation = anchor._parent?._orientation;
if (buttonIsFocused && anchor._parent != null) { final Axis orientation = (buttonIsFocused ? parentOrientation : null) ?? anchor._orientation;
orientation = anchor._parent!._orientation; final bool differentParent = orientation != parentOrientation;
} else {
orientation = anchor._orientation;
}
final bool firstItemIsFocused = anchor._firstItemFocusNode?.hasPrimaryFocus ?? false; final bool firstItemIsFocused = anchor._firstItemFocusNode?.hasPrimaryFocus ?? false;
final bool rtl = switch (Directionality.of(context)) {
TextDirection.rtl => true,
TextDirection.ltr => false,
};
assert(_debugMenuInfo('In _MenuDirectionalFocusAction, current node is ${anchor.widget.childFocusNode?.debugLabel}, ' assert(_debugMenuInfo('In _MenuDirectionalFocusAction, current node is ${anchor.widget.childFocusNode?.debugLabel}, '
'button is${buttonIsFocused ? '' : ' not'} focused. Assuming ${orientation.name} orientation.')); 'button is${buttonIsFocused ? '' : ' not'} focused. Assuming ${orientation.name} orientation.'));
switch (intent.direction) { final bool Function(_MenuAnchorState) traversal = switch ((intent.direction, orientation)) {
case TraversalDirection.up: (TraversalDirection.up, Axis.horizontal) => _moveToParent,
switch (orientation) { (TraversalDirection.up, Axis.vertical) => firstItemIsFocused ? _moveToParent: _moveToPrevious,
case Axis.horizontal: (TraversalDirection.down, Axis.horizontal) => _moveToSubmenu,
if (_moveToParent(anchor)) { (TraversalDirection.down, Axis.vertical) => _moveToNext,
return; (TraversalDirection.left, Axis.horizontal) => rtl ? _moveToNext : _moveToPrevious,
} (TraversalDirection.right, Axis.horizontal) => rtl ? _moveToPrevious : _moveToNext,
case Axis.vertical: (TraversalDirection.left, Axis.vertical) when rtl => buttonIsFocused ? _moveToSubmenu : _moveToNextFocusableTopLevel,
if (firstItemIsFocused) { (TraversalDirection.left, Axis.vertical) when differentParent => _moveToPreviousFocusableTopLevel,
if (_moveToParent(anchor)) { (TraversalDirection.left, Axis.vertical) => buttonIsFocused ? _moveToPreviousFocusableTopLevel : _moveToParent,
return; (TraversalDirection.right, Axis.vertical) when !rtl => buttonIsFocused ? _moveToSubmenu : _moveToNextFocusableTopLevel,
} (TraversalDirection.right, Axis.vertical) when differentParent => _moveToPreviousFocusableTopLevel,
} (TraversalDirection.right, Axis.vertical) => buttonIsFocused ? _moveToPreviousFocusableTopLevel : _moveToParent,
if (_moveToPrevious(anchor)) { };
return; if (!traversal(anchor)) {
} super.invoke(intent);
}
case TraversalDirection.down:
switch (orientation) {
case Axis.horizontal:
if (_moveToSubmenu(anchor)) {
return;
}
case Axis.vertical:
if (_moveToNext(anchor)) {
return;
}
}
case TraversalDirection.left:
switch (orientation) {
case Axis.horizontal:
switch (Directionality.of(context)) {
case TextDirection.rtl:
if (_moveToNext(anchor)) {
return;
}
case TextDirection.ltr:
if (_moveToPrevious(anchor)) {
return;
}
}
case Axis.vertical:
switch (Directionality.of(context)) {
case TextDirection.rtl:
if (buttonIsFocused) {
if (_moveToSubmenu(anchor)) {
return;
}
} else {
if (_moveToNextFocusableTopLevel(anchor)) {
return;
}
}
case TextDirection.ltr:
switch (anchor._parent?._orientation) {
case Axis.horizontal:
case null:
if (_moveToPreviousFocusableTopLevel(anchor)) {
return;
}
case Axis.vertical:
if (buttonIsFocused) {
if (_moveToPreviousFocusableTopLevel(anchor)) {
return;
}
} else {
if (_moveToParent(anchor)) {
return;
}
}
}
}
}
case TraversalDirection.right:
switch (orientation) {
case Axis.horizontal:
switch (Directionality.of(context)) {
case TextDirection.rtl:
if (_moveToPrevious(anchor)) {
return;
}
case TextDirection.ltr:
if (_moveToNext(anchor)) {
return;
}
}
case Axis.vertical:
switch (Directionality.of(context)) {
case TextDirection.rtl:
switch (anchor._parent?._orientation) {
case Axis.horizontal:
case null:
if (_moveToPreviousFocusableTopLevel(anchor)) {
return;
}
case Axis.vertical:
if (_moveToParent(anchor)) {
return;
}
}
case TextDirection.ltr:
if (buttonIsFocused) {
if (_moveToSubmenu(anchor)) {
return;
}
} else {
if (_moveToNextFocusableTopLevel(anchor)) {
return;
}
}
}
}
} }
super.invoke(intent);
} }
bool _moveToNext(_MenuAnchorState currentMenu) { bool _moveToNext(_MenuAnchorState currentMenu) {
......
...@@ -393,39 +393,22 @@ class _ReorderableListViewState extends State<ReorderableListView> { ...@@ -393,39 +393,22 @@ class _ReorderableListViewState extends State<ReorderableListView> {
// If there is a header or footer we can't just apply the padding to the list, // If there is a header or footer we can't just apply the padding to the list,
// so we break it up into padding for the header, footer and padding for the list. // so we break it up into padding for the header, footer and padding for the list.
final EdgeInsets padding = widget.padding ?? EdgeInsets.zero; final EdgeInsets padding = widget.padding ?? EdgeInsets.zero;
late final EdgeInsets headerPadding; double? start = widget.header == null ? null : 0.0;
late final EdgeInsets footerPadding; double? end = widget.footer == null ? null : 0.0;
late final EdgeInsets listPadding; if (widget.reverse) {
(start, end) = (end, start);
if (widget.header == null && widget.footer == null) {
headerPadding = EdgeInsets.zero;
footerPadding = EdgeInsets.zero;
listPadding = padding;
} else if (widget.header != null || widget.footer != null) {
switch (widget.scrollDirection) {
case Axis.horizontal:
if (widget.reverse) {
headerPadding = EdgeInsets.fromLTRB(0, padding.top, padding.right, padding.bottom);
listPadding = EdgeInsets.fromLTRB(widget.footer != null ? 0 : padding.left, padding.top, widget.header != null ? 0 : padding.right, padding.bottom);
footerPadding = EdgeInsets.fromLTRB(padding.left, padding.top, 0, padding.bottom);
} else {
headerPadding = EdgeInsets.fromLTRB(padding.left, padding.top, 0, padding.bottom);
listPadding = EdgeInsets.fromLTRB(widget.header != null ? 0 : padding.left, padding.top, widget.footer != null ? 0 : padding.right, padding.bottom);
footerPadding = EdgeInsets.fromLTRB(0, padding.top, padding.right, padding.bottom);
}
case Axis.vertical:
if (widget.reverse) {
headerPadding = EdgeInsets.fromLTRB(padding.left, 0, padding.right, padding.bottom);
listPadding = EdgeInsets.fromLTRB(padding.left, widget.footer != null ? 0 : padding.top, padding.right, widget.header != null ? 0 : padding.bottom);
footerPadding = EdgeInsets.fromLTRB(padding.left, padding.top, padding.right, 0);
} else {
headerPadding = EdgeInsets.fromLTRB(padding.left, padding.top, padding.right, 0);
listPadding = EdgeInsets.fromLTRB(padding.left, widget.header != null ? 0 : padding.top, padding.right, widget.footer != null ? 0 : padding.bottom);
footerPadding = EdgeInsets.fromLTRB(padding.left, 0, padding.right, padding.bottom);
}
}
} }
final EdgeInsets startPadding, endPadding, listPadding;
(startPadding, endPadding, listPadding) = switch (widget.scrollDirection) {
Axis.horizontal || Axis.vertical when (start ?? end) == null => (EdgeInsets.zero, EdgeInsets.zero, padding),
Axis.horizontal => (padding.copyWith(left: 0), padding.copyWith(right: 0), padding.copyWith(left: start, right: end)),
Axis.vertical => (padding.copyWith(top: 0), padding.copyWith(bottom: 0), padding.copyWith(top: start, bottom: end)),
};
final (EdgeInsets headerPadding, EdgeInsets footerPadding) = widget.reverse
? (startPadding, endPadding)
: (endPadding, startPadding);
return CustomScrollView( return CustomScrollView(
scrollDirection: widget.scrollDirection, scrollDirection: widget.scrollDirection,
reverse: widget.reverse, reverse: widget.reverse,
......
...@@ -689,29 +689,16 @@ class _SliderState extends State<Slider> with TickerProviderStateMixin { ...@@ -689,29 +689,16 @@ class _SliderState extends State<Slider> with TickerProviderStateMixin {
} }
void _actionHandler(_AdjustSliderIntent intent) { void _actionHandler(_AdjustSliderIntent intent) {
final _RenderSlider renderSlider = _renderObjectKey.currentContext!.findRenderObject()! as _RenderSlider; final TextDirection directionality = Directionality.of(_renderObjectKey.currentContext!);
final TextDirection textDirection = Directionality.of(_renderObjectKey.currentContext!); final bool shouldIncrease = switch (intent.type) {
_SliderAdjustmentType.up => true,
switch (intent.type) { _SliderAdjustmentType.down => false,
case _SliderAdjustmentType.right: _SliderAdjustmentType.left => directionality == TextDirection.rtl,
switch (textDirection) { _SliderAdjustmentType.right => directionality == TextDirection.ltr,
case TextDirection.rtl: };
renderSlider.decreaseAction();
case TextDirection.ltr: final _RenderSlider slider = _renderObjectKey.currentContext!.findRenderObject()! as _RenderSlider;
renderSlider.increaseAction(); return shouldIncrease ? slider.increaseAction() : slider.decreaseAction();
}
case _SliderAdjustmentType.left:
switch (textDirection) {
case TextDirection.rtl:
renderSlider.increaseAction();
case TextDirection.ltr:
renderSlider.decreaseAction();
}
case _SliderAdjustmentType.up:
renderSlider.increaseAction();
case _SliderAdjustmentType.down:
renderSlider.decreaseAction();
}
} }
bool _focused = false; bool _focused = false;
......
...@@ -1642,93 +1642,28 @@ class MediaQuery extends InheritedModel<_MediaQueryAspect> { ...@@ -1642,93 +1642,28 @@ class MediaQuery extends InheritedModel<_MediaQueryAspect> {
@override @override
bool updateShouldNotifyDependent(MediaQuery oldWidget, Set<Object> dependencies) { bool updateShouldNotifyDependent(MediaQuery oldWidget, Set<Object> dependencies) {
for (final Object dependency in dependencies) { return dependencies.any((Object dependency) => dependency is _MediaQueryAspect && switch (dependency) {
if (dependency is _MediaQueryAspect) { _MediaQueryAspect.size => data.size != oldWidget.data.size,
switch (dependency) { _MediaQueryAspect.orientation => data.orientation != oldWidget.data.orientation,
case _MediaQueryAspect.size: _MediaQueryAspect.devicePixelRatio => data.devicePixelRatio != oldWidget.data.devicePixelRatio,
if (data.size != oldWidget.data.size) { _MediaQueryAspect.textScaleFactor => data.textScaleFactor != oldWidget.data.textScaleFactor,
return true; _MediaQueryAspect.textScaler => data.textScaler != oldWidget.data.textScaler,
} _MediaQueryAspect.platformBrightness => data.platformBrightness != oldWidget.data.platformBrightness,
case _MediaQueryAspect.orientation: _MediaQueryAspect.padding => data.padding != oldWidget.data.padding,
if (data.orientation != oldWidget.data.orientation) { _MediaQueryAspect.viewInsets => data.viewInsets != oldWidget.data.viewInsets,
return true; _MediaQueryAspect.viewPadding => data.viewPadding != oldWidget.data.viewPadding,
} _MediaQueryAspect.invertColors => data.invertColors != oldWidget.data.invertColors,
case _MediaQueryAspect.devicePixelRatio: _MediaQueryAspect.highContrast => data.highContrast != oldWidget.data.highContrast,
if (data.devicePixelRatio != oldWidget.data.devicePixelRatio) { _MediaQueryAspect.onOffSwitchLabels => data.onOffSwitchLabels != oldWidget.data.onOffSwitchLabels,
return true; _MediaQueryAspect.disableAnimations => data.disableAnimations != oldWidget.data.disableAnimations,
} _MediaQueryAspect.boldText => data.boldText != oldWidget.data.boldText,
case _MediaQueryAspect.textScaleFactor: _MediaQueryAspect.navigationMode => data.navigationMode != oldWidget.data.navigationMode,
if (data.textScaleFactor != oldWidget.data.textScaleFactor) { _MediaQueryAspect.gestureSettings => data.gestureSettings != oldWidget.data.gestureSettings,
return true; _MediaQueryAspect.displayFeatures => data.displayFeatures != oldWidget.data.displayFeatures,
} _MediaQueryAspect.systemGestureInsets => data.systemGestureInsets != oldWidget.data.systemGestureInsets,
case _MediaQueryAspect.textScaler: _MediaQueryAspect.accessibleNavigation => data.accessibleNavigation != oldWidget.data.accessibleNavigation,
if (data.textScaler != oldWidget.data.textScaler) { _MediaQueryAspect.alwaysUse24HourFormat => data.alwaysUse24HourFormat != oldWidget.data.alwaysUse24HourFormat,
return true; });
}
case _MediaQueryAspect.platformBrightness:
if (data.platformBrightness != oldWidget.data.platformBrightness) {
return true;
}
case _MediaQueryAspect.padding:
if (data.padding != oldWidget.data.padding) {
return true;
}
case _MediaQueryAspect.viewInsets:
if (data.viewInsets != oldWidget.data.viewInsets) {
return true;
}
case _MediaQueryAspect.systemGestureInsets:
if (data.systemGestureInsets != oldWidget.data.systemGestureInsets) {
return true;
}
case _MediaQueryAspect.viewPadding:
if (data.viewPadding != oldWidget.data.viewPadding) {
return true;
}
case _MediaQueryAspect.alwaysUse24HourFormat:
if (data.alwaysUse24HourFormat != oldWidget.data.alwaysUse24HourFormat) {
return true;
}
case _MediaQueryAspect.accessibleNavigation:
if (data.accessibleNavigation != oldWidget.data.accessibleNavigation) {
return true;
}
case _MediaQueryAspect.invertColors:
if (data.invertColors != oldWidget.data.invertColors) {
return true;
}
case _MediaQueryAspect.highContrast:
if (data.highContrast != oldWidget.data.highContrast) {
return true;
}
case _MediaQueryAspect.onOffSwitchLabels:
if (data.onOffSwitchLabels != oldWidget.data.onOffSwitchLabels) {
return true;
}
case _MediaQueryAspect.disableAnimations:
if (data.disableAnimations != oldWidget.data.disableAnimations) {
return true;
}
case _MediaQueryAspect.boldText:
if (data.boldText != oldWidget.data.boldText) {
return true;
}
case _MediaQueryAspect.navigationMode:
if (data.navigationMode != oldWidget.data.navigationMode) {
return true;
}
case _MediaQueryAspect.gestureSettings:
if (data.gestureSettings != oldWidget.data.gestureSettings) {
return true;
}
case _MediaQueryAspect.displayFeatures:
if (data.displayFeatures != oldWidget.data.displayFeatures) {
return true;
}
}
}
}
return false;
} }
} }
......
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