<commit_before>#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "assetbrowser.h"
#include "editor/entity_template_system.h"
#include "editor/world_editor.h"
#include "engine/engine.h"
#include "entity_list.h"
#include "entity_template_list.h"
#include "fileserverwidget.h"
#include "gameview.h"
#include "log_widget.h"
#include "notifications.h"
#include "property_view.h"
#include "sceneview.h"
#include "scripts/scriptcompilerwidget.h"
#include "profilerui.h"
#include <qcombobox.h>
#include <qdir.h>
#include <qevent.h>
#include <qfiledialog.h>
#include <qinputdialog.h>
#include <qlabel.h>
#include <qsettings.h>

MainWindow::MainWindow(QWidget* parent) :
	QMainWindow(parent),
	m_ui(new Ui::MainWindow)
{
	m_ui->setupUi(this);
	m_ui->centralWidget->hide();
	setDockOptions(AllowNestedDocks | AnimatedDocks | AllowTabbedDocks);

	m_log = new LogWidget;
	m_property_view = new PropertyView;
	m_scene_view = new SceneView;
	m_game_view = new GameView;
	m_asset_browser = new AssetBrowser;
	m_script_compiler_ui = new ScriptCompilerWidget;
	m_file_server_ui = new FileServerWidget;
	m_profiler_ui = new ProfilerUI;
	m_entity_template_list_ui = new EntityTemplateList;
	m_notifications = Notifications::create(*this);
	m_entity_list = new EntityList(NULL);

	QSettings settings("Lumix", "QtEditor");
	bool geometry_restored = restoreGeometry(settings.value("mainWindowGeometry").toByteArray());

	m_window_menu = new QMenu("Windows", m_ui->menuView);
	m_ui->menuView->addMenu(m_window_menu);
	m_window_menu->connect(m_window_menu, &QMenu::aboutToShow, [this]()
	{
		for (auto info : m_dock_infos)
		{
			info.m_action->setChecked(info.m_widget->isVisible());
		}
	});
	addEditorDock(static_cast<Qt::DockWidgetArea>(2), m_asset_browser, &MainWindow::on_actionAsset_Browser_triggered);
	addEditorDock(static_cast<Qt::DockWidgetArea>(2), m_entity_list, &MainWindow::on_actionEntity_list_triggered);
	addEditorDock(static_cast<Qt::DockWidgetArea>(2), m_entity_template_list_ui, &MainWindow::on_actionEntity_templates_triggered);
	addEditorDock(static_cast<Qt::DockWidgetArea>(8), m_file_server_ui, &MainWindow::on_actionFile_server_triggered);
	addEditorDock(static_cast<Qt::DockWidgetArea>(1), m_game_view, &MainWindow::on_actionGame_view_triggered);
	addEditorDock(static_cast<Qt::DockWidgetArea>(8), m_log, &MainWindow::on_actionLog_triggered);
	addEditorDock(static_cast<Qt::DockWidgetArea>(1), m_profiler_ui, &MainWindow::on_actionProfiler_triggered);
	addEditorDock(static_cast<Qt::DockWidgetArea>(1), m_property_view, &MainWindow::on_actionProperties_triggered);
	addEditorDock(static_cast<Qt::DockWidgetArea>(2), m_scene_view, &MainWindow::on_actionScene_View_triggered);
	addEditorDock(static_cast<Qt::DockWidgetArea>(8), m_script_compiler_ui, &MainWindow::on_actionScript_compiler_triggered);

	createLayoutCombobox();

	m_property_view->setScriptCompiler(m_script_compiler_ui->getCompiler());
	m_property_view->setAssetBrowser(*m_asset_browser);
	m_property_view->setEntityTemplateList(m_entity_template_list_ui);

	int size = settings.beginReadArray("recent_files");
	for (int i = 0; i < size; ++i)
	{
		settings.setArrayIndex(i);
		m_recent_files.push_back(settings.value("filename").toString());
	}
	settings.endArray();
	m_recent_files_menu = new QMenu(m_ui->menuFile);
	m_recent_files_menu->setTitle("Recent Files");
	m_ui->menuFile->insertMenu(m_ui->actionSave, m_recent_files_menu);
	m_recent_files_menu->connect(m_recent_files_menu, &QMenu::triggered, [this](QAction* action)
	{
		auto path = action->text().toLatin1();
		m_world_editor->loadUniverse(path.data());
	});
	fillRecentFiles();

	geometry_restored = geometry_restored && restoreState(settings.value("mainWindowState").toByteArray());
	if (!geometry_restored)
	{
		QFile file("editor/layouts/main.bin");
		if (file.open(QIODevice::ReadWrite))
		{
			int size;
			file.read((char*)&size, sizeof(size));
			QByteArray geom = file.read(size);
			restoreGeometry(geom);
			file.read((char*)&size, sizeof(size));
			QByteArray state = file.read(size);
			restoreState(state);
		}
	}
}

void MainWindow::createLayoutCombobox()
{
	m_layout_combobox = new QComboBox();
	QWidget* widget = new QWidget(m_ui->menuBar);
	QHBoxLayout* layout = new QHBoxLayout(widget);
	QLabel* label = new QLabel("Layout");
	layout->setContentsMargins(0, 0, 0, 0);
	layout->addWidget(label);
	layout->addWidget(m_layout_combobox);
	m_ui->menuBar->setCornerWidget(widget);
	QDir dir("editor/layouts/");
	auto files = dir.entryInfoList();
	for (auto file : files)
	{
		if (file.baseName() != "")
		{
			m_layout_combobox->addItem(file.baseName());
		}
	}
	connect(m_layout_combobox, &QComboBox::currentTextChanged, [this](const QString & text)
	{
		QFile file(QString("editor/layouts/%1.bin").arg(text));
		if (file.open(QIODevice::ReadWrite))
		{
			int size;
			file.read((char*)&size, sizeof(size));
			QByteArray geom = file.read(size);
			restoreGeometry(geom);
			file.read((char*)&size, sizeof(size));
			QByteArray state = file.read(size);
			restoreState(state);
		}
	});;
}

void MainWindow::addEditorDock(Qt::DockWidgetArea area, QDockWidget* widget, void (MainWindow::*callback)())
{
	DockInfo info;
	info.m_widget = widget;
	QAction* action = new QAction(widget->windowTitle(), m_window_menu);
	action->setCheckable(true);
	m_window_menu->addAction(action);
	info.m_action = action;
	action->connect(action, &QAction::triggered, this, callback);
	m_dock_infos.push_back(info);
	addDockWidget(area, widget);
}

void MainWindow::fillRecentFiles()
{
	m_recent_files_menu->clear();
	for (auto file : m_recent_files)
	{
		m_recent_files_menu->addAction(file);
	}
}

void MainWindow::resizeEvent(QResizeEvent* event)
{
	m_resized.invoke(event->size());
}

void MainWindow::update()
{
	m_notifications->update(m_world_editor->getEngine().getLastTimeDelta());
}

void MainWindow::closeEvent(QCloseEvent *event)
{
	QSettings settings("Lumix", "QtEditor");
	settings.setValue("mainWindowGeometry", saveGeometry());
	settings.setValue("mainWindowState", saveState());
	settings.beginWriteArray("recent_files");
	int i = 0;
	for (auto file : m_recent_files)
	{
		settings.setArrayIndex(i);
		settings.setValue("filename", file);
		++i;
	}
	settings.endArray();
	QMainWindow::closeEvent(event);
}

MainWindow::~MainWindow()
{
	delete m_log;
	delete m_ui;
	delete m_scene_view;
	delete m_property_view;
	delete m_game_view;
	delete m_asset_browser;
	delete m_script_compiler_ui;
	delete m_file_server_ui;
	delete m_profiler_ui;
	delete m_entity_template_list_ui;
	Notifications::destroy(m_notifications);
}

void MainWindow::setWorldEditor(Lumix::WorldEditor& editor)
{
	m_world_editor = &editor;
	m_file_server_ui->setWorldEditor(editor);
	m_asset_browser->setWorldEditor(editor);
	m_property_view->setWorldEditor(editor);
	m_entity_template_list_ui->setWorldEditor(editor);
	m_game_view->setWorldEditor(editor);
	m_entity_list->setWorldEditor(editor);

	m_world_editor->universeLoaded().bind<MainWindow, &MainWindow::onUniverseLoaded>(this);
}

void MainWindow::onUniverseLoaded()
{
	const char* path = m_world_editor->getUniversePath().c_str();

	if (m_recent_files.indexOf(path, 0) < 0)
	{
		m_recent_files.push_back(path);
		if (m_recent_files.size() > 6)
		{
			m_recent_files.pop_front();
		}
		fillRecentFiles();
	}
}

GameView* MainWindow::getGameView() const
{
	return m_game_view;
}

SceneView* MainWindow::getSceneView() const
{
	return m_scene_view;
}

void MainWindow::on_actionLog_triggered()
{
	m_log->show();
}

void MainWindow::on_actionOpen_triggered()
{
	QString filename = QFileDialog::getOpenFileName(NULL, QString(), QString(), "universe (*.unv)");
	QByteArray path = filename.toLocal8Bit();
	if (!path.isEmpty())
	{
		m_world_editor->loadUniverse(path.data());
	}
}

void MainWindow::on_actionSave_As_triggered()
{
	QByteArray path = QFileDialog::getSaveFileName().toLocal8Bit();
	if (!path.isEmpty())
	{
		m_world_editor->saveUniverse(path.data());
	}
}

void MainWindow::on_actionCreate_triggered()
{
	m_world_editor->addEntity();
}

void MainWindow::on_actionProperties_triggered()
{
	m_property_view->show();
}

void MainWindow::on_actionE_xit_triggered()
{
	close();
}

void MainWindow::on_actionGame_view_triggered()
{
	m_game_view->show();
}

void MainWindow::on_actionScript_compiler_triggered()
{
	m_script_compiler_ui->show();
}

void MainWindow::on_actionFile_server_triggered()
{
	m_file_server_ui->show();
}

void MainWindow::on_actionAsset_Browser_triggered()
{
	m_asset_browser->show();
}

void MainWindow::on_actionScene_View_triggered()
{
	m_scene_view->show();
}

void MainWindow::on_actionProfiler_triggered()
{
	m_profiler_ui->show();
}

void MainWindow::on_actionPolygon_Mode_changed()
{
	m_world_editor->setWireframe(m_ui->actionPolygon_Mode->isChecked());
}

void MainWindow::on_actionGame_mode_triggered()
{
	m_world_editor->toggleGameMode();
}

void MainWindow::on_actionLook_at_selected_entity_triggered()
{
	m_world_editor->lookAtSelected();
}

void MainWindow::on_actionNew_triggered()
{
	m_world_editor->newUniverse();
}

void MainWindow::on_actionSave_triggered()
{
	if (m_world_editor->getUniversePath()[0] == '\0')
	{
		on_actionSave_As_triggered();
	}
	else
	{
		m_world_editor->saveUniverse(m_world_editor->getUniversePath());
	}
}

void MainWindow::on_actionSnap_to_terrain_triggered()
{
	m_world_editor->snapToTerrain();
}

void MainWindow::on_actionSave_as_template_triggered()
{
	if (m_world_editor->getSelectedEntities().size() == 1)
	{
		bool ok = false;
		QString text = QInputDialog::getText(this, tr("Entity template"), tr("Template name:"), QLineEdit::Normal, tr(""), &ok);
		if (ok)
		{
			m_world_editor->getEntityTemplateSystem().createTemplateFromEntity(text.toLatin1().data(), m_world_editor->getSelectedEntities()[0]);
		}
	}
}

void MainWindow::on_actionEntity_templates_triggered()
{
	m_entity_template_list_ui->show();
}

void MainWindow::on_actionInstantiate_template_triggered()
{
	m_entity_template_list_ui->instantiateTemplate();
}

void MainWindow::on_actionUndo_triggered()
{
	m_world_editor->undo();
}

void MainWindow::on_actionRedo_triggered()
{
	m_world_editor->redo();
}

void MainWindow::on_actionRemove_triggered()
{
	if (!m_world_editor->getSelectedEntities().empty())
	{
		m_world_editor->destroyEntities(&m_world_editor->getSelectedEntities()[0], m_world_editor->getSelectedEntities().size());
	}
}

void MainWindow::on_actionEntity_list_triggered()
{
	m_entity_list->show();
}

