Commit 6d8bc5cc authored by chocobo@chromium.org's avatar chocobo@chromium.org

Cache network connecting bitmaps.

Improve performance by reducing the number of bitmap copying.
BUG=chromium-os:12543
TEST=make sure network connecting looks the same
Review URL: http://codereview.chromium.org/6591017

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@76318 0039d316-1c4b-4281-b951-d872f2087c98
parent ef774340
......@@ -50,8 +50,8 @@ NetworkDropdownButton::~NetworkDropdownButton() {
void NetworkDropdownButton::AnimationProgressed(
const ui::Animation* animation) {
if (animation == &animation_connecting_) {
SetIcon(IconForNetworkConnecting(animation_connecting_.GetCurrentValue(),
true));
SetIcon(*IconForNetworkConnecting(animation_connecting_.GetCurrentValue(),
true));
SchedulePaint();
} else {
MenuButton::AnimationProgressed(animation);
......@@ -86,12 +86,12 @@ void NetworkDropdownButton::OnNetworkManagerChanged(NetworkLibrary* cros) {
} else if (active_network->type() == TYPE_WIFI) {
const WifiNetwork* wifi =
static_cast<const WifiNetwork*>(active_network);
SetIcon(IconForNetworkStrength(wifi, true));
SetIcon(*IconForNetworkStrength(wifi, true));
SetText(ASCIIToWide(wifi->name()));
} else if (active_network->type() == TYPE_CELLULAR) {
const CellularNetwork* cellular =
static_cast<const CellularNetwork*>(active_network);
SetIcon(IconForNetworkStrength(cellular, true));
SetIcon(*IconForNetworkStrength(cellular, true));
SetText(ASCIIToWide(cellular->name()));
} else {
NOTREACHED();
......@@ -100,7 +100,7 @@ void NetworkDropdownButton::OnNetworkManagerChanged(NetworkLibrary* cros) {
if (!animation_connecting_.is_animating()) {
animation_connecting_.Reset();
animation_connecting_.StartThrobbing(-1);
SetIcon(IconForNetworkConnecting(0, true));
SetIcon(*IconForNetworkConnecting(0, true));
}
if (cros->wifi_connecting())
SetText(ASCIIToWide(cros->wifi_network()->name()));
......
......@@ -80,6 +80,15 @@ const int NetworkMenu::kBarsImagesVLowData[kNumBarsImages] = {
};
*/
// static
const int NetworkMenu::kNumAnimatingImages = 10;
// static
SkBitmap NetworkMenu::kAnimatingImages[kNumAnimatingImages];
// static
SkBitmap NetworkMenu::kAnimatingImagesBlack[kNumAnimatingImages];
NetworkMenu::NetworkMenu()
: min_width_(-1) {
use_settings_ui_ = !CommandLine::ForCurrentProcess()->HasSwitch(
......@@ -355,11 +364,11 @@ void NetworkMenu::UpdateMenu() {
}
// static
SkBitmap NetworkMenu::IconForNetworkStrength(const WifiNetwork* wifi,
bool black) {
const SkBitmap* NetworkMenu::IconForNetworkStrength(const WifiNetwork* wifi,
bool black) {
DCHECK(wifi);
if (wifi->strength() == 0) {
return *ResourceBundle::GetSharedInstance().GetBitmapNamed(
return ResourceBundle::GetSharedInstance().GetBitmapNamed(
black ? IDR_STATUSBAR_NETWORK_BARS0_BLACK :
IDR_STATUSBAR_NETWORK_BARS0);
}
......@@ -367,17 +376,17 @@ SkBitmap NetworkMenu::IconForNetworkStrength(const WifiNetwork* wifi,
nextafter(static_cast<float>(kNumBarsImages), 0));
index = std::max(std::min(index, kNumBarsImages - 1), 0);
const int* images = black ? kBarsImagesBlack : kBarsImages;
return *ResourceBundle::GetSharedInstance().GetBitmapNamed(images[index]);
return ResourceBundle::GetSharedInstance().GetBitmapNamed(images[index]);
}
// static
SkBitmap NetworkMenu::IconForNetworkStrength(const CellularNetwork* cellular,
bool black) {
const SkBitmap* NetworkMenu::IconForNetworkStrength(
const CellularNetwork* cellular, bool black) {
DCHECK(cellular);
// If no data, then we show 0 bars.
if (cellular->strength() == 0 ||
cellular->data_left() == CellularNetwork::DATA_NONE) {
return *ResourceBundle::GetSharedInstance().GetBitmapNamed(
return ResourceBundle::GetSharedInstance().GetBitmapNamed(
black ? IDR_STATUSBAR_NETWORK_BARS0_BLACK :
IDR_STATUSBAR_NETWORK_BARS0);
}
......@@ -385,30 +394,43 @@ SkBitmap NetworkMenu::IconForNetworkStrength(const CellularNetwork* cellular,
nextafter(static_cast<float>(kNumBarsImages), 0));
index = std::max(std::min(index, kNumBarsImages - 1), 0);
const int* images = black ? kBarsImagesBlack : kBarsImages;
return *ResourceBundle::GetSharedInstance().GetBitmapNamed(images[index]);
return ResourceBundle::GetSharedInstance().GetBitmapNamed(images[index]);
}
// static
SkBitmap NetworkMenu::IconForNetworkConnecting(double animation_value,
bool black) {
const SkBitmap* NetworkMenu::IconForNetworkConnecting(double animation_value,
bool black) {
// Draw animation of bars icon fading in and out.
// We are fading between 0 bars and a third of the opacity of 4 bars.
// Use the current value of the animation to calculate the alpha value
// of how transparent the icon is.
ResourceBundle& rb = ResourceBundle::GetSharedInstance();
return SkBitmapOperations::CreateBlendedBitmap(
*rb.GetBitmapNamed(black ? IDR_STATUSBAR_NETWORK_BARS0_BLACK :
IDR_STATUSBAR_NETWORK_BARS0),
*rb.GetBitmapNamed(black ? IDR_STATUSBAR_NETWORK_BARS4_BLACK :
IDR_STATUSBAR_NETWORK_BARS4),
animation_value / 3);
int index = static_cast<int>(animation_value *
nextafter(static_cast<float>(kNumAnimatingImages), 0));
index = std::max(std::min(index, kNumAnimatingImages - 1), 0);
SkBitmap* images = black ? kAnimatingImagesBlack : kAnimatingImages;
// Lazily cache images.
if (images[index].empty()) {
// Divide index (0-9) by 9 (assume kNumAnimatingImages==10) to get (0.0-1.0)
// Then we take a third of that for the alpha value.
double alpha = (static_cast<double>(index) / (kNumAnimatingImages - 1)) / 3;
ResourceBundle& rb = ResourceBundle::GetSharedInstance();
images[index] = SkBitmapOperations::CreateBlendedBitmap(
*rb.GetBitmapNamed(black ? IDR_STATUSBAR_NETWORK_BARS0_BLACK :
IDR_STATUSBAR_NETWORK_BARS0),
*rb.GetBitmapNamed(black ? IDR_STATUSBAR_NETWORK_BARS4_BLACK :
IDR_STATUSBAR_NETWORK_BARS4),
alpha);
}
return &images[index];
}
// static
SkBitmap NetworkMenu::BadgeForNetworkTechnology(
const SkBitmap* NetworkMenu::BadgeForNetworkTechnology(
const CellularNetwork* cellular) {
if (!cellular)
return SkBitmap();
return NULL;
int id = -1;
switch (cellular->network_technology()) {
......@@ -471,21 +493,25 @@ SkBitmap NetworkMenu::BadgeForNetworkTechnology(
break;
}
if (id == -1)
return SkBitmap();
return NULL;
else
return *ResourceBundle::GetSharedInstance().GetBitmapNamed(id);
return ResourceBundle::GetSharedInstance().GetBitmapNamed(id);
}
// static
SkBitmap NetworkMenu::IconForDisplay(SkBitmap icon, SkBitmap badge) {
SkBitmap NetworkMenu::IconForDisplay(const SkBitmap* icon,
const SkBitmap* badge) {
DCHECK(icon);
if (badge == NULL)
return *icon;
// Draw badge at (14,14).
static const int kBadgeX = 14;
static const int kBadgeY = 14;
gfx::CanvasSkia canvas(icon.width(), icon.height(), false);
canvas.DrawBitmapInt(icon, 0, 0);
if (!badge.empty())
canvas.DrawBitmapInt(badge, kBadgeX, kBadgeY);
gfx::CanvasSkia canvas(icon->width(), icon->height(), false);
canvas.DrawBitmapInt(*icon, 0, 0);
canvas.DrawBitmapInt(*badge, kBadgeX, kBadgeY);
return canvas.ExtractBitmap();
}
......@@ -546,9 +572,9 @@ void NetworkMenu::InitMenuItems() {
} else {
label = l10n_util::GetStringUTF16(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET);
}
SkBitmap icon = *rb.GetBitmapNamed(IDR_STATUSBAR_WIRED_BLACK);
SkBitmap badge = ethernet_connecting || ethernet_connected ?
SkBitmap() : *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_DISCONNECTED);
const SkBitmap* icon = rb.GetBitmapNamed(IDR_STATUSBAR_WIRED_BLACK);
const SkBitmap* badge = ethernet_connecting || ethernet_connected ?
NULL : rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_DISCONNECTED);
int flag = FLAG_ETHERNET;
if (ethernet_connecting || ethernet_connected)
flag |= FLAG_ASSOCIATED;
......@@ -584,9 +610,9 @@ void NetworkMenu::InitMenuItems() {
}
}
SkBitmap icon = IconForNetworkStrength(wifi_networks[i], true);
SkBitmap badge = wifi_networks[i]->encrypted() ?
*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE) : SkBitmap();
const SkBitmap* icon = IconForNetworkStrength(wifi_networks[i], true);
const SkBitmap* badge = wifi_networks[i]->encrypted() ?
rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE) : NULL;
int flag = FLAG_WIFI;
if (!wifi_networks[i]->connectable())
flag |= FLAG_DISABLED;
......@@ -644,8 +670,8 @@ void NetworkMenu::InitMenuItems() {
}
}
SkBitmap icon = IconForNetworkStrength(cell_networks[i], true);
SkBitmap badge = BadgeForNetworkTechnology(cell_networks[i]);
const SkBitmap* icon = IconForNetworkStrength(cell_networks[i], true);
const SkBitmap* badge = BadgeForNetworkTechnology(cell_networks[i]);
int flag = FLAG_CELLULAR;
if (!cell_networks[i]->connectable())
flag |= FLAG_DISABLED;
......@@ -692,8 +718,7 @@ void NetworkMenu::InitMenuItems() {
menu_items_.push_back(MenuItem(
ui::MenuModel::TYPE_COMMAND,
l10n_util::GetStringUTF16(IDS_OPTIONS_SETTINGS_OTHER_NETWORKS),
IconForDisplay(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0_BLACK),
SkBitmap()),
*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0_BLACK),
std::string(), FLAG_OTHER_NETWORK));
}
......
......@@ -114,26 +114,37 @@ class NetworkMenu : public views::ViewMenuDelegate,
// Cancels the active menu.
void CancelMenu();
// The following methods returns pointer to a shared instance of the SkBitmap.
// This shared bitmap is owned by the resource bundle and should not be freed.
// Returns the Icon for a network strength for a WifiNetwork |wifi|.
// |black| is used to specify whether to return a black icon for display
// on a light background or a white icon for display on a dark background.
static SkBitmap IconForNetworkStrength(const WifiNetwork* wifi, bool black);
// Expected to never return NULL.
static const SkBitmap* IconForNetworkStrength(const WifiNetwork* wifi,
bool black);
// Returns the Icon for a network strength for CellularNetwork |cellular|.
// |black| is used to specify whether to return a black icon for display
// on a light background or a white icon for display on a dark background.
static SkBitmap IconForNetworkStrength(const CellularNetwork* cellular,
bool black);
// Expected to never return NULL.
static const SkBitmap* IconForNetworkStrength(const CellularNetwork* cellular,
bool black);
// Returns the Icon for animating network connecting.
// |animation_value| is the value from Animation.GetCurrentValue()
// |black| is used to specify whether to return a black icon for display
// on a light background or a white icon for display on a dark background.
static SkBitmap IconForNetworkConnecting(double animation_value, bool black);
// Expected to never return NULL.
static const SkBitmap* IconForNetworkConnecting(double animation_value,
bool black);
// Returns the Badge for a given network technology.
// This returns different colored symbols depending on cellular data left.
static SkBitmap BadgeForNetworkTechnology(const CellularNetwork* cellular);
// Returns NULL if not badge is needed.
static const SkBitmap* BadgeForNetworkTechnology(
const CellularNetwork* cellular);
// This method will convert the |icon| bitmap to the correct size for display.
// If the |badge| icon is not empty, it will draw that on top of the icon.
static SkBitmap IconForDisplay(SkBitmap icon, SkBitmap badge);
// |icon| must be non-NULL.
// If the |badge| icon is not NULL, it will draw that on top of the icon.
static SkBitmap IconForDisplay(const SkBitmap* icon, const SkBitmap* badge);
protected:
virtual bool IsBrowserMode() const = 0;
......@@ -211,6 +222,12 @@ class NetworkMenu : public views::ViewMenuDelegate,
// static const int kBarsImagesLowData[];
// static const int kBarsImagesVLowData[];
// The number of animating images for network connecting.
static const int kNumAnimatingImages;
// Animation images. These are created lazily.
static SkBitmap kAnimatingImages[];
static SkBitmap kAnimatingImagesBlack[];
// Our menu items.
MenuItemVector menu_items_;
......
......@@ -31,6 +31,8 @@ NetworkMenuButton::NetworkMenuButton(StatusAreaHost* host)
: StatusAreaButton(this),
NetworkMenu(),
host_(host),
icon_(NULL),
badge_(NULL),
ALLOW_THIS_IN_INITIALIZER_LIST(animation_connecting_(this)) {
animation_connecting_.SetThrobDuration(kThrobDuration);
animation_connecting_.SetTweenType(ui::Tween::EASE_IN_OUT);
......@@ -106,19 +108,19 @@ bool NetworkMenuButton::ShouldOpenButtonOptions() const {
////////////////////////////////////////////////////////////////////////////////
// NetworkMenuButton, private methods
void NetworkMenuButton::SetIconAndBadge(const SkBitmap& icon,
const SkBitmap& badge) {
void NetworkMenuButton::SetIconAndBadge(const SkBitmap* icon,
const SkBitmap* badge) {
icon_ = icon;
badge_ = badge;
SetIcon(IconForDisplay(icon_, badge_));
}
void NetworkMenuButton::SetIconOnly(const SkBitmap& icon) {
void NetworkMenuButton::SetIconOnly(const SkBitmap* icon) {
icon_ = icon;
SetIcon(IconForDisplay(icon_, badge_));
}
void NetworkMenuButton::SetBadgeOnly(const SkBitmap& badge) {
void NetworkMenuButton::SetBadgeOnly(const SkBitmap* badge) {
badge_ = badge;
SetIcon(IconForDisplay(icon_, badge_));
}
......@@ -128,8 +130,8 @@ void NetworkMenuButton::SetNetworkIcon(NetworkLibrary* cros,
ResourceBundle& rb = ResourceBundle::GetSharedInstance();
if (!cros || !CrosLibrary::Get()->EnsureLoaded()) {
SetIconAndBadge(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0),
*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_WARNING));
SetIconAndBadge(rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0),
rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_WARNING));
SetTooltipText(UTF16ToWide(l10n_util::GetStringUTF16(
IDS_STATUSBAR_NETWORK_NO_NETWORK_TOOLTIP)));
return;
......@@ -137,8 +139,8 @@ void NetworkMenuButton::SetNetworkIcon(NetworkLibrary* cros,
if (!cros->Connected() && !cros->Connecting()) {
animation_connecting_.Stop();
SetIconAndBadge(*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0),
*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_DISCONNECTED));
SetIconAndBadge(rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0),
rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_DISCONNECTED));
SetTooltipText(UTF16ToWide(l10n_util::GetStringUTF16(
IDS_STATUSBAR_NETWORK_NO_NETWORK_TOOLTIP)));
return;
......@@ -154,7 +156,7 @@ void NetworkMenuButton::SetNetworkIcon(NetworkLibrary* cros,
const WirelessNetwork* wireless = NULL;
if (cros->wifi_connecting()) {
wireless = cros->wifi_network();
SetBadgeOnly(SkBitmap());
SetBadgeOnly(NULL);
} else { // cellular_connecting
wireless = cros->cellular_network();
SetBadgeOnly(BadgeForNetworkTechnology(cros->cellular_network()));
......@@ -169,7 +171,7 @@ void NetworkMenuButton::SetNetworkIcon(NetworkLibrary* cros,
// Only set the icon, if it is an active network that changed.
if (network && network->is_active()) {
if (network->type() == TYPE_ETHERNET) {
SetIconAndBadge(*rb.GetBitmapNamed(IDR_STATUSBAR_WIRED), SkBitmap());
SetIconAndBadge(rb.GetBitmapNamed(IDR_STATUSBAR_WIRED), NULL);
SetTooltipText(
UTF16ToWide(l10n_util::GetStringFUTF16(
IDS_STATUSBAR_NETWORK_CONNECTED_TOOLTIP,
......@@ -177,7 +179,7 @@ void NetworkMenuButton::SetNetworkIcon(NetworkLibrary* cros,
IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET))));
} else if (network->type() == TYPE_WIFI) {
const WifiNetwork* wifi = static_cast<const WifiNetwork*>(network);
SetIconAndBadge(IconForNetworkStrength(wifi, false), SkBitmap());
SetIconAndBadge(IconForNetworkStrength(wifi, false), NULL);
SetTooltipText(UTF16ToWide(l10n_util::GetStringFUTF16(
IDS_STATUSBAR_NETWORK_CONNECTED_TOOLTIP,
UTF8ToUTF16(wifi->name()))));
......
......@@ -74,11 +74,11 @@ class NetworkMenuButton : public StatusAreaButton,
private:
// Sets the icon and the badge.
void SetIconAndBadge(const SkBitmap& icon, const SkBitmap& badge);
void SetIconAndBadge(const SkBitmap* icon, const SkBitmap* badge);
// Sets the icon only. Keep the previous badge.
void SetIconOnly(const SkBitmap& icon);
void SetIconOnly(const SkBitmap* icon);
// Sets the badge only. Keep the previous icon.
void SetBadgeOnly(const SkBitmap& badge);
void SetBadgeOnly(const SkBitmap* badge);
// Set the network icon based on the status of the |network|
void SetNetworkIcon(NetworkLibrary* cros, const Network* network);
......@@ -90,9 +90,9 @@ class NetworkMenuButton : public StatusAreaButton,
StatusAreaHost* host_;
// The icon showing the network strength.
SkBitmap icon_;
const SkBitmap* icon_;
// A badge icon displayed on top of the icon.
SkBitmap badge_;
const SkBitmap* badge_;
// The throb animation that does the wifi connecting animation.
ui::ThrobAnimation animation_connecting_;
......
......@@ -933,16 +933,14 @@ ListValue* InternetOptionsHandler::GetWiredList() {
if (cros->ethernet_enabled()) {
const chromeos::EthernetNetwork* ethernet_network =
cros->ethernet_network();
SkBitmap icon = *rb.GetBitmapNamed(IDR_STATUSBAR_WIRED_BLACK);
if (!ethernet_network || (!ethernet_network->connecting() &&
!ethernet_network->connected())) {
icon = chromeos::NetworkMenu::IconForDisplay(icon,
*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_DISCONNECTED));
}
const SkBitmap* icon = rb.GetBitmapNamed(IDR_STATUSBAR_WIRED_BLACK);
const SkBitmap* badge = !ethernet_network ||
(!ethernet_network->connecting() && !ethernet_network->connected()) ?
rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_DISCONNECTED) : NULL;
if (ethernet_network) {
list->Append(GetNetwork(
ethernet_network->service_path(),
icon,
chromeos::NetworkMenu::IconForDisplay(icon, badge),
l10n_util::GetStringUTF8(IDS_STATUSBAR_NETWORK_DEVICE_ETHERNET),
ethernet_network->connecting(),
ethernet_network->connected(),
......@@ -965,14 +963,13 @@ ListValue* InternetOptionsHandler::GetWirelessList() {
const chromeos::WifiNetworkVector& wifi_networks = cros->wifi_networks();
for (chromeos::WifiNetworkVector::const_iterator it =
wifi_networks.begin(); it != wifi_networks.end(); ++it) {
SkBitmap icon = chromeos::NetworkMenu::IconForNetworkStrength(*it, true);
if ((*it)->encrypted()) {
icon = chromeos::NetworkMenu::IconForDisplay(icon,
*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE));
}
const SkBitmap* icon =
chromeos::NetworkMenu::IconForNetworkStrength(*it, true);
const SkBitmap* badge = (*it)->encrypted() ?
rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE) : NULL;
list->Append(GetNetwork(
(*it)->service_path(),
icon,
chromeos::NetworkMenu::IconForDisplay(icon, badge),
(*it)->name(),
(*it)->connecting(),
(*it)->connected(),
......@@ -987,12 +984,13 @@ ListValue* InternetOptionsHandler::GetWirelessList() {
cros->cellular_networks();
for (chromeos::CellularNetworkVector::const_iterator it =
cellular_networks.begin(); it != cellular_networks.end(); ++it) {
SkBitmap icon = chromeos::NetworkMenu::IconForNetworkStrength(*it, true);
SkBitmap badge = chromeos::NetworkMenu::BadgeForNetworkTechnology(*it);
icon = chromeos::NetworkMenu::IconForDisplay(icon, badge);
const SkBitmap* icon =
chromeos::NetworkMenu::IconForNetworkStrength(*it, true);
const SkBitmap* badge =
chromeos::NetworkMenu::BadgeForNetworkTechnology(*it);
list->Append(GetNetwork(
(*it)->service_path(),
icon,
chromeos::NetworkMenu::IconForDisplay(icon, badge),
(*it)->name(),
(*it)->connecting(),
(*it)->connected(),
......@@ -1058,21 +1056,17 @@ ListValue* InternetOptionsHandler::GetRememberedList() {
// Don't show the active network in the remembered list.
if (found && (it->second)->connected())
continue;
SkBitmap icon;
if (found)
icon = chromeos::NetworkMenu::IconForNetworkStrength(it->second, true);
else
icon = *rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0_BLACK);
const SkBitmap* icon = found ?
chromeos::NetworkMenu::IconForNetworkStrength(it->second, true) :
rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_BARS0_BLACK);
// Place the secure badge on the icon if the remembered network is
// encrypted (the matching detected network, if any, will have the same
// encrypted property by definition).
if (wifi->encrypted()) {
icon = chromeos::NetworkMenu::IconForDisplay(icon,
*rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE));
}
const SkBitmap* badge = wifi->encrypted() ?
rb.GetBitmapNamed(IDR_STATUSBAR_NETWORK_SECURE) : NULL;
list->Append(GetNetwork(
wifi->service_path(),
icon,
chromeos::NetworkMenu::IconForDisplay(icon, badge),
wifi->name(),
wifi->connecting(),
wifi->connected(),
......
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