<commit_before>#include <znc/main.h>
#include <znc/User.h>
#include <znc/IRCNetwork.h>
#include <znc/Modules.h>
#include <znc/Nick.h>
#include <znc/Chan.h>
#include <znc/IRCSock.h>
#include <znc/version.h>

#include <limits>

#include "twitchtmi.h"
#include "jload.h"

#if (VERSION_MAJOR < 1) || (VERSION_MAJOR == 1 && VERSION_MINOR < 7)
#error This module needs at least ZNC 1.7.0 or later.
#endif

TwitchTMI::~TwitchTMI()
{

}

bool TwitchTMI::OnLoad(const CString& sArgsi, CString& sMessage)
{
	OnBoot();

	if(GetNetwork())
	{
		for(CChan *ch: GetNetwork()->GetChans())
		{
			ch->SetTopic(CString());

			CString chname = ch->GetName().substr(1);
			CThreadPool::Get().addJob(new TwitchTMIJob(this, chname));
		}
	}

	if(GetArgs().Token(0) != "FrankerZ")
		lastFrankerZ = std::numeric_limits<decltype(lastFrankerZ)>::max();

	return true;
}

bool TwitchTMI::OnBoot()
{
	initCurl();

	timer = new TwitchTMIUpdateTimer(this);
	AddTimer(timer);

	return true;
}

CModule::EModRet TwitchTMI::OnUserRaw(CString &sLine)
{
	if(sLine.Left(5).Equals("WHO #"))
		return CModule::HALT;

	if(sLine.Left(5).Equals("AWAY "))
		return CModule::HALT;

	if(sLine.Left(12).Equals("TWITCHCLIENT"))
		return CModule::HALT;

	if(sLine.Left(9).Equals("JTVCLIENT"))
		return CModule::HALT;

	return CModule::CONTINUE;
}

CModule::EModRet TwitchTMI::OnRawMessage(CMessage &msg)
{
	if(msg.GetCommand().Equals("WHISPER"))
		msg.SetCommand("PRIVMSG");

	CString realNick = msg.GetTag("display-name").Trim_n();
	if(realNick != "")
		msg.GetNick().SetNick(realNick);

	return CModule::CONTINUE;
}

CModule::EModRet TwitchTMI::OnUserJoin(CString& sChannel, CString& sKey)
{
	CString chname = sChannel.substr(1);
	CThreadPool::Get().addJob(new TwitchTMIJob(this, chname));

	return CModule::CONTINUE;
}

CModule::EModRet TwitchTMI::OnPrivMessage(CTextMessage &Message)
{
	if(Message.GetNick().GetNick().Equals("jtv"))
		return CModule::HALT;

	return CModule::CONTINUE;
}

void TwitchTMI::PutUserChanMessage(CChan *chan, const CString &from, const CString &msg)
{
	std::stringstream ss;
	ss << ":" << from << " PRIVMSG " << chan->GetName() << " :";
	CString s = ss.str();

	PutUser(s + msg);

	if(!chan->AutoClearChanBuffer() || !GetNetwork()->IsUserOnline() || chan->IsDetached())
		chan->AddBuffer(s + "{text}", msg);
}

CModule::EModRet TwitchTMI::OnChanMessage(CTextMessage &Message)
{
	if(Message.GetNick().GetNick().Equals("jtv"))
		return CModule::HALT;

	if(Message.GetText() == "FrankerZ" && std::time(nullptr) - lastFrankerZ > 10)
	{
		std::stringstream ss;
		CString mynick = GetNetwork()->GetIRCNick().GetNickMask();
		CChan *chan = Message.GetChan();

		ss << "PRIVMSG " << chan->GetName() << " :FrankerZ";
		PutIRC(ss.str());

		CThreadPool::Get().addJob(new GenericJob([]() {}, [this, chan, mynick]()
		{
			PutUserChanMessage(chan, mynick, "FrankerZ");
		}));

		lastFrankerZ = std::time(nullptr);
	}

	return CModule::CONTINUE;
}

bool TwitchTMI::OnServerCapAvailable(const CString &sCap)
{
	if(sCap == "twitch.tv/membership")
		return true;
	else if(sCap == "twitch.tv/tags")
		return true;
	else if(sCap == "twitch.tv/commands")
		return true;

	return false;
}

CModule::EModRet TwitchTMI::OnUserTextMessage(CTextMessage &msg)
{
	if(msg.GetTarget().Left(1).Equals("#"))
		return CModule::CONTINUE;

	msg.SetText(msg.GetText().insert(0, " ").insert(0, msg.GetTarget()).insert(0, "/w "));
	msg.SetTarget("#jtv");

	return CModule::HALT;
}

TwitchTMIUpdateTimer::TwitchTMIUpdateTimer(TwitchTMI *tmod)
	:CTimer(tmod, 30, 0, "TwitchTMIUpdateTimer", "Downloads Twitch information")
	,mod(tmod)
{
}

void TwitchTMIUpdateTimer::RunJob()
{
	if(!mod->GetNetwork())
		return;

	for(CChan *chan: mod->GetNetwork()->GetChans())
	{
		CString chname = chan->GetName().substr(1);
		CThreadPool::Get().addJob(new TwitchTMIJob(mod, chname));
	}
}

void TwitchTMIJob::runThread()
{
	std::stringstream ss, ss2;
	ss << "https://api.twitch.tv/kraken/channels/" << channel;
	ss2 << "https://api.twitch.tv/kraken/streams/" << channel;

	CString url = ss.str();
	CString url2 = ss2.str();

	Json::Value root = getJsonFromUrl(url.c_str(), "Accept: application/vnd.twitchtv.v3+json");
	Json::Value root2 = getJsonFromUrl(url2.c_str(), "Accept: application/vnd.twitchtv.v3+json");

	if(!root.isNull())
	{
		Json::Value &titleVal = root["status"];
		title = CString();

		if(!titleVal.isString())
			titleVal = root["title"];

		if(titleVal.isString())
		{
			title = titleVal.asString();
			title.Trim();
		}
	}

	live = false;

	if(!root2.isNull())
	{
		Json::Value &streamVal = root2["stream"];

		if(!streamVal.isNull())
			live = true;
	}
}

void TwitchTMIJob::runMain()
{
	if(title.empty())
		return;

	CChan *chan = mod->GetNetwork()->FindChan(CString("#") + channel);

	if(!chan)
		return;

	if(chan->GetTopic() != title)
	{
		chan->SetTopic(title);
		chan->SetTopicOwner("jtv");
		chan->SetTopicDate((unsigned long)time(nullptr));

		std::stringstream ss;
		ss << ":jtv TOPIC #" << channel << " :" << title;

		mod->PutUser(ss.str());
	}

	auto it = mod->liveChannels.find(channel);
	if(it != mod->liveChannels.end())
	{
		if(!live)
		{
			mod->liveChannels.erase(it);
			mod->PutUserChanMessage(chan, "jtv", ">>> Channel just went offline! <<<");
		}
	}
	else
	{
		if(live)
		{
			mod->liveChannels.insert(channel);
			mod->PutUserChanMessage(chan, "jtv", ">>> Channel just went live! <<<");
		}
	}
}

template<> void TModInfo<TwitchTMI>(CModInfo &info)
{
	info.SetWikiPage("Twitch");
	info.SetHasArgs(true);
}

NETWORKMODULEDEFS(TwitchTMI, "Twitch IRC helper module")
<commit_msg>Fix suprious HALT<commit_after>#include <znc/main.h>
#include <znc/User.h>
#include <znc/IRCNetwork.h>
#include <znc/Modules.h>
#include <znc/Nick.h>
#include <znc/Chan.h>
#include <znc/IRCSock.h>
#include <znc/version.h>

#include <limits>

#include "twitchtmi.h"
#include "jload.h"

#if (VERSION_MAJOR < 1) || (VERSION_MAJOR == 1 && VERSION_MINOR < 7)
#error This module needs at least ZNC 1.7.0 or later.
#endif

TwitchTMI::~TwitchTMI()
{

}

bool TwitchTMI::OnLoad(const CString& sArgsi, CString& sMessage)
{
	OnBoot();

	if(GetNetwork())
	{
		for(CChan *ch: GetNetwork()->GetChans())
		{
			ch->SetTopic(CString());

			CString chname = ch->GetName().substr(1);
			CThreadPool::Get().addJob(new TwitchTMIJob(this, chname));
		}
	}

	if(GetArgs().Token(0) != "FrankerZ")
		lastFrankerZ = std::numeric_limits<decltype(lastFrankerZ)>::max();

	return true;
}

bool TwitchTMI::OnBoot()
{
	initCurl();

	timer = new TwitchTMIUpdateTimer(this);
	AddTimer(timer);

	return true;
}

CModule::EModRet TwitchTMI::OnUserRaw(CString &sLine)
{
	if(sLine.Left(5).Equals("WHO #"))
		return CModule::HALT;

	if(sLine.Left(5).Equals("AWAY "))
		return CModule::HALT;

	if(sLine.Left(12).Equals("TWITCHCLIENT"))
		return CModule::HALT;

	if(sLine.Left(9).Equals("JTVCLIENT"))
		return CModule::HALT;

	return CModule::CONTINUE;
}

CModule::EModRet TwitchTMI::OnRawMessage(CMessage &msg)
{
	if(msg.GetCommand().Equals("WHISPER"))
		msg.SetCommand("PRIVMSG");

	CString realNick = msg.GetTag("display-name").Trim_n();
	if(realNick != "")
		msg.GetNick().SetNick(realNick);

	return CModule::CONTINUE;
}

CModule::EModRet TwitchTMI::OnUserJoin(CString& sChannel, CString& sKey)
{
	CString chname = sChannel.substr(1);
	CThreadPool::Get().addJob(new TwitchTMIJob(this, chname));

	return CModule::CONTINUE;
}

CModule::EModRet TwitchTMI::OnPrivMessage(CTextMessage &Message)
{
	if(Message.GetNick().GetNick().Equals("jtv"))
		return CModule::HALT;

	return CModule::CONTINUE;
}

void TwitchTMI::PutUserChanMessage(CChan *chan, const CString &from, const CString &msg)
{
	std::stringstream ss;
	ss << ":" << from << " PRIVMSG " << chan->GetName() << " :";
	CString s = ss.str();

	PutUser(s + msg);

	if(!chan->AutoClearChanBuffer() || !GetNetwork()->IsUserOnline() || chan->IsDetached())
		chan->AddBuffer(s + "{text}", msg);
}

CModule::EModRet TwitchTMI::OnChanMessage(CTextMessage &Message)
{
	if(Message.GetNick().GetNick().Equals("jtv"))
		return CModule::HALT;

	if(Message.GetText() == "FrankerZ" && std::time(nullptr) - lastFrankerZ > 10)
	{
		std::stringstream ss;
		CString mynick = GetNetwork()->GetIRCNick().GetNickMask();
		CChan *chan = Message.GetChan();

		ss << "PRIVMSG " << chan->GetName() << " :FrankerZ";
		PutIRC(ss.str());

		CThreadPool::Get().addJob(new GenericJob([]() {}, [this, chan, mynick]()
		{
			PutUserChanMessage(chan, mynick, "FrankerZ");
		}));

		lastFrankerZ = std::time(nullptr);
	}

	return CModule::CONTINUE;
}

bool TwitchTMI::OnServerCapAvailable(const CString &sCap)
{
	if(sCap == "twitch.tv/membership")
		return true;
	else if(sCap == "twitch.tv/tags")
		return true;
	else if(sCap == "twitch.tv/commands")
		return true;

	return false;
}

CModule::EModRet TwitchTMI::OnUserTextMessage(CTextMessage &msg)
{
	if(msg.GetTarget().Left(1).Equals("#"))
		return CModule::CONTINUE;

	msg.SetText(msg.GetText().insert(0, " ").insert(0, msg.GetTarget()).insert(0, "/w "));
	msg.SetTarget("#jtv");

	return CModule::CONTINUE;
}

TwitchTMIUpdateTimer::TwitchTMIUpdateTimer(TwitchTMI *tmod)
	:CTimer(tmod, 30, 0, "TwitchTMIUpdateTimer", "Downloads Twitch information")
	,mod(tmod)
{
}