void MainWindow::on_actionMeasure_triggered()
{
	m_world_editor->toggleMeasure();
}

void MainWindow::on_actionSave_Layout_triggered()
{
	bool ok;
	QString text = QInputDialog::getText(this, "Save layout", "Layout name:", QLineEdit::Normal, "", &ok);
	if (ok && !text.isEmpty())
	{
		QFile file(QString("editor/layouts/%1.bin").arg(text));
		if (file.open(QIODevice::ReadWrite))
		{
			auto geom = saveGeometry();
			auto state = saveState();
			int size = geom.size();
			file.write((const char*)&size, sizeof(size));
			file.write(geom);
			size = state.size();
			file.write((const char*)&size, sizeof(size));
			file.write(state);
			bool item_exists = false;
			for (int i = 0; i < m_layout_combobox->count(); ++i)
			{
				if (m_layout_combobox->itemText(i) == text)
				{
					item_exists = true;
					break;
				}
			}
			if (!item_exists)
			{
				m_layout_combobox->addItem(text);
			}
		}
	}
}
<commit_msg>reviewed code - missing reference added<commit_after>#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "assetbrowser.h"
#include "editor/entity_template_system.h"
#include "editor/world_editor.h"
#include "engine/engine.h"
#include "entity_list.h"
#include "entity_template_list.h"
#include "fileserverwidget.h"
#include "gameview.h"
#include "log_widget.h"
#include "notifications.h"
#include "property_view.h"
#include "sceneview.h"
#include "scripts/scriptcompilerwidget.h"
#include "profilerui.h"
#include <qcombobox.h>
#include <qdir.h>
#include <qevent.h>
#include <qfiledialog.h>
#include <qinputdialog.h>
#include <qlabel.h>
#include <qsettings.h>

MainWindow::MainWindow(QWidget* parent) :
	QMainWindow(parent),
	m_ui(new Ui::MainWindow)
{
	m_ui->setupUi(this);
	m_ui->centralWidget->hide();
	setDockOptions(AllowNestedDocks | AnimatedDocks | AllowTabbedDocks);

	m_log = new LogWidget;
	m_property_view = new PropertyView;
	m_scene_view = new SceneView;
	m_game_view = new GameView;
	m_asset_browser = new AssetBrowser;
	m_script_compiler_ui = new ScriptCompilerWidget;
	m_file_server_ui = new FileServerWidget;
	m_profiler_ui = new ProfilerUI;
	m_entity_template_list_ui = new EntityTemplateList;
	m_notifications = Notifications::create(*this);
	m_entity_list = new EntityList(NULL);

	QSettings settings("Lumix", "QtEditor");
	bool geometry_restored = restoreGeometry(settings.value("mainWindowGeometry").toByteArray());

	m_window_menu = new QMenu("Windows", m_ui->menuView);
	m_ui->menuView->addMenu(m_window_menu);
	m_window_menu->connect(m_window_menu, &QMenu::aboutToShow, [this]()
	{
		for (auto info : m_dock_infos)
		{
			info.m_action->setChecked(info.m_widget->isVisible());
		}
	});
	addEditorDock(static_cast<Qt::DockWidgetArea>(2), m_asset_browser, &MainWindow::on_actionAsset_Browser_triggered);
	addEditorDock(static_cast<Qt::DockWidgetArea>(2), m_entity_list, &MainWindow::on_actionEntity_list_triggered);
	addEditorDock(static_cast<Qt::DockWidgetArea>(2), m_entity_template_list_ui, &MainWindow::on_actionEntity_templates_triggered);
	addEditorDock(static_cast<Qt::DockWidgetArea>(8), m_file_server_ui, &MainWindow::on_actionFile_server_triggered);
	addEditorDock(static_cast<Qt::DockWidgetArea>(1), m_game_view, &MainWindow::on_actionGame_view_triggered);
	addEditorDock(static_cast<Qt::DockWidgetArea>(8), m_log, &MainWindow::on_actionLog_triggered);
	addEditorDock(static_cast<Qt::DockWidgetArea>(1), m_profiler_ui, &MainWindow::on_actionProfiler_triggered);
	addEditorDock(static_cast<Qt::DockWidgetArea>(1), m_property_view, &MainWindow::on_actionProperties_triggered);
	addEditorDock(static_cast<Qt::DockWidgetArea>(2), m_scene_view, &MainWindow::on_actionScene_View_triggered);
	addEditorDock(static_cast<Qt::DockWidgetArea>(8), m_script_compiler_ui, &MainWindow::on_actionScript_compiler_triggered);

	createLayoutCombobox();

	m_property_view->setScriptCompiler(m_script_compiler_ui->getCompiler());
	m_property_view->setAssetBrowser(*m_asset_browser);
	m_property_view->setEntityTemplateList(m_entity_template_list_ui);

	int size = settings.beginReadArray("recent_files");
	for (int i = 0; i < size; ++i)
	{
		settings.setArrayIndex(i);
		m_recent_files.push_back(settings.value("filename").toString());
	}
	settings.endArray();
	m_recent_files_menu = new QMenu(m_ui->menuFile);
	m_recent_files_menu->setTitle("Recent Files");
	m_ui->menuFile->insertMenu(m_ui->actionSave, m_recent_files_menu);
	m_recent_files_menu->connect(m_recent_files_menu, &QMenu::triggered, [this](QAction* action)
	{
		auto path = action->text().toLatin1();
		m_world_editor->loadUniverse(path.data());
	});
	fillRecentFiles();

	geometry_restored = geometry_restored && restoreState(settings.value("mainWindowState").toByteArray());
	if (!geometry_restored)
	{
		QFile file("editor/layouts/main.bin");
		if (file.open(QIODevice::ReadWrite))
		{
			int size;
			file.read((char*)&size, sizeof(size));
			QByteArray geom = file.read(size);
			restoreGeometry(geom);
			file.read((char*)&size, sizeof(size));
			QByteArray state = file.read(size);
			restoreState(state);
		}
	}
}

void MainWindow::createLayoutCombobox()
{
	m_layout_combobox = new QComboBox();
	QWidget* widget = new QWidget(m_ui->menuBar);
	QHBoxLayout* layout = new QHBoxLayout(widget);
	QLabel* label = new QLabel("Layout");
	layout->setContentsMargins(0, 0, 0, 0);
	layout->addWidget(label);
	layout->addWidget(m_layout_combobox);
	m_ui->menuBar->setCornerWidget(widget);
	QDir dir("editor/layouts/");
	auto files = dir.entryInfoList();
	for (const auto& file : files)
	{
		if (file.baseName() != "")
		{
			m_layout_combobox->addItem(file.baseName());
		}
	}
	connect(m_layout_combobox, &QComboBox::currentTextChanged, [this](const QString & text)
	{
		QFile file(QString("editor/layouts/%1.bin").arg(text));
		if (file.open(QIODevice::ReadWrite))
		{
			int size;
			file.read((char*)&size, sizeof(size));
			QByteArray geom = file.read(size);
			restoreGeometry(geom);
			file.read((char*)&size, sizeof(size));
			QByteArray state = file.read(size);
			restoreState(state);
		}
	});;
}

void MainWindow::addEditorDock(Qt::DockWidgetArea area, QDockWidget* widget, void (MainWindow::*callback)())
{
	DockInfo info;
	info.m_widget = widget;
	QAction* action = new QAction(widget->windowTitle(), m_window_menu);
	action->setCheckable(true);
	m_window_menu->addAction(action);
	info.m_action = action;
	action->connect(action, &QAction::triggered, this, callback);
	m_dock_infos.push_back(info);
	addDockWidget(area, widget);
}

void MainWindow::fillRecentFiles()
{
	m_recent_files_menu->clear();
	for (auto file : m_recent_files)
	{
		m_recent_files_menu->addAction(file);
	}
}

void MainWindow::resizeEvent(QResizeEvent* event)
{
	m_resized.invoke(event->size());
}

void MainWindow::update()
{
	m_notifications->update(m_world_editor->getEngine().getLastTimeDelta());
}

void MainWindow::closeEvent(QCloseEvent *event)
{
	QSettings settings("Lumix", "QtEditor");
	settings.setValue("mainWindowGeometry", saveGeometry());
	settings.setValue("mainWindowState", saveState());
	settings.beginWriteArray("recent_files");
	int i = 0;
	for (auto file : m_recent_files)
	{
		settings.setArrayIndex(i);
		settings.setValue("filename", file);
		++i;
	}
	settings.endArray();
	QMainWindow::closeEvent(event);
}

MainWindow::~MainWindow()
{
	delete m_log;
	delete m_ui;
	delete m_scene_view;
	delete m_property_view;
	delete m_game_view;
	delete m_asset_browser;
	delete m_script_compiler_ui;
	delete m_file_server_ui;
	delete m_profiler_ui;
	delete m_entity_template_list_ui;
	Notifications::destroy(m_notifications);
}

void MainWindow::setWorldEditor(Lumix::WorldEditor& editor)
{
	m_world_editor = &editor;
	m_file_server_ui->setWorldEditor(editor);
	m_asset_browser->setWorldEditor(editor);
	m_property_view->setWorldEditor(editor);
	m_entity_template_list_ui->setWorldEditor(editor);
	m_game_view->setWorldEditor(editor);
	m_entity_list->setWorldEditor(editor);

	m_world_editor->universeLoaded().bind<MainWindow, &MainWindow::onUniverseLoaded>(this);
}

void MainWindow::onUniverseLoaded()
{
	const char* path = m_world_editor->getUniversePath().c_str();

	if (m_recent_files.indexOf(path, 0) < 0)
	{
		m_recent_files.push_back(path);
		if (m_recent_files.size() > 6)
		{
			m_recent_files.pop_front();
		}
		fillRecentFiles();
	}
}

GameView* MainWindow::getGameView() const
{
	return m_game_view;
}

SceneView* MainWindow::getSceneView() const
{
	return m_scene_view;
}

void MainWindow::on_actionLog_triggered()
{
	m_log->show();
}

void MainWindow::on_actionOpen_triggered()
{
	QString filename = QFileDialog::getOpenFileName(NULL, QString(), QString(), "universe (*.unv)");
	QByteArray path = filename.toLocal8Bit();
	if (!path.isEmpty())
	{
		m_world_editor->loadUniverse(path.data());
	}
}

void MainWindow::on_actionSave_As_triggered()
{
	QByteArray path = QFileDialog::getSaveFileName().toLocal8Bit();
	if (!path.isEmpty())
	{
		m_world_editor->saveUniverse(path.data());
	}
}

void MainWindow::on_actionCreate_triggered()
{
	m_world_editor->addEntity();
}

void MainWindow::on_actionProperties_triggered()
{
	m_property_view->show();
}

void MainWindow::on_actionE_xit_triggered()
{
	close();
}

void MainWindow::on_actionGame_view_triggered()
{
	m_game_view->show();
}

void MainWindow::on_actionScript_compiler_triggered()
{
	m_script_compiler_ui->show();
}

void MainWindow::on_actionFile_server_triggered()
{
	m_file_server_ui->show();
}

void MainWindow::on_actionAsset_Browser_triggered()
{
	m_asset_browser->show();
}

void MainWindow::on_actionScene_View_triggered()
{
	m_scene_view->show();
}

void MainWindow::on_actionProfiler_triggered()
{
	m_profiler_ui->show();
}

void MainWindow::on_actionPolygon_Mode_changed()
{
	m_world_editor->setWireframe(m_ui->actionPolygon_Mode->isChecked());
}

void MainWindow::on_actionGame_mode_triggered()
{
	m_world_editor->toggleGameMode();
}

void MainWindow::on_actionLook_at_selected_entity_triggered()
{
	m_world_editor->lookAtSelected();
}

void MainWindow::on_actionNew_triggered()
{
	m_world_editor->newUniverse();
}

void MainWindow::on_actionSave_triggered()
{
	if (m_world_editor->getUniversePath()[0] == '\0')
	{
		on_actionSave_As_triggered();
	}
	else
	{
		m_world_editor->saveUniverse(m_world_editor->getUniversePath());
	}
}

void MainWindow::on_actionSnap_to_terrain_triggered()
{
	m_world_editor->snapToTerrain();
}

