void draw_messages (int x, int y, text_message *msgs, int msgs_size, Uint8 filter, int msg_start, int offset_start, int cursor, int width, int height, float text_zoom, select_info* select) { float displayed_font_x_size = DEFAULT_FONT_X_LEN * text_zoom; float displayed_font_y_size = DEFAULT_FONT_Y_LEN * text_zoom; float selection_red = 255 / 255.0f; float selection_green = 162 / 255.0f; float selection_blue = 0; unsigned char cur_char; int i; int imsg, ichar; int cur_x, cur_y; int cursor_x = x-1, cursor_y = y-1; unsigned char ch; int cur_line = 0; int cur_col = 0; unsigned char last_color_char = 0; int in_select = 0; imsg = msg_start; ichar = offset_start; if (msgs[imsg].data == NULL || msgs[imsg].deleted) return; if (width < displayed_font_x_size || height < displayed_font_y_size) // no point in trying return; #ifndef MAP_EDITOR2 if (filter != FILTER_ALL) { // skip all messages of the wrong channel while (1) { if (skip_message(&msgs[imsg], filter)) { ichar = 0; if (++imsg >= msgs_size) imsg = 0; if (msgs[imsg].data == NULL || imsg == msg_start || msgs[imsg].deleted) // nothing to draw return; } else { break; } } if (msgs[imsg].data == NULL || msgs[imsg].deleted) return; } #endif //! MAP_EDITOR2 ch = msgs[imsg].data[ichar]; if (!is_color (ch)) { // search backwards for the last color for (i = ichar-1; i >= 0; i--) { ch = msgs[imsg].data[i]; if (is_color (ch)) { find_font_char (ch); last_color_char = ch; break; } } if (i < 0) { // no color character found, try the message color if (msgs[imsg].r >= 0) glColor3f (msgs[imsg].r, msgs[imsg].g, msgs[imsg].b); } } glEnable (GL_ALPHA_TEST); // enable alpha filtering, so we have some alpha key glAlphaFunc (GL_GREATER, 0.1f); #ifdef NEW_TEXTURES bind_texture(font_text); #else /* NEW_TEXTURES */ get_and_set_texture_id(font_text); #endif /* NEW_TEXTURES */ i = 0; cur_x = x; cur_y = y; glBegin (GL_QUADS); while (1) { if (i == cursor) { cursor_x = cur_x; cursor_y = cur_y; if (cursor_x - x > width - displayed_font_x_size) { cursor_x = x; cursor_y = cur_y + displayed_font_y_size; } } cur_char = msgs[imsg].data[ichar]; // watch for special characters if (cur_char == '\0') { // end of message if (++imsg >= msgs_size) { imsg = 0; } #ifndef MAP_EDITOR2 if (filter != FILTER_ALL) { // skip all messages of the wrong channel while (skip_message (&msgs[imsg], filter)) { if (++imsg >= msgs_size) imsg = 0; if (msgs[imsg].data == NULL || imsg == msg_start) break; } } #endif if (msgs[imsg].data == NULL || imsg == msg_start || msgs[imsg].deleted) break; rewrap_message (&msgs[imsg], text_zoom, width, NULL); ichar = 0; last_color_char = 0; } if (select != NULL && select->lines && select->lines[cur_line].msg == -1) { select->lines[cur_line].msg = imsg; select->lines[cur_line].chr = ichar; } if (cur_char == '\n' || cur_char == '\r' || cur_char == '\0') { // newline cur_y += displayed_font_y_size; if (cur_y - y > height - displayed_font_y_size) break; cur_x = x; if (cur_char != '\0') ichar++; i++; cur_line++; cur_col = 0; continue; } if (pos_selected(imsg, ichar, select)) { if (!in_select) { glColor3f (selection_red, selection_green, selection_blue); in_select = 1; } } else { if (in_select) { if (last_color_char) find_font_char (last_color_char); else if (msgs[imsg].r < 0) find_font_char (to_color_char (c_grey1)); else glColor3f (msgs[imsg].r, msgs[imsg].g, msgs[imsg].b); in_select = 0; } } if (is_color (cur_char)) { last_color_char = cur_char; if (in_select) { // don't draw color characters in a selection i++; ichar++; continue; } } cur_x += draw_char_scaled (cur_char, cur_x, cur_y, displayed_font_x_size, displayed_font_y_size); cur_col++; ichar++; i++; if (cur_x - x > width - displayed_font_x_size) { // ignore rest of this line, but keep track of // color characters while (1) { ch = msgs[imsg].data[ichar]; if (ch == '\0' || ch == '\n' || ch == '\r') break; if (is_color (ch)) last_color_char = ch; ichar++; i++; } } } if (cursor_x >= x && cursor_y >= y && cursor_y - y <= height - displayed_font_y_size) { draw_char_scaled ('_', cursor_x, cursor_y, displayed_font_x_size, displayed_font_y_size); } glEnd(); glDisable(GL_ALPHA_TEST); #ifdef OPENGL_TRACE CHECK_GL_ERRORS(); #endif //OPENGL_TRACE }
message::cli_res message::extract_opts(std::vector<cli_arg> xs, help_factory f, bool no_help) const { std::string helpstr; auto make_error = [&](std::string err) -> cli_res { return {*this, std::set<std::string>{}, std::move(helpstr), std::move(err)}; }; // add default help item if user did not specify any help option auto pred = [](const cli_arg& arg) -> bool { std::vector<std::string> s; split(s, arg.name, is_any_of(","), token_compress_on); if (s.empty()) return false; auto has_short_help = [](const std::string& opt) { return opt.find_first_of("h?") != std::string::npos; }; return s[0] == "help" || std::find_if(s.begin() + 1, s.end(), has_short_help) != s.end(); }; if (! no_help && std::none_of(xs.begin(), xs.end(), pred)) { xs.push_back(cli_arg{"help,h,?", "print this text"}); } std::map<std::string, cli_arg*> shorts; std::map<std::string, cli_arg*> longs; for (auto& cliarg : xs) { std::vector<std::string> s; split(s, cliarg.name, is_any_of(","), token_compress_on); if (s.empty()) { return make_error("invalid option name: " + cliarg.name); } longs["--" + s.front()] = &cliarg; for (size_t i = 1; i < s.size(); ++i) { if (s[i].size() != 1) { return make_error("invalid short option name: " + s[i]); } shorts["-" + s[i]] = &cliarg; } // generate helptext for this item auto& ht = cliarg.helptext; if (s.size() == 1) { ht += "--"; ht += s.front(); } else { ht += "-"; ht += s[1]; ht += " ["; for (size_t i = 2; i < s.size(); ++i) { ht += "-"; ht += s[i]; ht += ","; } ht += "--"; ht += s.front(); ht += "]"; } if (cliarg.fun) { ht += " arg"; } } if (f) { helpstr = f(xs); } else { auto op = [](size_t tmp, const cli_arg& arg) { return std::max(tmp, arg.helptext.size()); }; auto name_width = std::accumulate(xs.begin(), xs.end(), size_t{0}, op); std::ostringstream oss; oss << std::left; oss << "Allowed options:" << std::endl; for (auto& ca : xs) { oss << " "; oss.width(static_cast<std::streamsize>(name_width)); oss << ca.helptext << " : " << ca.text << std::endl; } helpstr = oss.str(); } std::set<std::string> opts; auto insert_opt_name = [&](const cli_arg* ptr) { auto separator = ptr->name.find(','); if (separator == std::string::npos) { opts.insert(ptr->name); } else { opts.insert(ptr->name.substr(0, separator)); } }; // we can't `return make_error(...)` from inside `extract`, hence we // store any occurred error in a temporary variable returned at the end std::string error; auto res = extract({ [&](const std::string& arg) -> optional<skip_message_t> { if (arg.empty() || arg.front() != '-') { return skip_message(); } auto i = shorts.find(arg.substr(0, 2)); if (i != shorts.end()) { if (i->second->fun) { // this short opt expects two arguments if (arg.size() > 2) { // this short opt comes with a value (no space), e.g., -x2 if (! i->second->fun(arg.substr(2))) { error = "invalid value for " + i->second->name + ": " + arg; return skip_message(); } insert_opt_name(i->second); return none; } // no value given, try two-argument form below return skip_message(); } insert_opt_name(i->second); return none; } auto eq_pos = arg.find('='); auto j = longs.find(arg.substr(0, eq_pos)); if (j != longs.end()) { if (j->second->fun) { if (eq_pos == std::string::npos) { error = "missing argument to " + arg; return skip_message(); } if (! j->second->fun(arg.substr(eq_pos + 1))) { error = "invalid value for " + j->second->name + ": " + arg; return skip_message(); } insert_opt_name(j->second); return none; } insert_opt_name(j->second); return none; } error = "unknown command line option: " + arg; return skip_message(); }, [&](const std::string& arg1, const std::string& arg2) -> optional<skip_message_t> { if (arg1.size() < 2 || arg1[0] != '-' || arg1[1] == '-') { return skip_message(); } auto i = shorts.find(arg1.substr(0, 2)); if (i != shorts.end()) { if (! i->second->fun || arg1.size() > 2) { // this short opt either expects no argument or comes with a value // (no space), e.g., -x2, so we have to parse it with the // one-argument form above return skip_message(); } CAF_ASSERT(arg1.size() == 2); if (! i->second->fun(arg2)) { error = "invalid value for option " + i->second->name + ": " + arg2; return skip_message(); } insert_opt_name(i->second); return none; } error = "unknown command line option: " + arg1; return skip_message(); } }); return {res, std::move(opts), std::move(helpstr), std::move(error)}; }