41 #include <imgui_internal.h>
42 #include <rapidjson/document.h>
46 #include <OgreFileSystemLayer.h>
49 # include <curl/curl.h>
50 # include <curl/easy.h>
53 #if defined(_MSC_VER) && defined(GetObject) // This MS Windows macro from <wingdi.h> (Windows Kit 8.1) clashes with RapidJSON
62 static size_t CurlWriteFunc(
void *ptr,
size_t size,
size_t nmemb, std::string* data)
64 data->append((
char*)ptr, size * nmemb);
77 if (filesize_B <= 0.0)
84 double perc = (downloaded_B / filesize_B) * 100;
89 m.
payload =
reinterpret_cast<void*
>(
new int(perc));
90 m.
description =
fmt::format(
"{} {}\n{}: {:.2f}{}\n{}: {:.2f}{}",
"Downloading", context->
filename,
"File size", filesize_B/(1024 * 1024),
"MB",
"Downloaded", downloaded_B/(1024 * 1024),
"MB");
102 Ogre::DataStream* ogre_datastream =
static_cast<Ogre::DataStream*
>(userdata);
103 if (data_length > 0 && ogre_datastream->isWriteable())
105 return ogre_datastream->write((
const void*)data_ptr, data_length);
115 std::string repolist_url = portal_url +
"/resource-categories";
116 std::string response_payload;
117 std::string response_header;
118 long response_code = 0;
121 CURL *curl = curl_easy_init();
122 curl_easy_setopt(curl, CURLOPT_URL, repolist_url.c_str());
123 curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
125 curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
127 curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING,
"gzip");
128 curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent.c_str());
129 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
CurlWriteFunc);
130 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_payload);
131 curl_easy_setopt(curl, CURLOPT_HEADERDATA, &response_header);
133 CURLcode curl_result = curl_easy_perform(curl);
134 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
136 curl_easy_cleanup(curl);
139 std::vector<GUI::ResourceCategories> cat;
140 if (curl_result != CURLE_OK || response_code != 200)
142 Ogre::LogManager::getSingleton().stream()
143 <<
"[RoR|Repository] Failed to retrieve category list;"
144 <<
" Error: '" << curl_easy_strerror(curl_result) <<
"'; HTTP status code: " << response_code;
148 rapidjson::Document j_data_doc;
149 j_data_doc.Parse(response_payload.c_str());
151 rapidjson::Value& j_resp_body = j_data_doc[
"categories"];
152 size_t num_rows = j_resp_body.GetArray().Size();
153 cat.resize(num_rows);
154 for (
size_t i = 0; i < num_rows; i++)
156 rapidjson::Value& j_row = j_resp_body[
static_cast<rapidjson::SizeType
>(i)];
158 cat[i].title = j_row[
"title"].GetString();
159 cat[i].resource_category_id = j_row[
"resource_category_id"].GetInt();
160 cat[i].resource_count = j_row[
"resource_count"].GetInt();
161 cat[i].description = j_row[
"description"].GetString();
162 cat[i].display_order = j_row[
"display_order"].GetInt();
170 std::string repolist_url = portal_url +
"/resources";
171 std::string response_payload;
172 std::string response_header;
173 long response_code = 0;
176 CURL *curl = curl_easy_init();
177 curl_easy_setopt(curl, CURLOPT_URL, repolist_url.c_str());
178 curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
180 curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
182 curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING,
"gzip");
183 curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent.c_str());
184 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
CurlWriteFunc);
185 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_payload);
186 curl_easy_setopt(curl, CURLOPT_HEADERDATA, &response_header);
188 CURLcode curl_result = curl_easy_perform(curl);
189 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
191 curl_easy_cleanup(curl);
194 if (curl_result != CURLE_OK || response_code != 200)
196 Ogre::LogManager::getSingleton().stream()
197 <<
"[RoR|Repository] Failed to retrieve repolist;"
198 <<
" Error: '"<< curl_easy_strerror(curl_result) <<
"'; HTTP status code: " << response_code;
201 failinfo->
title =
_LC(
"RepositorySelector",
"Could not connect to server. Please check your connection.");
210 rapidjson::Document j_data_doc;
211 j_data_doc.Parse(response_payload.c_str());
212 if (j_data_doc.HasParseError() || !j_data_doc.IsObject())
214 Ogre::LogManager::getSingleton().stream()
215 <<
"[RoR|Repository] Error parsing repolist JSON, code: " << j_data_doc.GetParseError();
223 std::vector<GUI::ResourceItem> resc;
224 rapidjson::Value& j_resp_body = j_data_doc[
"resources"];
225 size_t num_rows = j_resp_body.GetArray().Size();
226 resc.resize(num_rows);
228 for (
size_t i = 0; i < num_rows; i++)
230 rapidjson::Value& j_row = j_resp_body[
static_cast<rapidjson::SizeType
>(i)];
232 resc[i].title = j_row[
"title"].GetString();
233 resc[i].tag_line = j_row[
"tag_line"].GetString();
234 resc[i].resource_id = j_row[
"resource_id"].GetInt();
235 resc[i].download_count = j_row[
"download_count"].GetInt();
236 resc[i].last_update = j_row[
"last_update"].GetInt();
237 resc[i].resource_category_id = j_row[
"resource_category_id"].GetInt();
238 resc[i].icon_url = j_row[
"icon_url"].GetString();
239 resc[i].rating_avg = j_row[
"rating_avg"].GetFloat();
240 resc[i].rating_count = j_row[
"rating_count"].GetInt();
241 resc[i].version = j_row[
"version"].GetString();
242 resc[i].authors = j_row[
"custom_fields"][
"authors"].GetString();
243 resc[i].view_url = j_row[
"view_url"].GetString();
244 resc[i].resource_date = j_row[
"resource_date"].GetInt();
245 resc[i].view_count = j_row[
"view_count"].GetInt();
246 resc[i].preview_tex = Ogre::TexturePtr();
249 cdata_ptr->
items = resc;
258 std::string response_payload;
259 std::string resource_url = portal_url +
"/resources/" + std::to_string(resource_id);
261 long response_code = 0;
263 CURL *curl = curl_easy_init();
264 curl_easy_setopt(curl, CURLOPT_URL, resource_url.c_str());
265 curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
267 curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
269 curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING,
"gzip");
270 curl_easy_setopt(curl, CURLOPT_USERAGENT, user_agent.c_str());
271 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
CurlWriteFunc);
272 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_payload);
274 CURLcode curl_result = curl_easy_perform(curl);
275 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
277 curl_easy_cleanup(curl);
280 if (curl_result != CURLE_OK || response_code != 200)
282 Ogre::LogManager::getSingleton().stream()
283 <<
"[RoR|Repository] Failed to retrieve resource;"
284 <<
" Error: '" << curl_easy_strerror(curl_result) <<
"'; HTTP status code: " << response_code;
291 rapidjson::Document j_data_doc;
292 j_data_doc.Parse(response_payload.c_str());
294 std::vector<GUI::ResourceFiles> resc;
295 rapidjson::Value& j_resp_body = j_data_doc[
"resource"][
"current_files"];
296 size_t num_rows = j_resp_body.GetArray().Size();
297 resc.resize(num_rows);
299 for (
size_t i = 0; i < num_rows; i++)
301 rapidjson::Value& j_row = j_resp_body[
static_cast<rapidjson::SizeType
>(i)];
303 resc[i].id = j_row[
"id"].GetInt();
304 resc[i].filename = j_row[
"filename"].GetString();
305 resc[i].size = j_row[
"size"].GetInt();
308 cdata_ptr->
files = resc;
318 m.
payload =
reinterpret_cast<void*
>(
new int(perc));
322 std::string url =
"https://forum.rigsofrods.org/resources/" + std::to_string(resource_id) +
"/download?file=" + std::to_string(
id);
327 progress_context.
filename = filename;
328 long response_code = 0;
330 CURL *curl = curl_easy_init();
334 Ogre::DataStreamPtr datastream = Ogre::ResourceGroupManager::getSingleton().createResource(
file,
RGN_REPO);
336 curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
337 curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
339 curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
342 curl_easy_setopt(curl, CURLOPT_WRITEDATA, datastream.get());
343 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, NULL);
344 curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, &progress_context);
347 CURLcode curl_result = curl_easy_perform(curl);
348 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code);
350 if (curl_result != CURLE_OK || response_code != 200)
352 Ogre::LogManager::getSingleton().stream()
353 <<
"[RoR|Repository] Failed to download resource;"
354 <<
" Error: '" << curl_easy_strerror(curl_result) <<
"'; HTTP status code: " << response_code;
359 catch (Ogre::Exception& oex)
363 fmt::format(
"Repository UI: cannot download file '{}' - {}",
364 url, oex.getFullDescription()));
366 curl_easy_cleanup(curl);
372 #endif // defined(USE_CURL)
376 Ogre::WorkQueue* wq = Ogre::Root::getSingleton().getWorkQueue();
391 ImGui::SetNextWindowSize(ImVec2((ImGui::GetIO().DisplaySize.x / 1.4), (ImGui::GetIO().DisplaySize.y / 1.2)), ImGuiCond_FirstUseEver);
392 ImGui::SetNextWindowPosCenter(ImGuiCond_Appearing);
393 ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse;
394 bool keep_open =
true;
395 Ogre::TexturePtr tex1 =
FetchIcon(
"arrow_rotate_anticlockwise.png");
396 Ogre::TexturePtr tex2 =
FetchIcon(
"accept.png");
397 Ogre::TexturePtr tex3 =
FetchIcon(
"star.png");
398 Ogre::TexturePtr tex4 =
FetchIcon(
"arrow_left.png");
400 ImGui::Begin(
_LC(
"RepositorySelector",
"Rigs of Rods Repository"), &keep_open, window_flags);
402 if (
m_resource_view && ImGui::ImageButton(
reinterpret_cast<ImTextureID
>(tex4->getHandle()), ImVec2(16, 16)))
406 else if (!
m_resource_view && ImGui::ImageButton(
reinterpret_cast<ImTextureID
>(tex1->getHandle()), ImVec2(16, 16)))
417 ImGui::PushItemFlag(ImGuiItemFlags_Disabled,
true);
418 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
422 ImGui::SetNextItemWidth(ImGui::GetWindowSize().
x
426 - 6*ImGui::GetStyle().ItemSpacing.x
427 - 2*ImGui::GetStyle().WindowPadding.x);
474 ImGui::SetItemDefaultFocus();
482 ImGui::SetNextItemWidth(170);
483 float search_pos = ImGui::GetCursorPosX();
488 ImGui::SetNextItemWidth(80);
490 if (ImGui::BeginCombo(
"##repo-selector-sort",
_LC(
"RepositorySelector",
"Sort")))
492 if (ImGui::Selectable(
_LC(
"RepositorySelector",
"Last Update"),
m_current_sort ==
"Last Update"))
497 if (ImGui::Selectable(
_LC(
"RepositorySelector",
"Date Added"),
m_current_sort ==
"Date Added"))
502 if (ImGui::Selectable(
_LC(
"RepositorySelector",
"Title"),
m_current_sort ==
"Title"))
507 if (ImGui::Selectable(
_LC(
"RepositorySelector",
"Downloads"),
m_current_sort ==
"Downloads"))
512 if (ImGui::Selectable(
_LC(
"RepositorySelector",
"Rating"),
m_current_sort ==
"Rating"))
517 if (ImGui::Selectable(
_LC(
"RepositorySelector",
"Rating Count"),
m_current_sort ==
"Rating Count"))
527 ImGui::SetNextItemWidth(80);
529 if (ImGui::BeginCombo(
"##repo-selector-view",
_LC(
"RepositorySelector",
"View")))
531 if (ImGui::Selectable(
_LC(
"RepositorySelector",
"List"),
m_view_mode ==
"List"))
535 if (ImGui::Selectable(
_LC(
"RepositorySelector",
"Compact"),
m_view_mode ==
"Compact"))
539 if (ImGui::Selectable(
_LC(
"RepositorySelector",
"Basic"),
m_view_mode ==
"Basic"))
550 ImGui::SetCursorPosX(search_pos + ImGui::GetStyle().ItemSpacing.x);
551 ImGui::TextDisabled(
"%s",
_LC(
"RepositorySelector",
"Search Title, Author"));
556 ImGui::PopItemFlag();
557 ImGui::PopStyleVar();
560 const float table_height = ImGui::GetWindowHeight()
561 - ((2.f * ImGui::GetStyle().WindowPadding.y) + (3.f * ImGui::GetItemsLineHeightWithSpacing())
562 - ImGui::GetStyle().ItemSpacing.y);
567 ImGui::BeginChild(
"resource-view-scrolling", ImVec2(0.f, table_height),
false);
569 float text_pos = 140.f;
571 ImGui::Text(
"%s",
_LC(
"RepositorySelector",
"Details:"));
574 ImGui::TextDisabled(
"%s",
_LC(
"RepositorySelector",
"Title:"));
576 ImGui::SetCursorPosX(text_pos);
580 ImGui::TextDisabled(
"%s",
_LC(
"RepositorySelector",
"Resource ID:"));
582 ImGui::SetCursorPosX(text_pos);
586 ImGui::TextDisabled(
"%s",
_LC(
"RepositorySelector",
"Category:"));
588 ImGui::SetCursorPosX(text_pos);
600 ImGui::TextDisabled(
"%s",
_LC(
"RepositorySelector",
"Description:"));
602 ImGui::SetCursorPosX(text_pos);
606 ImGui::TextDisabled(
"%s",
_LC(
"RepositorySelector",
"Downloads:"));
608 ImGui::SetCursorPosX(text_pos);
612 ImGui::TextDisabled(
"%s",
_LC(
"RepositorySelector",
"View Count:"));
614 ImGui::SetCursorPosX(text_pos);
618 ImGui::TextDisabled(
"%s",
_LC(
"RepositorySelector",
"Rating:"));
620 ImGui::SetCursorPosX(text_pos);
624 ImGui::TextDisabled(
"%s",
_LC(
"RepositorySelector",
"Rating Count:"));
626 ImGui::SetCursorPosX(text_pos);
630 ImGui::TextDisabled(
_LC(
"RepositorySelector",
"Date Added:"));
632 ImGui::SetCursorPosX(text_pos);
637 ImGui::TextDisabled(
_LC(
"RepositorySelector",
"Last Update:"));
639 ImGui::SetCursorPosX(text_pos);
644 ImGui::TextDisabled(
"%s",
_LC(
"RepositorySelector",
"Version:"));
646 ImGui::SetCursorPosX(text_pos);
650 ImGui::TextDisabled(
"%s",
_LC(
"RepositorySelector",
"Authors:"));
652 ImGui::SetCursorPosX(text_pos);
656 ImGui::TextDisabled(
"%s",
_LC(
"RepositorySelector",
"View URL:"));
658 ImGui::SetCursorPosX(text_pos);
662 ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 20);
663 ImGui::Text(
"%s",
_LC(
"RepositorySelector",
"Files:"));
669 float spinner_size = 7.f;
689 ImGui::AlignTextToFramePadding();
690 float pos_y = ImGui::GetCursorPosY();
692 ImGui::TextDisabled(
"%s",
_LC(
"RepositorySelector",
"Filename:"));
694 ImGui::SetCursorPosX(text_pos);
711 ImGui::BeginTooltip();
713 time_t c = (
const time_t)file_time;
714 ImGui::TextDisabled(
"%s %s",
"Installed on", asctime(gmtime (&c)));
723 ImGui::TextDisabled(
"(%d %s)", size,
"KB");
729 ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 3.5f);
730 ImGui::Image(
reinterpret_cast<ImTextureID
>(tex2->getHandle()), ImVec2(16, 16));
735 ImGui::SetCursorPosX(ImGui::GetWindowSize().
x - 220);
736 ImGui::SetCursorPosY(pos_y);
738 std::string btn_label;
752 if (ImGui::Button(btn_label.c_str(), ImVec2(100, 0)))
759 if (
FileExists(
file) && ImGui::Button(
_LC(
"RepositorySelector",
"Remove"), ImVec2(100, 0)))
761 Ogre::ArchiveManager::getSingleton().unload(
file);
762 Ogre::FileSystemLayer::removeFile(
file);
767 ImGui::PushItemFlag(ImGuiItemFlags_Disabled,
true);
768 ImGui::PushStyleVar(ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f);
769 ImGui::Button(
_LC(
"RepositorySelector",
"Remove"), ImVec2(100, 0));
770 ImGui::PopItemFlag();
771 ImGui::PopStyleVar();
782 float col0_width = 0.40f * ImGui::GetWindowContentRegionWidth();
783 float col1_width = 0.15f * ImGui::GetWindowContentRegionWidth();
784 float col2_width = 0.20f * ImGui::GetWindowContentRegionWidth();
785 float col3_width = 0.10f * ImGui::GetWindowContentRegionWidth();
789 ImGui::Columns(5,
"repo-selector-columns-basic-headers",
false);
790 ImGui::SetColumnWidth(0, col0_width + ImGui::GetStyle().ItemSpacing.x);
791 ImGui::SetColumnWidth(1, col1_width);
792 ImGui::SetColumnWidth(2, col2_width);
793 ImGui::SetColumnWidth(3, col3_width);
795 ImGui::TextDisabled(
"%s",
_LC(
"RepositorySelector",
"Title"));
797 ImGui::TextDisabled(
"%s",
_LC(
"RepositorySelector",
"Version"));
799 ImGui::TextDisabled(
"%s",
_LC(
"RepositorySelector",
"Last Update"));
801 ImGui::TextDisabled(
"%s",
_LC(
"RepositorySelector",
"Downloads"));
803 ImGui::TextDisabled(
"%s",
_LC(
"RepositorySelector",
"Rating"));
808 ImGui::BeginChild(
"scrolling", ImVec2(0.f, table_height),
false);
812 ImGui::Columns(2,
"repo-selector-columns");
813 ImGui::SetColumnWidth(0, 100.f);
818 ImGui::Columns(5,
"repo-selector-columns-basic");
819 ImGui::SetColumnWidth(0, col0_width);
820 ImGui::SetColumnWidth(1, col1_width);
821 ImGui::SetColumnWidth(2, col2_width);
822 ImGui::SetColumnWidth(3, col3_width);
827 int num_drawn_items = 0;
845 for(
auto& c : author)
850 for(
auto& c : search)
854 if (title.find(search) == std::string::npos && author.find(search) == std::string::npos)
864 ImGui::SetCursorPosX(ImGui::GetCursorPosX() - ImGui::GetStyle().ItemSpacing.x);
867 float width = (ImGui::GetColumnWidth() + 90);
871 ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.17f, 0.17f, 0.17f, 0.90f));
872 ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImGui::GetStyle().Colors[ImGuiCol_Header]);
873 ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.22f, 0.22f, 0.22f, 0.90f));
876 float orig_cursor_y = ImGui::GetCursorPosY();
877 std::string item_id =
"##" + std::to_string(i);
886 ImGui::SetCursorPosY(orig_cursor_y);
887 ImGui::PopStyleColor(3);
892 ImGui::TextDisabled(
"%s",
m_data.
items[i].version.c_str());
895 for (
int i = 1; i <= 5; i++)
898 ImGui::SetCursorPosX(ImGui::GetColumnWidth() + 16 * i);
899 ImGui::Image(
reinterpret_cast<ImTextureID
>(tex3->getHandle()), ImVec2(16, 16), ImVec2(0.f, 0.f), ImVec2(1.f, 1.f), ImVec4(1.f, 1.f, 1.f, 0.2f));
903 for (
int i = 1; i <= rating; i++)
906 ImGui::SetCursorPosX(ImGui::GetColumnWidth() + 16 * i);
907 ImGui::Image(
reinterpret_cast<ImTextureID
>(tex3->getHandle()), ImVec2(16, 16));
911 ImGui::TextDisabled(
"%s:",
_LC(
"RepositorySelector",
"Authors"));
913 ImGui::SetCursorPosX(width);
917 std::string rc = std::to_string(
m_data.
items[i].rating_count) +
" ratings";
918 ImGui::SetCursorPosX(ImGui::GetColumnWidth() - (ImGui::CalcTextSize(rc.c_str()).x / 2) + 16 * 3.5);
919 ImGui::TextDisabled(
"%s", rc.c_str());
921 ImGui::TextDisabled(
"%s:",
_LC(
"RepositorySelector",
"Last Update"));
923 ImGui::SetCursorPosX(width);
924 time_t rawtime = (
const time_t)
m_data.
items[i].last_update;
927 ImGui::TextDisabled(
"%s:",
_LC(
"RepositorySelector",
"Downloads"));
929 ImGui::SetCursorPosX(width);
932 ImGui::TextDisabled(
"%s:",
_LC(
"RepositorySelector",
"Description"));
934 ImGui::SetCursorPosX(width);
943 float orig_cursor_x = ImGui::GetCursorPos().x;
946 float box_width = (ImGui::GetIO().DisplaySize.x / 1.4) / 3;
947 if (ImGui::GetIO().DisplaySize.x <= 1280)
949 box_width = (ImGui::GetIO().DisplaySize.x / 1.4) / 2;
953 if (orig_cursor_x > ImGui::GetWindowContentRegionMax().x - (box_width * 0.5))
956 if (num_drawn_items != 0)
964 ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.70f, 0.70f, 0.70f, 0.90f));
965 ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImGui::GetStyle().Colors[ImGuiCol_Header]);
966 ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.90f, 0.90f, 0.90f, 0.90f));
969 float orig_cursor_y = ImGui::GetCursorPosY();
970 std::string item_id =
"##" + std::to_string(i);
980 ImVec2 p_min = ImGui::GetItemRectMin();
981 ImVec2 p_max = ImGui::GetItemRectMax();
982 ImGui::GetWindowDrawList()->AddRectFilled(p_min, p_max, ImColor(ImVec4(0.15f, 0.15f, 0.15f, 0.90f)));
984 ImGui::SetCursorPosY(orig_cursor_y);
985 ImGui::PopStyleColor(3);
992 for (
int i = 1; i <= 5; i++)
994 pos_y = ImGui::GetCursorPosY();
995 ImGui::Image(
reinterpret_cast<ImTextureID
>(tex3->getHandle()), ImVec2(11, 11), ImVec2(0.f, 0.f), ImVec2(1.f, 1.f), ImVec4(1.f, 1.f, 1.f, 0.2f));
996 if ( i < 5) { ImGui::SameLine(); }
1002 for (
int i = 1; i <= rating; i++)
1004 ImGui::SetCursorPosY(pos_y);
1005 ImGui::Image(
reinterpret_cast<ImTextureID
>(tex3->getHandle()), ImVec2(11, 11));
1006 if (i < rating) { ImGui::SameLine(); }
1011 ImGui::SetCursorPosX(ImGui::GetCursorPos().
x + 86);
1012 ImGui::SetCursorPosY(ImGui::GetCursorPos().
y - 100);
1016 if (ImGui::CalcTextSize(
tl.c_str()).x > box_width / 12)
1018 tl.resize(box_width / 12);
1023 ImGui::Text(
"%s",
tl.c_str());
1025 ImGui::SetCursorPosX(ImGui::GetCursorPos().
x + 86);
1028 ImGui::SetCursorPosX(ImGui::GetCursorPos().
x + 86);
1029 time_t rawtime = (
const time_t)
m_data.
items[i].last_update;
1032 ImGui::SetCursorPosX(ImGui::GetCursorPos().
x + 86);
1036 ImGui::SetCursorPosX(ImGui::GetCursorPos().
x + box_width);
1037 ImGui::SetCursorPosY(ImGui::GetCursorPos().
y + (1.5f * ImGui::GetStyle().WindowPadding.y));
1045 ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.18f, 0.18f, 0.18f, 0.90f));
1046 ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImGui::GetStyle().Colors[ImGuiCol_Header]);
1047 ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.22f, 0.22f, 0.22f, 0.90f));
1050 std::string item_id =
"##" + std::to_string(i);
1059 ImGui::PopStyleColor(3);
1063 ImGui::SetCursorPosX(ImGui::GetCursorPosX() - 2*ImGui::GetStyle().ItemSpacing.x);
1066 ImGui::NextColumn();
1070 ImGui::NextColumn();
1072 time_t rawtime = (
const time_t)
m_data.
items[i].last_update;
1075 ImGui::NextColumn();
1079 ImGui::NextColumn();
1081 float pos_x = ImGui::GetCursorPosX();
1084 for (
int i = 1; i <= 5; i++)
1086 ImGui::Image(
reinterpret_cast<ImTextureID
>(tex3->getHandle()), ImVec2(16, 16), ImVec2(0.f, 0.f), ImVec2(1.f, 1.f), ImVec4(1.f, 1.f, 1.f, 0.2f));
1087 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 16 * i);
1090 ImGui::SetCursorPosX(pos_x);
1093 for (
int i = 1; i <= rating; i++)
1095 ImGui::Image(
reinterpret_cast<ImTextureID
>(tex3->getHandle()), ImVec2(16, 16));
1096 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 16 * i);
1100 ImGui::NextColumn();
1114 float spinner_size = 27.f;
1115 ImGui::SetCursorPosX((ImGui::GetWindowSize().
x / 2.f) - spinner_size);
1116 ImGui::SetCursorPosY((ImGui::GetWindowSize().
y / 2.f) - spinner_size);
1122 const ImVec2 label_size = ImGui::CalcTextSize(
m_repolist_msg.c_str());
1123 float y = (ImGui::GetWindowSize().y / 2.f) - (ImGui::GetTextLineHeight() / 2.f);
1124 ImGui::SetCursorPosX((ImGui::GetWindowSize().
x / 2.f) - (label_size.x / 2.f));
1125 ImGui::SetCursorPosY(
y);
1127 y += ImGui::GetTextLineHeightWithSpacing();
1132 ImGui::SetCursorPosX((ImGui::GetWindowSize().
x / 2.f) - (detail_size.x / 2.f));
1133 ImGui::SetCursorPosY(
y);
1135 y += ImGui::GetTextLineHeight();
1141 ImGui::SetCursorPosX((ImGui::GetWindowSize().
x / 2.f) - (detail_size.x / 2.f));
1142 ImGui::SetCursorPosY(
y);
1156 #if defined(USE_CURL)
1161 std::packaged_task<void(std::string)> task(
GetResources);
1163 #endif // defined(USE_CURL)
1177 m_repolist_msg =
_LC(
"RepositorySelector",
"Sorry, the repository isn't available. Try again later.");
1203 #if defined(USE_CURL)
1208 #endif // defined(USE_CURL)
1213 #if defined(USE_CURL)
1217 std::thread(std::move(task), resource_id, filename,
id).detach();
1218 #endif // defined(USE_CURL)
1271 image_size = ImVec2(ImGui::GetColumnWidth() - ImGui::GetStyle().ItemSpacing.x, 96);
1275 image_size = ImVec2(76, 86);
1280 if (
m_data.
items[resource_item_idx].icon_url ==
"")
1288 if (ImGui::IsRectVisible(image_size)
1289 && !
m_data.
items[resource_item_idx].thumbnail_dl_queued)
1292 Ogre::Root::getSingleton().getWorkQueue()->addRequest(
m_ogre_workqueue_channel, 1234, Ogre::Any(resource_item_idx));
1293 m_data.
items[resource_item_idx].thumbnail_dl_queued =
true;
1302 reinterpret_cast<ImTextureID
>(
m_data.
items[resource_item_idx].preview_tex->getHandle()),
1310 float spinner_size = ImGui::GetColumnWidth() / 4;
1311 ImGui::SetCursorPosX(((ImGui::GetColumnWidth() - ImGui::GetStyle().ItemSpacing.x) / 2.f) - spinner_size);
1312 ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5*ImGui::GetStyle().ItemSpacing.y);
1317 float spinner_size = 25;
1318 ImGui::SetCursorPosX(ImGui::GetCursorPosX() + 2*ImGui::GetStyle().ItemSpacing.x);
1319 ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 20);
1321 ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 76 - (35 + spinner_size));
1332 int item_idx = Ogre::any_cast<int>(req->getData());
1333 std::string filename = std::to_string(
m_data.
items[item_idx].resource_id) +
".png";
1335 long response_code = 0;
1339 return OGRE_NEW Ogre::WorkQueue::Response(req,
false, Ogre::Any(item_idx));
1346 Ogre::DataStreamPtr datastream = Ogre::ResourceGroupManager::getSingleton().createResource(filename,
RGN_REPO);
1349 curl_easy_setopt(
curl_th, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
1351 curl_easy_setopt(
curl_th, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA);
1354 curl_easy_setopt(
curl_th, CURLOPT_WRITEDATA, datastream.get());
1355 CURLcode curl_result = curl_easy_perform(
curl_th);
1357 if (curl_result != CURLE_OK || response_code != 200)
1359 Ogre::LogManager::getSingleton().stream()
1360 <<
"[RoR|Repository] Failed to download thumbnail;"
1361 <<
" Error: '" << curl_easy_strerror(curl_result) <<
"'; HTTP status code: " << response_code;
1363 return OGRE_NEW Ogre::WorkQueue::Response(req,
false, Ogre::Any(item_idx));
1367 return OGRE_NEW Ogre::WorkQueue::Response(req,
true, Ogre::Any(item_idx));
1370 catch (Ogre::Exception& oex)
1374 fmt::format(
"Repository UI: cannot download thumbnail '{}' - {}",
1375 m_data.
items[item_idx].icon_url, oex.getFullDescription()));
1377 return OGRE_NEW Ogre::WorkQueue::Response(req,
false, Ogre::Any(item_idx));
1388 int item_idx = Ogre::any_cast<int>(req->getData());
1389 std::string filename = std::to_string(
m_data.
items[item_idx].resource_id) +
".png";