void MainWindow::on_actionSave_as_template_triggered()
{
	if (m_world_editor->getSelectedEntities().size() == 1)
	{
		bool ok = false;
		QString text = QInputDialog::getText(this, tr("Entity template"), tr("Template name:"), QLineEdit::Normal, tr(""), &ok);
		if (ok)
		{
			m_world_editor->getEntityTemplateSystem().createTemplateFromEntity(text.toLatin1().data(), m_world_editor->getSelectedEntities()[0]);
		}
	}
}

void MainWindow::on_actionEntity_templates_triggered()
{
	m_entity_template_list_ui->show();
}

void MainWindow::on_actionInstantiate_template_triggered()
{
	m_entity_template_list_ui->instantiateTemplate();
}

void MainWindow::on_actionUndo_triggered()
{
	m_world_editor->undo();
}

void MainWindow::on_actionRedo_triggered()
{
	m_world_editor->redo();
}

void MainWindow::on_actionRemove_triggered()
{
	if (!m_world_editor->getSelectedEntities().empty())
	{
		m_world_editor->destroyEntities(&m_world_editor->getSelectedEntities()[0], m_world_editor->getSelectedEntities().size());
	}
}

void MainWindow::on_actionEntity_list_triggered()
{
	m_entity_list->show();
}

void MainWindow::on_actionMeasure_triggered()
{
	m_world_editor->toggleMeasure();
}

void MainWindow::on_actionSave_Layout_triggered()
{
	bool ok;
	QString text = QInputDialog::getText(this, "Save layout", "Layout name:", QLineEdit::Normal, "", &ok);
	if (ok && !text.isEmpty())
	{
		QFile file(QString("editor/layouts/%1.bin").arg(text));
		if (file.open(QIODevice::ReadWrite))
		{
			auto geom = saveGeometry();
			auto state = saveState();
			int size = geom.size();
			file.write((const char*)&size, sizeof(size));
			file.write(geom);
			size = state.size();
			file.write((const char*)&size, sizeof(size));
			file.write(state);
			bool item_exists = false;
			for (int i = 0; i < m_layout_combobox->count(); ++i)
			{
				if (m_layout_combobox->itemText(i) == text)
				{
					item_exists = true;
					break;
				}
			}
			if (!item_exists)
			{
				m_layout_combobox->addItem(text);
			}
		}
	}
}
<|endoftext|>


<commit_before>/*************************************************************************/
/*  input.cpp                                                            */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                    http://www.godotengine.org                         */
/*************************************************************************/
/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur.                 */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/
#include "input.h"
#include "input_map.h"
#include "os/os.h"
#include "globals.h"
Input *Input::singleton=NULL;

Input *Input::get_singleton() {

	return singleton;
}

void Input::set_mouse_mode(MouseMode p_mode) {
	ERR_FAIL_INDEX(p_mode,3);
	OS::get_singleton()->set_mouse_mode((OS::MouseMode)p_mode);
}

Input::MouseMode Input::get_mouse_mode() const {

	return (MouseMode)OS::get_singleton()->get_mouse_mode();
}

void Input::_bind_methods() {

	ObjectTypeDB::bind_method(_MD("is_key_pressed","scancode"),&Input::is_key_pressed);
	ObjectTypeDB::bind_method(_MD("is_mouse_button_pressed","button"),&Input::is_mouse_button_pressed);
	ObjectTypeDB::bind_method(_MD("is_joy_button_pressed","device","button"),&Input::is_joy_button_pressed);
	ObjectTypeDB::bind_method(_MD("is_action_pressed","action"),&Input::is_action_pressed);
	ObjectTypeDB::bind_method(_MD("is_action_just_pressed","action"),&Input::is_action_just_pressed);
	ObjectTypeDB::bind_method(_MD("is_action_just_released","action"),&Input::is_action_just_released);
	ObjectTypeDB::bind_method(_MD("add_joy_mapping","mapping", "update_existing"),&Input::add_joy_mapping, DEFVAL(false));
	ObjectTypeDB::bind_method(_MD("remove_joy_mapping","guid"),&Input::remove_joy_mapping);
	ObjectTypeDB::bind_method(_MD("is_joy_known","device"),&Input::is_joy_known);
	ObjectTypeDB::bind_method(_MD("get_joy_axis","device","axis"),&Input::get_joy_axis);
	ObjectTypeDB::bind_method(_MD("get_joy_name","device"),&Input::get_joy_name);
	ObjectTypeDB::bind_method(_MD("get_joy_guid","device"),&Input::get_joy_guid);
	ObjectTypeDB::bind_method(_MD("get_connected_joysticks"),&Input::get_connected_joysticks);
	ObjectTypeDB::bind_method(_MD("get_joy_vibration_strength", "device"), &Input::get_joy_vibration_strength);
	ObjectTypeDB::bind_method(_MD("get_joy_vibration_duration", "device"), &Input::get_joy_vibration_duration);
	ObjectTypeDB::bind_method(_MD("get_joy_button_string", "button_index"), &Input::get_joy_button_string);
	ObjectTypeDB::bind_method(_MD("get_joy_button_index_from_string", "button"), &Input::get_joy_button_index_from_string);
	ObjectTypeDB::bind_method(_MD("get_joy_axis_string", "axis_index"), &Input::get_joy_axis_string);
	ObjectTypeDB::bind_method(_MD("get_joy_axis_index_from_string", "axis"), &Input::get_joy_axis_index_from_string);
	ObjectTypeDB::bind_method(_MD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0));
	ObjectTypeDB::bind_method(_MD("stop_joy_vibration", "device"), &Input::stop_joy_vibration);
	ObjectTypeDB::bind_method(_MD("get_accelerometer"),&Input::get_accelerometer);
	ObjectTypeDB::bind_method(_MD("get_magnetometer"),&Input::get_magnetometer);
	ObjectTypeDB::bind_method(_MD("get_gyroscope"),&Input::get_gyroscope);
	//ObjectTypeDB::bind_method(_MD("get_mouse_pos"),&Input::get_mouse_pos); - this is not the function you want
	ObjectTypeDB::bind_method(_MD("get_mouse_speed"),&Input::get_mouse_speed);
	ObjectTypeDB::bind_method(_MD("get_mouse_button_mask"),&Input::get_mouse_button_mask);
	ObjectTypeDB::bind_method(_MD("set_mouse_mode","mode"),&Input::set_mouse_mode);
	ObjectTypeDB::bind_method(_MD("get_mouse_mode"),&Input::get_mouse_mode);
	ObjectTypeDB::bind_method(_MD("warp_mouse_pos","to"),&Input::warp_mouse_pos);
	ObjectTypeDB::bind_method(_MD("action_press","action"),&Input::action_press);
	ObjectTypeDB::bind_method(_MD("action_release","action"),&Input::action_release);
	ObjectTypeDB::bind_method(_MD("set_custom_mouse_cursor","image:Texture","hotspot"),&Input::set_custom_mouse_cursor,DEFVAL(Vector2()));

	BIND_CONSTANT( MOUSE_MODE_VISIBLE );
	BIND_CONSTANT( MOUSE_MODE_HIDDEN );
	BIND_CONSTANT( MOUSE_MODE_CAPTURED );

	ADD_SIGNAL( MethodInfo("joy_connection_changed", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "connected")) );
}

void Input::get_argument_options(const StringName& p_function,int p_idx,List<String>*r_options) const {
#ifdef TOOLS_ENABLED

	String pf=p_function;
	if (p_idx==0 && (pf=="is_action_pressed" || pf=="action_press" || pf=="action_release")) {

		List<PropertyInfo> pinfo;
		Globals::get_singleton()->get_property_list(&pinfo);

		for(List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) {
			const PropertyInfo &pi=E->get();

			if (!pi.name.begins_with("input/"))
				continue;

			String name = pi.name.substr(pi.name.find("/")+1,pi.name.length());
			r_options->push_back("\""+name+"\"");

		}
	}
#endif

}

Input::Input() {

	singleton=this;
}

//////////////////////////////////////////////////////////

<commit_msg>Code completion for is_action_just_{pressed, released}<commit_after>/*************************************************************************/
/*  input.cpp                                                            */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                    http://www.godotengine.org                         */
/*************************************************************************/
/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur.                 */
/*                                                                       */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the       */
/* "Software"), to deal in the Software without restriction, including   */
/* without limitation the rights to use, copy, modify, merge, publish,   */
/* distribute, sublicense, and/or sell copies of the Software, and to    */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions:                                             */
/*                                                                       */
/* The above copyright notice and this permission notice shall be        */
/* included in all copies or substantial portions of the Software.       */
/*                                                                       */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
/*************************************************************************/
#include "input.h"
#include "input_map.h"
#include "os/os.h"
#include "globals.h"
Input *Input::singleton=NULL;

Input *Input::get_singleton() {

	return singleton;
}

void Input::set_mouse_mode(MouseMode p_mode) {
	ERR_FAIL_INDEX(p_mode,3);
	OS::get_singleton()->set_mouse_mode((OS::MouseMode)p_mode);
}

Input::MouseMode Input::get_mouse_mode() const {

	return (MouseMode)OS::get_singleton()->get_mouse_mode();
}

void Input::_bind_methods() {

	ObjectTypeDB::bind_method(_MD("is_key_pressed","scancode"),&Input::is_key_pressed);
	ObjectTypeDB::bind_method(_MD("is_mouse_button_pressed","button"),&Input::is_mouse_button_pressed);
	ObjectTypeDB::bind_method(_MD("is_joy_button_pressed","device","button"),&Input::is_joy_button_pressed);
	ObjectTypeDB::bind_method(_MD("is_action_pressed","action"),&Input::is_action_pressed);
	ObjectTypeDB::bind_method(_MD("is_action_just_pressed","action"),&Input::is_action_just_pressed);
	ObjectTypeDB::bind_method(_MD("is_action_just_released","action"),&Input::is_action_just_released);
	ObjectTypeDB::bind_method(_MD("add_joy_mapping","mapping", "update_existing"),&Input::add_joy_mapping, DEFVAL(false));
	ObjectTypeDB::bind_method(_MD("remove_joy_mapping","guid"),&Input::remove_joy_mapping);
	ObjectTypeDB::bind_method(_MD("is_joy_known","device"),&Input::is_joy_known);
	ObjectTypeDB::bind_method(_MD("get_joy_axis","device","axis"),&Input::get_joy_axis);
	ObjectTypeDB::bind_method(_MD("get_joy_name","device"),&Input::get_joy_name);
	ObjectTypeDB::bind_method(_MD("get_joy_guid","device"),&Input::get_joy_guid);
	ObjectTypeDB::bind_method(_MD("get_connected_joysticks"),&Input::get_connected_joysticks);
	ObjectTypeDB::bind_method(_MD("get_joy_vibration_strength", "device"), &Input::get_joy_vibration_strength);
	ObjectTypeDB::bind_method(_MD("get_joy_vibration_duration", "device"), &Input::get_joy_vibration_duration);
	ObjectTypeDB::bind_method(_MD("get_joy_button_string", "button_index"), &Input::get_joy_button_string);
	ObjectTypeDB::bind_method(_MD("get_joy_button_index_from_string", "button"), &Input::get_joy_button_index_from_string);
	ObjectTypeDB::bind_method(_MD("get_joy_axis_string", "axis_index"), &Input::get_joy_axis_string);
	ObjectTypeDB::bind_method(_MD("get_joy_axis_index_from_string", "axis"), &Input::get_joy_axis_index_from_string);
	ObjectTypeDB::bind_method(_MD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0));
	ObjectTypeDB::bind_method(_MD("stop_joy_vibration", "device"), &Input::stop_joy_vibration);
	ObjectTypeDB::bind_method(_MD("get_accelerometer"),&Input::get_accelerometer);
	ObjectTypeDB::bind_method(_MD("get_magnetometer"),&Input::get_magnetometer);
	ObjectTypeDB::bind_method(_MD("get_gyroscope"),&Input::get_gyroscope);
	//ObjectTypeDB::bind_method(_MD("get_mouse_pos"),&Input::get_mouse_pos); - this is not the function you want
	ObjectTypeDB::bind_method(_MD("get_mouse_speed"),&Input::get_mouse_speed);
	ObjectTypeDB::bind_method(_MD("get_mouse_button_mask"),&Input::get_mouse_button_mask);
	ObjectTypeDB::bind_method(_MD("set_mouse_mode","mode"),&Input::set_mouse_mode);
	ObjectTypeDB::bind_method(_MD("get_mouse_mode"),&Input::get_mouse_mode);
	ObjectTypeDB::bind_method(_MD("warp_mouse_pos","to"),&Input::warp_mouse_pos);
	ObjectTypeDB::bind_method(_MD("action_press","action"),&Input::action_press);
	ObjectTypeDB::bind_method(_MD("action_release","action"),&Input::action_release);
	ObjectTypeDB::bind_method(_MD("set_custom_mouse_cursor","image:Texture","hotspot"),&Input::set_custom_mouse_cursor,DEFVAL(Vector2()));

	BIND_CONSTANT( MOUSE_MODE_VISIBLE );
	BIND_CONSTANT( MOUSE_MODE_HIDDEN );
	BIND_CONSTANT( MOUSE_MODE_CAPTURED );

	ADD_SIGNAL( MethodInfo("joy_connection_changed", PropertyInfo(Variant::INT, "index"), PropertyInfo(Variant::BOOL, "connected")) );
}