void TwitchTMIUpdateTimer::RunJob()
{
	if(!mod->GetNetwork())
		return;

	for(CChan *chan: mod->GetNetwork()->GetChans())
	{
		CString chname = chan->GetName().substr(1);
		CThreadPool::Get().addJob(new TwitchTMIJob(mod, chname));
	}
}

void TwitchTMIJob::runThread()
{
	std::stringstream ss, ss2;
	ss << "https://api.twitch.tv/kraken/channels/" << channel;
	ss2 << "https://api.twitch.tv/kraken/streams/" << channel;

	CString url = ss.str();
	CString url2 = ss2.str();

	Json::Value root = getJsonFromUrl(url.c_str(), "Accept: application/vnd.twitchtv.v3+json");
	Json::Value root2 = getJsonFromUrl(url2.c_str(), "Accept: application/vnd.twitchtv.v3+json");

	if(!root.isNull())
	{
		Json::Value &titleVal = root["status"];
		title = CString();

		if(!titleVal.isString())
			titleVal = root["title"];

		if(titleVal.isString())
		{
			title = titleVal.asString();
			title.Trim();
		}
	}

	live = false;

	if(!root2.isNull())
	{
		Json::Value &streamVal = root2["stream"];

		if(!streamVal.isNull())
			live = true;
	}
}

void TwitchTMIJob::runMain()
{
	if(title.empty())
		return;

	CChan *chan = mod->GetNetwork()->FindChan(CString("#") + channel);

	if(!chan)
		return;

	if(chan->GetTopic() != title)
	{
		chan->SetTopic(title);
		chan->SetTopicOwner("jtv");
		chan->SetTopicDate((unsigned long)time(nullptr));

		std::stringstream ss;
		ss << ":jtv TOPIC #" << channel << " :" << title;

		mod->PutUser(ss.str());
	}

	auto it = mod->liveChannels.find(channel);
	if(it != mod->liveChannels.end())
	{
		if(!live)
		{
			mod->liveChannels.erase(it);
			mod->PutUserChanMessage(chan, "jtv", ">>> Channel just went offline! <<<");
		}
	}
	else
	{
		if(live)
		{
			mod->liveChannels.insert(channel);
			mod->PutUserChanMessage(chan, "jtv", ">>> Channel just went live! <<<");
		}
	}
}

template<> void TModInfo<TwitchTMI>(CModInfo &info)
{
	info.SetWikiPage("Twitch");
	info.SetHasArgs(true);
}

NETWORKMODULEDEFS(TwitchTMI, "Twitch IRC helper module")
<|endoftext|>


<commit_before>// Copyright (c) 2013 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 "ui/message_center/views/message_popup_collection.h"

#include <set>

#include "base/bind.h"
#include "base/timer.h"
#include "ui/gfx/screen.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/message_center_constants.h"
#include "ui/message_center/notification.h"
#include "ui/message_center/notification_list.h"
#include "ui/message_center/views/message_view.h"
#include "ui/message_center/views/notification_view.h"
#include "ui/views/background.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"

namespace message_center {

class ToastContentsView : public views::WidgetDelegateView {
 public:
  ToastContentsView(const Notification* notification,
                    MessagePopupCollection* collection)
      : collection_(collection) {
    DCHECK(collection_);

    set_notify_enter_exit_on_child(true);
    SetLayoutManager(new views::FillLayout());
    // Sets the transparent background. Then, when the message view is slid out,
    // the whole toast seems to slide although the actual bound of the widget
    // remains. This is hacky but easier to keep the consistency.
    set_background(views::Background::CreateSolidBackground(0, 0, 0, 0));

    int seconds = kAutocloseDefaultDelaySeconds;
    if (notification->priority() > DEFAULT_PRIORITY)
      seconds = kAutocloseHighPriorityDelaySeconds;
    delay_ = base::TimeDelta::FromSeconds(seconds);
  }

  views::Widget* CreateWidget(gfx::NativeView context) {
    views::Widget::InitParams params(
        views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
    params.keep_on_top = true;
    params.context = context;
    params.transparent = true;
    // The origin of the initial bounds are set to (0, 0). It'll then moved by
    // MessagePopupCollection.
    params.bounds = gfx::Rect(
        gfx::Size(kWebNotificationWidth, GetPreferredSize().height()));
    params.delegate = this;
    views::Widget* widget = new views::Widget();
    widget->Init(params);
    return widget;
  }

  void SetContents(MessageView* view) {
    RemoveAllChildViews(true);
    AddChildView(view);
    Layout();
  }

  void SuspendTimer() {
    timer_.Reset();
  }

  void RestartTimer() {
    base::TimeDelta passed = base::Time::Now() - start_time_;
    if (passed > delay_) {
      GetWidget()->Close();
    } else {
      delay_ -= passed;
      StartTimer();
    }
  }

  void StartTimer() {
    start_time_ = base::Time::Now();
    timer_.Start(FROM_HERE,
                 delay_,
                 base::Bind(&views::Widget::Close,
                            base::Unretained(GetWidget())));
  }

  // Overridden from views::WidgetDelegate:
  virtual views::View* GetContentsView() OVERRIDE {
    return this;
  }

  virtual void WindowClosing() OVERRIDE {
    if (timer_.IsRunning())
      SuspendTimer();
  }

  virtual bool CanActivate() const OVERRIDE {
    return false;
  }

  // Overridden from views::View:
  virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE {
    collection_->OnMouseEntered();
  }

  virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE {
    collection_->OnMouseExited();
  }

 private:
  base::TimeDelta delay_;
  base::Time start_time_;
  base::OneShotTimer<views::Widget> timer_;
  MessagePopupCollection* collection_;

  DISALLOW_COPY_AND_ASSIGN(ToastContentsView);
};

MessagePopupCollection::MessagePopupCollection(gfx::NativeView context,
                                               MessageCenter* message_center)
    : context_(context),
      message_center_(message_center) {
  DCHECK(message_center_);
}

MessagePopupCollection::~MessagePopupCollection() {
  CloseAllWidgets();
}

void MessagePopupCollection::UpdatePopups() {
  NotificationList::PopupNotifications popups =
      message_center_->notification_list()->GetPopupNotifications();

  if (popups.empty()) {
    CloseAllWidgets();
    return;
  }

  gfx::Screen* screen = gfx::Screen::GetScreenFor(context_);
  gfx::Rect work_area = screen->GetDisplayNearestWindow(context_).work_area();

  std::set<std::string> old_toast_ids;
  for (ToastContainer::iterator iter = toasts_.begin(); iter != toasts_.end();
       ++iter) {
    old_toast_ids.insert(iter->first);
  }

  int total_height = 0;
  std::vector<views::Widget*> widgets;
  for (NotificationList::PopupNotifications::const_iterator iter =
           popups.begin(); iter != popups.end(); ++iter) {
    ToastContainer::iterator toast_iter = toasts_.find((*iter)->id());
    views::Widget* widget = NULL;
    // NotificationViews are expanded by default here because
    // MessagePopupCollection hasn't been tested yet with changing subview
    // sizes, and such changes could come if those subviews were initially
    // collapsed and allowed to be expanded by users. TODO(dharcourt): Fix.
    MessageView* view = NotificationView::Create(*(*iter), message_center_,
                                                 true);
    if (toast_iter != toasts_.end()) {
      widget = toast_iter->second->GetWidget();
      old_toast_ids.erase((*iter)->id());
      // Need to replace the contents because |view| can be updated, like
      // image loads.
      toast_iter->second->SetContents(view);
    } else {
      ToastContentsView* toast = new ToastContentsView(*iter, this);
      toast->SetContents(view);
      widget = toast->CreateWidget(context_);
      widget->AddObserver(this);
      toast->StartTimer();
      toasts_[(*iter)->id()] = toast;
    }
    if (widget) {
      gfx::Rect bounds = widget->GetWindowBoundsInScreen();
      int new_height = total_height + bounds.height() + kMarginBetweenItems;
      if (new_height < work_area.height()) {
        total_height = new_height;
        widgets.push_back(widget);
      } else {
        if (toast_iter != toasts_.end())
          toasts_.erase(toast_iter);
        delete widget;
        break;
      }
    }
  }

  for (std::set<std::string>::const_iterator iter = old_toast_ids.begin();
       iter != old_toast_ids.end(); ++iter) {
    ToastContainer::iterator toast_iter = toasts_.find(*iter);
    DCHECK(toast_iter != toasts_.end());
    views::Widget* widget = toast_iter->second->GetWidget();
    widget->RemoveObserver(this);
    widget->Close();
    toasts_.erase(toast_iter);
  }

  // Place/move the toast widgets. Currently it stacks the widgets from the
  // right-bottom of the work area.
  // TODO(mukai): allow to specify the placement policy from outside of this
  // class. The policy should be specified from preference on Windows, or
  // the launcher alignment on ChromeOS.
  int top = work_area.bottom() - total_height;
  int left = work_area.right() - kWebNotificationWidth - kMarginBetweenItems;
  for (size_t i = 0; i < widgets.size(); ++i) {
    gfx::Rect widget_bounds = widgets[i]->GetWindowBoundsInScreen();
    widgets[i]->SetBounds(gfx::Rect(
        left, top, widget_bounds.width(), widget_bounds.height()));
    if (!widgets[i]->IsVisible())
      widgets[i]->Show();
    top += widget_bounds.height() + kMarginBetweenItems;
  }
}

void MessagePopupCollection::OnMouseEntered() {
  for (ToastContainer::iterator iter = toasts_.begin();
       iter != toasts_.end(); ++iter) {
    iter->second->SuspendTimer();
  }
}

void MessagePopupCollection::OnMouseExited() {
  for (ToastContainer::iterator iter = toasts_.begin();
       iter != toasts_.end(); ++iter) {
    iter->second->RestartTimer();
  }
}

void MessagePopupCollection::CloseAllWidgets() {
  for (ToastContainer::iterator iter = toasts_.begin();
       iter != toasts_.end(); ++iter) {
    iter->second->SuspendTimer();
    views::Widget* widget = iter->second->GetWidget();
    widget->RemoveObserver(this);
    widget->Close();
  }
  toasts_.clear();
}

void MessagePopupCollection::OnWidgetDestroying(views::Widget* widget) {
  widget->RemoveObserver(this);
  for (ToastContainer::iterator iter = toasts_.begin();
       iter != toasts_.end(); ++iter) {
    if (iter->second->GetWidget() == widget) {
      message_center_->notification_list()->MarkSinglePopupAsShown(
          iter->first, false);
      toasts_.erase(iter);
      break;
    }
  }
  UpdatePopups();
}

}  // namespace message_center
<commit_msg>Fixes message popup collection bugs.<commit_after>// Copyright (c) 2013 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 "ui/message_center/views/message_popup_collection.h"

#include <set>

#include "base/bind.h"
#include "base/timer.h"
#include "ui/gfx/screen.h"
#include "ui/message_center/message_center.h"
#include "ui/message_center/message_center_constants.h"
#include "ui/message_center/notification.h"
#include "ui/message_center/notification_list.h"
#include "ui/message_center/views/message_view.h"
#include "ui/message_center/views/notification_view.h"
#include "ui/views/background.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/view.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"

namespace message_center {

class ToastContentsView : public views::WidgetDelegateView {
 public:
  ToastContentsView(const Notification* notification,
                    MessagePopupCollection* collection)
      : collection_(collection) {
    DCHECK(collection_);

    set_notify_enter_exit_on_child(true);
    SetLayoutManager(new views::FillLayout());
    // Sets the transparent background. Then, when the message view is slid out,
    // the whole toast seems to slide although the actual bound of the widget
    // remains. This is hacky but easier to keep the consistency.
    set_background(views::Background::CreateSolidBackground(0, 0, 0, 0));

    int seconds = kAutocloseDefaultDelaySeconds;
    if (notification->priority() > DEFAULT_PRIORITY)
      seconds = kAutocloseHighPriorityDelaySeconds;
    delay_ = base::TimeDelta::FromSeconds(seconds);
  }

