Exemplo n.º 1
0
void custom_tod::update_tod_display(window& window)
{
	::image::set_color_adjustment(tod_red_field_->get_value(),
								tod_green_field_->get_value(),
								tod_blue_field_->get_value());

	// Prevent a floating slice of window appearing alone over the
	// theme UI sidebar after redrawing tiles and before we have a
	// chance to redraw the rest of this window.
	window.undraw();

	// NOTE: We only really want to re-render the gamemap tiles here.
	// Redrawing everything is a significantly more expensive task.
	// At this time, tiles are the only elements on which ToD tint is
	// meant to have a visible effect. This is very strongly tied to
	// the image caching mechanism.
	//
	// If this ceases to be the case in the future, you'll need to call
	// redraw_everything() instead.

	// invalidate all tiles so they are redrawn with the new ToD tint next
	display_.invalidate_all();

	// redraw tiles
	display_.draw(false);

	window.invalidate_layout();
}
Exemplo n.º 2
0
void custom_tod::update_tod_display(window& window)
{
	display* disp = display::get_singleton();
	assert(disp && "Display pointer is null!");

	// Prevent a floating slice of window appearing alone over the
	// theme UI sidebar after redrawing tiles and before we have a
	// chance to redraw the rest of this window.
	window.undraw();

	// NOTE: We only really want to re-render the gamemap tiles here.
	// Redrawing everything is a significantly more expensive task.
	// At this time, tiles are the only elements on which ToD tint is
	// meant to have a visible effect. This is very strongly tied to
	// the image caching mechanism.
	//
	// If this ceases to be the case in the future, you'll need to call
	// redraw_everything() instead.

	disp->update_tod(&get_selected_tod());

	// invalidate all tiles so they are redrawn with the new ToD tint next
	disp->invalidate_all();

	// redraw tiles
	disp->draw(false);

	// NOTE: revert to invalidate_layout if necessary to display the ToD mask image.
	window.set_is_dirty(true);
}
Exemplo n.º 3
0
/*  This function is called by the Windows function DispatchMessage()  */
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  static window wnd;

  switch (message)  /* handle the messages */
  {
  case WM_CREATE:
    wnd.WMCreate(hwnd, message, wParam, lParam);
    break;
  case WM_PAINT:
    wnd.WMPaint(hwnd, message, wParam, lParam);
    break;
  case WM_SIZE:
    wnd.WMSize(hwnd, message, wParam, lParam);
    break;
  case WM_COMMAND:
    wnd.WMCommand(hwnd, message, wParam, lParam);
    break;
  case WM_VSCROLL:
    wnd.WMVScroll(hwnd, message, wParam, lParam);
    break;
  case WM_HSCROLL:
    wnd.WMHScroll(hwnd, message, wParam, lParam);
    break;
  case WM_KEYDOWN:
    wnd.WMKeydown(hwnd, message, wParam, lParam);
    break;
  case WM_DESTROY:
    wnd.WMDestroy(hwnd, message, wParam, lParam);
    break;
  default:
    return DefWindowProc(hwnd, message, wParam, lParam);
  }
  return 0;
} /* End of 'WindowProcedure' function */
Exemplo n.º 4
0
void lobby_main::post_build(window& win)
{
	/** @todo Should become a global hotkey after 1.8, then remove it here. */
	win.register_hotkey(hotkey::HOTKEY_FULLSCREEN, std::bind(fullscreen, std::ref(win.video())));

	/*** Local hotkeys. ***/
	win.register_hotkey(hotkey::HOTKEY_PREFERENCES, [this](event::dispatcher& w, hotkey::HOTKEY_COMMAND)->bool {
		show_preferences_button_callback(dynamic_cast<window&>(w));
		return true;
	});
}
Exemplo n.º 5
0
Arquivo: window.hpp Projeto: 8l/x11
      //
      // For a child window. 'w' is its parent.
      //
      window ( window& w  ) 
	: m_display ( w.get_event_dispatcher().get_display() ),
	m_background ( m_display, 213, 206, 189 ),
	m_event_dispatcher ( w.get_event_dispatcher() ),
	m_border ( m_display, 255, 255, 255 ),
	m_is_child ( true ),
	m_rect ( w.get_rect() ),
	m_parent ( w.id() )
	{
	  m_window = 0;
	  m_atom[0] = 0;
	  show();
	}