void Input::get_argument_options(const StringName& p_function,int p_idx,List<String>*r_options) const {
#ifdef TOOLS_ENABLED

	String pf=p_function;
	if (p_idx==0 && (pf=="is_action_pressed" || pf=="action_press" || pf=="action_release" || pf=="is_action_just_pressed" || pf=="is_action_just_released")) {

		List<PropertyInfo> pinfo;
		Globals::get_singleton()->get_property_list(&pinfo);

		for(List<PropertyInfo>::Element *E=pinfo.front();E;E=E->next()) {
			const PropertyInfo &pi=E->get();

			if (!pi.name.begins_with("input/"))
				continue;

			String name = pi.name.substr(pi.name.find("/")+1,pi.name.length());
			r_options->push_back("\""+name+"\"");

		}
	}
#endif

}

Input::Input() {

	singleton=this;
}

//////////////////////////////////////////////////////////

<|endoftext|>


<commit_before>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/login/google_authenticator.h"

#include <string>
#include <vector>

#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/sha2.h"
#include "base/string_util.h"
#include "base/third_party/nss/blapi.h"
#include "base/third_party/nss/sha256.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/chromeos/cros/cryptohome_library.h"
#include "chrome/browser/chromeos/login/auth_response_handler.h"
#include "chrome/browser/chromeos/login/authentication_notification_details.h"
#include "chrome/browser/chromeos/login/login_status_consumer.h"
#include "chrome/browser/net/chrome_url_request_context.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/profile_manager.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/net/url_fetcher.h"
#include "chrome/common/notification_service.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/url_request/url_request_status.h"
#include "third_party/libjingle/source/talk/base/urlencode.h"

using base::Time;
using base::TimeDelta;
using file_util::GetFileSize;
using file_util::PathExists;
using file_util::ReadFile;
using file_util::ReadFileToString;

namespace chromeos {

// static
const char GoogleAuthenticator::kCookiePersistence[] = "true";
// static
const char GoogleAuthenticator::kAccountType[] = "HOSTED_OR_GOOGLE";
// static
const char GoogleAuthenticator::kSource[] = "chromeos";
// static
// Service name for Google Contacts API. API is used to get user's image.
const char GoogleAuthenticator::kService[] = "cp";
// static
const char GoogleAuthenticator::kFormat[] =
    "Email=%s&"
    "Passwd=%s&"
    "PersistentCookie=%s&"
    "accountType=%s&"
    "source=%s&"
    "service=%s";
// static
const char GoogleAuthenticator::kFormatCaptcha[] =
    "Email=%s&"
    "Passwd=%s&"
    "PersistentCookie=%s&"
    "accountType=%s&"
    "source=%s&"
    "service=%s&"
    "logintoken=%s&"
    "logincaptcha=%s";
// static
const char GoogleAuthenticator::kSecondFactor[] = "Info=InvalidSecondFactor";

// static
const char GoogleAuthenticator::kSystemSalt[] = "/home/.shadow/salt";
// static
const char GoogleAuthenticator::kOpenSSLMagic[] = "Salted__";
// static
const char GoogleAuthenticator::kLocalaccountFile[] = "localaccount";
// static
const char GoogleAuthenticator::kTmpfsTrigger[] = "incognito";

// static
const int GoogleAuthenticator::kClientLoginTimeoutMs = 5000;

const int kPassHashLen = 32;

GoogleAuthenticator::GoogleAuthenticator(LoginStatusConsumer* consumer)
    : Authenticator(consumer),
      fetcher_(NULL),
      getter_(NULL),
      checked_for_localaccount_(false),
      unlock_(false),
      try_again_(true),
      fetch_completed_(false) {
  CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded());
}

GoogleAuthenticator::~GoogleAuthenticator() {
  delete fetcher_;
}

// static
URLFetcher* GoogleAuthenticator::CreateClientLoginFetcher(
    URLRequestContextGetter* getter,
    const std::string& body,
    URLFetcher::Delegate* delegate) {
  URLFetcher* to_return =
      URLFetcher::Create(0,
                         GURL(AuthResponseHandler::kClientLoginUrl),
                         URLFetcher::POST,
                         delegate);
  to_return->set_request_context(getter);
  to_return->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES);
  to_return->set_upload_data("application/x-www-form-urlencoded", body);
  return to_return;
}

bool GoogleAuthenticator::AuthenticateToLogin(
    Profile* profile,
    const std::string& username,
    const std::string& password,
    const std::string& login_token,
    const std::string& login_captcha) {
  unlock_ = false;
  getter_ = profile->GetRequestContext();

  // TODO(cmasone): be more careful about zeroing memory that stores
  // the user's password.
  if (login_token.empty() || login_captcha.empty()) {
    request_body_ = StringPrintf(kFormat,
                                 UrlEncodeString(username).c_str(),
                                 UrlEncodeString(password).c_str(),
                                 kCookiePersistence,
                                 kAccountType,
                                 kSource,
                                 kService);
  } else {
    request_body_ = StringPrintf(kFormatCaptcha,
                                 UrlEncodeString(username).c_str(),
                                 UrlEncodeString(password).c_str(),
                                 kCookiePersistence,
                                 kAccountType,
                                 kSource,
                                 kService,
                                 UrlEncodeString(login_token).c_str(),
                                 UrlEncodeString(login_captcha).c_str());
  }
  // TODO(cmasone): Figure out how to parallelize fetch, username/password
  // processing without impacting testability.
  username_.assign(Canonicalize(username));
  StoreHashedPassword(password);
  TryClientLogin();
  return true;
}

bool GoogleAuthenticator::AuthenticateToUnlock(const std::string& username,
                                               const std::string& password) {
  username_.assign(Canonicalize(username));
  StoreHashedPassword(password);
  unlock_ = true;
  ChromeThread::PostTask(
      ChromeThread::UI, FROM_HERE,
      NewRunnableMethod(this, &GoogleAuthenticator::CheckOffline,
                        std::string("unlock failed")));
  return true;
}

void GoogleAuthenticator::LoginOffTheRecord() {
  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
  if (CrosLibrary::Get()->GetCryptohomeLibrary()->Mount(kTmpfsTrigger, "")) {
    AuthenticationNotificationDetails details(true);
    NotificationService::current()->Notify(
        NotificationType::LOGIN_AUTHENTICATION,
        NotificationService::AllSources(),
        Details<AuthenticationNotificationDetails>(&details));
    consumer_->OnOffTheRecordLoginSuccess();
  } else {
    consumer_->OnLoginFailure("Could not mount tmpfs cryptohome");
  }
}

void GoogleAuthenticator::OnURLFetchComplete(const URLFetcher* source,
                                             const GURL& url,
                                             const URLRequestStatus& status,
                                             int response_code,
                                             const ResponseCookies& cookies,
                                             const std::string& data) {
  fetch_completed_ = true;
  if (status.is_success() && response_code == kHttpSuccess) {
    LOG(INFO) << "Online login successful!";
    ChromeThread::PostTask(
        ChromeThread::UI, FROM_HERE,
        NewRunnableMethod(this, &GoogleAuthenticator::OnLoginSuccess, data));
  } else if (!status.is_success()) {
    if (status.status() == URLRequestStatus::CANCELED) {
      if (try_again_) {
        try_again_ = false;
        LOG(ERROR) << "Login attempt canceled!?!?  Trying again.";
        TryClientLogin();
        fetch_completed_ = false;
        return;
      }
      LOG(ERROR) << "Login attempt canceled again?  Already retried...";
    }
    LOG(WARNING) << "Could not reach Google Accounts servers: errno "
              << status.os_error();
    // The fetch failed for network reasons, try offline login.
    LoadLocalaccount(kLocalaccountFile);
    ChromeThread::PostTask(
        ChromeThread::UI, FROM_HERE,
        NewRunnableMethod(this, &GoogleAuthenticator::CheckOffline,
                          net::ErrorToString(status.os_error())));
  } else {
    if (IsSecondFactorSuccess(data)) {
      ChromeThread::PostTask(
          ChromeThread::UI, FROM_HERE,
          NewRunnableMethod(this,
                            &GoogleAuthenticator::OnLoginSuccess,
                            std::string()));
    } else {
      // The fetch succeeded, but ClientLogin said no.
      LoadLocalaccount(kLocalaccountFile);
      ChromeThread::PostTask(
          ChromeThread::UI, FROM_HERE,
          NewRunnableMethod(this,
                            &GoogleAuthenticator::CheckLocalaccount,
                            data));
    }
  }
}

void GoogleAuthenticator::OnLoginSuccess(const std::string& data) {
  // Send notification of success
  AuthenticationNotificationDetails details(true);
  NotificationService::current()->Notify(
      NotificationType::LOGIN_AUTHENTICATION,
      NotificationService::AllSources(),
      Details<AuthenticationNotificationDetails>(&details));

  if (unlock_ ||
      CrosLibrary::Get()->GetCryptohomeLibrary()->Mount(username_.c_str(),
                                                        ascii_hash_.c_str())) {
    consumer_->OnLoginSuccess(username_, data);
  } else {
    OnLoginFailure("Could not mount cryptohome");
  }
}

void GoogleAuthenticator::CheckOffline(const std::string& error) {
  LOG(INFO) << "Attempting offline login";
  if (CrosLibrary::Get()->GetCryptohomeLibrary()->CheckKey(
          username_.c_str(),
          ascii_hash_.c_str())) {
    // The fetch didn't succeed, but offline login did.
    LOG(INFO) << "Offline login successful!";
    OnLoginSuccess(std::string());
  } else {
    // We couldn't hit the network, and offline login failed.
    GoogleAuthenticator::CheckLocalaccount(error);
  }
}

void GoogleAuthenticator::CheckLocalaccount(const std::string& error) {
  if (!localaccount_.empty() && localaccount_ == username_ &&
      CrosLibrary::Get()->GetCryptohomeLibrary()->Mount(kTmpfsTrigger, "")) {
    LOG(WARNING) << "Logging in with localaccount: " << localaccount_;
    consumer_->OnLoginSuccess(username_, std::string());
  } else {
    OnLoginFailure(error);
  }
}

void GoogleAuthenticator::OnLoginFailure(const std::string& data) {
  // Send notification of failure
  AuthenticationNotificationDetails details(false);
  NotificationService::current()->Notify(
      NotificationType::LOGIN_AUTHENTICATION,
      NotificationService::AllSources(),
      Details<AuthenticationNotificationDetails>(&details));
  LOG(WARNING) << "Login failed: " << data;
  // TODO(cmasone): what can we do to expose these OS/server-side error strings
  // in an internationalizable way?
  consumer_->OnLoginFailure(data);
}

void GoogleAuthenticator::LoadSystemSalt() {
  if (!system_salt_.empty())
    return;
  system_salt_ = CrosLibrary::Get()->GetCryptohomeLibrary()->GetSystemSalt();
  CHECK(!system_salt_.empty());
  CHECK_EQ(system_salt_.size() % 2, 0U);
}

void GoogleAuthenticator::LoadLocalaccount(const std::string& filename) {
  if (checked_for_localaccount_)
    return;
  FilePath localaccount_file;
  std::string localaccount;
  if (PathService::Get(base::DIR_EXE, &localaccount_file)) {
    localaccount_file = localaccount_file.Append(filename);
    LOG(INFO) << "looking for localaccount in " << localaccount_file.value();

    ReadFileToString(localaccount_file, &localaccount);
    TrimWhitespaceASCII(localaccount, TRIM_TRAILING, &localaccount);
    LOG(INFO) << "Loading localaccount: " << localaccount;
  } else {
    LOG(INFO) << "Assuming no localaccount";
  }
  set_localaccount(localaccount);
}