  views::Widget* CreateWidget(gfx::NativeView context) {
    views::Widget::InitParams params(
        views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
    params.keep_on_top = true;
    params.context = context;
    params.transparent = true;
    // The origin of the initial bounds are set to (0, 0). It'll then moved by
    // MessagePopupCollection.
    params.bounds = gfx::Rect(
        gfx::Size(kWebNotificationWidth,
                  GetHeightForWidth(kWebNotificationWidth)));
    params.delegate = this;
    views::Widget* widget = new views::Widget();
    widget->Init(params);
    return widget;
  }

  void SetContents(MessageView* view) {
    RemoveAllChildViews(true);
    AddChildView(view);
    views::Widget* widget = GetWidget();
    if (widget) {
      gfx::Rect bounds = widget->GetWindowBoundsInScreen();
      bounds.set_height(view->GetHeightForWidth(kWebNotificationWidth));
      widget->SetBounds(bounds);
    }
    Layout();
  }

  void SuspendTimer() {
    timer_.Reset();
  }

  void RestartTimer() {
    base::TimeDelta passed = base::Time::Now() - start_time_;
    if (passed > delay_) {
      GetWidget()->Close();
    } else {
      delay_ -= passed;
      StartTimer();
    }
  }

  void StartTimer() {
    start_time_ = base::Time::Now();
    timer_.Start(FROM_HERE,
                 delay_,
                 base::Bind(&views::Widget::Close,
                            base::Unretained(GetWidget())));
  }

  // Overridden from views::WidgetDelegate:
  virtual views::View* GetContentsView() OVERRIDE {
    return this;
  }

  virtual void WindowClosing() OVERRIDE {
    if (timer_.IsRunning())
      SuspendTimer();
  }

  virtual bool CanActivate() const OVERRIDE {
    return false;
  }

  // Overridden from views::View:
  virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE {
    collection_->OnMouseEntered();
  }

  virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE {
    collection_->OnMouseExited();
  }

 private:
  base::TimeDelta delay_;
  base::Time start_time_;
  base::OneShotTimer<views::Widget> timer_;
  MessagePopupCollection* collection_;

  DISALLOW_COPY_AND_ASSIGN(ToastContentsView);
};

MessagePopupCollection::MessagePopupCollection(gfx::NativeView context,
                                               MessageCenter* message_center)
    : context_(context),
      message_center_(message_center) {
  DCHECK(message_center_);
}

MessagePopupCollection::~MessagePopupCollection() {
  CloseAllWidgets();
}

void MessagePopupCollection::UpdatePopups() {
  NotificationList::PopupNotifications popups =
      message_center_->notification_list()->GetPopupNotifications();

  if (popups.empty()) {
    CloseAllWidgets();
    return;
  }

  gfx::Screen* screen = gfx::Screen::GetScreenFor(context_);
  gfx::Rect work_area = screen->GetDisplayNearestWindow(context_).work_area();

  std::set<std::string> old_toast_ids;
  for (ToastContainer::iterator iter = toasts_.begin(); iter != toasts_.end();
       ++iter) {
    old_toast_ids.insert(iter->first);
  }

  int bottom = work_area.bottom() - kMarginBetweenItems;
  int left = work_area.right() - kWebNotificationWidth - kMarginBetweenItems;
  // Iterate in the reverse order to keep the oldest toasts on screen. Newer
  // items may be ignored if there are no room to place them.
  for (NotificationList::PopupNotifications::const_reverse_iterator iter =
           popups.rbegin(); iter != popups.rend(); ++iter) {
    MessageView* view =
        NotificationView::Create(*(*iter), message_center_, true);
    int view_height = view->GetHeightForWidth(kWebNotificationWidth);
    if (bottom - view_height - kMarginBetweenItems < 0) {
      delete view;
      break;
    }

    ToastContainer::iterator toast_iter = toasts_.find((*iter)->id());
    views::Widget* widget = NULL;
    if (toast_iter != toasts_.end()) {
      widget = toast_iter->second->GetWidget();
      old_toast_ids.erase((*iter)->id());
      // Need to replace the contents because |view| can be updated, like
      // image loads.
      toast_iter->second->SetContents(view);
    } else {
      ToastContentsView* toast = new ToastContentsView(*iter, this);
      toast->SetContents(view);
      widget = toast->CreateWidget(context_);
      widget->AddObserver(this);
      toast->StartTimer();
      toasts_[(*iter)->id()] = toast;
    }

    // Place/move the toast widgets. Currently it stacks the widgets from the
    // right-bottom of the work area.
    // TODO(mukai): allow to specify the placement policy from outside of this
    // class. The policy should be specified from preference on Windows, or
    // the launcher alignment on ChromeOS.
    if (widget) {
      gfx::Rect bounds(widget->GetWindowBoundsInScreen());
      bounds.set_origin(gfx::Point(left, bottom - bounds.height()));
      widget->SetBounds(bounds);
      if (!widget->IsVisible())
        widget->Show();
    }

    bottom -= view_height + kMarginBetweenItems;
  }

  for (std::set<std::string>::const_iterator iter = old_toast_ids.begin();
       iter != old_toast_ids.end(); ++iter) {
    ToastContainer::iterator toast_iter = toasts_.find(*iter);
    DCHECK(toast_iter != toasts_.end());
    views::Widget* widget = toast_iter->second->GetWidget();
    widget->RemoveObserver(this);
    widget->Close();
    toasts_.erase(toast_iter);
  }
}

void MessagePopupCollection::OnMouseEntered() {
  for (ToastContainer::iterator iter = toasts_.begin();
       iter != toasts_.end(); ++iter) {
    iter->second->SuspendTimer();
  }
}

void MessagePopupCollection::OnMouseExited() {
  for (ToastContainer::iterator iter = toasts_.begin();
       iter != toasts_.end(); ++iter) {
    iter->second->RestartTimer();
  }
}

void MessagePopupCollection::CloseAllWidgets() {
  for (ToastContainer::iterator iter = toasts_.begin();
       iter != toasts_.end(); ++iter) {
    iter->second->SuspendTimer();
    views::Widget* widget = iter->second->GetWidget();
    widget->RemoveObserver(this);
    widget->Close();
  }
  toasts_.clear();
}

void MessagePopupCollection::OnWidgetDestroying(views::Widget* widget) {
  widget->RemoveObserver(this);
  for (ToastContainer::iterator iter = toasts_.begin();
       iter != toasts_.end(); ++iter) {
    if (iter->second->GetWidget() == widget) {
      message_center_->notification_list()->MarkSinglePopupAsShown(
          iter->first, false);
      toasts_.erase(iter);
      break;
    }
  }
  UpdatePopups();
}

}  // namespace message_center
<|endoftext|>


<commit_before>#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <uv.h>
#include <string>
#include <sstream>
#include <map>
#include <memory>
#include <utility>
#include <vector>

#include "../worker_platform.h"
#include "../worker_thread.h"
#include "../../message.h"
#include "../../log.h"
#include "../../lock.h"

using std::string;
using std::wstring;
using std::ostringstream;
using std::unique_ptr;
using std::shared_ptr;
using std::default_delete;
using std::map;
using std::pair;
using std::make_pair;
using std::vector;
using std::move;
using std::endl;

const DWORD DEFAULT_BUFFER_SIZE = 1024 * 1024;
const DWORD NETWORK_BUFFER_SIZE = 64 * 1024;

static void CALLBACK command_perform_helper(__in ULONG_PTR payload);

static void CALLBACK event_helper(DWORD error_code, DWORD num_bytes, LPOVERLAPPED overlapped);

static Result<string> to_utf8(const wstring &in);

template < class V = void* >
static Result<V> windows_error_result(const string &prefix);

template < class V = void* >
static Result<V> windows_error_result(const string &prefix, DWORD error_code);

class WindowsWorkerPlatform;

class Subscription {
public:
  Subscription(ChannelID channel, HANDLE root, const string &path, WindowsWorkerPlatform *platform) :
    channel{channel},
    platform{platform},
    path{path},
    root{root},
    buffer_size{DEFAULT_BUFFER_SIZE},
    buffer{new BYTE[buffer_size]},
    written{new BYTE[buffer_size]}
  {
    ZeroMemory(&overlapped, sizeof(OVERLAPPED));
    overlapped.hEvent = this;
  }

  ~Subscription()
  {
    CloseHandle(root);
  }

  Result<> schedule()
  {
    int success = ReadDirectoryChangesW(
      root, // root directory handle
      buffer.get(), // result buffer
      buffer_size, // result buffer size
      TRUE, // recursive
      FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES
        | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS
        | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_SECURITY, // change flags
      NULL, // bytes returned
      &overlapped, // overlapped
      &event_helper // completion routine
    );
    if (!success) {
      return windows_error_result<>("Unable to subscribe to filesystem events");
    }

    return ok_result();
  }

  Result<> use_network_size()
  {
    if (buffer_size <= NETWORK_BUFFER_SIZE) {
      ostringstream out("Buffer size of ");
      out
        << buffer_size
        << " is already lower than the network buffer size "
        << NETWORK_BUFFER_SIZE;
      return error_result(out.str());
    }

    buffer_size = NETWORK_BUFFER_SIZE;
    buffer.reset(new BYTE[buffer_size]);
    written.reset(new BYTE[buffer_size]);

    return ok_result();
  }

  ChannelID get_channel() const {
    return channel;
  }

  WindowsWorkerPlatform* get_platform() const {
    return platform;
  }

  BYTE *get_written(DWORD written_size) {
    memcpy(written.get(), buffer.get(), written_size);
    return written.get();
  }

  string make_absolute(const string &sub_path)
  {
    ostringstream out;

    out << path;
    if (path.back() != '\\' && sub_path.front() != '\\') {
      out << '\\';
    }
    out << sub_path;

    return out.str();
  }

private:
  ChannelID channel;
  WindowsWorkerPlatform *platform;

  string path;
  HANDLE root;
  OVERLAPPED overlapped;

  DWORD buffer_size;
  unique_ptr<BYTE[]> buffer;
  unique_ptr<BYTE[]> written;
};

class WindowsWorkerPlatform : public WorkerPlatform {
public:
  WindowsWorkerPlatform(WorkerThread *thread) :
    WorkerPlatform(thread),
    thread_handle{0}
  {
    int err;

    err = uv_mutex_init(&thread_handle_mutex);
    if (err) {
      report_uv_error(err);
    }
  };

  ~WindowsWorkerPlatform() override
  {
    uv_mutex_destroy(&thread_handle_mutex);
  }

  Result<> wake() override
  {
    Lock lock(thread_handle_mutex);

    if (!thread_handle) {
      return ok_result();
    }

    BOOL success = QueueUserAPC(
      command_perform_helper,
      thread_handle,
      reinterpret_cast<ULONG_PTR>(this)
    );
    if (!success) {
      return windows_error_result<>("Unable to queue APC");
    }

    return ok_result();
  }

  Result<> listen() override
  {
    {
      Lock lock(thread_handle_mutex);

      HANDLE pseudo_handle = GetCurrentThread();
      BOOL success = DuplicateHandle(
        GetCurrentProcess(), // Source process
        pseudo_handle, // Source handle
        GetCurrentProcess(), // Destination process
        &thread_handle, // Destination handle
        0, // Desired access
        FALSE, // Inheritable by new processes
        DUPLICATE_SAME_ACCESS // options
      );
      if (!success) {
        Result<> r = windows_error_result<>("Unable to duplicate thread handle");
        report_error("Unable to acquire thread handle");
        return r;
      }
    }

    while (true) {
      SleepEx(INFINITE, true);
    }

    report_error("listen loop ended unexpectedly");
    return health_err_result();
  }

  Result<> handle_add_command(const ChannelID channel, const string &root_path)
  {
    LOGGER << "Watching: " << root_path << endl;

    // Convert the path to a wide-character array
    size_t wlen = MultiByteToWideChar(
      CP_UTF8, // code page
      0, // flags
      root_path.data(), // source string data
      -1, // source string length (null-terminated)
      0, // output buffer
      0 // output buffer size
    );
    if (wlen == 0) {
      return windows_error_result<>("Unable to measure UTF-16 buffer");
    }
    unique_ptr<WCHAR[]> root_path_w{new WCHAR[wlen]};
    size_t conv_success = MultiByteToWideChar(
      CP_UTF8, // code page
      0, // flags,
      root_path.data(), // source string data
      -1, // source string length (null-terminated)
      root_path_w.get(), // output buffer
      wlen // output buffer size
    );
    if (!conv_success) {
      return windows_error_result<>("Unable to convert root path to UTF-16");
    }

    // Open a directory handle
    HANDLE root = CreateFileW(
      root_path_w.get(), // file name
      FILE_LIST_DIRECTORY, // desired access
      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, // share mode
      NULL, // security attributes
      OPEN_EXISTING, // creation disposition
      FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, // flags and attributes
      NULL // template file
    );
    if (root == INVALID_HANDLE_VALUE) {
      return windows_error_result<>("Unable to open directory handle");
    }

    // Allocate and persist the subscription
    Subscription *sub = new Subscription(channel, root, root_path, this);
    auto insert_result = subscriptions.insert(make_pair(channel, sub));
    if (!insert_result.second) {
      delete sub;

      ostringstream msg("Channel collision: ");
      msg << channel;
      return error_result(msg.str());
    }

    LOGGER << "Now watching directory " << root_path << "." << endl;

    return sub->schedule();
  }