Exemplo n.º 6
0
	std::string make_window_hierarchy_id_string(const window& w) {
		if (!w.has_parent()) {
			return w.get_id();
		}
		else {
			std::string s = w.get_id();
			const window* cw = &w;
			while (cw->has_parent()) {
				cw = &cw->get_parent();
				s.insert(0, ".");
				s.insert(0, cw->get_id());
			}
			return s;
		}
	}
Exemplo n.º 7
0
hologram hologram_reconstructor::reconstruct(const hologram& h, const window& hw, vec3 light_direction, float_t light_distance) {
    hologram image(width, height);
    boost::timer timer;
    vec3 direction = normalize(output_window.direction());
    std::complex<float_t> ilambda(0, wavelength);
#pragma omp parallel for schedule(dynamic, 1)
    for (std::size_t y = 0; y < height; ++y) {
        for (std::size_t x = 0; x < width; ++x) {
            vec3 op = output_window.unproj(vec2(float_t(x) / h.width(), float_t(y) / h.height()));
            std::complex<float_t> wave(0);
            for (std::size_t hy = 0; hy < h.height(); ++hy) {
                for (std::size_t hx = 0; hx < h.width(); ++hx) {
                    vec3 hp = hw.unproj(vec2(float_t(hx) / width, float_t(hy) / height));
                    //float_t hp_to_light = dot(hp, light_direction) + light_distance;
                    vec3 op_to_hp = hp - op;
                    float_t op_to_hp_len2 = cml::length_squared(op_to_hp);
                    float_t op_to_hp_len = std::sqrt(op_to_hp_len2);
                    wave += std::polar(h(hx, hy) / op_to_hp_len2, wavenumber * op_to_hp_len) / ilambda * dot(direction, op_to_hp);
                }
            }
            image(x, y) = std::abs(wave);
        }
        std::clog << (y + 1) << "/" << height << " " << int(timer.elapsed() / (y + 1) * (height - y - 1)) << " s left" << std::endl;
    }
    return image;
}
Exemplo n.º 8
0
void unit_recall::rename_unit(window& window)
{
	listbox& list = find_widget<listbox>(&window, "recall_list", false);

	const int index = list.get_selected_row();
	unit& selected_unit = const_cast<unit&>(*recall_list_[index].get());

	std::string name = selected_unit.name();
	const std::string dialog_title(_("Rename Unit"));
	const std::string dialog_label(_("Name:"));

	if(gui2::dialogs::edit_text::execute(dialog_title, dialog_label, name)) {
		selected_unit.rename(name);

		find_widget<label>(list.get_row_grid(index), "unit_name", false).set_label(name);

		filter_options_.erase(filter_options_.begin() + index);
		std::ostringstream filter_text;
		filter_text << selected_unit.type_name() << " " << name << " " << std::to_string(selected_unit.level());
		for(const std::string& trait : selected_unit.trait_names()) {
			filter_text << " " << trait;
		}
		filter_options_.insert(filter_options_.begin() + index, filter_text.str());

		list_item_clicked(window);
		window.invalidate_layout();
	}
}
Exemplo n.º 9
0
void title_screen::update_tip(window& win, const bool previous)
{
	multi_page& tips = find_widget<multi_page>(&win, "tips", false);
	if(tips.get_page_count() == 0) {
		return;
	}

	int page = tips.get_selected_page();
	if(previous) {
		if(page <= 0) {
			page = tips.get_page_count();
		}
		--page;
	} else {
		++page;
		if(static_cast<unsigned>(page) >= tips.get_page_count()) {
			page = 0;
		}
	}

	tips.select_page(page);

	/**
	 * @todo Look for a proper fix.
	 *
	 * This dirtying is required to avoid the blurring to be rendered wrong.
	 * Not entirely sure why, but since we plan to move to SDL2 that change
	 * will probably fix this issue automatically.
	 */
	win.set_is_dirty(true);
}
Exemplo n.º 10
0
level_editor::layers_control::layers_control(window& win, preferences& prefs)
: Gtk::HBox(false, 4), m_preferences(prefs), m_window(win),
  m_button_new_layer("+"), m_button_delete_layer("-") {
  // Layer selection
  m_spin_layer.signal_changed().connect_notify(
    sigc::mem_fun(this, &layers_control::on_layer_changed));

  Gtk::Label& layer_label = *Gtk::manage(new Gtk::Label("Layer:"));
  pack_start(layer_label, Gtk::PACK_SHRINK);
  pack_start(m_spin_layer, Gtk::PACK_SHRINK);

  //Gtk::Label& layer_visiblity_label = *Gtk::manage(new Gtk::Label("Visible:"));
  //pack_start(layer_visiblity_label, Gtk::PACK_SHRINK);

  m_layer_visible.signal_toggled().connect_notify(
    sigc::mem_fun(this, &layers_control::on_layer_visible_toggled));

  pack_start(m_layer_visible, Gtk::PACK_SHRINK);

  m_button_new_layer.signal_clicked().connect_notify(
    sigc::mem_fun(this, &layers_control::on_add_layer));
  pack_start(m_button_new_layer, Gtk::PACK_SHRINK);

  m_button_delete_layer.signal_clicked().connect_notify(
    sigc::mem_fun(this, &layers_control::on_delete_layer));
  pack_start(m_button_delete_layer, Gtk::PACK_SHRINK);


  win.signal_switch_level_display().connect(
    sigc::mem_fun(this, &layers_control::on_switch_level_display));
}
Exemplo n.º 11
0
 /// @brief Construct a window.
 ///
 /// @param[in] parent The parent window.
 ///
 ///
 window(window & parent,
        size_type width, size_type height,
        size_type x_position, size_type y_position) :
     underlying_window_(
         ::subwin(parent.underlying_window(),
                  height, width, y_position, x_position))
 {};
