OsuUISongBrowserSongButton.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  1. //================ Copyright (c) 2016, PG, All rights reserved. =================//
  2. //
  3. // Purpose: beatmap + diff button
  4. //
  5. // $NoKeywords: $osusbsb
  6. //===============================================================================//
  7. #include "OsuUISongBrowserSongButton.h"
  8. #include "ConVar.h"
  9. #include "Engine.h"
  10. #include "Mouse.h"
  11. #include "Osu.h"
  12. #include "OsuBackgroundImageHandler.h"
  13. #include "OsuDatabaseBeatmap.h"
  14. #include "OsuNotificationOverlay.h"
  15. #include "OsuSkin.h"
  16. #include "OsuSkinImage.h"
  17. #include "OsuSongBrowser.h"
  18. #include "OsuUIContextMenu.h"
  19. #include "OsuUISongBrowserCollectionButton.h"
  20. #include "OsuUISongBrowserScoreButton.h"
  21. #include "OsuUISongBrowserSongDifficultyButton.h"
  22. #include "ResourceManager.h"
  23. ConVar osu_draw_songbrowser_thumbnails("osu_draw_songbrowser_thumbnails", true, FCVAR_NONE);
  24. ConVar osu_songbrowser_thumbnail_delay("osu_songbrowser_thumbnail_delay", 0.1f, FCVAR_NONE);
  25. ConVar osu_songbrowser_thumbnail_fade_in_duration("osu_songbrowser_thumbnail_fade_in_duration", 0.1f, FCVAR_NONE);
  26. float OsuUISongBrowserSongButton::thumbnailYRatio = 1.333333f;
  27. OsuUISongBrowserSongButton::OsuUISongBrowserSongButton(Osu *osu, OsuSongBrowser *songBrowser, CBaseUIScrollView *view,
  28. OsuUIContextMenu *contextMenu, float xPos, float yPos,
  29. float xSize, float ySize, UString name,
  30. OsuDatabaseBeatmap *databaseBeatmap)
  31. : OsuUISongBrowserButton(osu, songBrowser, view, contextMenu, xPos, yPos, xSize, ySize, name) {
  32. m_databaseBeatmap = databaseBeatmap;
  33. m_representativeDatabaseBeatmap = NULL;
  34. m_grade = Score::Grade::D;
  35. m_bHasGrade = false;
  36. // settings
  37. setHideIfSelected(true);
  38. // labels
  39. m_fThumbnailFadeInTime = 0.0f;
  40. m_fTextOffset = 0.0f;
  41. m_fGradeOffset = 0.0f;
  42. m_fTextSpacingScale = 0.075f;
  43. m_fTextMarginScale = 0.075f;
  44. m_fTitleScale = 0.22f;
  45. m_fSubTitleScale = 0.14f;
  46. m_fGradeScale = 0.45f;
  47. // build children
  48. if(m_databaseBeatmap != NULL) {
  49. const std::vector<OsuDatabaseBeatmap *> &difficulties = m_databaseBeatmap->getDifficulties();
  50. // and add them
  51. for(int i = 0; i < difficulties.size(); i++) {
  52. OsuUISongBrowserSongButton *songButton = new OsuUISongBrowserSongDifficultyButton(
  53. m_osu, m_songBrowser, m_view, m_contextMenu, 0, 0, 0, 0, "", difficulties[i], this);
  54. m_children.push_back(songButton);
  55. }
  56. }
  57. updateRepresentativeDatabaseBeatmap();
  58. updateLayoutEx();
  59. }
  60. OsuUISongBrowserSongButton::~OsuUISongBrowserSongButton() {
  61. for(int i = 0; i < m_children.size(); i++) {
  62. delete m_children[i];
  63. }
  64. }
  65. void OsuUISongBrowserSongButton::draw(Graphics *g) {
  66. OsuUISongBrowserButton::draw(g);
  67. if(!m_bVisible) return;
  68. // draw background image
  69. if(m_representativeDatabaseBeatmap != NULL)
  70. drawBeatmapBackgroundThumbnail(
  71. g, m_osu->getBackgroundImageHandler()->getLoadBackgroundImage(m_representativeDatabaseBeatmap));
  72. drawTitle(g);
  73. drawSubTitle(g);
  74. }
  75. void OsuUISongBrowserSongButton::mouse_update(bool *propagate_clicks) {
  76. if(!m_bVisible) return;
  77. OsuUISongBrowserButton::mouse_update(propagate_clicks);
  78. // HACKHACK: calling these two every frame is a bit insane, but too lazy to write delta detection logic atm. (UI
  79. // desync is not a problem since parent buttons are invisible while selected, so no resorting happens in that state)
  80. sortChildren();
  81. updateRepresentativeDatabaseBeatmap();
  82. }
  83. void OsuUISongBrowserSongButton::drawBeatmapBackgroundThumbnail(Graphics *g, Image *image) {
  84. if(!osu_draw_songbrowser_thumbnails.getBool() || m_osu->getSkin()->getVersion() < 2.2f) return;
  85. float alpha = 1.0f;
  86. if(osu_songbrowser_thumbnail_fade_in_duration.getFloat() > 0.0f) {
  87. if(image == NULL || !image->isReady())
  88. m_fThumbnailFadeInTime = engine->getTime();
  89. else if(m_fThumbnailFadeInTime > 0.0f && engine->getTime() > m_fThumbnailFadeInTime) {
  90. alpha = clamp<float>(
  91. (engine->getTime() - m_fThumbnailFadeInTime) / osu_songbrowser_thumbnail_fade_in_duration.getFloat(),
  92. 0.0f, 1.0f);
  93. alpha = 1.0f - (1.0f - alpha) * (1.0f - alpha);
  94. }
  95. }
  96. if(image == NULL || !image->isReady()) return;
  97. // scaling
  98. const Vector2 pos = getActualPos();
  99. const Vector2 size = getActualSize();
  100. const float beatmapBackgroundScale =
  101. Osu::getImageScaleToFillResolution(image, Vector2(size.y * thumbnailYRatio, size.y)) * 1.05f;
  102. Vector2 centerOffset = Vector2(std::round((size.y * thumbnailYRatio) / 2.0f), std::round(size.y / 2.0f));
  103. McRect clipRect = McRect(pos.x - 2, pos.y + 1, (int)(size.y * thumbnailYRatio) + 5, size.y + 2);
  104. g->setColor(0xffffffff);
  105. g->setAlpha(alpha);
  106. g->pushTransform();
  107. {
  108. g->scale(beatmapBackgroundScale, beatmapBackgroundScale);
  109. g->translate(pos.x + (int)centerOffset.x, pos.y + (int)centerOffset.y);
  110. g->pushClipRect(clipRect);
  111. { g->drawImage(image); }
  112. g->popClipRect();
  113. }
  114. g->popTransform();
  115. // debug cliprect bounding box
  116. if(Osu::debug->getBool()) {
  117. Vector2 clipRectPos = Vector2(clipRect.getX(), clipRect.getY() - 1);
  118. Vector2 clipRectSize = Vector2(clipRect.getWidth(), clipRect.getHeight());
  119. g->setColor(0xffffff00);
  120. g->drawLine(clipRectPos.x, clipRectPos.y, clipRectPos.x + clipRectSize.x, clipRectPos.y);
  121. g->drawLine(clipRectPos.x, clipRectPos.y, clipRectPos.x, clipRectPos.y + clipRectSize.y);
  122. g->drawLine(clipRectPos.x, clipRectPos.y + clipRectSize.y, clipRectPos.x + clipRectSize.x,
  123. clipRectPos.y + clipRectSize.y);
  124. g->drawLine(clipRectPos.x + clipRectSize.x, clipRectPos.y, clipRectPos.x + clipRectSize.x,
  125. clipRectPos.y + clipRectSize.y);
  126. }
  127. }
  128. void OsuUISongBrowserSongButton::drawGrade(Graphics *g) {
  129. // scaling
  130. const Vector2 pos = getActualPos();
  131. const Vector2 size = getActualSize();
  132. OsuSkinImage *grade = OsuUISongBrowserScoreButton::getGradeImage(m_osu, m_grade);
  133. g->pushTransform();
  134. {
  135. const float scale = calculateGradeScale();
  136. g->setColor(0xffffffff);
  137. grade->drawRaw(g, Vector2(pos.x + m_fGradeOffset + grade->getSizeBaseRaw().x * scale / 2, pos.y + size.y / 2),
  138. scale);
  139. }
  140. g->popTransform();
  141. }
  142. void OsuUISongBrowserSongButton::drawTitle(Graphics *g, float deselectedAlpha, bool forceSelectedStyle) {
  143. // scaling
  144. const Vector2 pos = getActualPos();
  145. const Vector2 size = getActualSize();
  146. const float titleScale = (size.y * m_fTitleScale) / m_font->getHeight();
  147. g->setColor((m_bSelected || forceSelectedStyle) ? m_osu->getSkin()->getSongSelectActiveText()
  148. : m_osu->getSkin()->getSongSelectInactiveText());
  149. if(!(m_bSelected || forceSelectedStyle)) g->setAlpha(deselectedAlpha);
  150. g->pushTransform();
  151. {
  152. g->scale(titleScale, titleScale);
  153. g->translate(pos.x + m_fTextOffset, pos.y + size.y * m_fTextMarginScale + m_font->getHeight() * titleScale);
  154. g->drawString(m_font, buildTitleString());
  155. // debugging
  156. // g->drawString(m_font, UString::format("%i, %i", m_diff->setID, m_diff->ID));
  157. }
  158. g->popTransform();
  159. }
  160. void OsuUISongBrowserSongButton::drawSubTitle(Graphics *g, float deselectedAlpha, bool forceSelectedStyle) {
  161. // scaling
  162. const Vector2 pos = getActualPos();
  163. const Vector2 size = getActualSize();
  164. const float titleScale = (size.y * m_fTitleScale) / m_font->getHeight();
  165. const float subTitleScale = (size.y * m_fSubTitleScale) / m_font->getHeight();
  166. g->setColor((m_bSelected || forceSelectedStyle) ? m_osu->getSkin()->getSongSelectActiveText()
  167. : m_osu->getSkin()->getSongSelectInactiveText());
  168. if(!(m_bSelected || forceSelectedStyle)) g->setAlpha(deselectedAlpha);
  169. g->pushTransform();
  170. {
  171. g->scale(subTitleScale, subTitleScale);
  172. g->translate(pos.x + m_fTextOffset, pos.y + size.y * m_fTextMarginScale + m_font->getHeight() * titleScale +
  173. size.y * m_fTextSpacingScale +
  174. m_font->getHeight() * subTitleScale * 0.85f);
  175. g->drawString(m_font, buildSubTitleString());
  176. // debug stuff
  177. /*
  178. g->translate(-300, 0);
  179. long long oldestTime = std::numeric_limits<long long>::min();
  180. for (int i=0; i<m_beatmap->getNumDifficulties(); i++)
  181. {
  182. if ((*m_beatmap->getDifficultiesPointer())[i]->lastModificationTime > oldestTime)
  183. oldestTime = (*m_beatmap->getDifficultiesPointer())[i]->lastModificationTime;
  184. }
  185. g->drawString(m_font, UString::format("t = %I64d", oldestTime));
  186. */
  187. }
  188. g->popTransform();
  189. }
  190. void OsuUISongBrowserSongButton::sortChildren() {
  191. std::sort(m_children.begin(), m_children.end(), OsuSongBrowser::SortByDifficulty());
  192. }
  193. void OsuUISongBrowserSongButton::updateLayoutEx() {
  194. OsuUISongBrowserButton::updateLayoutEx();
  195. // scaling
  196. const Vector2 size = getActualSize();
  197. m_fTextOffset = 0.0f;
  198. m_fGradeOffset = 0.0f;
  199. if(m_bHasGrade) m_fTextOffset += calculateGradeWidth();
  200. if(m_osu->getSkin()->getVersion() < 2.2f) {
  201. m_fTextOffset += size.x * 0.02f * 2.0f;
  202. if(m_bHasGrade) m_fGradeOffset += calculateGradeWidth() / 2;
  203. } else {
  204. m_fTextOffset += size.y * thumbnailYRatio + size.x * 0.02f;
  205. m_fGradeOffset += size.y * thumbnailYRatio + size.x * 0.0125f;
  206. }
  207. }
  208. void OsuUISongBrowserSongButton::onSelected(bool wasSelected, bool autoSelectBottomMostChild, bool wasParentSelected) {
  209. OsuUISongBrowserButton::onSelected(wasSelected, autoSelectBottomMostChild, wasParentSelected);
  210. // resort children (since they might have been updated in the meantime)
  211. sortChildren();
  212. // update grade on child
  213. for(int c = 0; c < m_children.size(); c++) {
  214. OsuUISongBrowserSongDifficultyButton *child = (OsuUISongBrowserSongDifficultyButton *)m_children[c];
  215. child->updateGrade();
  216. }
  217. m_songBrowser->onSelectionChange(this, false);
  218. // now, automatically select the bottom child (hardest diff, assuming default sorting, and respecting the current
  219. // search matches)
  220. if(autoSelectBottomMostChild) {
  221. for(int i = m_children.size() - 1; i >= 0; i--) {
  222. if(m_children[i]
  223. ->isSearchMatch()) // NOTE: if no search is active, then all search matches return true by default
  224. {
  225. m_children[i]->select(true, false, wasSelected);
  226. break;
  227. }
  228. }
  229. }
  230. }
  231. void OsuUISongBrowserSongButton::onRightMouseUpInside() { triggerContextMenu(engine->getMouse()->getPos()); }
  232. void OsuUISongBrowserSongButton::triggerContextMenu(Vector2 pos) {
  233. if(m_contextMenu != NULL) {
  234. m_contextMenu->setPos(pos);
  235. m_contextMenu->setRelPos(pos);
  236. m_contextMenu->begin(0, true);
  237. {
  238. if(m_databaseBeatmap != NULL && m_databaseBeatmap->getDifficulties().size() < 1)
  239. m_contextMenu->addButton("[+] Add to Collection", 1);
  240. m_contextMenu->addButton("[+Set] Add to Collection", 2);
  241. if(m_osu->getSongBrowser()->getGroupingMode() == OsuSongBrowser::GROUP::GROUP_COLLECTIONS) {
  242. // get the collection name for this diff/set
  243. UString collectionName;
  244. {
  245. const std::vector<OsuUISongBrowserCollectionButton *> &collectionButtons =
  246. m_osu->getSongBrowser()->getCollectionButtons();
  247. for(size_t i = 0; i < collectionButtons.size(); i++) {
  248. if(collectionButtons[i]->isSelected()) {
  249. collectionName = collectionButtons[i]->getCollectionName();
  250. break;
  251. }
  252. }
  253. }
  254. // check if this entry in the collection is coming from osu! or not
  255. // the entry could be either a set button, or an independent diff button
  256. bool isLegacyEntry = false;
  257. {
  258. const std::vector<OsuDatabase::Collection> &collections =
  259. m_osu->getSongBrowser()->getDatabase()->getCollections();
  260. for(size_t i = 0; i < collections.size(); i++) {
  261. if(collections[i].name == collectionName) {
  262. for(size_t e = 0; e < collections[i].hashes.size(); e++) {
  263. if(m_databaseBeatmap->getDifficulties().size() < 1) {
  264. // independent diff
  265. if(collections[i].hashes[e].hash == m_databaseBeatmap->getMD5Hash()) {
  266. isLegacyEntry = collections[i].hashes[e].isLegacyEntry;
  267. break;
  268. }
  269. } else {
  270. // set
  271. const std::vector<OsuDatabaseBeatmap *> &diffs =
  272. m_databaseBeatmap->getDifficulties();
  273. for(size_t d = 0; d < diffs.size(); d++) {
  274. if(collections[i].hashes[e].hash == diffs[d]->getMD5Hash()) {
  275. // one single entry of the set coming from osu! is enough to deny removing
  276. // the set (as a whole)
  277. if(collections[i].hashes[e].isLegacyEntry) {
  278. isLegacyEntry = true;
  279. break;
  280. }
  281. }
  282. }
  283. if(isLegacyEntry) break;
  284. }
  285. }
  286. break;
  287. }
  288. }
  289. }
  290. CBaseUIButton *spacer = m_contextMenu->addButton("---");
  291. spacer->setTextLeft(false);
  292. spacer->setEnabled(false);
  293. spacer->setTextColor(0xff888888);
  294. spacer->setTextDarkColor(0xff000000);
  295. CBaseUIButton *removeDiffButton = NULL;
  296. if(m_databaseBeatmap == NULL || m_databaseBeatmap->getDifficulties().size() < 1)
  297. removeDiffButton = m_contextMenu->addButton("[-] Remove from Collection", 3);
  298. CBaseUIButton *removeSetButton = m_contextMenu->addButton("[-Set] Remove from Collection", 4);
  299. if(isLegacyEntry) {
  300. if(removeDiffButton != NULL) {
  301. removeDiffButton->setTextColor(0xff888888);
  302. removeDiffButton->setTextDarkColor(0xff000000);
  303. }
  304. removeSetButton->setTextColor(0xff888888);
  305. removeSetButton->setTextDarkColor(0xff000000);
  306. }
  307. }
  308. }
  309. m_contextMenu->end(false, false);
  310. m_contextMenu->setClickCallback(fastdelegate::MakeDelegate(this, &OsuUISongBrowserSongButton::onContextMenu));
  311. OsuUIContextMenu::clampToRightScreenEdge(m_contextMenu);
  312. OsuUIContextMenu::clampToBottomScreenEdge(m_contextMenu);
  313. }
  314. }
  315. void OsuUISongBrowserSongButton::onContextMenu(UString text, int id) {
  316. if(id == 1 || id == 2) {
  317. m_contextMenu->begin(0, true);
  318. {
  319. const std::vector<OsuDatabase::Collection> &collections =
  320. m_osu->getSongBrowser()->getDatabase()->getCollections();
  321. m_contextMenu->addButton("[+] Create new Collection?", -id * 2);
  322. if(collections.size() > 0) {
  323. CBaseUIButton *spacer = m_contextMenu->addButton("---");
  324. spacer->setTextLeft(false);
  325. spacer->setEnabled(false);
  326. spacer->setTextColor(0xff888888);
  327. spacer->setTextDarkColor(0xff000000);
  328. for(size_t i = 0; i < collections.size(); i++) {
  329. bool isDiffAndAlreadyContained = false;
  330. if(m_databaseBeatmap != NULL && m_databaseBeatmap->getDifficulties().size() < 1) {
  331. for(size_t h = 0; h < collections[i].hashes.size(); h++) {
  332. if(collections[i].hashes[h].hash == m_databaseBeatmap->getMD5Hash()) {
  333. isDiffAndAlreadyContained = true;
  334. // edge case: allow adding the set of this diff if it does not represent the entire set
  335. // (and the set is not yet added completely)
  336. if(id == 2) {
  337. const OsuUISongBrowserSongDifficultyButton *diffButtonPointer =
  338. dynamic_cast<OsuUISongBrowserSongDifficultyButton *>(this);
  339. if(diffButtonPointer != NULL && diffButtonPointer->getParentSongButton() != NULL &&
  340. diffButtonPointer->getParentSongButton()->getDatabaseBeatmap() != NULL) {
  341. const OsuDatabaseBeatmap *setContainer =
  342. diffButtonPointer->getParentSongButton()->getDatabaseBeatmap();
  343. if(setContainer->getDifficulties().size() > 1) {
  344. const std::vector<OsuDatabaseBeatmap *> &diffs =
  345. setContainer->getDifficulties();
  346. bool isSetAlreadyContained = true;
  347. for(size_t s = 0; s < diffs.size(); s++) {
  348. bool isDiffAlreadyContained = false;
  349. for(size_t h2 = 0; h2 < collections[i].hashes.size(); h2++) {
  350. if(diffs[s]->getMD5Hash() == collections[i].hashes[h2].hash) {
  351. isDiffAlreadyContained = true;
  352. break;
  353. }
  354. }
  355. if(!isDiffAlreadyContained) {
  356. isSetAlreadyContained = false;
  357. break;
  358. }
  359. }
  360. if(!isSetAlreadyContained) isDiffAndAlreadyContained = false;
  361. }
  362. }
  363. }
  364. break;
  365. }
  366. }
  367. }
  368. bool isContainerAndSetAlreadyContained = false;
  369. if(m_databaseBeatmap != NULL && m_databaseBeatmap->getDifficulties().size() > 0) {
  370. const std::vector<OsuDatabaseBeatmap *> &diffs = m_databaseBeatmap->getDifficulties();
  371. bool foundAllDiffs = true;
  372. for(size_t d = 0; d < diffs.size(); d++) {
  373. const std::vector<OsuDatabaseBeatmap *> &diffs = m_databaseBeatmap->getDifficulties();
  374. bool foundDiff = false;
  375. for(size_t h = 0; h < collections[i].hashes.size(); h++) {
  376. if(collections[i].hashes[h].hash == diffs[d]->getMD5Hash()) {
  377. foundDiff = true;
  378. break;
  379. }
  380. }
  381. foundAllDiffs = foundDiff;
  382. if(!foundAllDiffs) break;
  383. }
  384. isContainerAndSetAlreadyContained = foundAllDiffs;
  385. }
  386. CBaseUIButton *collectionButton = m_contextMenu->addButton(collections[i].name, id);
  387. if(isDiffAndAlreadyContained || isContainerAndSetAlreadyContained) {
  388. collectionButton->setEnabled(false);
  389. collectionButton->setTextColor(0xff555555);
  390. collectionButton->setTextDarkColor(0xff000000);
  391. }
  392. }
  393. }
  394. }
  395. m_contextMenu->end(false, true);
  396. m_contextMenu->setClickCallback(
  397. fastdelegate::MakeDelegate(this, &OsuUISongBrowserSongButton::onAddToCollectionConfirmed));
  398. OsuUIContextMenu::clampToRightScreenEdge(m_contextMenu);
  399. OsuUIContextMenu::clampToBottomScreenEdge(m_contextMenu);
  400. } else if(id == 3 || id == 4) {
  401. // get the collection name for this diff/set
  402. UString collectionName;
  403. {
  404. const std::vector<OsuUISongBrowserCollectionButton *> &collectionButtons =
  405. m_osu->getSongBrowser()->getCollectionButtons();
  406. for(size_t i = 0; i < collectionButtons.size(); i++) {
  407. if(collectionButtons[i]->isSelected()) {
  408. collectionName = collectionButtons[i]->getCollectionName();
  409. break;
  410. }
  411. }
  412. }
  413. // check if this entry in the collection is coming from osu! or not
  414. // the entry could be either a set button, or an independent diff button
  415. bool isLegacyEntry = false;
  416. {
  417. const std::vector<OsuDatabase::Collection> &collections =
  418. m_osu->getSongBrowser()->getDatabase()->getCollections();
  419. for(size_t i = 0; i < collections.size(); i++) {
  420. if(collections[i].name == collectionName) {
  421. for(size_t e = 0; e < collections[i].hashes.size(); e++) {
  422. if(m_databaseBeatmap->getDifficulties().size() < 1) {
  423. // independent diff
  424. if(collections[i].hashes[e].hash == m_databaseBeatmap->getMD5Hash()) {
  425. isLegacyEntry = collections[i].hashes[e].isLegacyEntry;
  426. break;
  427. }
  428. } else {
  429. // set
  430. const std::vector<OsuDatabaseBeatmap *> &diffs = m_databaseBeatmap->getDifficulties();
  431. for(size_t d = 0; d < diffs.size(); d++) {
  432. if(collections[i].hashes[e].hash == diffs[d]->getMD5Hash()) {
  433. // one single entry of the set coming from osu! is enough to deny removing the set
  434. // (as a whole)
  435. if(collections[i].hashes[e].isLegacyEntry) {
  436. isLegacyEntry = true;
  437. break;
  438. }
  439. }
  440. }
  441. if(isLegacyEntry) break;
  442. }
  443. }
  444. break;
  445. }
  446. }
  447. }
  448. if(isLegacyEntry) {
  449. if(id == 3)
  450. m_osu->getNotificationOverlay()->addNotification("Can't remove collection entry loaded from osu!",
  451. 0xffffff00);
  452. else if(id == 4)
  453. m_osu->getNotificationOverlay()->addNotification("Can't remove collection set loaded from osu!",
  454. 0xffffff00);
  455. } else
  456. m_osu->getSongBrowser()->onSongButtonContextMenu(this, text, id);
  457. }
  458. }
  459. void OsuUISongBrowserSongButton::onAddToCollectionConfirmed(UString text, int id) {
  460. if(id == -2 || id == -4) {
  461. m_contextMenu->begin(0, true);
  462. {
  463. CBaseUIButton *label = m_contextMenu->addButton("Enter Collection Name:");
  464. label->setTextLeft(false);
  465. label->setEnabled(false);
  466. CBaseUIButton *spacer = m_contextMenu->addButton("---");
  467. spacer->setTextLeft(false);
  468. spacer->setEnabled(false);
  469. spacer->setTextColor(0xff888888);
  470. spacer->setTextDarkColor(0xff000000);
  471. m_contextMenu->addTextbox("", id);
  472. spacer = m_contextMenu->addButton("---");
  473. spacer->setTextLeft(false);
  474. spacer->setEnabled(false);
  475. spacer->setTextColor(0xff888888);
  476. spacer->setTextDarkColor(0xff000000);
  477. label = m_contextMenu->addButton(
  478. env->getOS() == Environment::OS::OS_HORIZON ? "(Click HERE to confirm)" : "(Press ENTER to confirm.)",
  479. id);
  480. label->setTextLeft(false);
  481. label->setTextColor(0xff555555);
  482. label->setTextDarkColor(0xff000000);
  483. }
  484. m_contextMenu->end(false, false);
  485. m_contextMenu->setClickCallback(
  486. fastdelegate::MakeDelegate(this, &OsuUISongBrowserSongButton::onCreateNewCollectionConfirmed));
  487. OsuUIContextMenu::clampToRightScreenEdge(m_contextMenu);
  488. OsuUIContextMenu::clampToBottomScreenEdge(m_contextMenu);
  489. } else {
  490. // just forward it
  491. m_osu->getSongBrowser()->onSongButtonContextMenu(this, text, id);
  492. }
  493. }
  494. void OsuUISongBrowserSongButton::onCreateNewCollectionConfirmed(UString text, int id) {
  495. if(id == -2 || id == -4) {
  496. // just forward it
  497. m_osu->getSongBrowser()->onSongButtonContextMenu(this, text, id);
  498. }
  499. }
  500. float OsuUISongBrowserSongButton::calculateGradeScale() {
  501. const Vector2 size = getActualSize();
  502. OsuSkinImage *grade = OsuUISongBrowserScoreButton::getGradeImage(m_osu, m_grade);
  503. return Osu::getImageScaleToFitResolution(grade->getSizeBaseRaw(), Vector2(size.x, size.y * m_fGradeScale));
  504. }
  505. float OsuUISongBrowserSongButton::calculateGradeWidth() {
  506. OsuSkinImage *grade = OsuUISongBrowserScoreButton::getGradeImage(m_osu, m_grade);
  507. return grade->getSizeBaseRaw().x * calculateGradeScale();
  508. }
  509. void OsuUISongBrowserSongButton::updateRepresentativeDatabaseBeatmap() {
  510. if(m_databaseBeatmap != NULL && m_children.size() > 0) {
  511. const OsuDatabaseBeatmap *previousRepresentativeDatabaseBeatmap = m_representativeDatabaseBeatmap;
  512. // use the bottom child (hardest diff, assuming default sorting, and respecting the current search matches)
  513. for(int i = m_children.size() - 1; i >= 0; i--) {
  514. if(m_children[i]
  515. ->isSearchMatch()) // NOTE: if no search is active, then all search matches return true by default
  516. {
  517. m_representativeDatabaseBeatmap =
  518. dynamic_cast<OsuUISongBrowserSongButton *>(m_children[i])->getDatabaseBeatmap();
  519. break;
  520. }
  521. }
  522. if(m_representativeDatabaseBeatmap != NULL &&
  523. m_representativeDatabaseBeatmap != previousRepresentativeDatabaseBeatmap) {
  524. m_sTitle = m_representativeDatabaseBeatmap->getTitle();
  525. m_sArtist = m_representativeDatabaseBeatmap->getArtist();
  526. m_sMapper = m_representativeDatabaseBeatmap->getCreator();
  527. }
  528. }
  529. }