  Result<> handle_remove_command(const ChannelID channel)
  {
    return ok_result();
  }

  Result<> handle_fs_event(DWORD error_code, DWORD num_bytes, Subscription* sub)
  {
    // Ensure that the subscription is valid.
    ChannelID channel = sub->get_channel();
    auto it = subscriptions.find(channel);
    if (it == subscriptions.end() || it->second != sub) {
      return ok_result();
    }

    // Handle errors.
    if (error_code == ERROR_OPERATION_ABORTED) {
      LOGGER << "Operation aborted." << endl;

      subscriptions.erase(it);
      delete sub;

      return ok_result();
    }

    if (error_code == ERROR_INVALID_PARAMETER) {
      Result<> resize = sub->use_network_size();
      if (resize.is_error()) return resize;

      return sub->schedule();
    }

    if (error_code == ERROR_NOTIFY_ENUM_DIR) {
      LOGGER << "Change buffer overflow. Some events may have been lost." << endl;
      return sub->schedule();
    }

    if (error_code != ERROR_SUCCESS) {
      return windows_error_result<>("Completion callback error", error_code);
    }

    // Schedule the next completion callback.
    BYTE *base = sub->get_written(num_bytes);
    Result<> next = sub->schedule();
    if (next.is_error()) {
      report_error(string(next.get_error()));
    }

    // Process received events.
    vector<Message> messages;
    bool old_path_seen = false;
    string old_path;

    while (true) {
      PFILE_NOTIFY_INFORMATION info = reinterpret_cast<PFILE_NOTIFY_INFORMATION>(base);

      wstring wpath{info->FileName, info->FileNameLength};
      Result<string> u8r = to_utf8(wpath);
      if (u8r.is_error()) {
        LOGGER << "Skipping path: " << u8r << "." << endl;
      } else {
        string path = u8r.get_value();

        switch (info->Action) {
        case FILE_ACTION_ADDED:
          {
            FileSystemPayload payload(channel, ACTION_CREATED, KIND_UNKNOWN, move(path), "");
            Message message(move(payload));

            LOGGER << "Emitting filesystem message " << message << "." << endl;
            messages.push_back(move(message));
          }
          break;
        case FILE_ACTION_MODIFIED:
          {
            FileSystemPayload payload(channel, ACTION_MODIFIED, KIND_UNKNOWN, move(path), "");
            Message message(move(payload));

            LOGGER << "Emitting filesystem message " << message << "." << endl;
            messages.push_back(move(message));
          }
          break;
        case FILE_ACTION_REMOVED:
          {
            FileSystemPayload payload(channel, ACTION_DELETED, KIND_UNKNOWN, move(path), "");
            Message message(move(payload));

            LOGGER << "Emitting filesystem message " << message << "." << endl;
            messages.push_back(move(message));
          }
          break;
        case FILE_ACTION_RENAMED_OLD_NAME:
          old_path_seen = true;
          old_path = move(path);
          break;
        case FILE_ACTION_RENAMED_NEW_NAME:
          if (old_path_seen) {
            // Old name received first
            {
              FileSystemPayload payload(channel, ACTION_RENAMED, KIND_UNKNOWN, move(old_path), move(path));
              Message message(move(payload));

              LOGGER << "Emitting filesystem message " << message << "." << endl;
              messages.push_back(move(message));
            }

            old_path_seen = false;
          } else {
            // No old name. Treat it as a creation
            {
              FileSystemPayload payload(channel, ACTION_CREATED, KIND_UNKNOWN, move(path), "");
              Message message(move(payload));

              LOGGER << "Emitting filesystem message " << message << "." << endl;
              messages.push_back(move(message));
            }
          }
          break;
        default:
          LOGGER << "Skipping unexpected action " << info->Action << "." << endl;
          break;
        }
      }

      if (info->NextEntryOffset == 0) {
        break;
      }
      base += info->NextEntryOffset;
    }

    if (!messages.empty()) {
      Result<> er = emit_all(messages.begin(), messages.end());
      if (er.is_error()) {
        LOGGER << "Unable to emit messages: " << er << "." << endl;
      }
    }

    return next;
  }

private:
  uv_mutex_t thread_handle_mutex;
  HANDLE thread_handle;

  map<ChannelID, Subscription*> subscriptions;
};

unique_ptr<WorkerPlatform> WorkerPlatform::for_worker(WorkerThread *thread)
{
  return unique_ptr<WorkerPlatform>(new WindowsWorkerPlatform(thread));
}

void CALLBACK command_perform_helper(__in ULONG_PTR payload)
{
  WindowsWorkerPlatform *platform = reinterpret_cast<WindowsWorkerPlatform*>(payload);
  platform->handle_commands();
}

static void CALLBACK event_helper(DWORD error_code, DWORD num_bytes, LPOVERLAPPED overlapped)
{
  Subscription *sub = static_cast<Subscription*>(overlapped->hEvent);
  Result<> r = sub->get_platform()->handle_fs_event(error_code, num_bytes, sub);
  if (r.is_error()) {
    LOGGER << "Unable to handle filesystem events: " << r << "." << endl;
  }
}

Result<string> to_utf8(const wstring &in)
{
  size_t len = WideCharToMultiByte(
    CP_UTF8, // code page
    0, // flags
    in.data(), // source string
    in.size(), // source string length
    nullptr, // destination string, null to measure
    0, // destination string length
    nullptr, // default char
    nullptr  // used default char
  );
  if (!len) {
    return windows_error_result<string>("Unable to measure path as UTF-8");
  }

  char *out = new char[len];
  size_t copied = WideCharToMultiByte(
    CP_UTF8, // code page
    0, // flags
    in.data(), // source string
    in.size(), // source string length
    out, // destination string
    len, // destination string length
    nullptr, // default char
    nullptr // used default char
  );
  if (!copied) {
    delete [] out;
    return windows_error_result<string>("Unable to convert path to UTF-8");
  }

  return ok_result(string{out, len});
}

template < class V >
Result<V> windows_error_result(const string &prefix)
{
  return windows_error_result<V>(prefix, GetLastError());
}

template < class V >
Result<V> windows_error_result(const string &prefix, DWORD error_code)
{
  LPVOID msg_buffer;

  FormatMessage(
    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
    NULL, // source
    error_code, // message ID
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // language ID
    (LPSTR) &msg_buffer, // output buffer
    0, // size
    NULL // arguments
  );

  ostringstream msg;
  msg << prefix << "\n (" << error_code << ") " << (char*) msg_buffer;
  LocalFree(msg_buffer);

  return Result<V>::make_error(msg.str());
}
<commit_msg>Report absolute paths<commit_after>#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <uv.h>
#include <string>
#include <sstream>
#include <map>
#include <memory>
#include <utility>
#include <vector>

#include "../worker_platform.h"
#include "../worker_thread.h"
#include "../../message.h"
#include "../../log.h"
#include "../../lock.h"

using std::string;
using std::wstring;
using std::ostringstream;
using std::unique_ptr;
using std::shared_ptr;
using std::default_delete;
using std::map;
using std::pair;
using std::make_pair;
using std::vector;
using std::move;
using std::endl;

const DWORD DEFAULT_BUFFER_SIZE = 1024 * 1024;
const DWORD NETWORK_BUFFER_SIZE = 64 * 1024;

static void CALLBACK command_perform_helper(__in ULONG_PTR payload);

static void CALLBACK event_helper(DWORD error_code, DWORD num_bytes, LPOVERLAPPED overlapped);

static Result<string> to_utf8(const wstring &in);

template < class V = void* >
static Result<V> windows_error_result(const string &prefix);

template < class V = void* >
static Result<V> windows_error_result(const string &prefix, DWORD error_code);

class WindowsWorkerPlatform;

class Subscription {
public:
  Subscription(ChannelID channel, HANDLE root, const string &path, WindowsWorkerPlatform *platform) :
    channel{channel},
    platform{platform},
    path{path},
    root{root},
    buffer_size{DEFAULT_BUFFER_SIZE},
    buffer{new BYTE[buffer_size]},
    written{new BYTE[buffer_size]}
  {
    ZeroMemory(&overlapped, sizeof(OVERLAPPED));
    overlapped.hEvent = this;
  }

  ~Subscription()
  {
    CloseHandle(root);
  }

  Result<> schedule()
  {
    int success = ReadDirectoryChangesW(
      root, // root directory handle
      buffer.get(), // result buffer
      buffer_size, // result buffer size
      TRUE, // recursive
      FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES
        | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_LAST_ACCESS
        | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_SECURITY, // change flags
      NULL, // bytes returned
      &overlapped, // overlapped
      &event_helper // completion routine
    );
    if (!success) {
      return windows_error_result<>("Unable to subscribe to filesystem events");
    }

    return ok_result();
  }

  Result<> use_network_size()
  {
    if (buffer_size <= NETWORK_BUFFER_SIZE) {
      ostringstream out("Buffer size of ");
      out
        << buffer_size
        << " is already lower than the network buffer size "
        << NETWORK_BUFFER_SIZE;
      return error_result(out.str());
    }

    buffer_size = NETWORK_BUFFER_SIZE;
    buffer.reset(new BYTE[buffer_size]);
    written.reset(new BYTE[buffer_size]);

    return ok_result();
  }

  ChannelID get_channel() const {
    return channel;
  }

  WindowsWorkerPlatform* get_platform() const {
    return platform;
  }

  BYTE *get_written(DWORD written_size) {
    memcpy(written.get(), buffer.get(), written_size);
    return written.get();
  }

  string make_absolute(const string &sub_path)
  {
    ostringstream out;

    out << path;
    if (path.back() != '\\' && sub_path.front() != '\\') {
      out << '\\';
    }
    out << sub_path;

    return out.str();
  }

private:
  ChannelID channel;
  WindowsWorkerPlatform *platform;

  string path;
  HANDLE root;
  OVERLAPPED overlapped;

  DWORD buffer_size;
  unique_ptr<BYTE[]> buffer;
  unique_ptr<BYTE[]> written;
};

class WindowsWorkerPlatform : public WorkerPlatform {
public:
  WindowsWorkerPlatform(WorkerThread *thread) :
    WorkerPlatform(thread),
    thread_handle{0}
  {
    int err;

    err = uv_mutex_init(&thread_handle_mutex);
    if (err) {
      report_uv_error(err);
    }
  };

  ~WindowsWorkerPlatform() override
  {
    uv_mutex_destroy(&thread_handle_mutex);
  }

  Result<> wake() override
  {
    Lock lock(thread_handle_mutex);

    if (!thread_handle) {
      return ok_result();
    }

    BOOL success = QueueUserAPC(
      command_perform_helper,
      thread_handle,
      reinterpret_cast<ULONG_PTR>(this)
    );
    if (!success) {
      return windows_error_result<>("Unable to queue APC");
    }

    return ok_result();
  }

  Result<> listen() override
  {
    {
      Lock lock(thread_handle_mutex);

      HANDLE pseudo_handle = GetCurrentThread();
      BOOL success = DuplicateHandle(
        GetCurrentProcess(), // Source process
        pseudo_handle, // Source handle
        GetCurrentProcess(), // Destination process
        &thread_handle, // Destination handle
        0, // Desired access
        FALSE, // Inheritable by new processes
        DUPLICATE_SAME_ACCESS // options
      );
      if (!success) {
        Result<> r = windows_error_result<>("Unable to duplicate thread handle");
        report_error("Unable to acquire thread handle");
        return r;
      }
    }

    while (true) {
      SleepEx(INFINITE, true);
    }

    report_error("listen loop ended unexpectedly");
    return health_err_result();
  }