void GoogleAuthenticator::StoreHashedPassword(const std::string& password) {
  // Get salt, ascii encode, update sha with that, then update with ascii
  // of password, then end.
  std::string ascii_salt = SaltAsAscii();
  unsigned char passhash_buf[kPassHashLen];
  char ascii_buf[kPassHashLen + 1];

  // Hash salt and password
  SHA256Context ctx;
  SHA256_Begin(&ctx);
  SHA256_Update(&ctx,
                reinterpret_cast<const unsigned char*>(ascii_salt.data()),
                static_cast<unsigned int>(ascii_salt.length()));
  SHA256_Update(&ctx,
                reinterpret_cast<const unsigned char*>(password.data()),
                static_cast<unsigned int>(password.length()));
  SHA256_End(&ctx,
             passhash_buf,
             NULL,
             static_cast<unsigned int>(sizeof(passhash_buf)));

  std::vector<unsigned char> passhash(passhash_buf,
                                      passhash_buf + sizeof(passhash_buf));
  BinaryToHex(passhash,
              passhash.size() / 2,  // only want top half, at least for now.
              ascii_buf,
              sizeof(ascii_buf));
  ascii_hash_.assign(ascii_buf, sizeof(ascii_buf) - 1);
}

std::string GoogleAuthenticator::SaltAsAscii() {
  LoadSystemSalt();  // no-op if it's already loaded.
  unsigned int salt_len = system_salt_.size();
  char ascii_salt[2 * salt_len + 1];
  if (GoogleAuthenticator::BinaryToHex(system_salt_,
                                       salt_len,
                                       ascii_salt,
                                       sizeof(ascii_salt))) {
    return std::string(ascii_salt, sizeof(ascii_salt) - 1);
  } else {
    return std::string();
  }
}

void GoogleAuthenticator::Cancel() {
  if (!fetch_completed_ && fetcher_) {
    delete fetcher_;
    fetcher_ = NULL;
  }
  OnLoginFailure("Login has timed out; please try again!");
}

void GoogleAuthenticator::TryClientLogin() {
  if (fetcher_)
    delete fetcher_;
  fetcher_ = CreateClientLoginFetcher(getter_, request_body_, this);
  fetcher_->Start();
  ChromeThread::PostDelayedTask(
      ChromeThread::UI, FROM_HERE,
      NewRunnableMethod(this,
                        &GoogleAuthenticator::Cancel), kClientLoginTimeoutMs);
}

// static
bool GoogleAuthenticator::IsSecondFactorSuccess(
    const std::string& alleged_error) {
  return alleged_error.find(GoogleAuthenticator::kSecondFactor) !=
      std::string::npos;
}

// static
bool GoogleAuthenticator::BinaryToHex(const std::vector<unsigned char>& binary,
                                      const unsigned int binary_len,
                                      char* hex_string,
                                      const unsigned int len) {
  if (len < 2*binary_len)
    return false;
  memset(hex_string, 0, len);
  for (uint i = 0, j = 0; i < binary_len; i++, j+=2)
    snprintf(hex_string + j, len - j, "%02x", binary[i]);
  return true;
}

// static
std::string GoogleAuthenticator::Canonicalize(
    const std::string& email_address) {
  std::vector<std::string> parts;
  char at = '@';
  SplitString(email_address, at, &parts);
  DCHECK_EQ(parts.size(), 2U) << "email_address should have only one @";
  RemoveChars(parts[0], ".", &parts[0]);
  if (parts[0].find('+') != std::string::npos)
    parts[0].erase(parts[0].find('+'));
  std::string new_email = StringToLowerASCII(JoinString(parts, at));
  LOG(INFO) << "Canonicalized " << email_address << " to " << new_email;
  return new_email;
}

}  // namespace chromeos
<commit_msg>In case of successful login attempt do nothing in Cancel task. Fixes crash introduced in http://codereview.chromium.org/2867012<commit_after>// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/login/google_authenticator.h"

#include <string>
#include <vector>

#include "base/file_path.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/sha2.h"
#include "base/string_util.h"
#include "base/third_party/nss/blapi.h"
#include "base/third_party/nss/sha256.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_thread.h"
#include "chrome/browser/chromeos/cros/cryptohome_library.h"
#include "chrome/browser/chromeos/login/auth_response_handler.h"
#include "chrome/browser/chromeos/login/authentication_notification_details.h"
#include "chrome/browser/chromeos/login/login_status_consumer.h"
#include "chrome/browser/net/chrome_url_request_context.h"
#include "chrome/browser/profile.h"
#include "chrome/browser/profile_manager.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/net/url_fetcher.h"
#include "chrome/common/notification_service.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/url_request/url_request_status.h"
#include "third_party/libjingle/source/talk/base/urlencode.h"

using base::Time;
using base::TimeDelta;
using file_util::GetFileSize;
using file_util::PathExists;
using file_util::ReadFile;
using file_util::ReadFileToString;

namespace chromeos {

// static
const char GoogleAuthenticator::kCookiePersistence[] = "true";
// static
const char GoogleAuthenticator::kAccountType[] = "HOSTED_OR_GOOGLE";
// static
const char GoogleAuthenticator::kSource[] = "chromeos";
// static
// Service name for Google Contacts API. API is used to get user's image.
const char GoogleAuthenticator::kService[] = "cp";
// static
const char GoogleAuthenticator::kFormat[] =
    "Email=%s&"
    "Passwd=%s&"
    "PersistentCookie=%s&"
    "accountType=%s&"
    "source=%s&"
    "service=%s";
// static
const char GoogleAuthenticator::kFormatCaptcha[] =
    "Email=%s&"
    "Passwd=%s&"
    "PersistentCookie=%s&"
    "accountType=%s&"
    "source=%s&"
    "service=%s&"
    "logintoken=%s&"
    "logincaptcha=%s";
// static
const char GoogleAuthenticator::kSecondFactor[] = "Info=InvalidSecondFactor";

// static
const char GoogleAuthenticator::kSystemSalt[] = "/home/.shadow/salt";
// static
const char GoogleAuthenticator::kOpenSSLMagic[] = "Salted__";
// static
const char GoogleAuthenticator::kLocalaccountFile[] = "localaccount";
// static
const char GoogleAuthenticator::kTmpfsTrigger[] = "incognito";

// static
const int GoogleAuthenticator::kClientLoginTimeoutMs = 5000;

const int kPassHashLen = 32;

GoogleAuthenticator::GoogleAuthenticator(LoginStatusConsumer* consumer)
    : Authenticator(consumer),
      fetcher_(NULL),
      getter_(NULL),
      checked_for_localaccount_(false),
      unlock_(false),
      try_again_(true),
      fetch_completed_(false) {
  CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded());
}

GoogleAuthenticator::~GoogleAuthenticator() {
  delete fetcher_;
}

// static
URLFetcher* GoogleAuthenticator::CreateClientLoginFetcher(
    URLRequestContextGetter* getter,
    const std::string& body,
    URLFetcher::Delegate* delegate) {
  URLFetcher* to_return =
      URLFetcher::Create(0,
                         GURL(AuthResponseHandler::kClientLoginUrl),
                         URLFetcher::POST,
                         delegate);
  to_return->set_request_context(getter);
  to_return->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES);
  to_return->set_upload_data("application/x-www-form-urlencoded", body);
  return to_return;
}

bool GoogleAuthenticator::AuthenticateToLogin(
    Profile* profile,
    const std::string& username,
    const std::string& password,
    const std::string& login_token,
    const std::string& login_captcha) {
  unlock_ = false;
  getter_ = profile->GetRequestContext();

  // TODO(cmasone): be more careful about zeroing memory that stores
  // the user's password.
  if (login_token.empty() || login_captcha.empty()) {
    request_body_ = StringPrintf(kFormat,
                                 UrlEncodeString(username).c_str(),
                                 UrlEncodeString(password).c_str(),
                                 kCookiePersistence,
                                 kAccountType,
                                 kSource,
                                 kService);
  } else {
    request_body_ = StringPrintf(kFormatCaptcha,
                                 UrlEncodeString(username).c_str(),
                                 UrlEncodeString(password).c_str(),
                                 kCookiePersistence,
                                 kAccountType,
                                 kSource,
                                 kService,
                                 UrlEncodeString(login_token).c_str(),
                                 UrlEncodeString(login_captcha).c_str());
  }
  // TODO(cmasone): Figure out how to parallelize fetch, username/password
  // processing without impacting testability.
  username_.assign(Canonicalize(username));
  StoreHashedPassword(password);
  TryClientLogin();
  return true;
}

bool GoogleAuthenticator::AuthenticateToUnlock(const std::string& username,
                                               const std::string& password) {
  username_.assign(Canonicalize(username));
  StoreHashedPassword(password);
  unlock_ = true;
  ChromeThread::PostTask(
      ChromeThread::UI, FROM_HERE,
      NewRunnableMethod(this, &GoogleAuthenticator::CheckOffline,
                        std::string("unlock failed")));
  return true;
}

void GoogleAuthenticator::LoginOffTheRecord() {
  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
  if (CrosLibrary::Get()->GetCryptohomeLibrary()->Mount(kTmpfsTrigger, "")) {
    AuthenticationNotificationDetails details(true);
    NotificationService::current()->Notify(
        NotificationType::LOGIN_AUTHENTICATION,
        NotificationService::AllSources(),
        Details<AuthenticationNotificationDetails>(&details));
    consumer_->OnOffTheRecordLoginSuccess();
  } else {
    consumer_->OnLoginFailure("Could not mount tmpfs cryptohome");
  }
}

void GoogleAuthenticator::OnURLFetchComplete(const URLFetcher* source,
                                             const GURL& url,
                                             const URLRequestStatus& status,
                                             int response_code,
                                             const ResponseCookies& cookies,
                                             const std::string& data) {
  fetch_completed_ = true;
  if (status.is_success() && response_code == kHttpSuccess) {
    LOG(INFO) << "Online login successful!";
    ChromeThread::PostTask(
        ChromeThread::UI, FROM_HERE,
        NewRunnableMethod(this, &GoogleAuthenticator::OnLoginSuccess, data));
  } else if (!status.is_success()) {
    if (status.status() == URLRequestStatus::CANCELED) {
      if (try_again_) {
        try_again_ = false;
        LOG(ERROR) << "Login attempt canceled!?!?  Trying again.";
        TryClientLogin();
        fetch_completed_ = false;
        return;
      }
      LOG(ERROR) << "Login attempt canceled again?  Already retried...";
    }
    LOG(WARNING) << "Could not reach Google Accounts servers: errno "
              << status.os_error();
    // The fetch failed for network reasons, try offline login.
    LoadLocalaccount(kLocalaccountFile);
    ChromeThread::PostTask(
        ChromeThread::UI, FROM_HERE,
        NewRunnableMethod(this, &GoogleAuthenticator::CheckOffline,
                          net::ErrorToString(status.os_error())));
  } else {
    if (IsSecondFactorSuccess(data)) {
      ChromeThread::PostTask(
          ChromeThread::UI, FROM_HERE,
          NewRunnableMethod(this,
                            &GoogleAuthenticator::OnLoginSuccess,
                            std::string()));
    } else {
      // The fetch succeeded, but ClientLogin said no.
      LoadLocalaccount(kLocalaccountFile);
      ChromeThread::PostTask(
          ChromeThread::UI, FROM_HERE,
          NewRunnableMethod(this,
                            &GoogleAuthenticator::CheckLocalaccount,
                            data));
    }
  }
}

void GoogleAuthenticator::OnLoginSuccess(const std::string& data) {
  // Send notification of success
  AuthenticationNotificationDetails details(true);
  NotificationService::current()->Notify(
      NotificationType::LOGIN_AUTHENTICATION,
      NotificationService::AllSources(),
      Details<AuthenticationNotificationDetails>(&details));

  if (unlock_ ||
      CrosLibrary::Get()->GetCryptohomeLibrary()->Mount(username_.c_str(),
                                                        ascii_hash_.c_str())) {
    consumer_->OnLoginSuccess(username_, data);
  } else {
    OnLoginFailure("Could not mount cryptohome");
  }
}