Exemplo n.º 12
0
// Make wind the front window
void window_select(window &wind)
{
	window *prev = window_get_front();
	d_event event;
	if (&wind == FrontWindow)
		return;
	if (&wind == FirstWindow && wind.next)
		FirstWindow = FirstWindow->next;

	if (wind.next)
		wind.next->prev = wind.prev;
	if (wind.prev)
		wind.prev->next = wind.next;
	wind.prev = FrontWindow;
	FrontWindow->next = &wind;
	wind.next = nullptr;
	FrontWindow = &wind;
	
	if (wind.is_visible())
	{
		if (prev)
			WINDOW_SEND_EVENT(prev, EVENT_WINDOW_DEACTIVATED);
		WINDOW_SEND_EVENT(&wind, EVENT_WINDOW_ACTIVATED);
	}
}
Exemplo n.º 13
0
void mp_staging::network_handler(window& window)
{
	// First, send off any changes if they've been accumulated
	if(state_changed_) {
		connect_engine_.update_and_send_diff();
	}

	// Next, check for any incoming changes
	config data;
	if(!state_changed_ && (!network_connection_ || !network_connection_->receive_data(data))) {
		return;
	}

	// Update chat
	find_widget<chatbox>(&window, "chat", false).process_network_data(data);

	// TODO: why is this needed...
	const bool was_able_to_start = connect_engine_.can_start_game();

	bool quit_signal_received;
	std::tie(quit_signal_received, std::ignore) = connect_engine_.process_network_data(data);

	if(quit_signal_received) {
		window.set_retval(retval::CANCEL);
	}

	// Update side leader displays
	// This is basically only needed when a new player joins and selects their faction
	for(auto& tree_entry : side_tree_map_) {
		ng::side_engine_ptr side = tree_entry.first;

		grid& row_grid = tree_entry.second->get_grid();

		update_leader_display(side, row_grid);

		std::vector<config> controller_names;
		for(const auto& controller : side->controller_options()) {
			controller_names.emplace_back("label", controller.second);
		}

		menu_button& controller_selection = find_widget<menu_button>(&row_grid, "controller", false);

		controller_selection.set_values(controller_names, side->current_controller_index());
		controller_selection.set_active(controller_names.size() > 1);
	}

	// Update player list
	if(data.has_child("user")) {
		player_list_->update_list(data.child_range("user"));
	}

	// Update status label and buttons
	update_status_label_and_buttons(window);

	if(!was_able_to_start && connect_engine_.can_start_game()) {
		mp_ui_alerts::ready_for_start();
	}

	state_changed_ = false;
}
Exemplo n.º 14
0
color bullet(window &w, color skycolor, tank &left, tank &right, bool &leftTurn) { // goes through bullets path. returns color of what is hit.
    setBrushAndPenColor(w, skycolor); // erasing up top to make sure it doesnt count hitting the text
    w.DrawRectangle(0, 0, w.GetWidth(), 200, FILLED);
    double dtime = 0.007;
    int bulletRadius = 4;
    int bulletX, bulletY;
    double radianAngle;
    tank &currentTank = leftTurn ? left : right;
    int angle = currentTank.getAngle();
    if (leftTurn) {
        radianAngle = angle * cdPi / 180;
        bulletX = currentTank.getXend() + 3;
    } else {
        radianAngle = ((180 - angle) * cdPi / 180);
        bulletX = currentTank.getXend() - 3;
    }
    int velocity = currentTank.getSpeed();
    bulletY = currentTank.getYend() - 3;
    leftTurn = !leftTurn;
    double xVel = cos(radianAngle) * velocity;
    double yVel = sin(radianAngle) * velocity;
    double time = 0;
    while (w.GetColor(bulletX, bulletY) == skycolor) {
        setBrushAndPenColor(w, BLACK);
        w.DrawCircle(bulletX, bulletY, bulletRadius, FILLED);
        w.UpdateBuffer();
        Sleep(1);
        setBrushAndPenColor(w, skycolor);
        w.DrawCircle(bulletX, bulletY, bulletRadius + 1, FILLED);
        time += dtime;
        bulletX += xVel * time;
        bulletY += -yVel * time + 40 * time * time;
    }
    return w.GetColor(bulletX, bulletY);
}
Exemplo n.º 15
0
void drawLandscape(window &w, color skycolor, color groundcolor) { // creates landscape. only called once
    setBrushAndPenColor(w, skycolor);
    w.DrawRectangle(0, 0, w.GetWidth(), w.GetHeight(), FILLED);
    RandGen r;
    int ystart = r.RandInt((double) w.GetHeight() * 0.666, (double) w.GetHeight() * 0.75); // semi random height of ground near bottom of screen
    setBrushAndPenColor(w, groundcolor);
    for (double x = 0; x < w.GetWidth(); x++) {
        int yval = ystart + sin((double) (x / 60)) * 15; // ground will be sin curve
        w.DrawLine(x, yval, x, w.GetHeight());    // draws line from ground level to bottom of screen
    }
}
Exemplo n.º 16
0
void outro::draw_callback(window& window)
{
	if(SDL_GetTicks() < next_draw_) {
		return;
	}

	/* If we've faded fully in...
	 *
	 * NOTE: we want fading to take around half a second. Given this function runs about every 3 frames, we
	 * limit ourselves to a reasonable 10 fade steps with an alpha difference (rounded up) of 25.5 each cycle.
	 * The actual calculation for alpha is done in the window definition in WFL.
	 */
	if(fading_in_ && fade_step_ > 10) {
		// Schedule the fadeout after the provided delay.
		if(timer_id_ == 0) {
			timer_id_ = add_timer(duration_, [this](size_t) { fading_in_ = false; });
		}

		return;
	}

	// If we've faded fully out...
	if(!fading_in_ && fade_step_ < 0) {
		window.close();
		return;
	}

	canvas& window_canvas = window.get_canvas(0);

	window_canvas.set_variable("fade_step", wfl::variant(fade_step_));
	window_canvas.set_is_dirty(true);

	window.set_is_dirty(true);

	if(fading_in_) {
		fade_step_ ++;
	} else {
		fade_step_ --;
	}

	set_next_draw();
}
Exemplo n.º 17
0
void mp_staging::select_leader_callback(window& window, ng::side_engine_ptr side, grid& row_grid)
{
	gui2::dialogs::faction_select dlg(side->flg(), std::to_string(side->color() + 1), side->index() + 1);
	dlg.show(window.video());

	if(dlg.get_retval() == window::OK) {
		update_leader_display(side, row_grid);

		set_state_changed();
	}
}
Exemplo n.º 18
0
/*
 * This function is used to test the tooltip placement algorithms as
 * described in the »Tooltip placement« section in the GUI2 design
 * document.
 *
 * Use a 1024 x 768 screen size, set the maximum loop iteration to:
 * - 0   to test with a normal tooltip placement.
 * - 30  to test with a larger normal tooltip placement.
 * - 60  to test with a huge tooltip placement.
 * - 150 to test with a borderline to insanely huge tooltip placement.
 * - 180 to test with an insanely huge tooltip placement.
 */