  Result<> handle_add_command(const ChannelID channel, const string &root_path)
  {
    LOGGER << "Watching: " << root_path << endl;

    // Convert the path to a wide-character array
    size_t wlen = MultiByteToWideChar(
      CP_UTF8, // code page
      0, // flags
      root_path.data(), // source string data
      -1, // source string length (null-terminated)
      0, // output buffer
      0 // output buffer size
    );
    if (wlen == 0) {
      return windows_error_result<>("Unable to measure UTF-16 buffer");
    }
    unique_ptr<WCHAR[]> root_path_w{new WCHAR[wlen]};
    size_t conv_success = MultiByteToWideChar(
      CP_UTF8, // code page
      0, // flags,
      root_path.data(), // source string data
      -1, // source string length (null-terminated)
      root_path_w.get(), // output buffer
      wlen // output buffer size
    );
    if (!conv_success) {
      return windows_error_result<>("Unable to convert root path to UTF-16");
    }

    // Open a directory handle
    HANDLE root = CreateFileW(
      root_path_w.get(), // file name
      FILE_LIST_DIRECTORY, // desired access
      FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, // share mode
      NULL, // security attributes
      OPEN_EXISTING, // creation disposition
      FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, // flags and attributes
      NULL // template file
    );
    if (root == INVALID_HANDLE_VALUE) {
      return windows_error_result<>("Unable to open directory handle");
    }

    // Allocate and persist the subscription
    Subscription *sub = new Subscription(channel, root, root_path, this);
    auto insert_result = subscriptions.insert(make_pair(channel, sub));
    if (!insert_result.second) {
      delete sub;

      ostringstream msg("Channel collision: ");
      msg << channel;
      return error_result(msg.str());
    }

    LOGGER << "Now watching directory " << root_path << "." << endl;

    return sub->schedule();
  }

  Result<> handle_remove_command(const ChannelID channel)
  {
    return ok_result();
  }

  Result<> handle_fs_event(DWORD error_code, DWORD num_bytes, Subscription* sub)
  {
    // Ensure that the subscription is valid.
    ChannelID channel = sub->get_channel();
    auto it = subscriptions.find(channel);
    if (it == subscriptions.end() || it->second != sub) {
      return ok_result();
    }

    // Handle errors.
    if (error_code == ERROR_OPERATION_ABORTED) {
      LOGGER << "Operation aborted." << endl;

      subscriptions.erase(it);
      delete sub;

      return ok_result();
    }

    if (error_code == ERROR_INVALID_PARAMETER) {
      Result<> resize = sub->use_network_size();
      if (resize.is_error()) return resize;

      return sub->schedule();
    }

    if (error_code == ERROR_NOTIFY_ENUM_DIR) {
      LOGGER << "Change buffer overflow. Some events may have been lost." << endl;
      return sub->schedule();
    }

    if (error_code != ERROR_SUCCESS) {
      return windows_error_result<>("Completion callback error", error_code);
    }

    // Schedule the next completion callback.
    BYTE *base = sub->get_written(num_bytes);
    Result<> next = sub->schedule();
    if (next.is_error()) {
      report_error(string(next.get_error()));
    }

    // Process received events.
    vector<Message> messages;
    bool old_path_seen = false;
    string old_path;

    while (true) {
      PFILE_NOTIFY_INFORMATION info = reinterpret_cast<PFILE_NOTIFY_INFORMATION>(base);

      wstring wpath{info->FileName, info->FileNameLength};
      Result<string> u8r = to_utf8(wpath);
      if (u8r.is_error()) {
        LOGGER << "Skipping path: " << u8r << "." << endl;
      } else {
        string path = sub->make_absolute(u8r.get_value());

        switch (info->Action) {
        case FILE_ACTION_ADDED:
          {
            FileSystemPayload payload(channel, ACTION_CREATED, KIND_UNKNOWN, move(path), "");
            Message message(move(payload));

            LOGGER << "Emitting filesystem message " << message << "." << endl;
            messages.push_back(move(message));
          }
          break;
        case FILE_ACTION_MODIFIED:
          {
            FileSystemPayload payload(channel, ACTION_MODIFIED, KIND_UNKNOWN, move(path), "");
            Message message(move(payload));

            LOGGER << "Emitting filesystem message " << message << "." << endl;
            messages.push_back(move(message));
          }
          break;
        case FILE_ACTION_REMOVED:
          {
            FileSystemPayload payload(channel, ACTION_DELETED, KIND_UNKNOWN, move(path), "");
            Message message(move(payload));

            LOGGER << "Emitting filesystem message " << message << "." << endl;
            messages.push_back(move(message));
          }
          break;
        case FILE_ACTION_RENAMED_OLD_NAME:
          old_path_seen = true;
          old_path = move(path);
          break;
        case FILE_ACTION_RENAMED_NEW_NAME:
          if (old_path_seen) {
            // Old name received first
            {
              FileSystemPayload payload(channel, ACTION_RENAMED, KIND_UNKNOWN, move(old_path), move(path));
              Message message(move(payload));

              LOGGER << "Emitting filesystem message " << message << "." << endl;
              messages.push_back(move(message));
            }

            old_path_seen = false;
          } else {
            // No old name. Treat it as a creation
            {
              FileSystemPayload payload(channel, ACTION_CREATED, KIND_UNKNOWN, move(path), "");
              Message message(move(payload));

              LOGGER << "Emitting filesystem message " << message << "." << endl;
              messages.push_back(move(message));
            }
          }
          break;
        default:
          LOGGER << "Skipping unexpected action " << info->Action << "." << endl;
          break;
        }
      }

      if (info->NextEntryOffset == 0) {
        break;
      }
      base += info->NextEntryOffset;
    }

    if (!messages.empty()) {
      Result<> er = emit_all(messages.begin(), messages.end());
      if (er.is_error()) {
        LOGGER << "Unable to emit messages: " << er << "." << endl;
      }
    }

    return next;
  }

private:
  uv_mutex_t thread_handle_mutex;
  HANDLE thread_handle;