void GoogleAuthenticator::CheckOffline(const std::string& error) {
  LOG(INFO) << "Attempting offline login";
  if (CrosLibrary::Get()->GetCryptohomeLibrary()->CheckKey(
          username_.c_str(),
          ascii_hash_.c_str())) {
    // The fetch didn't succeed, but offline login did.
    LOG(INFO) << "Offline login successful!";
    OnLoginSuccess(std::string());
  } else {
    // We couldn't hit the network, and offline login failed.
    GoogleAuthenticator::CheckLocalaccount(error);
  }
}

void GoogleAuthenticator::CheckLocalaccount(const std::string& error) {
  if (!localaccount_.empty() && localaccount_ == username_ &&
      CrosLibrary::Get()->GetCryptohomeLibrary()->Mount(kTmpfsTrigger, "")) {
    LOG(WARNING) << "Logging in with localaccount: " << localaccount_;
    consumer_->OnLoginSuccess(username_, std::string());
  } else {
    OnLoginFailure(error);
  }
}

void GoogleAuthenticator::OnLoginFailure(const std::string& data) {
  // Send notification of failure
  AuthenticationNotificationDetails details(false);
  NotificationService::current()->Notify(
      NotificationType::LOGIN_AUTHENTICATION,
      NotificationService::AllSources(),
      Details<AuthenticationNotificationDetails>(&details));
  LOG(WARNING) << "Login failed: " << data;
  // TODO(cmasone): what can we do to expose these OS/server-side error strings
  // in an internationalizable way?
  consumer_->OnLoginFailure(data);
}

void GoogleAuthenticator::LoadSystemSalt() {
  if (!system_salt_.empty())
    return;
  system_salt_ = CrosLibrary::Get()->GetCryptohomeLibrary()->GetSystemSalt();
  CHECK(!system_salt_.empty());
  CHECK_EQ(system_salt_.size() % 2, 0U);
}

void GoogleAuthenticator::LoadLocalaccount(const std::string& filename) {
  if (checked_for_localaccount_)
    return;
  FilePath localaccount_file;
  std::string localaccount;
  if (PathService::Get(base::DIR_EXE, &localaccount_file)) {
    localaccount_file = localaccount_file.Append(filename);
    LOG(INFO) << "looking for localaccount in " << localaccount_file.value();

    ReadFileToString(localaccount_file, &localaccount);
    TrimWhitespaceASCII(localaccount, TRIM_TRAILING, &localaccount);
    LOG(INFO) << "Loading localaccount: " << localaccount;
  } else {
    LOG(INFO) << "Assuming no localaccount";
  }
  set_localaccount(localaccount);
}

void GoogleAuthenticator::StoreHashedPassword(const std::string& password) {
  // Get salt, ascii encode, update sha with that, then update with ascii
  // of password, then end.
  std::string ascii_salt = SaltAsAscii();
  unsigned char passhash_buf[kPassHashLen];
  char ascii_buf[kPassHashLen + 1];

  // Hash salt and password
  SHA256Context ctx;
  SHA256_Begin(&ctx);
  SHA256_Update(&ctx,
                reinterpret_cast<const unsigned char*>(ascii_salt.data()),
                static_cast<unsigned int>(ascii_salt.length()));
  SHA256_Update(&ctx,
                reinterpret_cast<const unsigned char*>(password.data()),
                static_cast<unsigned int>(password.length()));
  SHA256_End(&ctx,
             passhash_buf,
             NULL,
             static_cast<unsigned int>(sizeof(passhash_buf)));

  std::vector<unsigned char> passhash(passhash_buf,
                                      passhash_buf + sizeof(passhash_buf));
  BinaryToHex(passhash,
              passhash.size() / 2,  // only want top half, at least for now.
              ascii_buf,
              sizeof(ascii_buf));
  ascii_hash_.assign(ascii_buf, sizeof(ascii_buf) - 1);
}

std::string GoogleAuthenticator::SaltAsAscii() {
  LoadSystemSalt();  // no-op if it's already loaded.
  unsigned int salt_len = system_salt_.size();
  char ascii_salt[2 * salt_len + 1];
  if (GoogleAuthenticator::BinaryToHex(system_salt_,
                                       salt_len,
                                       ascii_salt,
                                       sizeof(ascii_salt))) {
    return std::string(ascii_salt, sizeof(ascii_salt) - 1);
  } else {
    return std::string();
  }
}

void GoogleAuthenticator::Cancel() {
  if (fetch_completed_)
   return;

  if (fetcher_) {
    delete fetcher_;
    fetcher_ = NULL;
  }
  OnLoginFailure("Login has timed out; please try again!");
}

void GoogleAuthenticator::TryClientLogin() {
  if (fetcher_)
    delete fetcher_;
  fetcher_ = CreateClientLoginFetcher(getter_, request_body_, this);
  fetcher_->Start();
  ChromeThread::PostDelayedTask(
      ChromeThread::UI, FROM_HERE,
      NewRunnableMethod(this,
                        &GoogleAuthenticator::Cancel), kClientLoginTimeoutMs);
}

// static
bool GoogleAuthenticator::IsSecondFactorSuccess(
    const std::string& alleged_error) {
  return alleged_error.find(GoogleAuthenticator::kSecondFactor) !=
      std::string::npos;
}

// static
bool GoogleAuthenticator::BinaryToHex(const std::vector<unsigned char>& binary,
                                      const unsigned int binary_len,
                                      char* hex_string,
                                      const unsigned int len) {
  if (len < 2*binary_len)
    return false;
  memset(hex_string, 0, len);
  for (uint i = 0, j = 0; i < binary_len; i++, j+=2)
    snprintf(hex_string + j, len - j, "%02x", binary[i]);
  return true;
}

// static
std::string GoogleAuthenticator::Canonicalize(
    const std::string& email_address) {
  std::vector<std::string> parts;
  char at = '@';
  SplitString(email_address, at, &parts);
  DCHECK_EQ(parts.size(), 2U) << "email_address should have only one @";
  RemoveChars(parts[0], ".", &parts[0]);
  if (parts[0].find('+') != std::string::npos)
    parts[0].erase(parts[0].find('+'));
  std::string new_email = StringToLowerASCII(JoinString(parts, at));
  LOG(INFO) << "Canonicalized " << email_address << " to " << new_email;
  return new_email;
}

}  // namespace chromeos
<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/status/network_menu_button.h"

#include <algorithm>
#include <limits>

#include "base/logging.h"
#include "base/message_loop.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/login/helper.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/chromeos/options/network_config_view.h"
#include "chrome/browser/chromeos/sim_dialog_delegate.h"
#include "chrome/browser/chromeos/status/status_area_host.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/common/pref_names.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "views/widget/widget.h"

namespace {

// Time in milliseconds to delay showing of promo
// notification when Chrome window is not on screen.
const int kPromoShowDelayMs = 10000;

const int kNotificationCountPrefDefault = -1;

bool GetBooleanPref(const char* pref_name) {
  Browser* browser = BrowserList::GetLastActive();
  // Default to safe value which is false (not to show bubble).
  if (!browser || !browser->profile())
    return false;

  PrefService* prefs = browser->profile()->GetPrefs();
  return prefs->GetBoolean(pref_name);
}

int GetIntegerLocalPref(const char* pref_name) {
  PrefService* prefs = g_browser_process->local_state();
  return prefs->GetInteger(pref_name);
}

void SetBooleanPref(const char* pref_name, bool value) {
  Browser* browser = BrowserList::GetLastActive();
  if (!browser || !browser->profile())
    return;

  PrefService* prefs = browser->profile()->GetPrefs();
  prefs->SetBoolean(pref_name, value);
}

void SetIntegerLocalPref(const char* pref_name, int value) {
  PrefService* prefs = g_browser_process->local_state();
  prefs->SetInteger(pref_name, value);
}

// Returns prefs::kShow3gPromoNotification or false
// if there's no active browser.
bool ShouldShow3gPromoNotification() {
  return GetBooleanPref(prefs::kShow3gPromoNotification);
}

void SetShow3gPromoNotification(bool value) {
  SetBooleanPref(prefs::kShow3gPromoNotification, value);
}

// Returns prefs::kCarrierDealPromoShown which is number of times
// carrier deal notification has been shown to users on this machine.
int GetCarrierDealPromoShown() {
  return GetIntegerLocalPref(prefs::kCarrierDealPromoShown);
}

void SetCarrierDealPromoShown(int value) {
  SetIntegerLocalPref(prefs::kCarrierDealPromoShown, value);
}

}  // namespace

namespace chromeos {

////////////////////////////////////////////////////////////////////////////////
// NetworkMenuButton

NetworkMenuButton::NetworkMenuButton(StatusAreaHost* host)
    : StatusAreaButton(host, this),
      mobile_data_bubble_(NULL),
      is_browser_mode_(false),
      check_for_promo_(true),
      was_sim_locked_(false),
      ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
  is_browser_mode_ = (host->GetScreenMode() == StatusAreaHost::kBrowserMode);
  network_menu_.reset(new NetworkMenu(this, is_browser_mode_));
  network_icon_.reset(
      new NetworkMenuIcon(this, NetworkMenuIcon::MENU_MODE));

  NetworkLibrary* network_library = CrosLibrary::Get()->GetNetworkLibrary();
  OnNetworkManagerChanged(network_library);
  network_library->AddNetworkManagerObserver(this);
  network_library->AddCellularDataPlanObserver(this);
  const NetworkDevice* cellular = network_library->FindCellularDevice();
  if (cellular) {
    cellular_device_path_ = cellular->device_path();
    was_sim_locked_ = cellular->is_sim_locked();
    network_library->AddNetworkDeviceObserver(cellular_device_path_, this);
  }
}

NetworkMenuButton::~NetworkMenuButton() {
  NetworkLibrary* netlib = CrosLibrary::Get()->GetNetworkLibrary();
  netlib->RemoveNetworkManagerObserver(this);
  netlib->RemoveObserverForAllNetworks(this);
  netlib->RemoveCellularDataPlanObserver(this);
  if (!cellular_device_path_.empty())
    netlib->RemoveNetworkDeviceObserver(cellular_device_path_, this);
  if (mobile_data_bubble_)
    mobile_data_bubble_->Close();
}

// static
void NetworkMenuButton::RegisterPrefs(PrefService* local_state) {
  // Carrier deal notification shown count defaults to 0.
  local_state->RegisterIntegerPref(prefs::kCarrierDealPromoShown, 0);
}

////////////////////////////////////////////////////////////////////////////////
// NetworkLibrary::NetworkDeviceObserver implementation:

void NetworkMenuButton::OnNetworkDeviceChanged(NetworkLibrary* cros,
                                               const NetworkDevice* device) {
  // Device status, such as SIMLock may have changed.
  SetNetworkIcon();
  network_menu_->UpdateMenu();
  const NetworkDevice* cellular = cros->FindCellularDevice();
  if (cellular) {
    // We make an assumption (which is valid for now) that the SIM
    // unlock dialog is put up only when the user is trying to enable
    // mobile data. So if the SIM is now unlocked, initiate the
    // enable operation that the user originally requested.
    if (was_sim_locked_ && !cellular->is_sim_locked() &&
        !cros->cellular_enabled()) {
      cros->EnableCellularNetworkDevice(true);
    }
    was_sim_locked_ = cellular->is_sim_locked();
  }
}

////////////////////////////////////////////////////////////////////////////////
// NetworkMenuButton, NetworkLibrary::NetworkManagerObserver implementation:

void NetworkMenuButton::OnNetworkManagerChanged(NetworkLibrary* cros) {
  // This gets called on initialization, so any changes should be reflected
  // in CrosMock::SetNetworkLibraryStatusAreaExpectations().
  SetNetworkIcon();
  network_menu_->UpdateMenu();
  RefreshNetworkObserver(cros);
  RefreshNetworkDeviceObserver(cros);
  ShowOptionalMobileDataPromoNotification(cros);
}

////////////////////////////////////////////////////////////////////////////////
// NetworkMenuButton, NetworkLibrary::NetworkObserver implementation:
void NetworkMenuButton::OnNetworkChanged(NetworkLibrary* cros,
                                         const Network* network) {
  SetNetworkIcon();
  network_menu_->UpdateMenu();
}

void NetworkMenuButton::OnCellularDataPlanChanged(NetworkLibrary* cros) {
  // Call OnNetworkManagerChanged which will update the icon.
  SetNetworkIcon();
  network_menu_->UpdateMenu();
}

////////////////////////////////////////////////////////////////////////////////
// NetworkMenuButton, NetworkMenu implementation:

views::MenuButton* NetworkMenuButton::GetMenuButton() {
  return this;
}

gfx::NativeWindow NetworkMenuButton::GetNativeWindow() const {
  return host_->GetNativeWindow();
}

void NetworkMenuButton::OpenButtonOptions() {
  host_->OpenButtonOptions(this);
}

bool NetworkMenuButton::ShouldOpenButtonOptions() const {
  return host_->ShouldOpenButtonOptions(this);
}

////////////////////////////////////////////////////////////////////////////////
// NetworkMenuButton, views::View implementation:

void NetworkMenuButton::OnLocaleChanged() {
  SetNetworkIcon();
  network_menu_->UpdateMenu();
}

////////////////////////////////////////////////////////////////////////////////
// NetworkMenuButton, views::ViewMenuDelegate implementation:
void NetworkMenuButton::RunMenu(views::View* source, const gfx::Point& pt) {
  network_menu_->RunMenu(source);
}

////////////////////////////////////////////////////////////////////////////////
// NetworkMenuButton, NetworkMenuIcon::Delegate implementation:
void NetworkMenuButton::NetworkMenuIconChanged() {
  const SkBitmap* bitmap = network_icon_->GetIconAndText(NULL);
  SetIcon(*bitmap);
  SchedulePaint();
}

////////////////////////////////////////////////////////////////////////////////
// MessageBubbleDelegate implementation:

void NetworkMenuButton::BubbleClosing(Bubble* bubble, bool closed_by_escape) {
  mobile_data_bubble_ = NULL;
  deal_info_url_.clear();
  deal_topup_url_.clear();
}

bool NetworkMenuButton::CloseOnEscape() {
  return true;
}

bool NetworkMenuButton::FadeInOnShow() {
  return false;
}

void NetworkMenuButton::OnLinkActivated(size_t index) {
  // If we have deal info URL defined that means that there're
  // 2 links in bubble. Let the user close it manually then thus giving ability
  // to navigate to second link.
  // mobile_data_bubble_ will be set to NULL in BubbleClosing callback.
  if (deal_info_url_.empty() && mobile_data_bubble_)
    mobile_data_bubble_->Close();

  std::string deal_url_to_open;
  if (index == 0) {
    if (!deal_topup_url_.empty()) {
      deal_url_to_open = deal_topup_url_;
    } else {
      const Network* cellular =
          CrosLibrary::Get()->GetNetworkLibrary()->cellular_network();
      if (!cellular)
        return;
      network_menu_->ShowTabbedNetworkSettings(cellular);
      return;
    }
  } else if (index == 1) {
    deal_url_to_open = deal_info_url_;
  }

  if (!deal_url_to_open.empty()) {
    Browser* browser = BrowserList::GetLastActive();
    if (!browser)
      return;
    browser->ShowSingletonTab(GURL(deal_url_to_open));
  }
}

////////////////////////////////////////////////////////////////////////////////
// NetworkMenuButton, private methods:

const ServicesCustomizationDocument::CarrierDeal*
NetworkMenuButton::GetCarrierDeal(
    NetworkLibrary* cros) {
  std::string carrier_id = cros->GetCellularHomeCarrierId();
  if (carrier_id.empty()) {
    LOG(ERROR) << "Empty carrier ID with a cellular connected.";
    return NULL;
  }

  ServicesCustomizationDocument* customization =
      ServicesCustomizationDocument::GetInstance();
  if (!customization->IsReady())
    return NULL;

  const ServicesCustomizationDocument::CarrierDeal* deal =
      customization->GetCarrierDeal(carrier_id, true);
  if (deal) {
    // Check deal for validity.
    int carrier_deal_promo_pref = GetCarrierDealPromoShown();
    if (carrier_deal_promo_pref >= deal->notification_count())
      return NULL;
    const std::string locale = g_browser_process->GetApplicationLocale();
    std::string deal_text = deal->GetLocalizedString(locale,
                                                     "notification_text");
    if (deal_text.empty())
      return NULL;
  }
  return deal;
}

void NetworkMenuButton::SetNetworkIcon() {
  string16 tooltip;
  const SkBitmap* bitmap = network_icon_->GetIconAndText(&tooltip);
  SetIcon(*bitmap);
  SetTooltipAndAccessibleName(tooltip);
  SchedulePaint();
}

void NetworkMenuButton::RefreshNetworkObserver(NetworkLibrary* cros) {
  const Network* network = cros->active_network();
  std::string new_network = network ? network->service_path() : std::string();
  if (active_network_ != new_network) {
    if (!active_network_.empty()) {
      cros->RemoveNetworkObserver(active_network_, this);
    }
    if (!new_network.empty()) {
      cros->AddNetworkObserver(new_network, this);
    }
    active_network_ = new_network;
  }
}

void NetworkMenuButton::RefreshNetworkDeviceObserver(NetworkLibrary* cros) {
  const NetworkDevice* cellular = cros->FindCellularDevice();
  std::string new_cellular_device_path = cellular ?
      cellular->device_path() : std::string();
  if (cellular_device_path_ != new_cellular_device_path) {
    if (!cellular_device_path_.empty()) {
      cros->RemoveNetworkDeviceObserver(cellular_device_path_, this);
    }
    if (!new_cellular_device_path.empty()) {
      was_sim_locked_ = cellular->is_sim_locked();
      cros->AddNetworkDeviceObserver(new_cellular_device_path, this);
    }
    cellular_device_path_ = new_cellular_device_path;
  }
}

void NetworkMenuButton::ShowOptionalMobileDataPromoNotification(
    NetworkLibrary* cros) {
  // Display one-time notification for non-Guest users on first use
  // of Mobile Data connection or if there's a carrier deal defined
  // show that even if user has already seen generic promo.
  if (is_browser_mode_ && !UserManager::Get()->IsLoggedInAsGuest() &&
      check_for_promo_ && BrowserList::GetLastActive() &&
      cros->cellular_connected() && !cros->ethernet_connected() &&
      !cros->wifi_connected()) {
    const ServicesCustomizationDocument::CarrierDeal* deal =
        GetCarrierDeal(cros);
    std::string deal_text;
    int carrier_deal_promo_pref = -1;
    if (deal) {
      carrier_deal_promo_pref = GetCarrierDealPromoShown();
      const std::string locale = g_browser_process->GetApplicationLocale();
      deal_text = deal->GetLocalizedString(locale, "notification_text");
      deal_info_url_ = deal->info_url();
      deal_topup_url_ = deal->top_up_url();
    } else if (!ShouldShow3gPromoNotification()) {
      check_for_promo_ = false;
      return;
    }

    gfx::Rect button_bounds = GetScreenBounds();
    // StatusArea button Y position is usually -1, fix it so that
    // Contains() method for screen bounds works correctly.
    button_bounds.set_y(button_bounds.y() + 1);
    gfx::Rect screen_bounds(chromeos::CalculateScreenBounds(gfx::Size()));

    // Chrome window is initialized in visible state off screen and then is
    // moved into visible screen area. Make sure that we're on screen
    // so that bubble is shown correctly.
    if (!screen_bounds.Contains(button_bounds)) {
      // If we're not on screen yet, delay notification display.
      // It may be shown earlier, on next NetworkLibrary callback processing.
      if (method_factory_.empty()) {
        MessageLoop::current()->PostDelayedTask(FROM_HERE,
            method_factory_.NewRunnableMethod(
                &NetworkMenuButton::ShowOptionalMobileDataPromoNotification,
                cros),
            kPromoShowDelayMs);
      }
      return;
    }

    // Add deal text if it's defined.
    std::wstring notification_text;
    std::wstring default_text =
        UTF16ToWide(l10n_util::GetStringUTF16(IDS_3G_NOTIFICATION_MESSAGE));
    if (!deal_text.empty()) {
      notification_text = StringPrintf(L"%ls\n\n%ls",
                                       UTF8ToWide(deal_text).c_str(),
                                       default_text.c_str());
    } else {
      notification_text = default_text;
    }

    // Use deal URL if it's defined or general "Network Settings" URL.
    int link_message_id;
    if (deal_topup_url_.empty())
      link_message_id = IDS_OFFLINE_NETWORK_SETTINGS;
    else
      link_message_id = IDS_STATUSBAR_NETWORK_VIEW_ACCOUNT;

    std::vector<std::wstring> links;
    links.push_back(UTF16ToWide(l10n_util::GetStringUTF16(link_message_id)));
    if (!deal_topup_url_.empty())
      links.push_back(UTF16ToWide(l10n_util::GetStringUTF16(IDS_LEARN_MORE)));
    mobile_data_bubble_ = MessageBubble::ShowWithLinks(
        GetWidget(),
        button_bounds,
        BubbleBorder::TOP_RIGHT ,
        ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_NOTIFICATION_3G),
        notification_text,
        links,
        this);

    check_for_promo_ = false;
    SetShow3gPromoNotification(false);
    if (carrier_deal_promo_pref != kNotificationCountPrefDefault)
      SetCarrierDealPromoShown(carrier_deal_promo_pref + 1);
  }
}

void NetworkMenuButton::SetTooltipAndAccessibleName(const string16& label) {
  SetTooltipText(UTF16ToWide(label));
  SetAccessibleName(label);
}

}  // namespace chromeos
<commit_msg>Show Learn more link in 3G promo bubble only if deal_info_url is not empty<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/status/network_menu_button.h"

#include <algorithm>
#include <limits>

#include "base/logging.h"
#include "base/message_loop.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/login/helper.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/chromeos/options/network_config_view.h"
#include "chrome/browser/chromeos/sim_dialog_delegate.h"
#include "chrome/browser/chromeos/status/status_area_host.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/common/pref_names.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "views/widget/widget.h"

namespace {

// Time in milliseconds to delay showing of promo
// notification when Chrome window is not on screen.
const int kPromoShowDelayMs = 10000;

const int kNotificationCountPrefDefault = -1;

bool GetBooleanPref(const char* pref_name) {
  Browser* browser = BrowserList::GetLastActive();
  // Default to safe value which is false (not to show bubble).
  if (!browser || !browser->profile())
    return false;

  PrefService* prefs = browser->profile()->GetPrefs();
  return prefs->GetBoolean(pref_name);
}

int GetIntegerLocalPref(const char* pref_name) {
  PrefService* prefs = g_browser_process->local_state();
  return prefs->GetInteger(pref_name);
}

void SetBooleanPref(const char* pref_name, bool value) {
  Browser* browser = BrowserList::GetLastActive();
  if (!browser || !browser->profile())
    return;

  PrefService* prefs = browser->profile()->GetPrefs();
  prefs->SetBoolean(pref_name, value);
}

void SetIntegerLocalPref(const char* pref_name, int value) {
  PrefService* prefs = g_browser_process->local_state();
  prefs->SetInteger(pref_name, value);
}

// Returns prefs::kShow3gPromoNotification or false
// if there's no active browser.
bool ShouldShow3gPromoNotification() {
  return GetBooleanPref(prefs::kShow3gPromoNotification);
}

void SetShow3gPromoNotification(bool value) {
  SetBooleanPref(prefs::kShow3gPromoNotification, value);
}

// Returns prefs::kCarrierDealPromoShown which is number of times
// carrier deal notification has been shown to users on this machine.
int GetCarrierDealPromoShown() {
  return GetIntegerLocalPref(prefs::kCarrierDealPromoShown);
}

void SetCarrierDealPromoShown(int value) {
  SetIntegerLocalPref(prefs::kCarrierDealPromoShown, value);
}

}  // namespace

namespace chromeos {

////////////////////////////////////////////////////////////////////////////////
// NetworkMenuButton

NetworkMenuButton::NetworkMenuButton(StatusAreaHost* host)
    : StatusAreaButton(host, this),
      mobile_data_bubble_(NULL),
      is_browser_mode_(false),
      check_for_promo_(true),
      was_sim_locked_(false),
      ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) {
  is_browser_mode_ = (host->GetScreenMode() == StatusAreaHost::kBrowserMode);
  network_menu_.reset(new NetworkMenu(this, is_browser_mode_));
  network_icon_.reset(
      new NetworkMenuIcon(this, NetworkMenuIcon::MENU_MODE));

  NetworkLibrary* network_library = CrosLibrary::Get()->GetNetworkLibrary();
  OnNetworkManagerChanged(network_library);
  network_library->AddNetworkManagerObserver(this);
  network_library->AddCellularDataPlanObserver(this);
  const NetworkDevice* cellular = network_library->FindCellularDevice();
  if (cellular) {
    cellular_device_path_ = cellular->device_path();
    was_sim_locked_ = cellular->is_sim_locked();
    network_library->AddNetworkDeviceObserver(cellular_device_path_, this);
  }
}