static void debug_tooltip(window& window, bool& handled, const point& coordinate)
{
	std::string message = "Hello world.";

	for(int i = 0; i < 0; ++i) {
		message += " More greetings.";
	}

	gui2::tip::remove();
	gui2::tip::show(window.video(), "tooltip", message, coordinate);

	handled = true;
}
Exemplo n.º 19
0
    // create_child_window
    window create_child_window(int x, int y, int cx, int cy, window const& parent)
    {
        // create impl_type
        boost::shared_ptr<window::impl_type> pimpl(new window::impl_type());

        // create window
        pimpl->win = khaos::create_child_window(x, y, cx, cy, parent.khaos_window());

        // create/set event handler
        pimpl->win->handler = detail::event_handler(window(pimpl), pimpl);

        return window(pimpl);
    }
Exemplo n.º 20
0
void mp_change_control::post_show(window& window)
{
	if(window.get_retval() == window::OK) {
		DBG_GUI << "Main: changing control of side "
		        << sides_[selected_side_] << " to nick "
		        << nicks_[selected_nick_] << std::endl;

		menu_handler_.request_control_change(
			sides_[selected_side_],
			nicks_[selected_nick_]
		);
	}
}
Exemplo n.º 21
0
void mp_staging::post_show(window& window)
{
	if(update_timer_ != 0) {
		remove_timer(update_timer_);
		update_timer_ = 0;
	}

	if(window.get_retval() == window::OK) {
		connect_engine_.start_game();
	} else {
		connect_engine_.leave_game();
	}
}
Exemplo n.º 22
0
shadow_map::shadow_map(const window& wnd, const glm::mat4& vp, size_t sz)
: m_shader(), m_buffer(sz, sz), m_size(sz), m_transform(vp)
{
	m_screen = wnd.render_size();

	m_shader.compile("data/shader/shadow.vs", GL_VERTEX_SHADER);
	m_shader.compile("data/shader/shadow.fs", GL_FRAGMENT_SHADER);
	m_shader.link();

	m_shader.declare_uniform("M");
	m_shader.declare_uniform("VP");

	m_buffer.attach_depth(GL_DEPTH_COMPONENT24);
	m_buffer.finish();
}
Exemplo n.º 23
0
void lobby_main::show_preferences_button_callback(window& window)
{
	if(preferences_callback_) {
		preferences_callback_();

		/**
		 * The screen size might have changed force an update of the size.
		 *
		 * @todo This might no longer be needed when gui2 is done.
		 */
		window.invalidate_layout();

		wesnothd_connection_.send_data(config("refresh_lobby"));
	}
}
Exemplo n.º 24
0
void loading_screen::timer_callback(window& window)
{
	if (!work_ || !worker_ || worker_->timed_join(boost::posix_time::milliseconds(0))) {
		if (exception_) {
			clear_timer();
			std::rethrow_exception(exception_);
		}
		window.close();
	}
	if (!work_) {
		return;
	}
	const char* stage = current_stage_
#if defined(_MSC_VER) && _MSC_VER < 1900
		;
#else
		.load(std::memory_order_acquire);
Exemplo n.º 25
0
int main(int argc, char ** argv)
{
    cout << "Space Station Build Game" << endl;
    
    glutInit(&argc,argv);
    glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
    
    glWindow.setPosition(100,100);
    glWindow.setDimensions(640, 480);
    glWindow.setTitle("HELLO WORLD");
    glWindow.setDisplayFunction(render);
    
    cout << "Width:" << glWindow.getWidth() << endl;
    cout << "Height:" << glWindow.getHeight() << endl;
    
    cout << "Title:" << glWindow.getTitle() << endl;
    
    
    glWindow.createWindow();
    
    //initGlutWindow(x,y,width,height,"Space Station Build Game",render);
}
Exemplo n.º 26
0
void widget::populate_dirty_list(window& caller,
								  std::vector<widget*>& call_stack)
{
	assert(call_stack.empty() || call_stack.back() != this);

	if(visible_ != visibility::visible) {
		return;
	}

	if(get_drawing_action() == redraw_action::none) {
		return;
	}

	call_stack.push_back(this);
	if(is_dirty_) {
		caller.add_to_dirty_list(call_stack);
	} else {
		// virtual function which only does something for container items.
		child_populate_dirty_list(caller, call_stack);
	}
}
Exemplo n.º 27
0
    // handle_event
    bool handle_event(window const& win, int et, void * ei)
    {
        bool result = false;

        boost::shared_ptr<window::impl_type> pimpl = win.pimpl.lock();

        if (et == khaos::resize::value && win.khaos_window()->is_toplevel)
        {
            point oldw = khaos::detail::resize_old_width(ei);

            reposition_layout(win, oldw.x, oldw.y);
        }

        if (pimpl->main)
            result |= pimpl->main(et, ei);

        BOOST_FOREACH(iris::event_handler const& handler, pimpl->handlers)
        {
            result |= handler(et, ei);
        }

        return result;
    }
Exemplo n.º 28
0
	color make_window_alpha_scaled_color(const window& window, const color& c) {
		return color(c, c.get_a() * window.get_alpha());
	}
Exemplo n.º 29
0
void unit_recall::dismiss_unit(window& window)
{
	LOG_DP << "Recall list units:\n"; dump_recall_list_to_console(recall_list_);

	listbox& list = find_widget<listbox>(&window, "recall_list", false);
	const int index = list.get_selected_row();

	const unit& u = *recall_list_[index].get();

	// If the unit is of level > 1, or is close to advancing, we warn the player about it
	std::stringstream message;
	if(u.loyal()) {
		message << _("This unit is loyal and requires no upkeep.") << " " << (u.gender() == unit_race::MALE
		         ? _("Do you really want to dismiss him?")
		         : _("Do you really want to dismiss her?"));

	} else if(u.level() > 1) {
		message << _("This unit is an experienced one, having advanced levels.") << " " << (u.gender() == unit_race::MALE
		         ? _("Do you really want to dismiss him?")
		         : _("Do you really want to dismiss her?"));

	} else if(u.experience() > u.max_experience()/2) {
		message << _("This unit is close to advancing a level.") << " " << (u.gender() == unit_race::MALE
		         ? _("Do you really want to dismiss him?")
		         : _("Do you really want to dismiss her?"));
	}

	if(!message.str().empty()) {
		const int res = gui2::show_message(_("Dismiss Unit"), message.str(), message::yes_no_buttons);

		if(res != gui2::retval::OK) {
			return;
		}
	}

	recall_list_.erase(recall_list_.begin() + index);

	// Remove the entry from the dialog list
	list.remove_row(index);
	list_item_clicked(window);

	// Remove the entry from the filter list
	filter_options_.erase(filter_options_.begin() + index);
	assert(filter_options_.size() == list.get_item_count());

	LOG_DP << "Dismissing a unit, side = " << u.side() << ", id = '" << u.id() << "'\n";
	LOG_DP << "That side's recall list:\n";
	dump_recall_list_to_console(team_.recall_list());

	// Find the unit in the recall list.
	unit_ptr dismissed_unit = team_.recall_list().find_if_matches_id(u.id());
	assert(dismissed_unit);

	// Record the dismissal, then delete the unit.
	synced_context::run_and_throw("disband", replay_helper::get_disband(dismissed_unit->id()));

	// Close the dialog if all units are dismissed
	if(list.get_item_count() == 0) {
		window.set_retval(retval::CANCEL);
	}
}
Exemplo n.º 30
0
void unit_recall::pre_show(window& window)
{
	label& title = find_widget<label>(&window, "title", true);
	title.set_label(title.get_label() + get_title_suffix(team_.side()));

	text_box* filter
			= find_widget<text_box>(&window, "filter_box", false, true);

	filter->set_text_changed_callback(
			std::bind(&unit_recall::filter_text_changed, this, _1, _2));

	listbox& list = find_widget<listbox>(&window, "recall_list", false);

	connect_signal_notify_modified(list, std::bind(&unit_recall::list_item_clicked, this, std::ref(window)));

	list.clear();

	window.keyboard_capture(filter);
	window.add_to_keyboard_chain(&list);

	connect_signal_mouse_left_click(
		find_widget<button>(&window, "rename", false),
		std::bind(&unit_recall::rename_unit, this, std::ref(window)));

	connect_signal_mouse_left_click(
		find_widget<button>(&window, "dismiss", false),
		std::bind(&unit_recall::dismiss_unit, this, std::ref(window)));

	connect_signal_mouse_left_click(
		find_widget<button>(&window, "show_help", false),
		std::bind(&unit_recall::show_help, this));

	for(const unit_const_ptr& unit : recall_list_) {
		std::map<std::string, string_map> row_data;
		string_map column;

		std::string mods = unit->image_mods();

		if(unit->can_recruit()) {
			mods += "~BLIT(" + unit::leader_crown() + ")";
		}

		for(const std::string& overlay : unit->overlays()) {
			mods += "~BLIT(" + overlay + ")";
		}

		column["use_markup"] = "true";

		column["label"] = unit->absolute_image() + mods;
		row_data.emplace("unit_image", column);

		column["label"] = unit->type_name();
		row_data.emplace("unit_type", column);

		column["label"] = format_cost_string(unit->recall_cost(), team_.recall_cost());
		row_data.emplace("unit_recall_cost", column);

		const std::string& name = !unit->name().empty() ? unit->name().str() : font::unicode_en_dash;
		column["label"] = name;
		row_data.emplace("unit_name", column);

		column["label"] = format_level_string(unit->level());
		row_data.emplace("unit_level", column);

		std::stringstream exp_str;
		exp_str << font::span_color(unit->xp_color());
		if(unit->can_advance()) {
			exp_str << unit->experience() << "/" << unit->max_experience();
		} else {
			exp_str << font::unicode_en_dash;
		}
		exp_str << "</span>";

		column["label"] = exp_str.str();
		row_data.emplace("unit_experience", column);

		// Since the table widgets use heavy formatting, we save a bare copy
		// of certain options to filter on.
		std::string filter_text = unit->type_name() + " " + name + " " + std::to_string(unit->level());

		std::string traits;
		for(const std::string& trait : unit->trait_names()) {
			traits += (traits.empty() ? "" : "\n") + trait;
			filter_text += " " + trait;
		}

		column["label"] = !traits.empty() ? traits : font::unicode_en_dash;
		row_data.emplace("unit_traits", column);

		list.add_row(row_data);
		filter_options_.push_back(filter_text);
	}

	list.register_translatable_sorting_option(0, [this](const int i) { return recall_list_[i]->type_name().str(); });
	list.register_translatable_sorting_option(1, [this](const int i) { return recall_list_[i]->name().str(); });
	list.register_sorting_option(2, [this](const int i) {
		const unit& u = *recall_list_[i];
		return std::make_tuple(u.level(), -static_cast<int>(u.experience_to_advance()));
	});
	list.register_sorting_option(3, [this](const int i) { return recall_list_[i]->experience(); });
	list.register_translatable_sorting_option(4, [this](const int i) {
		return !recall_list_[i]->trait_names().empty() ? recall_list_[i]->trait_names().front().str() : "";
	});

	list.set_active_sorting_option(sort_last.first >= 0 ? sort_last	: sort_default, true);

	list_item_clicked(window);
}