  map<ChannelID, Subscription*> subscriptions;
};

unique_ptr<WorkerPlatform> WorkerPlatform::for_worker(WorkerThread *thread)
{
  return unique_ptr<WorkerPlatform>(new WindowsWorkerPlatform(thread));
}

void CALLBACK command_perform_helper(__in ULONG_PTR payload)
{
  WindowsWorkerPlatform *platform = reinterpret_cast<WindowsWorkerPlatform*>(payload);
  platform->handle_commands();
}

static void CALLBACK event_helper(DWORD error_code, DWORD num_bytes, LPOVERLAPPED overlapped)
{
  Subscription *sub = static_cast<Subscription*>(overlapped->hEvent);
  Result<> r = sub->get_platform()->handle_fs_event(error_code, num_bytes, sub);
  if (r.is_error()) {
    LOGGER << "Unable to handle filesystem events: " << r << "." << endl;
  }
}

Result<string> to_utf8(const wstring &in)
{
  size_t len = WideCharToMultiByte(
    CP_UTF8, // code page
    0, // flags
    in.data(), // source string
    in.size(), // source string length
    nullptr, // destination string, null to measure
    0, // destination string length
    nullptr, // default char
    nullptr  // used default char
  );
  if (!len) {
    return windows_error_result<string>("Unable to measure path as UTF-8");
  }

  char *out = new char[len];
  size_t copied = WideCharToMultiByte(
    CP_UTF8, // code page
    0, // flags
    in.data(), // source string
    in.size(), // source string length
    out, // destination string
    len, // destination string length
    nullptr, // default char
    nullptr // used default char
  );
  if (!copied) {
    delete [] out;
    return windows_error_result<string>("Unable to convert path to UTF-8");
  }

  return ok_result(string{out, len});
}

template < class V >
Result<V> windows_error_result(const string &prefix)
{
  return windows_error_result<V>(prefix, GetLastError());
}

template < class V >
Result<V> windows_error_result(const string &prefix, DWORD error_code)
{
  LPVOID msg_buffer;

  FormatMessage(
    FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
    NULL, // source
    error_code, // message ID
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // language ID
    (LPSTR) &msg_buffer, // output buffer
    0, // size
    NULL // arguments
  );

  ostringstream msg;
  msg << prefix << "\n (" << error_code << ") " << (char*) msg_buffer;
  LocalFree(msg_buffer);

  return Result<V>::make_error(msg.str());
}
<|endoftext|>


<commit_before>/****************************************************************/
/* MOOSE - Multiphysics Object Oriented Simulation Environment  */
/*                                                              */
/*          All contents are licensed under LGPL V2.1           */
/*             See LICENSE for full restrictions                */
/****************************************************************/
#include "INSMomentumLaplaceForm.h"

template<>
InputParameters validParams<INSMomentumLaplaceForm>()
{
  InputParameters params = validParams<INSMomentumBase>();
  return params;
}

INSMomentumLaplaceForm::INSMomentumLaplaceForm(const InputParameters & parameters) :
  INSMomentumBase(parameters)
{
}

Real INSMomentumLaplaceForm::computeQpResidualViscousPart()
{
  // Simplified version: mu * Laplacian(u_component)
  return _mu * (_grad_u[_qp] * _grad_test[_i][_qp]);
}

Real INSMomentumLaplaceForm::computeQpJacobianViscousPart()
{
  // Viscous part, Laplacian version
  return _mu * (_grad_phi[_j][_qp] * _grad_test[_i][_qp]);
}

Real INSMomentumLaplaceForm::computeQpOffDiagJacobianViscousPart(unsigned jvar)
{
  return 0.;
}
<commit_msg>Fix unused variable warning.<commit_after>/****************************************************************/
/* MOOSE - Multiphysics Object Oriented Simulation Environment  */
/*                                                              */
/*          All contents are licensed under LGPL V2.1           */
/*             See LICENSE for full restrictions                */
/****************************************************************/
#include "INSMomentumLaplaceForm.h"

template<>
InputParameters validParams<INSMomentumLaplaceForm>()
{
  InputParameters params = validParams<INSMomentumBase>();
  return params;
}

INSMomentumLaplaceForm::INSMomentumLaplaceForm(const InputParameters & parameters) :
  INSMomentumBase(parameters)
{
}

Real INSMomentumLaplaceForm::computeQpResidualViscousPart()
{
  // Simplified version: mu * Laplacian(u_component)
  return _mu * (_grad_u[_qp] * _grad_test[_i][_qp]);
}

Real INSMomentumLaplaceForm::computeQpJacobianViscousPart()
{
  // Viscous part, Laplacian version
  return _mu * (_grad_phi[_j][_qp] * _grad_test[_i][_qp]);
}

Real INSMomentumLaplaceForm::computeQpOffDiagJacobianViscousPart(unsigned /*jvar*/)
{
  return 0.;
}
<|endoftext|>


<commit_before>/******************************************************************************\
* File:          util.cpp
* Purpose:       Implementation of wxextension utility methods
* Author:        Anton van Wezenbeek
* RCS-ID:        $Id$
*
* Copyright (c) 2009 Anton van Wezenbeek
* All rights are reserved. Reproduction in whole or part is prohibited
* without the written consent of the copyright owner.
\******************************************************************************/

#include <wx/clipbrd.h>
#include <wx/regex.h>
#include <wx/stdpaths.h>
#include <wx/textfile.h> // for wxTextFile::GetEOL()
#include <wx/tokenzr.h>
#include <wx/extension/util.h>

bool exClipboardAdd(const wxString& text)
{
  wxClipboardLocker locker;
  if (!locker) return false;
  if (!wxTheClipboard->AddData(new wxTextDataObject(text))) return false;
  if (!wxTheClipboard->Flush()) return false; // take care that clipboard data remain after exiting
  return true;
}

const wxString exClipboardGet()
{
  wxBusyCursor wait;
  wxClipboardLocker locker;

  if (!locker)
  {
    wxLogError("Cannot open clipboard");
    return wxEmptyString;
  }

  if (!wxTheClipboard->IsSupported(wxDF_TEXT))
  {
    wxLogError("Clipboard format not supported");
    return wxEmptyString;
  }

  wxTextDataObject data;
  if (!wxTheClipboard->GetData(data))
  {
    wxLogError("Cannot get clipboard data");
    return wxEmptyString;
  }

  return data.GetText();
}

#if wxUSE_GUI
void exComboBoxFromString(
  wxComboBox* cb,
  const wxString& text,
  const wxChar field_separator)
{
  wxStringTokenizer tkz(text, field_separator);
  while (tkz.HasMoreTokens())
  {
    const wxString val = tkz.GetNextToken();
    if (cb->FindString(val) == wxNOT_FOUND)
    {
      cb->Append(val);
    }
  }

  if (cb->GetCount() > 0) cb->SetValue(cb->GetString(0));
}

bool exComboBoxToString(
  const wxComboBox* cb,
  wxString& text,
  const wxChar field_separator,
  size_t max_items)
{
  if (cb == NULL)
  {
    return false;
  }

  text = cb->GetValue();
  switch (cb->FindString(cb->GetValue()))
  {
    case wxNOT_FOUND:
    {
      // Add the string, as it is not in the combo box, to the text,
      // simply by appending all combobox items.
      for (size_t i = 0; i < max_items; i++)
        if (i < max_items - 1 && i < cb->GetCount())
          text += field_separator + cb->GetString(i);
    }
    break;
    // No change necessary, the string is already present as the first one.
    case 0: return false; break;
    default:
    {
      // Reorder. The new first element already present, just add all others.
      for (size_t i = 0; i < cb->GetCount(); i++)
      {
        const wxString cb_element = cb->GetString(i);
        if (cb_element != cb->GetValue())
          text += field_separator + cb_element;
      }
    }
  }

  return true;
}

#endif // wxUSE_GUI

long exColourToLong(const wxColour& c)
{
  return c.Red() | (c.Green() << 8) | (c.Blue() << 16);
}

const wxString exEllipsed(const wxString& text, const wxString& control)
{
  return text + "..." + (!control.empty() ? "\t" + control: wxString(wxEmptyString));
}

const wxString exGetEndOfWord(
  const wxString& text,
  size_t max_chars)
{
  wxString text_out(text);

  if (text_out.length() > max_chars)
  {
    text_out = "..." + text_out.substr(text_out.length() - max_chars);
  }

  return text_out;
}

int exGetNumberOfLines(const wxString& text)
{
  if (text.empty())
  {
    return 0;
  }
  else if (text.Find(wxChar('\r')) != wxNOT_FOUND)
  {
    return text.Freq(wxChar('\r')) + 1;
  }
  else if (text.Find(wxChar('\n')) != wxNOT_FOUND)
  {
    return text.Freq(wxChar('\n')) + 1;
  }
  else
  {
    return 1;
  }
}

int exGetLineNumberFromText(const wxString& text)
{
  // Get text after :.
  const size_t pos_char = text.rfind(":");

  if (pos_char == wxString::npos)
  {
    return 0;
  }

  const wxString linenumber = text.substr(pos_char + 1);

  long line;

  if (linenumber.ToLong(&line))
  {
    return line;
  }
  else
  {
    return 0;
  }
}

void exLog(const wxString& text, const exFileName& filename)
{
  wxFile(
    filename.GetFullPath(),
    wxFile::write_append).Write(
      wxDateTime::Now().Format() + " " + text + wxTextFile::GetEOL());
}

const exFileName exLogfileName()
{
  if (wxTheApp == NULL)
  {
    return exFileName("app.log");
  }

#ifdef EX_PORTABLE
  return exFileName(
    wxPathOnly(wxStandardPaths::Get().GetExecutablePath()) + wxFileName::GetPathSeparator() +
    wxTheApp->GetAppName().Lower() + ".log");
#else
  return exFileName(
    wxStandardPaths::Get().GetUserDataDir() + wxFileName::GetPathSeparator() +
    wxTheApp->GetAppName().Lower() + ".log");
#endif
}

bool exMatchesOneOf(const wxFileName& filename, const wxString& pattern)
{
  if (pattern == "*") return true; // asterix matches always.

  const wxString fullname_uppercase = filename.GetFullName().Upper();

  wxStringTokenizer tokenizer(pattern.Upper(), ";");
  while (tokenizer.HasMoreTokens())
  {
    if (fullname_uppercase.Matches(tokenizer.GetNextToken())) return true;
  }

  return false;
}

const wxString exSkipWhiteSpace(const wxString& text, const wxString& replace_with)
{
  wxString output = text;
  wxRegEx("[ \t\n]+").ReplaceAll(&output, replace_with);
  return output;
}

const wxString exTranslate(const wxString& text, int pageNum, int numPages)
{
  wxString translation = text;
  wxString num;

  num.Printf("%i", pageNum);
  translation.Replace("@PAGENUM@", num);

  num.Printf("%i", numPages);
  translation.Replace("@PAGESCNT@", num);

  return translation;
}
<commit_msg>fix for ubuntu<commit_after>/******************************************************************************\
* File:          util.cpp
* Purpose:       Implementation of wxextension utility methods
* Author:        Anton van Wezenbeek
* RCS-ID:        $Id$
*
* Copyright (c) 2009 Anton van Wezenbeek
* All rights are reserved. Reproduction in whole or part is prohibited
* without the written consent of the copyright owner.
\******************************************************************************/

#include <wx/clipbrd.h>
#include <wx/regex.h>
#include <wx/stdpaths.h>
#include <wx/textfile.h> // for wxTextFile::GetEOL()
#include <wx/tokenzr.h>
#include <wx/extension/util.h>

bool exClipboardAdd(const wxString& text)
{
  wxClipboardLocker locker;
  if (!locker) return false;
  if (!wxTheClipboard->AddData(new wxTextDataObject(text))) return false;

  // Take care that clipboard data remain after exiting
  // This is a boolean method as well, we don't check it, as
  // clipboard data is copied.
  // At least on Ubuntu 8.10 FLush returns false.
  wxTheClipboard->Flush();

  return true;
}

const wxString exClipboardGet()
{
  wxBusyCursor wait;
  wxClipboardLocker locker;

  if (!locker)
  {
    wxLogError("Cannot open clipboard");
    return wxEmptyString;
  }

  if (!wxTheClipboard->IsSupported(wxDF_TEXT))
  {
    wxLogError("Clipboard format not supported");
    return wxEmptyString;
  }

  wxTextDataObject data;
  if (!wxTheClipboard->GetData(data))
  {
    wxLogError("Cannot get clipboard data");
    return wxEmptyString;
  }

  return data.GetText();
}

#if wxUSE_GUI
void exComboBoxFromString(
  wxComboBox* cb,
  const wxString& text,
  const wxChar field_separator)
{
  wxStringTokenizer tkz(text, field_separator);
  while (tkz.HasMoreTokens())
  {
    const wxString val = tkz.GetNextToken();
    if (cb->FindString(val) == wxNOT_FOUND)
    {
      cb->Append(val);
    }
  }

  if (cb->GetCount() > 0) cb->SetValue(cb->GetString(0));
}

bool exComboBoxToString(
  const wxComboBox* cb,
  wxString& text,
  const wxChar field_separator,
  size_t max_items)
{
  if (cb == NULL)
  {
    return false;
  }

  text = cb->GetValue();
  switch (cb->FindString(cb->GetValue()))
  {
    case wxNOT_FOUND:
    {
      // Add the string, as it is not in the combo box, to the text,
      // simply by appending all combobox items.
      for (size_t i = 0; i < max_items; i++)
        if (i < max_items - 1 && i < cb->GetCount())
          text += field_separator + cb->GetString(i);
    }
    break;
    // No change necessary, the string is already present as the first one.
    case 0: return false; break;
    default:
    {
      // Reorder. The new first element already present, just add all others.
      for (size_t i = 0; i < cb->GetCount(); i++)
      {
        const wxString cb_element = cb->GetString(i);
        if (cb_element != cb->GetValue())
          text += field_separator + cb_element;
      }
    }
  }

  return true;
}

#endif // wxUSE_GUI

long exColourToLong(const wxColour& c)
{
  return c.Red() | (c.Green() << 8) | (c.Blue() << 16);
}

const wxString exEllipsed(const wxString& text, const wxString& control)
{
  return text + "..." + (!control.empty() ? "\t" + control: wxString(wxEmptyString));
}

const wxString exGetEndOfWord(
  const wxString& text,
  size_t max_chars)
{
  wxString text_out(text);

  if (text_out.length() > max_chars)
  {
    text_out = "..." + text_out.substr(text_out.length() - max_chars);
  }

  return text_out;
}

int exGetNumberOfLines(const wxString& text)
{
  if (text.empty())
  {
    return 0;
  }
  else if (text.Find(wxChar('\r')) != wxNOT_FOUND)
  {
    return text.Freq(wxChar('\r')) + 1;
  }
  else if (text.Find(wxChar('\n')) != wxNOT_FOUND)
  {
    return text.Freq(wxChar('\n')) + 1;
  }
  else
  {
    return 1;
  }
}

int exGetLineNumberFromText(const wxString& text)
{
  // Get text after :.
  const size_t pos_char = text.rfind(":");

  if (pos_char == wxString::npos)
  {
    return 0;
  }

  const wxString linenumber = text.substr(pos_char + 1);

  long line;

  if (linenumber.ToLong(&line))
  {
    return line;
  }
  else
  {
    return 0;
  }
}

void exLog(const wxString& text, const exFileName& filename)
{
  wxFile(
    filename.GetFullPath(),
    wxFile::write_append).Write(
      wxDateTime::Now().Format() + " " + text + wxTextFile::GetEOL());
}

const exFileName exLogfileName()
{
  if (wxTheApp == NULL)
  {
    return exFileName("app.log");
  }

#ifdef EX_PORTABLE
  return exFileName(
    wxPathOnly(wxStandardPaths::Get().GetExecutablePath()) + wxFileName::GetPathSeparator() +
    wxTheApp->GetAppName().Lower() + ".log");
#else
  return exFileName(
    wxStandardPaths::Get().GetUserDataDir() + wxFileName::GetPathSeparator() +
    wxTheApp->GetAppName().Lower() + ".log");
#endif
}

bool exMatchesOneOf(const wxFileName& filename, const wxString& pattern)
{
  if (pattern == "*") return true; // asterix matches always.

  const wxString fullname_uppercase = filename.GetFullName().Upper();

  wxStringTokenizer tokenizer(pattern.Upper(), ";");
  while (tokenizer.HasMoreTokens())
  {
    if (fullname_uppercase.Matches(tokenizer.GetNextToken())) return true;
  }

  return false;
}

const wxString exSkipWhiteSpace(const wxString& text, const wxString& replace_with)
{
  wxString output = text;
  wxRegEx("[ \t\n]+").ReplaceAll(&output, replace_with);
  return output;
}

const wxString exTranslate(const wxString& text, int pageNum, int numPages)
{
  wxString translation = text;
  wxString num;

  num.Printf("%i", pageNum);
  translation.Replace("@PAGENUM@", num);

  num.Printf("%i", numPages);
  translation.Replace("@PAGESCNT@", num);

  return translation;
}
<|endoftext|>


<commit_before>TEveWindowSlot *s = 0;

void test_windows()
{
   TEveManager::Create();

   TEveUtil::Macro("pointset_test.C");

   TEveWindowSlot  *slot = 0;
   TEveWindowFrame *evef = 0;

   TEveViewer *v = 0;

   TGCompositeFrame *cf = 0;

   // ----------------------------------------------------------------

   slot = TEveWindow::CreateWindowInTab(gEve->GetBrowser()->GetTabRight());

   TEveWindowPack* pack1 = slot->MakePack();

   slot = pack1->NewSlot();
   // Embedded viewer.
   v = new TEveViewer("BarViewer");
   TGLEmbeddedViewer* xx = new TGLEmbeddedViewer(0, 0, 0);
   v->SetGLViewer(xx);
   evef = slot->MakeFrame(xx->GetFrame());
   evef->SetElementName("Bar Embedded Viewer");

   gEve->GetViewers()->AddElement(v);
   v->AddScene(gEve->GetEventScene());

   slot = pack1->NewSlot();
   TEveWindowPack* pack2 = slot->MakePack();
   pack2->FlipOrientation();

   slot = pack2->NewSlot();
   slot->StartEmbedding();
   new TCanvas;
   slot->StopEmbedding();

   slot = pack2->NewSlot();
   // SA viewer.
   v = new TEveViewer("FooViewer");
   slot->StartEmbedding();
   v->SpawnGLViewer(gClient->GetRoot(), gEve->GetEditor());
   slot->StopEmbedding("Foo StandAlone Viewer");

   gEve->GetViewers()->AddElement(v);
   v->AddScene(gEve->GetEventScene());

   // ----------------------------------------------------------------

   slot = TEveWindow::CreateWindowInTab(gEve->GetBrowser()->GetTabRight());

   TEveWindowTab* tab1 = slot->MakeTab();
   tab1->NewSlot();
   slot = tab1->NewSlot();

   TEveWindowTab* tab2 = slot->MakeTab();
   tab2->NewSlot();
   tab2->NewSlot();

   // ----------------------------------------------------------------

   gEve->GetBrowser()->GetTabRight()->SetTab(1);
}
<commit_msg>From Bertrand: allow running via ACLiC.<commit_after>#include "TEveWindow.h"
#include "TEveViewer.h"
#include "TEveManager.h"
#include "TEveBrowser.h"
#include "TEveGedEditor.h"
#include "TGLEmbeddedViewer.h"
#include "TCanvas.h"
#include "TGTab.h"

void test_windows()
{
   TEveManager::Create();

   TEveUtil::Macro("pointset_test.C");

   TEveWindowSlot  *slot = 0;
   TEveWindowFrame *evef = 0;

   TEveViewer *v = 0;

   // ----------------------------------------------------------------

   slot = TEveWindow::CreateWindowInTab(gEve->GetBrowser()->GetTabRight());

   TEveWindowPack* pack1 = slot->MakePack();

   slot = pack1->NewSlot();
   // Embedded viewer.
   v = new TEveViewer("BarViewer");
   TGLEmbeddedViewer* xx = new TGLEmbeddedViewer(0, 0, 0);
   v->SetGLViewer(xx);
   evef = slot->MakeFrame(xx->GetFrame());
   evef->SetElementName("Bar Embedded Viewer");

   gEve->GetViewers()->AddElement(v);
   v->AddScene(gEve->GetEventScene());

   slot = pack1->NewSlot();
   TEveWindowPack* pack2 = slot->MakePack();
   pack2->FlipOrientation();

   slot = pack2->NewSlot();
   slot->StartEmbedding();
   new TCanvas(); // Sometimes crashes on destroy - should use embedded canvas?
   slot->StopEmbedding();

   slot = pack2->NewSlot();
   // SA viewer.
   v = new TEveViewer("FooViewer");
   slot->StartEmbedding();
   v->SpawnGLViewer(gClient->GetRoot(), gEve->GetEditor());
   slot->StopEmbedding("Foo StandAlone Viewer");

   gEve->GetViewers()->AddElement(v);
   v->AddScene(gEve->GetEventScene());

   // ----------------------------------------------------------------

   slot = TEveWindow::CreateWindowInTab(gEve->GetBrowser()->GetTabRight());

   TEveWindowTab* tab1 = slot->MakeTab();
   tab1->NewSlot();
   slot = tab1->NewSlot();

   TEveWindowTab* tab2 = slot->MakeTab();
   tab2->NewSlot();
   tab2->NewSlot();

   // ----------------------------------------------------------------

   gEve->GetBrowser()->GetTabRight()->SetTab(1);
}
<|endoftext|>


<commit_before>#ifndef STAN_MATH_PRIM_SCAL_FUN_GRAD_REG_INC_GAMMA_HPP
#define STAN_MATH_PRIM_SCAL_FUN_GRAD_REG_INC_GAMMA_HPP

#include <stan/math/prim/scal/err/domain_error.hpp>
#include <stan/math/prim/scal/err/check_not_nan.hpp>
#include <stan/math/prim/scal/fun/gamma_p.hpp>
#include <stan/math/prim/scal/fun/gamma_q.hpp>
#include <stan/math/prim/scal/fun/is_inf.hpp>
#include <stan/math/prim/scal/fun/square.hpp>
#include <cmath>
#include <limits>

namespace stan {
  namespace math {