NetworkMenuButton::~NetworkMenuButton() {
  NetworkLibrary* netlib = CrosLibrary::Get()->GetNetworkLibrary();
  netlib->RemoveNetworkManagerObserver(this);
  netlib->RemoveObserverForAllNetworks(this);
  netlib->RemoveCellularDataPlanObserver(this);
  if (!cellular_device_path_.empty())
    netlib->RemoveNetworkDeviceObserver(cellular_device_path_, this);
  if (mobile_data_bubble_)
    mobile_data_bubble_->Close();
}

// static
void NetworkMenuButton::RegisterPrefs(PrefService* local_state) {
  // Carrier deal notification shown count defaults to 0.
  local_state->RegisterIntegerPref(prefs::kCarrierDealPromoShown, 0);
}

////////////////////////////////////////////////////////////////////////////////
// NetworkLibrary::NetworkDeviceObserver implementation:

void NetworkMenuButton::OnNetworkDeviceChanged(NetworkLibrary* cros,
                                               const NetworkDevice* device) {
  // Device status, such as SIMLock may have changed.
  SetNetworkIcon();
  network_menu_->UpdateMenu();
  const NetworkDevice* cellular = cros->FindCellularDevice();
  if (cellular) {
    // We make an assumption (which is valid for now) that the SIM
    // unlock dialog is put up only when the user is trying to enable
    // mobile data. So if the SIM is now unlocked, initiate the
    // enable operation that the user originally requested.
    if (was_sim_locked_ && !cellular->is_sim_locked() &&
        !cros->cellular_enabled()) {
      cros->EnableCellularNetworkDevice(true);
    }
    was_sim_locked_ = cellular->is_sim_locked();
  }
}

////////////////////////////////////////////////////////////////////////////////
// NetworkMenuButton, NetworkLibrary::NetworkManagerObserver implementation:

void NetworkMenuButton::OnNetworkManagerChanged(NetworkLibrary* cros) {
  // This gets called on initialization, so any changes should be reflected
  // in CrosMock::SetNetworkLibraryStatusAreaExpectations().
  SetNetworkIcon();
  network_menu_->UpdateMenu();
  RefreshNetworkObserver(cros);
  RefreshNetworkDeviceObserver(cros);
  ShowOptionalMobileDataPromoNotification(cros);
}

////////////////////////////////////////////////////////////////////////////////
// NetworkMenuButton, NetworkLibrary::NetworkObserver implementation:
void NetworkMenuButton::OnNetworkChanged(NetworkLibrary* cros,
                                         const Network* network) {
  SetNetworkIcon();
  network_menu_->UpdateMenu();
}

void NetworkMenuButton::OnCellularDataPlanChanged(NetworkLibrary* cros) {
  // Call OnNetworkManagerChanged which will update the icon.
  SetNetworkIcon();
  network_menu_->UpdateMenu();
}

////////////////////////////////////////////////////////////////////////////////
// NetworkMenuButton, NetworkMenu implementation:

views::MenuButton* NetworkMenuButton::GetMenuButton() {
  return this;
}

gfx::NativeWindow NetworkMenuButton::GetNativeWindow() const {
  return host_->GetNativeWindow();
}

void NetworkMenuButton::OpenButtonOptions() {
  host_->OpenButtonOptions(this);
}

bool NetworkMenuButton::ShouldOpenButtonOptions() const {
  return host_->ShouldOpenButtonOptions(this);
}

////////////////////////////////////////////////////////////////////////////////
// NetworkMenuButton, views::View implementation:

void NetworkMenuButton::OnLocaleChanged() {
  SetNetworkIcon();
  network_menu_->UpdateMenu();
}

////////////////////////////////////////////////////////////////////////////////
// NetworkMenuButton, views::ViewMenuDelegate implementation:
void NetworkMenuButton::RunMenu(views::View* source, const gfx::Point& pt) {
  network_menu_->RunMenu(source);
}

////////////////////////////////////////////////////////////////////////////////
// NetworkMenuButton, NetworkMenuIcon::Delegate implementation:
void NetworkMenuButton::NetworkMenuIconChanged() {
  const SkBitmap* bitmap = network_icon_->GetIconAndText(NULL);
  SetIcon(*bitmap);
  SchedulePaint();
}

////////////////////////////////////////////////////////////////////////////////
// MessageBubbleDelegate implementation:

void NetworkMenuButton::BubbleClosing(Bubble* bubble, bool closed_by_escape) {
  mobile_data_bubble_ = NULL;
  deal_info_url_.clear();
  deal_topup_url_.clear();
}

bool NetworkMenuButton::CloseOnEscape() {
  return true;
}

bool NetworkMenuButton::FadeInOnShow() {
  return false;
}

void NetworkMenuButton::OnLinkActivated(size_t index) {
  // If we have deal info URL defined that means that there're
  // 2 links in bubble. Let the user close it manually then thus giving ability
  // to navigate to second link.
  // mobile_data_bubble_ will be set to NULL in BubbleClosing callback.
  if (deal_info_url_.empty() && mobile_data_bubble_)
    mobile_data_bubble_->Close();

  std::string deal_url_to_open;
  if (index == 0) {
    if (!deal_topup_url_.empty()) {
      deal_url_to_open = deal_topup_url_;
    } else {
      const Network* cellular =
          CrosLibrary::Get()->GetNetworkLibrary()->cellular_network();
      if (!cellular)
        return;
      network_menu_->ShowTabbedNetworkSettings(cellular);
      return;
    }
  } else if (index == 1) {
    deal_url_to_open = deal_info_url_;
  }

  if (!deal_url_to_open.empty()) {
    Browser* browser = BrowserList::GetLastActive();
    if (!browser)
      return;
    browser->ShowSingletonTab(GURL(deal_url_to_open));
  }
}

////////////////////////////////////////////////////////////////////////////////
// NetworkMenuButton, private methods:

const ServicesCustomizationDocument::CarrierDeal*
NetworkMenuButton::GetCarrierDeal(
    NetworkLibrary* cros) {
  std::string carrier_id = cros->GetCellularHomeCarrierId();
  if (carrier_id.empty()) {
    LOG(ERROR) << "Empty carrier ID with a cellular connected.";
    return NULL;
  }

  ServicesCustomizationDocument* customization =
      ServicesCustomizationDocument::GetInstance();
  if (!customization->IsReady())
    return NULL;

  const ServicesCustomizationDocument::CarrierDeal* deal =
      customization->GetCarrierDeal(carrier_id, true);
  if (deal) {
    // Check deal for validity.
    int carrier_deal_promo_pref = GetCarrierDealPromoShown();
    if (carrier_deal_promo_pref >= deal->notification_count())
      return NULL;
    const std::string locale = g_browser_process->GetApplicationLocale();
    std::string deal_text = deal->GetLocalizedString(locale,
                                                     "notification_text");
    if (deal_text.empty())
      return NULL;
  }
  return deal;
}

void NetworkMenuButton::SetNetworkIcon() {
  string16 tooltip;
  const SkBitmap* bitmap = network_icon_->GetIconAndText(&tooltip);
  SetIcon(*bitmap);
  SetTooltipAndAccessibleName(tooltip);
  SchedulePaint();
}

void NetworkMenuButton::RefreshNetworkObserver(NetworkLibrary* cros) {
  const Network* network = cros->active_network();
  std::string new_network = network ? network->service_path() : std::string();
  if (active_network_ != new_network) {
    if (!active_network_.empty()) {
      cros->RemoveNetworkObserver(active_network_, this);
    }
    if (!new_network.empty()) {
      cros->AddNetworkObserver(new_network, this);
    }
    active_network_ = new_network;
  }
}

void NetworkMenuButton::RefreshNetworkDeviceObserver(NetworkLibrary* cros) {
  const NetworkDevice* cellular = cros->FindCellularDevice();
  std::string new_cellular_device_path = cellular ?
      cellular->device_path() : std::string();
  if (cellular_device_path_ != new_cellular_device_path) {
    if (!cellular_device_path_.empty()) {
      cros->RemoveNetworkDeviceObserver(cellular_device_path_, this);
    }
    if (!new_cellular_device_path.empty()) {
      was_sim_locked_ = cellular->is_sim_locked();
      cros->AddNetworkDeviceObserver(new_cellular_device_path, this);
    }
    cellular_device_path_ = new_cellular_device_path;
  }
}

void NetworkMenuButton::ShowOptionalMobileDataPromoNotification(
    NetworkLibrary* cros) {
  // Display one-time notification for non-Guest users on first use
  // of Mobile Data connection or if there's a carrier deal defined
  // show that even if user has already seen generic promo.
  if (is_browser_mode_ && !UserManager::Get()->IsLoggedInAsGuest() &&
      check_for_promo_ && BrowserList::GetLastActive() &&
      cros->cellular_connected() && !cros->ethernet_connected() &&
      !cros->wifi_connected()) {
    const ServicesCustomizationDocument::CarrierDeal* deal =
        GetCarrierDeal(cros);
    std::string deal_text;
    int carrier_deal_promo_pref = -1;
    if (deal) {
      carrier_deal_promo_pref = GetCarrierDealPromoShown();
      const std::string locale = g_browser_process->GetApplicationLocale();
      deal_text = deal->GetLocalizedString(locale, "notification_text");
      deal_info_url_ = deal->info_url();
      deal_topup_url_ = deal->top_up_url();
    } else if (!ShouldShow3gPromoNotification()) {
      check_for_promo_ = false;
      return;
    }

    gfx::Rect button_bounds = GetScreenBounds();
    // StatusArea button Y position is usually -1, fix it so that
    // Contains() method for screen bounds works correctly.
    button_bounds.set_y(button_bounds.y() + 1);
    gfx::Rect screen_bounds(chromeos::CalculateScreenBounds(gfx::Size()));

    // Chrome window is initialized in visible state off screen and then is
    // moved into visible screen area. Make sure that we're on screen
    // so that bubble is shown correctly.
    if (!screen_bounds.Contains(button_bounds)) {
      // If we're not on screen yet, delay notification display.
      // It may be shown earlier, on next NetworkLibrary callback processing.
      if (method_factory_.empty()) {
        MessageLoop::current()->PostDelayedTask(FROM_HERE,
            method_factory_.NewRunnableMethod(
                &NetworkMenuButton::ShowOptionalMobileDataPromoNotification,
                cros),
            kPromoShowDelayMs);
      }
      return;
    }

    // Add deal text if it's defined.
    std::wstring notification_text;
    std::wstring default_text =
        UTF16ToWide(l10n_util::GetStringUTF16(IDS_3G_NOTIFICATION_MESSAGE));
    if (!deal_text.empty()) {
      notification_text = StringPrintf(L"%ls\n\n%ls",
                                       UTF8ToWide(deal_text).c_str(),
                                       default_text.c_str());
    } else {
      notification_text = default_text;
    }

    // Use deal URL if it's defined or general "Network Settings" URL.
    int link_message_id;
    if (deal_topup_url_.empty())
      link_message_id = IDS_OFFLINE_NETWORK_SETTINGS;
    else
      link_message_id = IDS_STATUSBAR_NETWORK_VIEW_ACCOUNT;

    std::vector<std::wstring> links;
    links.push_back(UTF16ToWide(l10n_util::GetStringUTF16(link_message_id)));
    if (!deal_info_url_.empty())
      links.push_back(UTF16ToWide(l10n_util::GetStringUTF16(IDS_LEARN_MORE)));
    mobile_data_bubble_ = MessageBubble::ShowWithLinks(
        GetWidget(),
        button_bounds,
        BubbleBorder::TOP_RIGHT ,
        ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_NOTIFICATION_3G),
        notification_text,
        links,
        this);

    check_for_promo_ = false;
    SetShow3gPromoNotification(false);
    if (carrier_deal_promo_pref != kNotificationCountPrefDefault)
      SetCarrierDealPromoShown(carrier_deal_promo_pref + 1);
  }
}

void NetworkMenuButton::SetTooltipAndAccessibleName(const string16& label) {
  SetTooltipText(UTF16ToWide(label));
  SetAccessibleName(label);
}

}  // namespace chromeos
<|endoftext|>