    /**
     * Gradient of the regularized incomplete gamma functions igamma(a, z)
     *
     * For small z, the gradient is computed via the series expansion;
     * for large z, the series is numerically inaccurate due to cancellation
     * and the asymptotic expansion is used.
     *
     * @param a   shape parameter, a > 0
     * @param z   location z >= 0
     * @param g   boost::math::tgamma(a) (precomputed value)
     * @param dig boost::math::digamma(a) (precomputed value)
     * @param precision required precision; applies to series expansion only
     * @param max_steps number of steps to take.
     * @throw throws std::domain_error if not converged after max_steps
     *   or increment overflows to inf.
     *
     * For the asymptotic expansion, the gradient is given by:
       \f[
       \begin{array}{rcl}
       \Gamma(a, z) & = & z^{a-1}e^{-z} \sum_{k=0}^N \frac{(a-1)_k}{z^k} \qquad , z \gg a\\
       Q(a, z) & = & \frac{z^{a-1}e^{-z}}{\Gamma(a)} \sum_{k=0}^N \frac{(a-1)_k}{z^k}\\
       (a)_k & = & (a)_{k-1}(a-k)\\
       \frac{d}{da} (a)_k & = & (a)_{k-1} + (a-k)\frac{d}{da} (a)_{k-1}\\
       \frac{d}{da}Q(a, z) & = & (log(z) - \psi(a)) Q(a, z)\\
       && + \frac{z^{a-1}e^{-z}}{\Gamma(a)} \sum_{k=0}^N \left(\frac{d}{da} (a-1)_k\right) \frac{1}{z^k}
       \end{array}
       \f]
     */
    template<typename T>
    T grad_reg_inc_gamma(T a, T z, T g, T dig, double precision = 1e-6,
                         int max_steps = 1e5) {
      using std::domain_error;
      using std::exp;
      using std::fabs;
      using std::log;

      check_not_nan("grad_reg_inc_gamma", "a", a);
      check_not_nan("grad_reg_inc_gamma", "z", z);
      check_not_nan("grad_reg_inc_gamma", "g", g);
      check_not_nan("grad_reg_inc_gamma", "dig", dig);

      T l = log(z);
      if (z >= a && z >= 8) {
        // asymptotic expansion http://dlmf.nist.gov/8.11#E2
        T S = 0;
        T a_minus_one_minus_k = a - 1;
        T fac = a_minus_one_minus_k;  // falling_factorial(a-1, k)
        T dfac = 1;   // d/da[falling_factorial(a-1, k)]
        T zpow = z;   // z ** k
        T delta = dfac / zpow;

        for (int k = 1; k < 10; ++k) {
          a_minus_one_minus_k -= 1;

          S += delta;

          zpow *= z;
          dfac = a_minus_one_minus_k * dfac + fac;
          fac *= a_minus_one_minus_k;
          delta = dfac / zpow;

          if (is_inf(delta))
            stan::math::domain_error("grad_reg_inc_gamma",
                                     "is not converging", "", "");
        }

        return gamma_q(a, z) * (l - dig) + exp(-z + (a - 1) * l) * S / g;
      } else {
        // gradient of series expansion http://dlmf.nist.gov/8.7#E3

        T S = 0;
        T log_s = 0.0;
        double s_sign = 1.0;
        T log_z = log(z);
        T log_delta = log_s - 2 * log(a);
        for (int k = 1; k <= max_steps; ++k) {
          S += s_sign >= 0.0 ? exp(log_delta) : -exp(log_delta);
          log_s += log_z - log(k);
          s_sign = -s_sign;
          log_delta = log_s - 2 * log(k + a);
          if (is_inf(log_delta))
            stan::math::domain_error("grad_reg_inc_gamma",
                                     "is not converging", "", "");
          if (log_delta <= log(precision))
            return gamma_p(a, z) * ( dig - l ) + exp( a * l ) * S / g;
        }
        stan::math::domain_error("grad_reg_inc_gamma",
          "k (internal counter)",
          max_steps, "exceeded ",
          " iterations, gamma function gradient did not converge.");
        return std::numeric_limits<T>::infinity();
      }
    }

  }
}
#endif
<commit_msg>tests expect grad_reg_inc_gamma to return NaN silently.<commit_after>#ifndef STAN_MATH_PRIM_SCAL_FUN_GRAD_REG_INC_GAMMA_HPP
#define STAN_MATH_PRIM_SCAL_FUN_GRAD_REG_INC_GAMMA_HPP

#include <stan/math/prim/scal/err/domain_error.hpp>
#include <stan/math/prim/scal/fun/gamma_p.hpp>
#include <stan/math/prim/scal/fun/gamma_q.hpp>
#include <stan/math/prim/scal/fun/is_inf.hpp>
#include <stan/math/prim/scal/fun/is_nan.hpp>
#include <stan/math/prim/scal/fun/square.hpp>
#include <cmath>
#include <limits>

namespace stan {
  namespace math {

    /**
     * Gradient of the regularized incomplete gamma functions igamma(a, z)
     *
     * For small z, the gradient is computed via the series expansion;
     * for large z, the series is numerically inaccurate due to cancellation
     * and the asymptotic expansion is used.
     *
     * @param a   shape parameter, a > 0
     * @param z   location z >= 0
     * @param g   boost::math::tgamma(a) (precomputed value)
     * @param dig boost::math::digamma(a) (precomputed value)
     * @param precision required precision; applies to series expansion only
     * @param max_steps number of steps to take.
     * @throw throws std::domain_error if not converged after max_steps
     *   or increment overflows to inf.
     *
     * For the asymptotic expansion, the gradient is given by:
       \f[
       \begin{array}{rcl}
       \Gamma(a, z) & = & z^{a-1}e^{-z} \sum_{k=0}^N \frac{(a-1)_k}{z^k} \qquad , z \gg a\\
       Q(a, z) & = & \frac{z^{a-1}e^{-z}}{\Gamma(a)} \sum_{k=0}^N \frac{(a-1)_k}{z^k}\\
       (a)_k & = & (a)_{k-1}(a-k)\\
       \frac{d}{da} (a)_k & = & (a)_{k-1} + (a-k)\frac{d}{da} (a)_{k-1}\\
       \frac{d}{da}Q(a, z) & = & (log(z) - \psi(a)) Q(a, z)\\
       && + \frac{z^{a-1}e^{-z}}{\Gamma(a)} \sum_{k=0}^N \left(\frac{d}{da} (a-1)_k\right) \frac{1}{z^k}
       \end{array}
       \f]
     */
    template<typename T>
    T grad_reg_inc_gamma(T a, T z, T g, T dig, double precision = 1e-6,
                         int max_steps = 1e5) {
      using std::domain_error;
      using std::exp;
      using std::fabs;
      using std::log;

      if (is_nan(a)) return std::numeric_limits<T>::quiet_NaN();
      if (is_nan(z)) return std::numeric_limits<T>::quiet_NaN();
      if (is_nan(g)) return std::numeric_limits<T>::quiet_NaN();
      if (is_nan(dig)) return std::numeric_limits<T>::quiet_NaN();

      T l = log(z);
      if (z >= a && z >= 8) {
        // asymptotic expansion http://dlmf.nist.gov/8.11#E2
        T S = 0;
        T a_minus_one_minus_k = a - 1;
        T fac = a_minus_one_minus_k;  // falling_factorial(a-1, k)
        T dfac = 1;   // d/da[falling_factorial(a-1, k)]
        T zpow = z;   // z ** k
        T delta = dfac / zpow;

        for (int k = 1; k < 10; ++k) {
          a_minus_one_minus_k -= 1;

          S += delta;

          zpow *= z;
          dfac = a_minus_one_minus_k * dfac + fac;
          fac *= a_minus_one_minus_k;
          delta = dfac / zpow;

          if (is_inf(delta))
            stan::math::domain_error("grad_reg_inc_gamma",
                                     "is not converging", "", "");
        }

        return gamma_q(a, z) * (l - dig) + exp(-z + (a - 1) * l) * S / g;
      } else {
        // gradient of series expansion http://dlmf.nist.gov/8.7#E3

        T S = 0;
        T log_s = 0.0;
        double s_sign = 1.0;
        T log_z = log(z);
        T log_delta = log_s - 2 * log(a);
        for (int k = 1; k <= max_steps; ++k) {
          S += s_sign >= 0.0 ? exp(log_delta) : -exp(log_delta);
          log_s += log_z - log(k);
          s_sign = -s_sign;
          log_delta = log_s - 2 * log(k + a);
          if (is_inf(log_delta))
            stan::math::domain_error("grad_reg_inc_gamma",
                                     "is not converging", "", "");
          if (log_delta <= log(precision))
            return gamma_p(a, z) * ( dig - l ) + exp( a * l ) * S / g;
        }
        stan::math::domain_error("grad_reg_inc_gamma",
          "k (internal counter)",
          max_steps, "exceeded ",
          " iterations, gamma function gradient did not converge.");
        return std::numeric_limits<T>::infinity();
      }
    }

  }
}
#endif
<|endoftext|>


<commit_before>#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <ncurses.h>

#define N 0x10

struct TCoordenada{
    double x;
    double y;
};

struct TAnillo{
    double x;
    double y;
};

void rellena_con_mierda_la_piii_serpiente(struct TAnillo coordenada[N]){

    for (int i=0; i<N; i++){
        coordenada[i].x = 10 + i;
        coordenada[i].y = 10;
    }
}

void muestra(struct TAnillo coordenada[N]){
    clear();
    for (int i=0; i<N; i++)
        mvprintw( coordenada[i].y, coordenada[i].x, "*");
    refresh();
}

void mover(struct TCoordenada incremento, struct TAnillo coordenada[N]){

    for (int i=N-1; i>0; i--){
        coordenada[i].x = coordenada[i-1].x;
        coordenada[i].y = coordenada[i-1].y;
    }
    coordenada[0].x += incremento.x;
    coordenada[0].y += incremento.y;
}

int main(int argc, char *argv[]){

    struct TAnillo serpiente[N];
    struct TCoordenada movimiento = {0, -1};
    int user_input;

    rellena_con_mierda_la_piii_serpiente(serpiente);

    initscr();             // Crea una matriz para pintar
    halfdelay(3);          // Hace que getch espere 3 decimas de segundo
    keypad(stdscr, TRUE);  // Vale para leer las flechas
    noecho();              // Para que no se vea el caracter pulsado.
    curs_set(0);           // No se ve el cursor.
    while(1){
        user_input = getch();
        switch(tolower(user_input)){
            case 'q':
            case KEY_UP:
                movimiento.x = 0;
                movimiento.y = -1;
                break;
            case 'a':
            case KEY_DOWN:
                movimiento.x = 0;
                movimiento.y = 1;
                break;
            case 'o':
            case KEY_LEFT:
                movimiento.x = -1;
                movimiento.y = 0;
                break;
            case 'p':
            case KEY_RIGHT:
                movimiento.x = 1;
                movimiento.y = 0;
                break;

        }
        mover( movimiento, serpiente);
        muestra(serpiente);
    }
    getchar();
    endwin();              // Libera la matriz.

    return EXIT_SUCCESS;
}
<commit_msg>Delete snake.cpp<commit_after><|endoftext|>


<commit_before>#include "encoder.hh"
#include "../format/format-stream.hh"

namespace mimosa
{
  namespace json
  {
    Encoder::Encoder(stream::Stream::Ptr output)
      : output_(output)
    {
    }

    bool
    Encoder::startObject()
    {
      if (!state_.empty() && state_.back() == kObjectKey)
        throw SyntaxError();

      if (!pushSeparator())
        return false;

      if (output_->loopWrite("{", 1) != 1)
        return false;
      state_.push_back(kObjectKey);
      return true;
    }

    bool
    Encoder::endObject()
    {
      if (state_.empty() ||
          (state_.back() != kObjectKey && state_.back() != kObjectNext))
        throw SyntaxError();

      if (output_->loopWrite("}", 1) != 1)
        return false;

      state_.pop_back();
      nextState();
      return true;
    }

    bool
    Encoder::startArray()
    {
      if (!state_.empty() && state_.back() == kObjectKey)
        throw SyntaxError();

      if (!pushSeparator())
        return false;

      if (output_->loopWrite("[", 1) != 1)
        return false;

      state_.push_back(kArray);
      return true;
    }

    bool
    Encoder::endArray()
    {
      if (state_.empty() || (state_.back() != kArray && state_.back() != kArrayNext))
        throw SyntaxError();

      if (output_->loopWrite("]", 1) != 1)
        return false;

      state_.pop_back();
      nextState();
      return true;
    }

    bool
    Encoder::pushBoolean(bool value)
    {
      if (!state_.empty() && state_.back() == kObjectKey)
        throw SyntaxError();

      if (!pushSeparator())
        return false;

      if ((value && output_->loopWrite("true", 4) != 4) ||
          (!value && output_->loopWrite("false", 5) != 5))
        return false;

      nextState();
      return true;
    }

    bool
    Encoder::pushNull()
    {
      if (!state_.empty() && state_.back() == kObjectKey)
        throw SyntaxError();

      if (!pushSeparator())
        return false;

      if (output_->loopWrite("null", 4) != 4)
        return false;

      nextState();
      return true;
    }

    bool
    Encoder::pushString(const std::string & data)
    {
      if (!pushSeparator())
        return false;

      if (output_->loopWrite("\"", 1) != 1)
        return false;

      for (auto it = data.begin(); it != data.end(); ++it) {
        if (*it == '\\' || *it == '"')
          if (output_->loopWrite("\\", 1) != 1)
            return false;

        switch (*it) {
        case '\r':
          if (output_->loopWrite("\\r", 2) != 2)
            return false;
          break;

        case '\n':
          if (output_->loopWrite("\\n", 2) != 2)
            return false;
          break;

        case '\t':
          if (output_->loopWrite("\\t", 2) != 2)
            return false;
          break;

        case '\v':
          if (output_->loopWrite("\\v", 2) != 2)
            return false;
          break;

        case '\b':
          if (output_->loopWrite("\\b", 2) != 2)
            return false;
          break;

        case '\\':
        case '"':
          if (output_->loopWrite("\\", 1) != 1)
            return false;
          /* fall through */

        default:
          if (output_->loopWrite(&*it, 1) != 1)
            return false;
        }
      }

      if (output_->loopWrite("\"", 1) != 1)
        return false;

      nextState();
      return true;
    }

    bool
    Encoder::pushNumber(int64_t value)
    {
      if (!state_.empty() && state_.back() == kObjectKey)
        throw SyntaxError();

      if (!pushSeparator())
        return false;

      if (!format::format(*output_, "%d", value))
        return false;

      nextState();
      return true;
    }

    bool
    Encoder::pushFloat(double value)
    {
      if (!state_.empty() && state_.back() == kObjectKey)
        throw SyntaxError();

      if (!pushSeparator())
        return false;

      if (!format::format(*output_, "%v", value))
        return false;

      nextState();
      return true;
    }

    bool
    Encoder::pushSeparator()
    {
      if (state_.empty())
        return true;

      switch (state_.back()) {
      case kArray:
      case kObjectKey:
        return true;

      case kArrayNext:
        if (output_->loopWrite(",", 1) != 1)
          return false;
        state_.back() = kArray;
        return true;

      case kObjectNext:
        if (output_->loopWrite(",", 1) != 1)
          return false;
        state_.back() = kObjectKey;
        return true;

      case kObjectValue:
        return output_->loopWrite(":", 1) == 1;

      default:
        return true;
      }
    }

    void
    Encoder::nextState()
    {
      if (state_.empty())
        return;

      switch (state_.back()) {
      case kArray:
        state_.back() = kArrayNext;
        break;

      case kArrayNext:
        break;

      case kObjectKey:
        state_.back() = kObjectValue;
        break;

      case kObjectValue:
        state_.back() = kObjectNext;
        break;

      case kObjectNext:
        state_.back() = kObjectKey;
        break;

      default:
        break;
      }
    }
  }
}
<commit_msg>Remove bug<commit_after>#include "encoder.hh"
#include "../format/format-stream.hh"

namespace mimosa
{
  namespace json
  {
    Encoder::Encoder(stream::Stream::Ptr output)
      : output_(output)
    {
    }

    bool
    Encoder::startObject()
    {
      if (!state_.empty() && state_.back() == kObjectKey)
        throw SyntaxError();

      if (!pushSeparator())
        return false;

      if (output_->loopWrite("{", 1) != 1)
        return false;
      state_.push_back(kObjectKey);
      return true;
    }

    bool
    Encoder::endObject()
    {
      if (state_.empty() ||
          (state_.back() != kObjectKey && state_.back() != kObjectNext))
        throw SyntaxError();

      if (output_->loopWrite("}", 1) != 1)
        return false;

      state_.pop_back();
      nextState();
      return true;
    }

    bool
    Encoder::startArray()
    {
      if (!state_.empty() && state_.back() == kObjectKey)
        throw SyntaxError();

      if (!pushSeparator())
        return false;

      if (output_->loopWrite("[", 1) != 1)
        return false;

      state_.push_back(kArray);
      return true;
    }

    bool
    Encoder::endArray()
    {
      if (state_.empty() || (state_.back() != kArray && state_.back() != kArrayNext))
        throw SyntaxError();

      if (output_->loopWrite("]", 1) != 1)
        return false;

      state_.pop_back();
      nextState();
      return true;
    }

    bool
    Encoder::pushBoolean(bool value)
    {
      if (!state_.empty() && state_.back() == kObjectKey)
        throw SyntaxError();

      if (!pushSeparator())
        return false;

      if ((value && output_->loopWrite("true", 4) != 4) ||
          (!value && output_->loopWrite("false", 5) != 5))
        return false;

      nextState();
      return true;
    }

    bool
    Encoder::pushNull()
    {
      if (!state_.empty() && state_.back() == kObjectKey)
        throw SyntaxError();

      if (!pushSeparator())
        return false;

      if (output_->loopWrite("null", 4) != 4)
        return false;

      nextState();
      return true;
    }

    bool
    Encoder::pushString(const std::string & data)
    {
      if (!pushSeparator())
        return false;

      if (output_->loopWrite("\"", 1) != 1)
        return false;

      for (auto it = data.begin(); it != data.end(); ++it) {
        if (*it == '\\' || *it == '"')
          if (output_->loopWrite("\\", 1) != 1)
            return false;

        switch (*it) {
        case '\r':
          if (output_->loopWrite("\\r", 2) != 2)
            return false;
          break;

        case '\n':
          if (output_->loopWrite("\\n", 2) != 2)
            return false;
          break;

        case '\t':
          if (output_->loopWrite("\\t", 2) != 2)
            return false;
          break;

        case '\v':
          if (output_->loopWrite("\\v", 2) != 2)
            return false;
          break;

        case '\b':
          if (output_->loopWrite("\\b", 2) != 2)
            return false;
          break;

        default:
          if (output_->loopWrite(&*it, 1) != 1)
            return false;
        }
      }

      if (output_->loopWrite("\"", 1) != 1)
        return false;

      nextState();
      return true;
    }

    bool
    Encoder::pushNumber(int64_t value)
    {
      if (!state_.empty() && state_.back() == kObjectKey)
        throw SyntaxError();

      if (!pushSeparator())
        return false;

      if (!format::format(*output_, "%d", value))
        return false;

      nextState();
      return true;
    }

    bool
    Encoder::pushFloat(double value)
    {
      if (!state_.empty() && state_.back() == kObjectKey)
        throw SyntaxError();

      if (!pushSeparator())
        return false;

      if (!format::format(*output_, "%v", value))
        return false;

      nextState();
      return true;
    }

    bool
    Encoder::pushSeparator()
    {
      if (state_.empty())
        return true;

      switch (state_.back()) {
      case kArray:
      case kObjectKey:
        return true;

      case kArrayNext:
        if (output_->loopWrite(",", 1) != 1)
          return false;
        state_.back() = kArray;
        return true;

      case kObjectNext:
        if (output_->loopWrite(",", 1) != 1)
          return false;
        state_.back() = kObjectKey;
        return true;

      case kObjectValue:
        return output_->loopWrite(":", 1) == 1;

      default:
        return true;
      }
    }

    void
    Encoder::nextState()
    {
      if (state_.empty())
        return;

      switch (state_.back()) {
      case kArray:
        state_.back() = kArrayNext;
        break;

      case kArrayNext:
        break;

      case kObjectKey:
        state_.back() = kObjectValue;
        break;

      case kObjectValue:
        state_.back() = kObjectNext;
        break;

      case kObjectNext:
        state_.back() = kObjectKey;
        break;

      default:
        break;
      }
    }
  }
}
<|endoftext|>