guiFormSpecMenu.cpp 93 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439
  1. /*
  2. Minetest
  3. Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #include <cstdlib>
  17. #include <algorithm>
  18. #include <iterator>
  19. #include <sstream>
  20. #include <limits>
  21. #include "guiFormSpecMenu.h"
  22. #include "guiTable.h"
  23. #include "constants.h"
  24. #include "gamedef.h"
  25. #include "keycode.h"
  26. #include "strfnd.h"
  27. #include <IGUICheckBox.h>
  28. #include <IGUIEditBox.h>
  29. #include <IGUIButton.h>
  30. #include <IGUIStaticText.h>
  31. #include <IGUIFont.h>
  32. #include <IGUITabControl.h>
  33. #include <IGUIComboBox.h>
  34. #include "log.h"
  35. #include "tile.h" // ITextureSource
  36. #include "hud.h" // drawItemStack
  37. #include "hex.h"
  38. #include "util/string.h"
  39. #include "util/numeric.h"
  40. #include "filesys.h"
  41. #include "gettime.h"
  42. #include "gettext.h"
  43. #include "scripting_game.h"
  44. #include "porting.h"
  45. #include "main.h"
  46. #include "settings.h"
  47. #define MY_CHECKPOS(a,b) \
  48. if (v_pos.size() != 2) { \
  49. errorstream<< "Invalid pos for element " << a << "specified: \"" \
  50. << parts[b] << "\"" << std::endl; \
  51. return; \
  52. }
  53. #define MY_CHECKGEOM(a,b) \
  54. if (v_geom.size() != 2) { \
  55. errorstream<< "Invalid pos for element " << a << "specified: \"" \
  56. << parts[b] << "\"" << std::endl; \
  57. return; \
  58. }
  59. /*
  60. GUIFormSpecMenu
  61. */
  62. GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev,
  63. gui::IGUIElement* parent, s32 id, IMenuManager *menumgr,
  64. InventoryManager *invmgr, IGameDef *gamedef,
  65. ISimpleTextureSource *tsrc, IFormSource* fsrc, TextDest* tdst,
  66. GUIFormSpecMenu** ext_ptr) :
  67. GUIModalMenu(dev->getGUIEnvironment(), parent, id, menumgr),
  68. m_device(dev),
  69. m_invmgr(invmgr),
  70. m_gamedef(gamedef),
  71. m_tsrc(tsrc),
  72. m_selected_item(NULL),
  73. m_selected_amount(0),
  74. m_selected_dragging(false),
  75. m_tooltip_element(NULL),
  76. m_old_tooltip_id(-1),
  77. m_allowclose(true),
  78. m_lock(false),
  79. m_form_src(fsrc),
  80. m_text_dst(tdst),
  81. m_ext_ptr(ext_ptr),
  82. m_font(dev->getGUIEnvironment()->getSkin()->getFont()),
  83. m_formspec_version(0)
  84. #ifdef __ANDROID__
  85. ,m_JavaDialogFieldName(L"")
  86. #endif
  87. {
  88. current_keys_pending.key_down = false;
  89. current_keys_pending.key_up = false;
  90. current_keys_pending.key_enter = false;
  91. current_keys_pending.key_escape = false;
  92. m_doubleclickdetect[0].time = 0;
  93. m_doubleclickdetect[1].time = 0;
  94. m_doubleclickdetect[0].pos = v2s32(0, 0);
  95. m_doubleclickdetect[1].pos = v2s32(0, 0);
  96. m_tooltip_show_delay = (u32)g_settings->getS32("tooltip_show_delay");
  97. m_btn_height = g_settings->getS32("font_size") +2;
  98. assert(m_btn_height > 0);
  99. }
  100. GUIFormSpecMenu::~GUIFormSpecMenu()
  101. {
  102. removeChildren();
  103. for (u32 i = 0; i < m_tables.size(); ++i) {
  104. GUITable *table = m_tables[i].second;
  105. table->drop();
  106. }
  107. delete m_selected_item;
  108. if (m_form_src != NULL) {
  109. delete m_form_src;
  110. }
  111. if (m_text_dst != NULL) {
  112. delete m_text_dst;
  113. }
  114. if (m_ext_ptr != NULL) {
  115. assert(*m_ext_ptr == this);
  116. *m_ext_ptr = NULL;
  117. }
  118. }
  119. void GUIFormSpecMenu::removeChildren()
  120. {
  121. const core::list<gui::IGUIElement*> &children = getChildren();
  122. while(!children.empty()) {
  123. (*children.getLast())->remove();
  124. }
  125. if(m_tooltip_element) {
  126. m_tooltip_element->remove();
  127. m_tooltip_element->drop();
  128. m_tooltip_element = NULL;
  129. }
  130. }
  131. void GUIFormSpecMenu::setInitialFocus()
  132. {
  133. // Set initial focus according to following order of precedence:
  134. // 1. first empty editbox
  135. // 2. first editbox
  136. // 3. first table
  137. // 4. last button
  138. // 5. first focusable (not statictext, not tabheader)
  139. // 6. first child element
  140. core::list<gui::IGUIElement*> children = getChildren();
  141. // in case "children" contains any NULL elements, remove them
  142. for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
  143. it != children.end();) {
  144. if (*it)
  145. ++it;
  146. else
  147. it = children.erase(it);
  148. }
  149. // 1. first empty editbox
  150. for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
  151. it != children.end(); ++it) {
  152. if ((*it)->getType() == gui::EGUIET_EDIT_BOX
  153. && (*it)->getText()[0] == 0) {
  154. Environment->setFocus(*it);
  155. return;
  156. }
  157. }
  158. // 2. first editbox
  159. for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
  160. it != children.end(); ++it) {
  161. if ((*it)->getType() == gui::EGUIET_EDIT_BOX) {
  162. Environment->setFocus(*it);
  163. return;
  164. }
  165. }
  166. // 3. first table
  167. for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
  168. it != children.end(); ++it) {
  169. if ((*it)->getTypeName() == std::string("GUITable")) {
  170. Environment->setFocus(*it);
  171. return;
  172. }
  173. }
  174. // 4. last button
  175. for (core::list<gui::IGUIElement*>::Iterator it = children.getLast();
  176. it != children.end(); --it) {
  177. if ((*it)->getType() == gui::EGUIET_BUTTON) {
  178. Environment->setFocus(*it);
  179. return;
  180. }
  181. }
  182. // 5. first focusable (not statictext, not tabheader)
  183. for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
  184. it != children.end(); ++it) {
  185. if ((*it)->getType() != gui::EGUIET_STATIC_TEXT &&
  186. (*it)->getType() != gui::EGUIET_TAB_CONTROL) {
  187. Environment->setFocus(*it);
  188. return;
  189. }
  190. }
  191. // 6. first child element
  192. if (children.empty())
  193. Environment->setFocus(this);
  194. else
  195. Environment->setFocus(*(children.begin()));
  196. }
  197. GUITable* GUIFormSpecMenu::getTable(std::wstring tablename)
  198. {
  199. for (u32 i = 0; i < m_tables.size(); ++i) {
  200. if (tablename == m_tables[i].first.fname)
  201. return m_tables[i].second;
  202. }
  203. return 0;
  204. }
  205. std::vector<std::string> split(const std::string &s, char delim) {
  206. std::vector<std::string> tokens;
  207. std::string current = "";
  208. bool last_was_escape = false;
  209. for(unsigned int i=0; i < s.size(); i++) {
  210. if (last_was_escape) {
  211. current += '\\';
  212. current += s.c_str()[i];
  213. last_was_escape = false;
  214. }
  215. else {
  216. if (s.c_str()[i] == delim) {
  217. tokens.push_back(current);
  218. current = "";
  219. last_was_escape = false;
  220. }
  221. else if (s.c_str()[i] == '\\'){
  222. last_was_escape = true;
  223. }
  224. else {
  225. current += s.c_str()[i];
  226. last_was_escape = false;
  227. }
  228. }
  229. }
  230. //push last element
  231. tokens.push_back(current);
  232. return tokens;
  233. }
  234. void GUIFormSpecMenu::parseSize(parserData* data,std::string element)
  235. {
  236. std::vector<std::string> parts = split(element,',');
  237. if (((parts.size() == 2) || parts.size() == 3) ||
  238. ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
  239. {
  240. v2f invsize;
  241. if (parts[1].find(';') != std::string::npos)
  242. parts[1] = parts[1].substr(0,parts[1].find(';'));
  243. invsize.X = stof(parts[0]);
  244. invsize.Y = stof(parts[1]);
  245. lockSize(false);
  246. if (parts.size() == 3) {
  247. if (parts[2] == "true") {
  248. lockSize(true,v2u32(800,600));
  249. }
  250. }
  251. double cur_scaling = porting::getDisplayDensity() *
  252. g_settings->getFloat("gui_scaling");
  253. if (m_lock) {
  254. v2u32 current_screensize = m_device->getVideoDriver()->getScreenSize();
  255. v2u32 delta = current_screensize - m_lockscreensize;
  256. if (current_screensize.Y > m_lockscreensize.Y)
  257. delta.Y /= 2;
  258. else
  259. delta.Y = 0;
  260. if (current_screensize.X > m_lockscreensize.X)
  261. delta.X /= 2;
  262. else
  263. delta.X = 0;
  264. offset = v2s32(delta.X,delta.Y);
  265. data->screensize = m_lockscreensize;
  266. // fixed scaling for fixed size gui elements */
  267. cur_scaling = LEGACY_SCALING;
  268. }
  269. else {
  270. offset = v2s32(0,0);
  271. }
  272. /* adjust image size to dpi */
  273. int y_partition = 15;
  274. imgsize = v2s32(data->screensize.Y/y_partition, data->screensize.Y/y_partition);
  275. int min_imgsize = DEFAULT_IMGSIZE * cur_scaling;
  276. while ((min_imgsize > imgsize.Y) && (y_partition > 1)) {
  277. y_partition--;
  278. imgsize = v2s32(data->screensize.Y/y_partition, data->screensize.Y/y_partition);
  279. }
  280. assert(y_partition > 0);
  281. /* adjust spacing to dpi */
  282. spacing = v2s32(imgsize.X+(DEFAULT_XSPACING * cur_scaling),
  283. imgsize.Y+(DEFAULT_YSPACING * cur_scaling));
  284. padding = v2s32(data->screensize.Y/imgsize.Y, data->screensize.Y/imgsize.Y);
  285. /* adjust padding to dpi */
  286. padding = v2s32(
  287. (padding.X/(2.0/3.0)) * cur_scaling,
  288. (padding.X/(2.0/3.0)) * cur_scaling
  289. );
  290. data->size = v2s32(
  291. padding.X*2+spacing.X*(invsize.X-1.0)+imgsize.X,
  292. padding.Y*2+spacing.Y*(invsize.Y-1.0)+imgsize.Y + m_btn_height - 5
  293. );
  294. data->rect = core::rect<s32>(
  295. data->screensize.X/2 - data->size.X/2 + offset.X,
  296. data->screensize.Y/2 - data->size.Y/2 + offset.Y,
  297. data->screensize.X/2 + data->size.X/2 + offset.X,
  298. data->screensize.Y/2 + data->size.Y/2 + offset.Y
  299. );
  300. DesiredRect = data->rect;
  301. recalculateAbsolutePosition(false);
  302. data->basepos = getBasePos();
  303. data->bp_set = 2;
  304. return;
  305. }
  306. errorstream<< "Invalid size element (" << parts.size() << "): '" << element << "'" << std::endl;
  307. }
  308. void GUIFormSpecMenu::parseList(parserData* data,std::string element)
  309. {
  310. if (m_gamedef == 0) {
  311. errorstream<<"WARNING: invalid use of 'list' with m_gamedef==0"<<std::endl;
  312. return;
  313. }
  314. std::vector<std::string> parts = split(element,';');
  315. if (((parts.size() == 4) || (parts.size() == 5)) ||
  316. ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
  317. {
  318. std::string location = parts[0];
  319. std::string listname = parts[1];
  320. std::vector<std::string> v_pos = split(parts[2],',');
  321. std::vector<std::string> v_geom = split(parts[3],',');
  322. std::string startindex = "";
  323. if (parts.size() == 5)
  324. startindex = parts[4];
  325. MY_CHECKPOS("list",2);
  326. MY_CHECKGEOM("list",3);
  327. InventoryLocation loc;
  328. if(location == "context" || location == "current_name")
  329. loc = m_current_inventory_location;
  330. else
  331. loc.deSerialize(location);
  332. v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
  333. pos.X += stof(v_pos[0]) * (float)spacing.X;
  334. pos.Y += stof(v_pos[1]) * (float)spacing.Y;
  335. v2s32 geom;
  336. geom.X = stoi(v_geom[0]);
  337. geom.Y = stoi(v_geom[1]);
  338. s32 start_i = 0;
  339. if(startindex != "")
  340. start_i = stoi(startindex);
  341. if (geom.X < 0 || geom.Y < 0 || start_i < 0) {
  342. errorstream<< "Invalid list element: '" << element << "'" << std::endl;
  343. return;
  344. }
  345. if(data->bp_set != 2)
  346. errorstream<<"WARNING: invalid use of list without a size[] element"<<std::endl;
  347. m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom, start_i));
  348. return;
  349. }
  350. errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'" << std::endl;
  351. }
  352. void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element)
  353. {
  354. std::vector<std::string> parts = split(element,';');
  355. if (((parts.size() >= 3) || (parts.size() <= 4)) ||
  356. ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
  357. {
  358. std::vector<std::string> v_pos = split(parts[0],',');
  359. std::string name = parts[1];
  360. std::string label = parts[2];
  361. std::string selected = "";
  362. if (parts.size() >= 4)
  363. selected = parts[3];
  364. MY_CHECKPOS("checkbox",0);
  365. v2s32 pos = padding;
  366. pos.X += stof(v_pos[0]) * (float) spacing.X;
  367. pos.Y += stof(v_pos[1]) * (float) spacing.Y;
  368. bool fselected = false;
  369. if (selected == "true")
  370. fselected = true;
  371. std::wstring wlabel = narrow_to_wide(label.c_str());
  372. core::rect<s32> rect = core::rect<s32>(
  373. pos.X, pos.Y + ((imgsize.Y/2) - m_btn_height),
  374. pos.X + m_font->getDimension(wlabel.c_str()).Width + 25, // text size + size of checkbox
  375. pos.Y + ((imgsize.Y/2) + m_btn_height));
  376. FieldSpec spec(
  377. narrow_to_wide(name.c_str()),
  378. wlabel, //Needed for displaying text on MSVC
  379. wlabel,
  380. 258+m_fields.size()
  381. );
  382. spec.ftype = f_CheckBox;
  383. gui::IGUICheckBox* e = Environment->addCheckBox(fselected, rect, this,
  384. spec.fid, spec.flabel.c_str());
  385. if (spec.fname == data->focused_fieldname) {
  386. Environment->setFocus(e);
  387. }
  388. m_checkboxes.push_back(std::pair<FieldSpec,gui::IGUICheckBox*>(spec,e));
  389. m_fields.push_back(spec);
  390. return;
  391. }
  392. errorstream<< "Invalid checkbox element(" << parts.size() << "): '" << element << "'" << std::endl;
  393. }
  394. void GUIFormSpecMenu::parseScrollBar(parserData* data, std::string element)
  395. {
  396. std::vector<std::string> parts = split(element,';');
  397. if (parts.size() >= 5) {
  398. std::vector<std::string> v_pos = split(parts[0],',');
  399. std::vector<std::string> v_dim = split(parts[1],',');
  400. std::string name = parts[2];
  401. std::string value = parts[4];
  402. MY_CHECKPOS("scrollbar",0);
  403. v2s32 pos = padding;
  404. pos.X += stof(v_pos[0]) * (float) spacing.X;
  405. pos.Y += stof(v_pos[1]) * (float) spacing.Y;
  406. if (v_dim.size() != 2) {
  407. errorstream<< "Invalid size for element " << "scrollbar"
  408. << "specified: \"" << parts[1] << "\"" << std::endl;
  409. return;
  410. }
  411. v2s32 dim;
  412. dim.X = stof(v_dim[0]) * (float) spacing.X;
  413. dim.Y = stof(v_dim[1]) * (float) spacing.Y;
  414. core::rect<s32> rect =
  415. core::rect<s32>(pos.X, pos.Y, pos.X + dim.X, pos.Y + dim.Y);
  416. FieldSpec spec(
  417. narrow_to_wide(name.c_str()),
  418. L"",
  419. L"",
  420. 258+m_fields.size()
  421. );
  422. bool is_horizontal = true;
  423. if (parts[2] == "vertical")
  424. is_horizontal = false;
  425. spec.ftype = f_ScrollBar;
  426. spec.send = true;
  427. gui::IGUIScrollBar* e =
  428. Environment->addScrollBar(is_horizontal,rect,this,spec.fid);
  429. e->setMax(1000);
  430. e->setMin(0);
  431. e->setPos(stoi(parts[4]));
  432. e->setSmallStep(10);
  433. e->setLargeStep(100);
  434. if (!m_lock) {
  435. core::rect<s32> relative_rect = e->getRelativePosition();
  436. if (!is_horizontal) {
  437. s32 original_width = relative_rect.getWidth();
  438. s32 width = (original_width/(2.0/3.0))
  439. * porting::getDisplayDensity()
  440. * g_settings->getFloat("gui_scaling");
  441. e->setRelativePosition(core::rect<s32>(
  442. relative_rect.UpperLeftCorner.X,
  443. relative_rect.UpperLeftCorner.Y,
  444. relative_rect.LowerRightCorner.X + (width - original_width),
  445. relative_rect.LowerRightCorner.Y
  446. ));
  447. }
  448. else {
  449. s32 original_height = relative_rect.getHeight();
  450. s32 height = (original_height/(2.0/3.0))
  451. * porting::getDisplayDensity()
  452. * g_settings->getFloat("gui_scaling");
  453. e->setRelativePosition(core::rect<s32>(
  454. relative_rect.UpperLeftCorner.X,
  455. relative_rect.UpperLeftCorner.Y,
  456. relative_rect.LowerRightCorner.X,
  457. relative_rect.LowerRightCorner.Y + (height - original_height)
  458. ));
  459. }
  460. }
  461. m_scrollbars.push_back(std::pair<FieldSpec,gui::IGUIScrollBar*>(spec,e));
  462. m_fields.push_back(spec);
  463. return;
  464. }
  465. errorstream<< "Invalid scrollbar element(" << parts.size() << "): '" << element << "'" << std::endl;
  466. }
  467. void GUIFormSpecMenu::parseImage(parserData* data,std::string element)
  468. {
  469. std::vector<std::string> parts = split(element,';');
  470. if ((parts.size() == 3) ||
  471. ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
  472. {
  473. std::vector<std::string> v_pos = split(parts[0],',');
  474. std::vector<std::string> v_geom = split(parts[1],',');
  475. std::string name = unescape_string(parts[2]);
  476. MY_CHECKPOS("image",0);
  477. MY_CHECKGEOM("image",1);
  478. v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
  479. pos.X += stof(v_pos[0]) * (float) spacing.X;
  480. pos.Y += stof(v_pos[1]) * (float) spacing.Y;
  481. v2s32 geom;
  482. geom.X = stof(v_geom[0]) * (float)imgsize.X;
  483. geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
  484. if(data->bp_set != 2)
  485. errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
  486. m_images.push_back(ImageDrawSpec(name, pos, geom));
  487. return;
  488. }
  489. if (parts.size() == 2) {
  490. std::vector<std::string> v_pos = split(parts[0],',');
  491. std::string name = unescape_string(parts[1]);
  492. MY_CHECKPOS("image",0);
  493. v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
  494. pos.X += stof(v_pos[0]) * (float) spacing.X;
  495. pos.Y += stof(v_pos[1]) * (float) spacing.Y;
  496. if(data->bp_set != 2)
  497. errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
  498. m_images.push_back(ImageDrawSpec(name, pos));
  499. return;
  500. }
  501. errorstream<< "Invalid image element(" << parts.size() << "): '" << element << "'" << std::endl;
  502. }
  503. void GUIFormSpecMenu::parseItemImage(parserData* data,std::string element)
  504. {
  505. std::vector<std::string> parts = split(element,';');
  506. if ((parts.size() == 3) ||
  507. ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
  508. {
  509. std::vector<std::string> v_pos = split(parts[0],',');
  510. std::vector<std::string> v_geom = split(parts[1],',');
  511. std::string name = parts[2];
  512. MY_CHECKPOS("itemimage",0);
  513. MY_CHECKGEOM("itemimage",1);
  514. v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
  515. pos.X += stof(v_pos[0]) * (float) spacing.X;
  516. pos.Y += stof(v_pos[1]) * (float) spacing.Y;
  517. v2s32 geom;
  518. geom.X = stof(v_geom[0]) * (float)imgsize.X;
  519. geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
  520. if(data->bp_set != 2)
  521. errorstream<<"WARNING: invalid use of item_image without a size[] element"<<std::endl;
  522. m_itemimages.push_back(ImageDrawSpec(name, pos, geom));
  523. return;
  524. }
  525. errorstream<< "Invalid ItemImage element(" << parts.size() << "): '" << element << "'" << std::endl;
  526. }
  527. void GUIFormSpecMenu::parseButton(parserData* data,std::string element,
  528. std::string type)
  529. {
  530. std::vector<std::string> parts = split(element,';');
  531. if ((parts.size() == 4) ||
  532. ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
  533. {
  534. std::vector<std::string> v_pos = split(parts[0],',');
  535. std::vector<std::string> v_geom = split(parts[1],',');
  536. std::string name = parts[2];
  537. std::string label = parts[3];
  538. MY_CHECKPOS("button",0);
  539. MY_CHECKGEOM("button",1);
  540. v2s32 pos = padding;
  541. pos.X += stof(v_pos[0]) * (float)spacing.X;
  542. pos.Y += stof(v_pos[1]) * (float)spacing.Y;
  543. v2s32 geom;
  544. geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
  545. pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
  546. core::rect<s32> rect =
  547. core::rect<s32>(pos.X, pos.Y - m_btn_height,
  548. pos.X + geom.X, pos.Y + m_btn_height);
  549. if(data->bp_set != 2)
  550. errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
  551. label = unescape_string(label);
  552. std::wstring wlabel = narrow_to_wide(label.c_str());
  553. FieldSpec spec(
  554. narrow_to_wide(name.c_str()),
  555. wlabel,
  556. L"",
  557. 258+m_fields.size()
  558. );
  559. spec.ftype = f_Button;
  560. if(type == "button_exit")
  561. spec.is_exit = true;
  562. gui::IGUIButton* e = Environment->addButton(rect, this, spec.fid,
  563. spec.flabel.c_str());
  564. if (spec.fname == data->focused_fieldname) {
  565. Environment->setFocus(e);
  566. }
  567. m_fields.push_back(spec);
  568. return;
  569. }
  570. errorstream<< "Invalid button element(" << parts.size() << "): '" << element << "'" << std::endl;
  571. }
  572. void GUIFormSpecMenu::parseBackground(parserData* data,std::string element)
  573. {
  574. std::vector<std::string> parts = split(element,';');
  575. if (((parts.size() == 3) || (parts.size() == 4)) ||
  576. ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
  577. {
  578. std::vector<std::string> v_pos = split(parts[0],',');
  579. std::vector<std::string> v_geom = split(parts[1],',');
  580. std::string name = unescape_string(parts[2]);
  581. MY_CHECKPOS("background",0);
  582. MY_CHECKGEOM("background",1);
  583. v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
  584. pos.X += stof(v_pos[0]) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2;
  585. pos.Y += stof(v_pos[1]) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2;
  586. v2s32 geom;
  587. geom.X = stof(v_geom[0]) * (float)spacing.X;
  588. geom.Y = stof(v_geom[1]) * (float)spacing.Y;
  589. if (parts.size() == 4) {
  590. m_clipbackground = is_yes(parts[3]);
  591. if (m_clipbackground) {
  592. pos.X = stoi(v_pos[0]); //acts as offset
  593. pos.Y = stoi(v_pos[1]); //acts as offset
  594. }
  595. }
  596. if(data->bp_set != 2)
  597. errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl;
  598. m_backgrounds.push_back(ImageDrawSpec(name, pos, geom));
  599. return;
  600. }
  601. errorstream<< "Invalid background element(" << parts.size() << "): '" << element << "'" << std::endl;
  602. }
  603. void GUIFormSpecMenu::parseTableOptions(parserData* data,std::string element)
  604. {
  605. std::vector<std::string> parts = split(element,';');
  606. data->table_options.clear();
  607. for (size_t i = 0; i < parts.size(); ++i) {
  608. // Parse table option
  609. std::string opt = unescape_string(parts[i]);
  610. data->table_options.push_back(GUITable::splitOption(opt));
  611. }
  612. }
  613. void GUIFormSpecMenu::parseTableColumns(parserData* data,std::string element)
  614. {
  615. std::vector<std::string> parts = split(element,';');
  616. data->table_columns.clear();
  617. for (size_t i = 0; i < parts.size(); ++i) {
  618. std::vector<std::string> col_parts = split(parts[i],',');
  619. GUITable::TableColumn column;
  620. // Parse column type
  621. if (!col_parts.empty())
  622. column.type = col_parts[0];
  623. // Parse column options
  624. for (size_t j = 1; j < col_parts.size(); ++j) {
  625. std::string opt = unescape_string(col_parts[j]);
  626. column.options.push_back(GUITable::splitOption(opt));
  627. }
  628. data->table_columns.push_back(column);
  629. }
  630. }
  631. void GUIFormSpecMenu::parseTable(parserData* data,std::string element)
  632. {
  633. std::vector<std::string> parts = split(element,';');
  634. if (((parts.size() == 4) || (parts.size() == 5)) ||
  635. ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
  636. {
  637. std::vector<std::string> v_pos = split(parts[0],',');
  638. std::vector<std::string> v_geom = split(parts[1],',');
  639. std::string name = parts[2];
  640. std::vector<std::string> items = split(parts[3],',');
  641. std::string str_initial_selection = "";
  642. std::string str_transparent = "false";
  643. if (parts.size() >= 5)
  644. str_initial_selection = parts[4];
  645. MY_CHECKPOS("table",0);
  646. MY_CHECKGEOM("table",1);
  647. v2s32 pos = padding;
  648. pos.X += stof(v_pos[0]) * (float)spacing.X;
  649. pos.Y += stof(v_pos[1]) * (float)spacing.Y;
  650. v2s32 geom;
  651. geom.X = stof(v_geom[0]) * (float)spacing.X;
  652. geom.Y = stof(v_geom[1]) * (float)spacing.Y;
  653. core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
  654. std::wstring fname_w = narrow_to_wide(name.c_str());
  655. FieldSpec spec(
  656. fname_w,
  657. L"",
  658. L"",
  659. 258+m_fields.size()
  660. );
  661. spec.ftype = f_Table;
  662. for (unsigned int i = 0; i < items.size(); ++i) {
  663. items[i] = unescape_string(items[i]);
  664. }
  665. //now really show table
  666. GUITable *e = new GUITable(Environment, this, spec.fid, rect,
  667. m_tsrc);
  668. if (spec.fname == data->focused_fieldname) {
  669. Environment->setFocus(e);
  670. }
  671. e->setTable(data->table_options, data->table_columns, items);
  672. if (data->table_dyndata.find(fname_w) != data->table_dyndata.end()) {
  673. e->setDynamicData(data->table_dyndata[fname_w]);
  674. }
  675. if ((str_initial_selection != "") &&
  676. (str_initial_selection != "0"))
  677. e->setSelected(stoi(str_initial_selection.c_str()));
  678. m_tables.push_back(std::pair<FieldSpec,GUITable*>(spec, e));
  679. m_fields.push_back(spec);
  680. return;
  681. }
  682. errorstream<< "Invalid table element(" << parts.size() << "): '" << element << "'" << std::endl;
  683. }
  684. void GUIFormSpecMenu::parseTextList(parserData* data,std::string element)
  685. {
  686. std::vector<std::string> parts = split(element,';');
  687. if (((parts.size() == 4) || (parts.size() == 5) || (parts.size() == 6)) ||
  688. ((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION)))
  689. {
  690. std::vector<std::string> v_pos = split(parts[0],',');
  691. std::vector<std::string> v_geom = split(parts[1],',');
  692. std::string name = parts[2];
  693. std::vector<std::string> items = split(parts[3],',');
  694. std::string str_initial_selection = "";
  695. std::string str_transparent = "false";
  696. if (parts.size() >= 5)
  697. str_initial_selection = parts[4];
  698. if (parts.size() >= 6)
  699. str_transparent = parts[5];
  700. MY_CHECKPOS("textlist",0);
  701. MY_CHECKGEOM("textlist",1);
  702. v2s32 pos = padding;
  703. pos.X += stof(v_pos[0]) * (float)spacing.X;
  704. pos.Y += stof(v_pos[1]) * (float)spacing.Y;
  705. v2s32 geom;
  706. geom.X = stof(v_geom[0]) * (float)spacing.X;
  707. geom.Y = stof(v_geom[1]) * (float)spacing.Y;
  708. core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
  709. std::wstring fname_w = narrow_to_wide(name.c_str());
  710. FieldSpec spec(
  711. fname_w,
  712. L"",
  713. L"",
  714. 258+m_fields.size()
  715. );
  716. spec.ftype = f_Table;
  717. for (unsigned int i = 0; i < items.size(); ++i) {
  718. items[i] = unescape_string(items[i]);
  719. }
  720. //now really show list
  721. GUITable *e = new GUITable(Environment, this, spec.fid, rect,
  722. m_tsrc);
  723. if (spec.fname == data->focused_fieldname) {
  724. Environment->setFocus(e);
  725. }
  726. e->setTextList(items, is_yes(str_transparent));
  727. if (data->table_dyndata.find(fname_w) != data->table_dyndata.end()) {
  728. e->setDynamicData(data->table_dyndata[fname_w]);
  729. }
  730. if ((str_initial_selection != "") &&
  731. (str_initial_selection != "0"))
  732. e->setSelected(stoi(str_initial_selection.c_str()));
  733. m_tables.push_back(std::pair<FieldSpec,GUITable*>(spec, e));
  734. m_fields.push_back(spec);
  735. return;
  736. }
  737. errorstream<< "Invalid textlist element(" << parts.size() << "): '" << element << "'" << std::endl;
  738. }
  739. void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element)
  740. {
  741. std::vector<std::string> parts = split(element,';');
  742. if ((parts.size() == 5) ||
  743. ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
  744. {
  745. std::vector<std::string> v_pos = split(parts[0],',');
  746. std::string name = parts[2];
  747. std::vector<std::string> items = split(parts[3],',');
  748. std::string str_initial_selection = "";
  749. str_initial_selection = parts[4];
  750. MY_CHECKPOS("dropdown",0);
  751. v2s32 pos = padding;
  752. pos.X += stof(v_pos[0]) * (float)spacing.X;
  753. pos.Y += stof(v_pos[1]) * (float)spacing.Y;
  754. s32 width = stof(parts[1]) * (float)spacing.Y;
  755. core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y,
  756. pos.X + width, pos.Y + (m_btn_height * 2));
  757. std::wstring fname_w = narrow_to_wide(name.c_str());
  758. FieldSpec spec(
  759. fname_w,
  760. L"",
  761. L"",
  762. 258+m_fields.size()
  763. );
  764. spec.ftype = f_DropDown;
  765. spec.send = true;
  766. //now really show list
  767. gui::IGUIComboBox *e = Environment->addComboBox(rect, this,spec.fid);
  768. if (spec.fname == data->focused_fieldname) {
  769. Environment->setFocus(e);
  770. }
  771. for (unsigned int i=0; i < items.size(); i++) {
  772. e->addItem(narrow_to_wide(items[i]).c_str());
  773. }
  774. if (str_initial_selection != "")
  775. e->setSelected(stoi(str_initial_selection.c_str())-1);
  776. m_fields.push_back(spec);
  777. return;
  778. }
  779. errorstream << "Invalid dropdown element(" << parts.size() << "): '"
  780. << element << "'" << std::endl;
  781. }
  782. void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element)
  783. {
  784. std::vector<std::string> parts = split(element,';');
  785. if ((parts.size() == 4) ||
  786. ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
  787. {
  788. std::vector<std::string> v_pos = split(parts[0],',');
  789. std::vector<std::string> v_geom = split(parts[1],',');
  790. std::string name = parts[2];
  791. std::string label = parts[3];
  792. MY_CHECKPOS("pwdfield",0);
  793. MY_CHECKGEOM("pwdfield",1);
  794. v2s32 pos;
  795. pos.X += stof(v_pos[0]) * (float)spacing.X;
  796. pos.Y += stof(v_pos[1]) * (float)spacing.Y;
  797. v2s32 geom;
  798. geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
  799. pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
  800. pos.Y -= m_btn_height;
  801. geom.Y = m_btn_height*2;
  802. core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
  803. label = unescape_string(label);
  804. std::wstring wlabel = narrow_to_wide(label.c_str());
  805. FieldSpec spec(
  806. narrow_to_wide(name.c_str()),
  807. wlabel,
  808. L"",
  809. 258+m_fields.size()
  810. );
  811. spec.send = true;
  812. gui::IGUIEditBox * e = Environment->addEditBox(0, rect, true, this, spec.fid);
  813. if (spec.fname == data->focused_fieldname) {
  814. Environment->setFocus(e);
  815. }
  816. if (label.length() >= 1)
  817. {
  818. rect.UpperLeftCorner.Y -= m_btn_height;
  819. rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + m_btn_height;
  820. Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
  821. }
  822. e->setPasswordBox(true,L'*');
  823. irr::SEvent evt;
  824. evt.EventType = EET_KEY_INPUT_EVENT;
  825. evt.KeyInput.Key = KEY_END;
  826. evt.KeyInput.Char = 0;
  827. evt.KeyInput.Control = 0;
  828. evt.KeyInput.Shift = 0;
  829. evt.KeyInput.PressedDown = true;
  830. e->OnEvent(evt);
  831. m_fields.push_back(spec);
  832. return;
  833. }
  834. errorstream<< "Invalid pwdfield element(" << parts.size() << "): '" << element << "'" << std::endl;
  835. }
  836. void GUIFormSpecMenu::parseSimpleField(parserData* data,
  837. std::vector<std::string> &parts)
  838. {
  839. std::string name = parts[0];
  840. std::string label = parts[1];
  841. std::string default_val = parts[2];
  842. core::rect<s32> rect;
  843. if(!data->bp_set)
  844. {
  845. rect = core::rect<s32>(
  846. data->screensize.X/2 - 580/2,
  847. data->screensize.Y/2 - 300/2,
  848. data->screensize.X/2 + 580/2,
  849. data->screensize.Y/2 + 300/2
  850. );
  851. DesiredRect = rect;
  852. recalculateAbsolutePosition(false);
  853. data->basepos = getBasePos();
  854. data->bp_set = 1;
  855. }
  856. else if(data->bp_set == 2)
  857. errorstream<<"WARNING: invalid use of unpositioned \"field\" in inventory"<<std::endl;
  858. v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
  859. pos.Y = ((m_fields.size()+2)*60);
  860. v2s32 size = DesiredRect.getSize();
  861. rect = core::rect<s32>(size.X / 2 - 150, pos.Y,
  862. (size.X / 2 - 150) + 300, pos.Y + (m_btn_height*2));
  863. if(m_form_src)
  864. default_val = m_form_src->resolveText(default_val);
  865. default_val = unescape_string(default_val);
  866. label = unescape_string(label);
  867. std::wstring wlabel = narrow_to_wide(label.c_str());
  868. FieldSpec spec(
  869. narrow_to_wide(name.c_str()),
  870. wlabel,
  871. narrow_to_wide(default_val.c_str()),
  872. 258+m_fields.size()
  873. );
  874. if (name == "")
  875. {
  876. // spec field id to 0, this stops submit searching for a value that isn't there
  877. Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
  878. }
  879. else
  880. {
  881. spec.send = true;
  882. gui::IGUIEditBox *e =
  883. Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
  884. if (spec.fname == data->focused_fieldname) {
  885. Environment->setFocus(e);
  886. }
  887. irr::SEvent evt;
  888. evt.EventType = EET_KEY_INPUT_EVENT;
  889. evt.KeyInput.Key = KEY_END;
  890. evt.KeyInput.Char = 0;
  891. evt.KeyInput.Control = 0;
  892. evt.KeyInput.Shift = 0;
  893. evt.KeyInput.PressedDown = true;
  894. e->OnEvent(evt);
  895. if (label.length() >= 1)
  896. {
  897. rect.UpperLeftCorner.Y -= m_btn_height;
  898. rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + m_btn_height;
  899. Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
  900. }
  901. }
  902. m_fields.push_back(spec);
  903. }
  904. void GUIFormSpecMenu::parseTextArea(parserData* data,
  905. std::vector<std::string>& parts,std::string type)
  906. {
  907. std::vector<std::string> v_pos = split(parts[0],',');
  908. std::vector<std::string> v_geom = split(parts[1],',');
  909. std::string name = parts[2];
  910. std::string label = parts[3];
  911. std::string default_val = parts[4];
  912. MY_CHECKPOS(type,0);
  913. MY_CHECKGEOM(type,1);
  914. v2s32 pos;
  915. pos.X = stof(v_pos[0]) * (float) spacing.X;
  916. pos.Y = stof(v_pos[1]) * (float) spacing.Y;
  917. v2s32 geom;
  918. geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
  919. if (type == "textarea")
  920. {
  921. geom.Y = (stof(v_geom[1]) * (float)imgsize.Y) - (spacing.Y-imgsize.Y);
  922. pos.Y += m_btn_height;
  923. }
  924. else
  925. {
  926. pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
  927. pos.Y -= m_btn_height;
  928. geom.Y = m_btn_height*2;
  929. }
  930. core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
  931. if(data->bp_set != 2)
  932. errorstream<<"WARNING: invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
  933. if(m_form_src)
  934. default_val = m_form_src->resolveText(default_val);
  935. default_val = unescape_string(default_val);
  936. label = unescape_string(label);
  937. std::wstring wlabel = narrow_to_wide(label.c_str());
  938. FieldSpec spec(
  939. narrow_to_wide(name.c_str()),
  940. wlabel,
  941. narrow_to_wide(default_val.c_str()),
  942. 258+m_fields.size()
  943. );
  944. if (name == "")
  945. {
  946. // spec field id to 0, this stops submit searching for a value that isn't there
  947. Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
  948. }
  949. else
  950. {
  951. spec.send = true;
  952. gui::IGUIEditBox *e =
  953. Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
  954. if (spec.fname == data->focused_fieldname) {
  955. Environment->setFocus(e);
  956. }
  957. if (type == "textarea")
  958. {
  959. e->setMultiLine(true);
  960. e->setWordWrap(true);
  961. e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_UPPERLEFT);
  962. } else {
  963. irr::SEvent evt;
  964. evt.EventType = EET_KEY_INPUT_EVENT;
  965. evt.KeyInput.Key = KEY_END;
  966. evt.KeyInput.Char = 0;
  967. evt.KeyInput.Control = 0;
  968. evt.KeyInput.Shift = 0;
  969. evt.KeyInput.PressedDown = true;
  970. e->OnEvent(evt);
  971. }
  972. if (label.length() >= 1)
  973. {
  974. rect.UpperLeftCorner.Y -= m_btn_height;
  975. rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + m_btn_height;
  976. Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
  977. }
  978. }
  979. m_fields.push_back(spec);
  980. }
  981. void GUIFormSpecMenu::parseField(parserData* data,std::string element,
  982. std::string type)
  983. {
  984. std::vector<std::string> parts = split(element,';');
  985. if (parts.size() == 3 || parts.size() == 4) {
  986. parseSimpleField(data,parts);
  987. return;
  988. }
  989. if ((parts.size() == 5) ||
  990. ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
  991. {
  992. parseTextArea(data,parts,type);
  993. return;
  994. }
  995. errorstream<< "Invalid field element(" << parts.size() << "): '" << element << "'" << std::endl;
  996. }
  997. void GUIFormSpecMenu::parseLabel(parserData* data,std::string element)
  998. {
  999. std::vector<std::string> parts = split(element,';');
  1000. if ((parts.size() == 2) ||
  1001. ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
  1002. {
  1003. std::vector<std::string> v_pos = split(parts[0],',');
  1004. std::string text = parts[1];
  1005. MY_CHECKPOS("label",0);
  1006. v2s32 pos = padding;
  1007. pos.X += stof(v_pos[0]) * (float)spacing.X;
  1008. pos.Y += stof(v_pos[1]) * (float)spacing.Y;
  1009. if(data->bp_set != 2)
  1010. errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
  1011. text = unescape_string(text);
  1012. std::wstring wlabel = narrow_to_wide(text.c_str());
  1013. core::rect<s32> rect = core::rect<s32>(
  1014. pos.X, pos.Y+((imgsize.Y/2) - m_btn_height),
  1015. pos.X + m_font->getDimension(wlabel.c_str()).Width,
  1016. pos.Y+((imgsize.Y/2) + m_btn_height));
  1017. FieldSpec spec(
  1018. L"",
  1019. wlabel,
  1020. L"",
  1021. 258+m_fields.size()
  1022. );
  1023. Environment->addStaticText(spec.flabel.c_str(), rect, false, false, this, spec.fid);
  1024. m_fields.push_back(spec);
  1025. return;
  1026. }
  1027. errorstream<< "Invalid label element(" << parts.size() << "): '" << element << "'" << std::endl;
  1028. }
  1029. void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element)
  1030. {
  1031. std::vector<std::string> parts = split(element,';');
  1032. if ((parts.size() == 2) ||
  1033. ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
  1034. {
  1035. std::vector<std::string> v_pos = split(parts[0],',');
  1036. std::wstring text = narrow_to_wide(unescape_string(parts[1]));
  1037. MY_CHECKPOS("vertlabel",1);
  1038. v2s32 pos = padding;
  1039. pos.X += stof(v_pos[0]) * (float)spacing.X;
  1040. pos.Y += stof(v_pos[1]) * (float)spacing.Y;
  1041. core::rect<s32> rect = core::rect<s32>(
  1042. pos.X, pos.Y+((imgsize.Y/2)- m_btn_height),
  1043. pos.X+15, pos.Y +
  1044. (m_font->getKerningHeight() +
  1045. m_font->getDimension(text.c_str()).Height)
  1046. * (text.length()+1));
  1047. //actually text.length() would be correct but adding +1 avoids to break all mods
  1048. if(data->bp_set != 2)
  1049. errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
  1050. std::wstring label = L"";
  1051. for (unsigned int i=0; i < text.length(); i++) {
  1052. label += text[i];
  1053. label += L"\n";
  1054. }
  1055. FieldSpec spec(
  1056. L"",
  1057. label,
  1058. L"",
  1059. 258+m_fields.size()
  1060. );
  1061. gui::IGUIStaticText *t =
  1062. Environment->addStaticText(spec.flabel.c_str(), rect, false, false, this, spec.fid);
  1063. t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
  1064. m_fields.push_back(spec);
  1065. return;
  1066. }
  1067. errorstream<< "Invalid vertlabel element(" << parts.size() << "): '" << element << "'" << std::endl;
  1068. }
  1069. void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,
  1070. std::string type)
  1071. {
  1072. std::vector<std::string> parts = split(element,';');
  1073. if ((((parts.size() >= 5) && (parts.size() <= 8)) && (parts.size() != 6)) ||
  1074. ((parts.size() > 8) && (m_formspec_version > FORMSPEC_API_VERSION)))
  1075. {
  1076. std::vector<std::string> v_pos = split(parts[0],',');
  1077. std::vector<std::string> v_geom = split(parts[1],',');
  1078. std::string image_name = parts[2];
  1079. std::string name = parts[3];
  1080. std::string label = parts[4];
  1081. MY_CHECKPOS("imagebutton",0);
  1082. MY_CHECKGEOM("imagebutton",1);
  1083. v2s32 pos = padding;
  1084. pos.X += stof(v_pos[0]) * (float)spacing.X;
  1085. pos.Y += stof(v_pos[1]) * (float)spacing.Y;
  1086. v2s32 geom;
  1087. geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
  1088. geom.Y = (stof(v_geom[1]) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
  1089. bool noclip = false;
  1090. bool drawborder = true;
  1091. std::string pressed_image_name = "";
  1092. if (parts.size() >= 7) {
  1093. if (parts[5] == "true")
  1094. noclip = true;
  1095. if (parts[6] == "false")
  1096. drawborder = false;
  1097. }
  1098. if (parts.size() >= 8) {
  1099. pressed_image_name = parts[7];
  1100. }
  1101. core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
  1102. if(data->bp_set != 2)
  1103. errorstream<<"WARNING: invalid use of image_button without a size[] element"<<std::endl;
  1104. image_name = unescape_string(image_name);
  1105. pressed_image_name = unescape_string(pressed_image_name);
  1106. label = unescape_string(label);
  1107. std::wstring wlabel = narrow_to_wide(label.c_str());
  1108. FieldSpec spec(
  1109. narrow_to_wide(name.c_str()),
  1110. wlabel,
  1111. narrow_to_wide(image_name.c_str()),
  1112. 258+m_fields.size()
  1113. );
  1114. spec.ftype = f_Button;
  1115. if(type == "image_button_exit")
  1116. spec.is_exit = true;
  1117. video::ITexture *texture = 0;
  1118. video::ITexture *pressed_texture = 0;
  1119. texture = m_tsrc->getTexture(image_name);
  1120. if (pressed_image_name != "")
  1121. pressed_texture = m_tsrc->getTexture(pressed_image_name);
  1122. else
  1123. pressed_texture = texture;
  1124. gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
  1125. if (spec.fname == data->focused_fieldname) {
  1126. Environment->setFocus(e);
  1127. }
  1128. e->setUseAlphaChannel(true);
  1129. e->setImage(texture);
  1130. e->setPressedImage(pressed_texture);
  1131. e->setScaleImage(true);
  1132. e->setNotClipped(noclip);
  1133. e->setDrawBorder(drawborder);
  1134. m_fields.push_back(spec);
  1135. return;
  1136. }
  1137. errorstream<< "Invalid imagebutton element(" << parts.size() << "): '" << element << "'" << std::endl;
  1138. }
  1139. void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element)
  1140. {
  1141. std::vector<std::string> parts = split(element,';');
  1142. if (((parts.size() == 4) || (parts.size() == 6)) ||
  1143. ((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION)))
  1144. {
  1145. std::vector<std::string> v_pos = split(parts[0],',');
  1146. std::string name = parts[1];
  1147. std::vector<std::string> buttons = split(parts[2],',');
  1148. std::string str_index = parts[3];
  1149. bool show_background = true;
  1150. bool show_border = true;
  1151. int tab_index = stoi(str_index) -1;
  1152. MY_CHECKPOS("tabheader",0);
  1153. if (parts.size() == 6) {
  1154. if (parts[4] == "true")
  1155. show_background = false;
  1156. if (parts[5] == "false")
  1157. show_border = false;
  1158. }
  1159. FieldSpec spec(
  1160. narrow_to_wide(name.c_str()),
  1161. L"",
  1162. L"",
  1163. 258+m_fields.size()
  1164. );
  1165. spec.ftype = f_TabHeader;
  1166. v2s32 pos(0,0);
  1167. pos.X += stof(v_pos[0]) * (float)spacing.X;
  1168. pos.Y += stof(v_pos[1]) * (float)spacing.Y - m_btn_height * 2;
  1169. v2s32 geom;
  1170. geom.X = data->screensize.Y;
  1171. geom.Y = m_btn_height*2;
  1172. core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X,
  1173. pos.Y+geom.Y);
  1174. gui::IGUITabControl *e = Environment->addTabControl(rect, this,
  1175. show_background, show_border, spec.fid);
  1176. e->setAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT,
  1177. irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT);
  1178. e->setTabHeight(m_btn_height*2);
  1179. if (spec.fname == data->focused_fieldname) {
  1180. Environment->setFocus(e);
  1181. }
  1182. e->setNotClipped(true);
  1183. for (unsigned int i=0; i< buttons.size(); i++) {
  1184. e->addTab(narrow_to_wide(buttons[i]).c_str(), -1);
  1185. }
  1186. if ((tab_index >= 0) &&
  1187. (buttons.size() < INT_MAX) &&
  1188. (tab_index < (int) buttons.size()))
  1189. e->setActiveTab(tab_index);
  1190. m_fields.push_back(spec);
  1191. return;
  1192. }
  1193. errorstream << "Invalid TabHeader element(" << parts.size() << "): '"
  1194. << element << "'" << std::endl;
  1195. }
  1196. void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element)
  1197. {
  1198. if (m_gamedef == 0) {
  1199. errorstream <<
  1200. "WARNING: invalid use of item_image_button with m_gamedef==0"
  1201. << std::endl;
  1202. return;
  1203. }
  1204. std::vector<std::string> parts = split(element,';');
  1205. if ((parts.size() == 5) ||
  1206. ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
  1207. {
  1208. std::vector<std::string> v_pos = split(parts[0],',');
  1209. std::vector<std::string> v_geom = split(parts[1],',');
  1210. std::string item_name = parts[2];
  1211. std::string name = parts[3];
  1212. std::string label = parts[4];
  1213. MY_CHECKPOS("itemimagebutton",0);
  1214. MY_CHECKGEOM("itemimagebutton",1);
  1215. v2s32 pos = padding;
  1216. pos.X += stof(v_pos[0]) * (float)spacing.X;
  1217. pos.Y += stof(v_pos[1]) * (float)spacing.Y;
  1218. v2s32 geom;
  1219. geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
  1220. geom.Y = (stof(v_geom[1]) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
  1221. core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
  1222. if(data->bp_set != 2)
  1223. errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;
  1224. IItemDefManager *idef = m_gamedef->idef();
  1225. ItemStack item;
  1226. item.deSerialize(item_name, idef);
  1227. video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
  1228. m_tooltips[narrow_to_wide(name.c_str())] =
  1229. TooltipSpec (item.getDefinition(idef).description,
  1230. m_default_tooltip_bgcolor,
  1231. m_default_tooltip_color);
  1232. label = unescape_string(label);
  1233. FieldSpec spec(
  1234. narrow_to_wide(name.c_str()),
  1235. narrow_to_wide(label.c_str()),
  1236. narrow_to_wide(item_name.c_str()),
  1237. 258+m_fields.size()
  1238. );
  1239. gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
  1240. if (spec.fname == data->focused_fieldname) {
  1241. Environment->setFocus(e);
  1242. }
  1243. e->setUseAlphaChannel(true);
  1244. e->setImage(texture);
  1245. e->setPressedImage(texture);
  1246. e->setScaleImage(true);
  1247. spec.ftype = f_Button;
  1248. rect+=data->basepos-padding;
  1249. spec.rect=rect;
  1250. m_fields.push_back(spec);
  1251. return;
  1252. }
  1253. errorstream<< "Invalid ItemImagebutton element(" << parts.size() << "): '" << element << "'" << std::endl;
  1254. }
  1255. void GUIFormSpecMenu::parseBox(parserData* data,std::string element)
  1256. {
  1257. std::vector<std::string> parts = split(element,';');
  1258. if ((parts.size() == 3) ||
  1259. ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
  1260. {
  1261. std::vector<std::string> v_pos = split(parts[0],',');
  1262. std::vector<std::string> v_geom = split(parts[1],',');
  1263. MY_CHECKPOS("box",0);
  1264. MY_CHECKGEOM("box",1);
  1265. v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
  1266. pos.X += stof(v_pos[0]) * (float) spacing.X;
  1267. pos.Y += stof(v_pos[1]) * (float) spacing.Y;
  1268. v2s32 geom;
  1269. geom.X = stof(v_geom[0]) * (float)spacing.X;
  1270. geom.Y = stof(v_geom[1]) * (float)spacing.Y;
  1271. video::SColor tmp_color;
  1272. if (parseColor(parts[2], tmp_color, false)) {
  1273. BoxDrawSpec spec(pos, geom, tmp_color);
  1274. m_boxes.push_back(spec);
  1275. }
  1276. else {
  1277. errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "' INVALID COLOR" << std::endl;
  1278. }
  1279. return;
  1280. }
  1281. errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'" << std::endl;
  1282. }
  1283. void GUIFormSpecMenu::parseBackgroundColor(parserData* data,std::string element)
  1284. {
  1285. std::vector<std::string> parts = split(element,';');
  1286. if (((parts.size() == 1) || (parts.size() == 2)) ||
  1287. ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
  1288. {
  1289. parseColor(parts[0],m_bgcolor,false);
  1290. if (parts.size() == 2) {
  1291. std::string fullscreen = parts[1];
  1292. m_bgfullscreen = is_yes(fullscreen);
  1293. }
  1294. return;
  1295. }
  1296. errorstream<< "Invalid bgcolor element(" << parts.size() << "): '" << element << "'" << std::endl;
  1297. }
  1298. void GUIFormSpecMenu::parseListColors(parserData* data,std::string element)
  1299. {
  1300. std::vector<std::string> parts = split(element,';');
  1301. if (((parts.size() == 2) || (parts.size() == 3) || (parts.size() == 5)) ||
  1302. ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
  1303. {
  1304. parseColor(parts[0], m_slotbg_n, false);
  1305. parseColor(parts[1], m_slotbg_h, false);
  1306. if (parts.size() >= 3) {
  1307. if (parseColor(parts[2], m_slotbordercolor, false)) {
  1308. m_slotborder = true;
  1309. }
  1310. }
  1311. if (parts.size() == 5) {
  1312. video::SColor tmp_color;
  1313. if (parseColor(parts[3], tmp_color, false))
  1314. m_default_tooltip_bgcolor = tmp_color;
  1315. if (parseColor(parts[4], tmp_color, false))
  1316. m_default_tooltip_color = tmp_color;
  1317. }
  1318. return;
  1319. }
  1320. errorstream<< "Invalid listcolors element(" << parts.size() << "): '" << element << "'" << std::endl;
  1321. }
  1322. void GUIFormSpecMenu::parseTooltip(parserData* data, std::string element)
  1323. {
  1324. std::vector<std::string> parts = split(element,';');
  1325. if (parts.size() == 2) {
  1326. std::string name = parts[0];
  1327. m_tooltips[narrow_to_wide(name.c_str())] = TooltipSpec (parts[1], m_default_tooltip_bgcolor, m_default_tooltip_color);
  1328. return;
  1329. } else if (parts.size() == 4) {
  1330. std::string name = parts[0];
  1331. video::SColor tmp_color1, tmp_color2;
  1332. if ( parseColor(parts[2], tmp_color1, false) && parseColor(parts[3], tmp_color2, false) ) {
  1333. m_tooltips[narrow_to_wide(name.c_str())] = TooltipSpec (parts[1], tmp_color1, tmp_color2);
  1334. return;
  1335. }
  1336. }
  1337. errorstream<< "Invalid tooltip element(" << parts.size() << "): '" << element << "'" << std::endl;
  1338. }
  1339. bool GUIFormSpecMenu::parseVersionDirect(std::string data)
  1340. {
  1341. //some prechecks
  1342. if (data == "")
  1343. return false;
  1344. std::vector<std::string> parts = split(data,'[');
  1345. if (parts.size() < 2) {
  1346. return false;
  1347. }
  1348. if (parts[0] != "formspec_version") {
  1349. return false;
  1350. }
  1351. if (is_number(parts[1])) {
  1352. m_formspec_version = mystoi(parts[1]);
  1353. return true;
  1354. }
  1355. return false;
  1356. }
  1357. void GUIFormSpecMenu::parseElement(parserData* data, std::string element)
  1358. {
  1359. //some prechecks
  1360. if (element == "")
  1361. return;
  1362. std::vector<std::string> parts = split(element,'[');
  1363. // ugly workaround to keep compatibility
  1364. if (parts.size() > 2) {
  1365. if (trim(parts[0]) == "image") {
  1366. for (unsigned int i=2;i< parts.size(); i++) {
  1367. parts[1] += "[" + parts[i];
  1368. }
  1369. }
  1370. else { return; }
  1371. }
  1372. if (parts.size() < 2) {
  1373. return;
  1374. }
  1375. std::string type = trim(parts[0]);
  1376. std::string description = trim(parts[1]);
  1377. if (type == "size") {
  1378. parseSize(data,description);
  1379. return;
  1380. }
  1381. if (type == "invsize") {
  1382. log_deprecated("Deprecated formspec element \"invsize\" is used");
  1383. parseSize(data,description);
  1384. return;
  1385. }
  1386. if (type == "list") {
  1387. parseList(data,description);
  1388. return;
  1389. }
  1390. if (type == "checkbox") {
  1391. parseCheckbox(data,description);
  1392. return;
  1393. }
  1394. if (type == "image") {
  1395. parseImage(data,description);
  1396. return;
  1397. }
  1398. if (type == "item_image") {
  1399. parseItemImage(data,description);
  1400. return;
  1401. }
  1402. if ((type == "button") || (type == "button_exit")) {
  1403. parseButton(data,description,type);
  1404. return;
  1405. }
  1406. if (type == "background") {
  1407. parseBackground(data,description);
  1408. return;
  1409. }
  1410. if (type == "tableoptions"){
  1411. parseTableOptions(data,description);
  1412. return;
  1413. }
  1414. if (type == "tablecolumns"){
  1415. parseTableColumns(data,description);
  1416. return;
  1417. }
  1418. if (type == "table"){
  1419. parseTable(data,description);
  1420. return;
  1421. }
  1422. if (type == "textlist"){
  1423. parseTextList(data,description);
  1424. return;
  1425. }
  1426. if (type == "dropdown"){
  1427. parseDropDown(data,description);
  1428. return;
  1429. }
  1430. if (type == "pwdfield") {
  1431. parsePwdField(data,description);
  1432. return;
  1433. }
  1434. if ((type == "field") || (type == "textarea")){
  1435. parseField(data,description,type);
  1436. return;
  1437. }
  1438. if (type == "label") {
  1439. parseLabel(data,description);
  1440. return;
  1441. }
  1442. if (type == "vertlabel") {
  1443. parseVertLabel(data,description);
  1444. return;
  1445. }
  1446. if (type == "item_image_button") {
  1447. parseItemImageButton(data,description);
  1448. return;
  1449. }
  1450. if ((type == "image_button") || (type == "image_button_exit")) {
  1451. parseImageButton(data,description,type);
  1452. return;
  1453. }
  1454. if (type == "tabheader") {
  1455. parseTabHeader(data,description);
  1456. return;
  1457. }
  1458. if (type == "box") {
  1459. parseBox(data,description);
  1460. return;
  1461. }
  1462. if (type == "bgcolor") {
  1463. parseBackgroundColor(data,description);
  1464. return;
  1465. }
  1466. if (type == "listcolors") {
  1467. parseListColors(data,description);
  1468. return;
  1469. }
  1470. if (type == "tooltip") {
  1471. parseTooltip(data,description);
  1472. return;
  1473. }
  1474. if (type == "scrollbar") {
  1475. parseScrollBar(data, description);
  1476. return;
  1477. }
  1478. // Ignore others
  1479. infostream
  1480. << "Unknown DrawSpec: type="<<type<<", data=\""<<description<<"\""
  1481. <<std::endl;
  1482. }
  1483. void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
  1484. {
  1485. /* useless to regenerate without a screensize */
  1486. if ((screensize.X <= 0) || (screensize.Y <= 0)) {
  1487. return;
  1488. }
  1489. parserData mydata;
  1490. //preserve tables
  1491. for (u32 i = 0; i < m_tables.size(); ++i) {
  1492. std::wstring tablename = m_tables[i].first.fname;
  1493. GUITable *table = m_tables[i].second;
  1494. mydata.table_dyndata[tablename] = table->getDynamicData();
  1495. }
  1496. //preserve focus
  1497. gui::IGUIElement *focused_element = Environment->getFocus();
  1498. if (focused_element && focused_element->getParent() == this) {
  1499. s32 focused_id = focused_element->getID();
  1500. if (focused_id > 257) {
  1501. for (u32 i=0; i<m_fields.size(); i++) {
  1502. if (m_fields[i].fid == focused_id) {
  1503. mydata.focused_fieldname =
  1504. m_fields[i].fname;
  1505. break;
  1506. }
  1507. }
  1508. }
  1509. }
  1510. // Remove children
  1511. removeChildren();
  1512. for (u32 i = 0; i < m_tables.size(); ++i) {
  1513. GUITable *table = m_tables[i].second;
  1514. table->drop();
  1515. }
  1516. mydata.size= v2s32(100,100);
  1517. mydata.screensize = screensize;
  1518. // Base position of contents of form
  1519. mydata.basepos = getBasePos();
  1520. // State of basepos, 0 = not set, 1= set by formspec, 2 = set by size[] element
  1521. // Used to adjust form size automatically if needed
  1522. // A proceed button is added if there is no size[] element
  1523. mydata.bp_set = 0;
  1524. /* Convert m_init_draw_spec to m_inventorylists */
  1525. m_inventorylists.clear();
  1526. m_images.clear();
  1527. m_backgrounds.clear();
  1528. m_itemimages.clear();
  1529. m_tables.clear();
  1530. m_checkboxes.clear();
  1531. m_scrollbars.clear();
  1532. m_fields.clear();
  1533. m_boxes.clear();
  1534. m_tooltips.clear();
  1535. // Set default values (fits old formspec values)
  1536. m_bgcolor = video::SColor(140,0,0,0);
  1537. m_bgfullscreen = false;
  1538. m_slotbg_n = video::SColor(255,128,128,128);
  1539. m_slotbg_h = video::SColor(255,192,192,192);
  1540. m_default_tooltip_bgcolor = video::SColor(255,110,130,60);
  1541. m_default_tooltip_color = video::SColor(255,255,255,255);
  1542. m_slotbordercolor = video::SColor(200,0,0,0);
  1543. m_slotborder = false;
  1544. m_clipbackground = false;
  1545. // Add tooltip
  1546. {
  1547. assert(m_tooltip_element == NULL);
  1548. // Note: parent != this so that the tooltip isn't clipped by the menu rectangle
  1549. m_tooltip_element = Environment->addStaticText(L"",core::rect<s32>(0,0,110,18));
  1550. m_tooltip_element->enableOverrideColor(true);
  1551. m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor);
  1552. m_tooltip_element->setDrawBackground(true);
  1553. m_tooltip_element->setDrawBorder(true);
  1554. m_tooltip_element->setOverrideColor(m_default_tooltip_color);
  1555. m_tooltip_element->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
  1556. m_tooltip_element->setWordWrap(false);
  1557. //we're not parent so no autograb for this one!
  1558. m_tooltip_element->grab();
  1559. }
  1560. std::vector<std::string> elements = split(m_formspec_string,']');
  1561. unsigned int i = 0;
  1562. /* try to read version from first element only */
  1563. if (elements.size() >= 1) {
  1564. if ( parseVersionDirect(elements[0]) ) {
  1565. i++;
  1566. }
  1567. }
  1568. for (; i< elements.size(); i++) {
  1569. parseElement(&mydata, elements[i]);
  1570. }
  1571. // If there's fields, add a Proceed button
  1572. if (m_fields.size() && mydata.bp_set != 2) {
  1573. // if the size wasn't set by an invsize[] or size[] adjust it now to fit all the fields
  1574. mydata.rect = core::rect<s32>(
  1575. mydata.screensize.X/2 - 580/2,
  1576. mydata.screensize.Y/2 - 300/2,
  1577. mydata.screensize.X/2 + 580/2,
  1578. mydata.screensize.Y/2 + 240/2+(m_fields.size()*60)
  1579. );
  1580. DesiredRect = mydata.rect;
  1581. recalculateAbsolutePosition(false);
  1582. mydata.basepos = getBasePos();
  1583. {
  1584. v2s32 pos = mydata.basepos;
  1585. pos.Y = ((m_fields.size()+2)*60);
  1586. v2s32 size = DesiredRect.getSize();
  1587. mydata.rect =
  1588. core::rect<s32>(size.X/2-70, pos.Y,
  1589. (size.X/2-70)+140, pos.Y + (m_btn_height*2));
  1590. wchar_t* text = wgettext("Proceed");
  1591. Environment->addButton(mydata.rect, this, 257, text);
  1592. delete[] text;
  1593. }
  1594. }
  1595. //set initial focus if parser didn't set it
  1596. focused_element = Environment->getFocus();
  1597. if (!focused_element
  1598. || !isMyChild(focused_element)
  1599. || focused_element->getType() == gui::EGUIET_TAB_CONTROL)
  1600. setInitialFocus();
  1601. }
  1602. #ifdef __ANDROID__
  1603. bool GUIFormSpecMenu::getAndroidUIInput()
  1604. {
  1605. /* no dialog shown */
  1606. if (m_JavaDialogFieldName == L"") {
  1607. return false;
  1608. }
  1609. /* still waiting */
  1610. if (porting::getInputDialogState() == -1) {
  1611. return true;
  1612. }
  1613. std::wstring fieldname = m_JavaDialogFieldName;
  1614. m_JavaDialogFieldName = L"";
  1615. /* no value abort dialog processing */
  1616. if (porting::getInputDialogState() != 0) {
  1617. return false;
  1618. }
  1619. for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
  1620. iter != m_fields.end(); iter++) {
  1621. if (iter->fname != fieldname) {
  1622. continue;
  1623. }
  1624. IGUIElement* tochange = getElementFromId(iter->fid);
  1625. if (tochange == 0) {
  1626. return false;
  1627. }
  1628. if (tochange->getType() != irr::gui::EGUIET_EDIT_BOX) {
  1629. return false;
  1630. }
  1631. std::string text = porting::getInputDialogValue();
  1632. ((gui::IGUIEditBox*) tochange)->
  1633. setText(narrow_to_wide(text).c_str());
  1634. }
  1635. return false;
  1636. }
  1637. #endif
  1638. GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
  1639. {
  1640. core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
  1641. for(u32 i=0; i<m_inventorylists.size(); i++)
  1642. {
  1643. const ListDrawSpec &s = m_inventorylists[i];
  1644. for(s32 i=0; i<s.geom.X*s.geom.Y; i++) {
  1645. s32 item_i = i + s.start_item_i;
  1646. s32 x = (i%s.geom.X) * spacing.X;
  1647. s32 y = (i/s.geom.X) * spacing.Y;
  1648. v2s32 p0(x,y);
  1649. core::rect<s32> rect = imgrect + s.pos + p0;
  1650. if(rect.isPointInside(p))
  1651. {
  1652. return ItemSpec(s.inventoryloc, s.listname, item_i);
  1653. }
  1654. }
  1655. }
  1656. return ItemSpec(InventoryLocation(), "", -1);
  1657. }
  1658. void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
  1659. {
  1660. video::IVideoDriver* driver = Environment->getVideoDriver();
  1661. // Get font
  1662. gui::IGUIFont *font = NULL;
  1663. gui::IGUISkin* skin = Environment->getSkin();
  1664. if (skin)
  1665. font = skin->getFont();
  1666. Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
  1667. if(!inv){
  1668. infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
  1669. <<"The inventory location "
  1670. <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
  1671. <<std::endl;
  1672. return;
  1673. }
  1674. InventoryList *ilist = inv->getList(s.listname);
  1675. if(!ilist){
  1676. infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
  1677. <<"The inventory list \""<<s.listname<<"\" @ \""
  1678. <<s.inventoryloc.dump()<<"\" doesn't exist"
  1679. <<std::endl;
  1680. return;
  1681. }
  1682. core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
  1683. for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
  1684. {
  1685. s32 item_i = i + s.start_item_i;
  1686. if(item_i >= (s32) ilist->getSize())
  1687. break;
  1688. s32 x = (i%s.geom.X) * spacing.X;
  1689. s32 y = (i/s.geom.X) * spacing.Y;
  1690. v2s32 p(x,y);
  1691. core::rect<s32> rect = imgrect + s.pos + p;
  1692. ItemStack item;
  1693. if(ilist)
  1694. item = ilist->getItem(item_i);
  1695. bool selected = m_selected_item
  1696. && m_invmgr->getInventory(m_selected_item->inventoryloc) == inv
  1697. && m_selected_item->listname == s.listname
  1698. && m_selected_item->i == item_i;
  1699. bool hovering = rect.isPointInside(m_pointer);
  1700. if(phase == 0)
  1701. {
  1702. if(hovering)
  1703. driver->draw2DRectangle(m_slotbg_h, rect, &AbsoluteClippingRect);
  1704. else
  1705. driver->draw2DRectangle(m_slotbg_n, rect, &AbsoluteClippingRect);
  1706. }
  1707. //Draw inv slot borders
  1708. if (m_slotborder) {
  1709. s32 x1 = rect.UpperLeftCorner.X;
  1710. s32 y1 = rect.UpperLeftCorner.Y;
  1711. s32 x2 = rect.LowerRightCorner.X;
  1712. s32 y2 = rect.LowerRightCorner.Y;
  1713. s32 border = 1;
  1714. driver->draw2DRectangle(m_slotbordercolor,
  1715. core::rect<s32>(v2s32(x1 - border, y1 - border),
  1716. v2s32(x2 + border, y1)), NULL);
  1717. driver->draw2DRectangle(m_slotbordercolor,
  1718. core::rect<s32>(v2s32(x1 - border, y2),
  1719. v2s32(x2 + border, y2 + border)), NULL);
  1720. driver->draw2DRectangle(m_slotbordercolor,
  1721. core::rect<s32>(v2s32(x1 - border, y1),
  1722. v2s32(x1, y2)), NULL);
  1723. driver->draw2DRectangle(m_slotbordercolor,
  1724. core::rect<s32>(v2s32(x2, y1),
  1725. v2s32(x2 + border, y2)), NULL);
  1726. }
  1727. if(phase == 1)
  1728. {
  1729. // Draw item stack
  1730. if(selected)
  1731. {
  1732. item.takeItem(m_selected_amount);
  1733. }
  1734. if(!item.empty())
  1735. {
  1736. drawItemStack(driver, font, item,
  1737. rect, &AbsoluteClippingRect, m_gamedef);
  1738. }
  1739. // Draw tooltip
  1740. std::string tooltip_text = "";
  1741. if(hovering && !m_selected_item)
  1742. tooltip_text = item.getDefinition(m_gamedef->idef()).description;
  1743. if(tooltip_text != "")
  1744. {
  1745. m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor);
  1746. m_tooltip_element->setOverrideColor(m_default_tooltip_color);
  1747. m_tooltip_element->setVisible(true);
  1748. this->bringToFront(m_tooltip_element);
  1749. m_tooltip_element->setText(narrow_to_wide(tooltip_text).c_str());
  1750. s32 tooltip_x = m_pointer.X + m_btn_height;
  1751. s32 tooltip_y = m_pointer.Y + m_btn_height;
  1752. s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
  1753. s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
  1754. m_tooltip_element->setRelativePosition(core::rect<s32>(
  1755. core::position2d<s32>(tooltip_x, tooltip_y),
  1756. core::dimension2d<s32>(tooltip_width, tooltip_height)));
  1757. }
  1758. }
  1759. }
  1760. }
  1761. void GUIFormSpecMenu::drawSelectedItem()
  1762. {
  1763. if(!m_selected_item)
  1764. return;
  1765. video::IVideoDriver* driver = Environment->getVideoDriver();
  1766. // Get font
  1767. gui::IGUIFont *font = NULL;
  1768. gui::IGUISkin* skin = Environment->getSkin();
  1769. if (skin)
  1770. font = skin->getFont();
  1771. Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
  1772. assert(inv);
  1773. InventoryList *list = inv->getList(m_selected_item->listname);
  1774. assert(list);
  1775. ItemStack stack = list->getItem(m_selected_item->i);
  1776. stack.count = m_selected_amount;
  1777. core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
  1778. core::rect<s32> rect = imgrect + (m_pointer - imgrect.getCenter());
  1779. drawItemStack(driver, font, stack, rect, NULL, m_gamedef);
  1780. }
  1781. void GUIFormSpecMenu::drawMenu()
  1782. {
  1783. if(m_form_src){
  1784. std::string newform = m_form_src->getForm();
  1785. if(newform != m_formspec_string){
  1786. m_formspec_string = newform;
  1787. regenerateGui(m_screensize_old);
  1788. }
  1789. }
  1790. updateSelectedItem();
  1791. gui::IGUISkin* skin = Environment->getSkin();
  1792. if (!skin)
  1793. return;
  1794. video::IVideoDriver* driver = Environment->getVideoDriver();
  1795. v2u32 screenSize = driver->getScreenSize();
  1796. core::rect<s32> allbg(0, 0, screenSize.X , screenSize.Y);
  1797. if (m_bgfullscreen)
  1798. driver->draw2DRectangle(m_bgcolor, allbg, &allbg);
  1799. else
  1800. driver->draw2DRectangle(m_bgcolor, AbsoluteRect, &AbsoluteClippingRect);
  1801. m_tooltip_element->setVisible(false);
  1802. /*
  1803. Draw backgrounds
  1804. */
  1805. for(u32 i=0; i<m_backgrounds.size(); i++)
  1806. {
  1807. const ImageDrawSpec &spec = m_backgrounds[i];
  1808. video::ITexture *texture = m_tsrc->getTexture(spec.name);
  1809. if (texture != 0) {
  1810. // Image size on screen
  1811. core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
  1812. // Image rectangle on screen
  1813. core::rect<s32> rect = imgrect + spec.pos;
  1814. if (m_clipbackground) {
  1815. core::dimension2d<s32> absrec_size = AbsoluteRect.getSize();
  1816. rect = core::rect<s32>(AbsoluteRect.UpperLeftCorner.X - spec.pos.X,
  1817. AbsoluteRect.UpperLeftCorner.Y - spec.pos.Y,
  1818. AbsoluteRect.UpperLeftCorner.X + absrec_size.Width + spec.pos.X,
  1819. AbsoluteRect.UpperLeftCorner.Y + absrec_size.Height + spec.pos.Y);
  1820. }
  1821. const video::SColor color(255,255,255,255);
  1822. const video::SColor colors[] = {color,color,color,color};
  1823. driver->draw2DImage(texture, rect,
  1824. core::rect<s32>(core::position2d<s32>(0,0),
  1825. core::dimension2di(texture->getOriginalSize())),
  1826. NULL/*&AbsoluteClippingRect*/, colors, true);
  1827. }
  1828. else {
  1829. errorstream << "GUIFormSpecMenu::drawMenu() Draw backgrounds unable to load texture:" << std::endl;
  1830. errorstream << "\t" << spec.name << std::endl;
  1831. }
  1832. }
  1833. /*
  1834. Draw Boxes
  1835. */
  1836. for(u32 i=0; i<m_boxes.size(); i++)
  1837. {
  1838. const BoxDrawSpec &spec = m_boxes[i];
  1839. irr::video::SColor todraw = spec.color;
  1840. todraw.setAlpha(140);
  1841. core::rect<s32> rect(spec.pos.X,spec.pos.Y,
  1842. spec.pos.X + spec.geom.X,spec.pos.Y + spec.geom.Y);
  1843. driver->draw2DRectangle(todraw, rect, 0);
  1844. }
  1845. /*
  1846. Draw images
  1847. */
  1848. for(u32 i=0; i<m_images.size(); i++)
  1849. {
  1850. const ImageDrawSpec &spec = m_images[i];
  1851. video::ITexture *texture = m_tsrc->getTexture(spec.name);
  1852. if (texture != 0) {
  1853. const core::dimension2d<u32>& img_origsize = texture->getOriginalSize();
  1854. // Image size on screen
  1855. core::rect<s32> imgrect;
  1856. if (spec.scale)
  1857. imgrect = core::rect<s32>(0,0,spec.geom.X, spec.geom.Y);
  1858. else {
  1859. imgrect = core::rect<s32>(0,0,img_origsize.Width,img_origsize.Height);
  1860. }
  1861. // Image rectangle on screen
  1862. core::rect<s32> rect = imgrect + spec.pos;
  1863. const video::SColor color(255,255,255,255);
  1864. const video::SColor colors[] = {color,color,color,color};
  1865. driver->draw2DImage(texture, rect,
  1866. core::rect<s32>(core::position2d<s32>(0,0),img_origsize),
  1867. NULL/*&AbsoluteClippingRect*/, colors, true);
  1868. }
  1869. else {
  1870. errorstream << "GUIFormSpecMenu::drawMenu() Draw images unable to load texture:" << std::endl;
  1871. errorstream << "\t" << spec.name << std::endl;
  1872. }
  1873. }
  1874. /*
  1875. Draw item images
  1876. */
  1877. for(u32 i=0; i<m_itemimages.size(); i++)
  1878. {
  1879. if (m_gamedef == 0)
  1880. break;
  1881. const ImageDrawSpec &spec = m_itemimages[i];
  1882. IItemDefManager *idef = m_gamedef->idef();
  1883. ItemStack item;
  1884. item.deSerialize(spec.name, idef);
  1885. video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
  1886. // Image size on screen
  1887. core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
  1888. // Image rectangle on screen
  1889. core::rect<s32> rect = imgrect + spec.pos;
  1890. const video::SColor color(255,255,255,255);
  1891. const video::SColor colors[] = {color,color,color,color};
  1892. driver->draw2DImage(texture, rect,
  1893. core::rect<s32>(core::position2d<s32>(0,0),
  1894. core::dimension2di(texture->getOriginalSize())),
  1895. NULL/*&AbsoluteClippingRect*/, colors, true);
  1896. }
  1897. /*
  1898. Draw items
  1899. Phase 0: Item slot rectangles
  1900. Phase 1: Item images; prepare tooltip
  1901. */
  1902. int start_phase=0;
  1903. for(int phase=start_phase; phase<=1; phase++)
  1904. for(u32 i=0; i<m_inventorylists.size(); i++)
  1905. {
  1906. drawList(m_inventorylists[i], phase);
  1907. }
  1908. /*
  1909. Call base class
  1910. */
  1911. gui::IGUIElement::draw();
  1912. /* TODO find way to show tooltips on touchscreen */
  1913. #ifndef HAVE_TOUCHSCREENGUI
  1914. m_pointer = m_device->getCursorControl()->getPosition();
  1915. #endif
  1916. /*
  1917. Draw fields/buttons tooltips
  1918. */
  1919. gui::IGUIElement *hovered =
  1920. Environment->getRootGUIElement()->getElementFromPoint(m_pointer);
  1921. if (hovered != NULL) {
  1922. s32 id = hovered->getID();
  1923. u32 delta;
  1924. if (id == -1) {
  1925. m_old_tooltip_id = id;
  1926. m_old_tooltip = "";
  1927. delta = 0;
  1928. } else if (id != m_old_tooltip_id) {
  1929. m_hoovered_time = getTimeMs();
  1930. m_old_tooltip_id = id;
  1931. delta = 0;
  1932. } else if (id == m_old_tooltip_id) {
  1933. delta = porting::getDeltaMs(m_hoovered_time, getTimeMs());
  1934. }
  1935. if (id != -1 && delta >= m_tooltip_show_delay) {
  1936. for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
  1937. iter != m_fields.end(); iter++) {
  1938. if ( (iter->fid == id) && (m_tooltips[iter->fname].tooltip != "") ){
  1939. if (m_old_tooltip != m_tooltips[iter->fname].tooltip) {
  1940. m_old_tooltip = m_tooltips[iter->fname].tooltip;
  1941. m_tooltip_element->setText(narrow_to_wide(m_tooltips[iter->fname].tooltip).c_str());
  1942. s32 tooltip_x = m_pointer.X + m_btn_height;
  1943. s32 tooltip_y = m_pointer.Y + m_btn_height;
  1944. s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
  1945. if (tooltip_x + tooltip_width > (s32)screenSize.X)
  1946. tooltip_x = (s32)screenSize.X - tooltip_width - m_btn_height;
  1947. int lines_count = 1;
  1948. size_t i = 0;
  1949. while ((i = m_tooltips[iter->fname].tooltip.find("\n", i)) != std::string::npos) {
  1950. lines_count++;
  1951. i += 2;
  1952. }
  1953. s32 tooltip_height = m_tooltip_element->getTextHeight() * lines_count + 5;
  1954. m_tooltip_element->setRelativePosition(core::rect<s32>(
  1955. core::position2d<s32>(tooltip_x, tooltip_y),
  1956. core::dimension2d<s32>(tooltip_width, tooltip_height)));
  1957. }
  1958. m_tooltip_element->setBackgroundColor(m_tooltips[iter->fname].bgcolor);
  1959. m_tooltip_element->setOverrideColor(m_tooltips[iter->fname].color);
  1960. m_tooltip_element->setVisible(true);
  1961. this->bringToFront(m_tooltip_element);
  1962. break;
  1963. }
  1964. }
  1965. }
  1966. }
  1967. /*
  1968. Draw dragged item stack
  1969. */
  1970. drawSelectedItem();
  1971. }
  1972. void GUIFormSpecMenu::updateSelectedItem()
  1973. {
  1974. // If the selected stack has become empty for some reason, deselect it.
  1975. // If the selected stack has become inaccessible, deselect it.
  1976. // If the selected stack has become smaller, adjust m_selected_amount.
  1977. ItemStack selected = verifySelectedItem();
  1978. // WARNING: BLACK MAGIC
  1979. // See if there is a stack suited for our current guess.
  1980. // If such stack does not exist, clear the guess.
  1981. if(m_selected_content_guess.name != "" &&
  1982. selected.name == m_selected_content_guess.name &&
  1983. selected.count == m_selected_content_guess.count){
  1984. // Selected item fits the guess. Skip the black magic.
  1985. }
  1986. else if(m_selected_content_guess.name != ""){
  1987. bool found = false;
  1988. for(u32 i=0; i<m_inventorylists.size() && !found; i++){
  1989. const ListDrawSpec &s = m_inventorylists[i];
  1990. Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
  1991. if(!inv)
  1992. continue;
  1993. InventoryList *list = inv->getList(s.listname);
  1994. if(!list)
  1995. continue;
  1996. for(s32 i=0; i<s.geom.X*s.geom.Y && !found; i++){
  1997. u32 item_i = i + s.start_item_i;
  1998. if(item_i >= list->getSize())
  1999. continue;
  2000. ItemStack stack = list->getItem(item_i);
  2001. if(stack.name == m_selected_content_guess.name &&
  2002. stack.count == m_selected_content_guess.count){
  2003. found = true;
  2004. infostream<<"Client: Changing selected content guess to "
  2005. <<s.inventoryloc.dump()<<" "<<s.listname
  2006. <<" "<<item_i<<std::endl;
  2007. delete m_selected_item;
  2008. m_selected_item = new ItemSpec(s.inventoryloc, s.listname, item_i);
  2009. m_selected_amount = stack.count;
  2010. }
  2011. }
  2012. }
  2013. if(!found){
  2014. infostream<<"Client: Discarding selected content guess: "
  2015. <<m_selected_content_guess.getItemString()<<std::endl;
  2016. m_selected_content_guess.name = "";
  2017. }
  2018. }
  2019. // If craftresult is nonempty and nothing else is selected, select it now.
  2020. if(!m_selected_item)
  2021. {
  2022. for(u32 i=0; i<m_inventorylists.size(); i++)
  2023. {
  2024. const ListDrawSpec &s = m_inventorylists[i];
  2025. if(s.listname == "craftpreview")
  2026. {
  2027. Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
  2028. InventoryList *list = inv->getList("craftresult");
  2029. if(list && list->getSize() >= 1 && !list->getItem(0).empty())
  2030. {
  2031. m_selected_item = new ItemSpec;
  2032. m_selected_item->inventoryloc = s.inventoryloc;
  2033. m_selected_item->listname = "craftresult";
  2034. m_selected_item->i = 0;
  2035. m_selected_amount = 0;
  2036. m_selected_dragging = false;
  2037. break;
  2038. }
  2039. }
  2040. }
  2041. }
  2042. // If craftresult is selected, keep the whole stack selected
  2043. if(m_selected_item && m_selected_item->listname == "craftresult")
  2044. {
  2045. m_selected_amount = verifySelectedItem().count;
  2046. }
  2047. }
  2048. ItemStack GUIFormSpecMenu::verifySelectedItem()
  2049. {
  2050. // If the selected stack has become empty for some reason, deselect it.
  2051. // If the selected stack has become inaccessible, deselect it.
  2052. // If the selected stack has become smaller, adjust m_selected_amount.
  2053. // Return the selected stack.
  2054. if(m_selected_item)
  2055. {
  2056. if(m_selected_item->isValid())
  2057. {
  2058. Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
  2059. if(inv)
  2060. {
  2061. InventoryList *list = inv->getList(m_selected_item->listname);
  2062. if(list && (u32) m_selected_item->i < list->getSize())
  2063. {
  2064. ItemStack stack = list->getItem(m_selected_item->i);
  2065. if(m_selected_amount > stack.count)
  2066. m_selected_amount = stack.count;
  2067. if(!stack.empty())
  2068. return stack;
  2069. }
  2070. }
  2071. }
  2072. // selection was not valid
  2073. delete m_selected_item;
  2074. m_selected_item = NULL;
  2075. m_selected_amount = 0;
  2076. m_selected_dragging = false;
  2077. }
  2078. return ItemStack();
  2079. }
  2080. void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no)
  2081. {
  2082. if(m_text_dst)
  2083. {
  2084. std::map<std::string, std::string> fields;
  2085. if (quitmode == quit_mode_accept) {
  2086. fields["quit"] = "true";
  2087. }
  2088. if (quitmode == quit_mode_cancel) {
  2089. fields["quit"] = "true";
  2090. m_text_dst->gotText(fields);
  2091. return;
  2092. }
  2093. if (current_keys_pending.key_down) {
  2094. fields["key_down"] = "true";
  2095. current_keys_pending.key_down = false;
  2096. }
  2097. if (current_keys_pending.key_up) {
  2098. fields["key_up"] = "true";
  2099. current_keys_pending.key_up = false;
  2100. }
  2101. if (current_keys_pending.key_enter) {
  2102. fields["key_enter"] = "true";
  2103. current_keys_pending.key_enter = false;
  2104. }
  2105. if (current_keys_pending.key_escape) {
  2106. fields["key_escape"] = "true";
  2107. current_keys_pending.key_escape = false;
  2108. }
  2109. for(unsigned int i=0; i<m_fields.size(); i++) {
  2110. const FieldSpec &s = m_fields[i];
  2111. if(s.send) {
  2112. std::string name = wide_to_narrow(s.fname);
  2113. if(s.ftype == f_Button) {
  2114. fields[name] = wide_to_narrow(s.flabel);
  2115. }
  2116. else if(s.ftype == f_Table) {
  2117. GUITable *table = getTable(s.fname);
  2118. if (table) {
  2119. fields[name] = table->checkEvent();
  2120. }
  2121. }
  2122. else if(s.ftype == f_DropDown) {
  2123. // no dynamic cast possible due to some distributions shipped
  2124. // without rtti support in irrlicht
  2125. IGUIElement * element = getElementFromId(s.fid);
  2126. gui::IGUIComboBox *e = NULL;
  2127. if ((element) && (element->getType() == gui::EGUIET_COMBO_BOX)) {
  2128. e = static_cast<gui::IGUIComboBox*>(element);
  2129. }
  2130. s32 selected = e->getSelected();
  2131. if (selected >= 0) {
  2132. fields[name] =
  2133. wide_to_narrow(e->getItem(selected));
  2134. }
  2135. }
  2136. else if (s.ftype == f_TabHeader) {
  2137. // no dynamic cast possible due to some distributions shipped
  2138. // without rtti support in irrlicht
  2139. IGUIElement * element = getElementFromId(s.fid);
  2140. gui::IGUITabControl *e = NULL;
  2141. if ((element) && (element->getType() == gui::EGUIET_TAB_CONTROL)) {
  2142. e = static_cast<gui::IGUITabControl*>(element);
  2143. }
  2144. if (e != 0) {
  2145. std::stringstream ss;
  2146. ss << (e->getActiveTab() +1);
  2147. fields[name] = ss.str();
  2148. }
  2149. }
  2150. else if (s.ftype == f_CheckBox) {
  2151. // no dynamic cast possible due to some distributions shipped
  2152. // without rtti support in irrlicht
  2153. IGUIElement * element = getElementFromId(s.fid);
  2154. gui::IGUICheckBox *e = NULL;
  2155. if ((element) && (element->getType() == gui::EGUIET_CHECK_BOX)) {
  2156. e = static_cast<gui::IGUICheckBox*>(element);
  2157. }
  2158. if (e != 0) {
  2159. if (e->isChecked())
  2160. fields[name] = "true";
  2161. else
  2162. fields[name] = "false";
  2163. }
  2164. }
  2165. else if (s.ftype == f_ScrollBar) {
  2166. // no dynamic cast possible due to some distributions shipped
  2167. // without rtti support in irrlicht
  2168. IGUIElement * element = getElementFromId(s.fid);
  2169. gui::IGUIScrollBar *e = NULL;
  2170. if ((element) && (element->getType() == gui::EGUIET_SCROLL_BAR)) {
  2171. e = static_cast<gui::IGUIScrollBar*>(element);
  2172. }
  2173. if (e != 0) {
  2174. std::stringstream os;
  2175. os << e->getPos();
  2176. if (s.fdefault == L"Changed")
  2177. fields[name] = "CHG:" + os.str();
  2178. else
  2179. fields[name] = "VAL:" + os.str();
  2180. }
  2181. }
  2182. else
  2183. {
  2184. IGUIElement* e = getElementFromId(s.fid);
  2185. if(e != NULL) {
  2186. fields[name] = wide_to_narrow(e->getText());
  2187. }
  2188. }
  2189. }
  2190. }
  2191. m_text_dst->gotText(fields);
  2192. }
  2193. }
  2194. static bool isChild(gui::IGUIElement * tocheck, gui::IGUIElement * parent)
  2195. {
  2196. while(tocheck != NULL) {
  2197. if (tocheck == parent) {
  2198. return true;
  2199. }
  2200. tocheck = tocheck->getParent();
  2201. }
  2202. return false;
  2203. }
  2204. bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
  2205. {
  2206. // Fix Esc/Return key being eaten by checkboxen and tables
  2207. if(event.EventType==EET_KEY_INPUT_EVENT) {
  2208. KeyPress kp(event.KeyInput);
  2209. if (kp == EscapeKey || kp == CancelKey
  2210. || kp == getKeySetting("keymap_inventory")
  2211. || event.KeyInput.Key==KEY_RETURN) {
  2212. gui::IGUIElement *focused = Environment->getFocus();
  2213. if (focused && isMyChild(focused) &&
  2214. (focused->getType() == gui::EGUIET_LIST_BOX ||
  2215. focused->getType() == gui::EGUIET_CHECK_BOX)) {
  2216. OnEvent(event);
  2217. return true;
  2218. }
  2219. }
  2220. }
  2221. // Mouse wheel events: send to hovered element instead of focused
  2222. if(event.EventType==EET_MOUSE_INPUT_EVENT
  2223. && event.MouseInput.Event == EMIE_MOUSE_WHEEL) {
  2224. s32 x = event.MouseInput.X;
  2225. s32 y = event.MouseInput.Y;
  2226. gui::IGUIElement *hovered =
  2227. Environment->getRootGUIElement()->getElementFromPoint(
  2228. core::position2d<s32>(x, y));
  2229. if (hovered && isMyChild(hovered)) {
  2230. hovered->OnEvent(event);
  2231. return true;
  2232. }
  2233. }
  2234. if (event.EventType == EET_MOUSE_INPUT_EVENT) {
  2235. s32 x = event.MouseInput.X;
  2236. s32 y = event.MouseInput.Y;
  2237. gui::IGUIElement *hovered =
  2238. Environment->getRootGUIElement()->getElementFromPoint(
  2239. core::position2d<s32>(x, y));
  2240. if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
  2241. m_old_tooltip_id = -1;
  2242. m_old_tooltip = "";
  2243. }
  2244. if (!isChild(hovered,this)) {
  2245. if (DoubleClickDetection(event)) {
  2246. return true;
  2247. }
  2248. }
  2249. }
  2250. #ifdef __ANDROID__
  2251. // display software keyboard when clicking edit boxes
  2252. if (event.EventType == EET_MOUSE_INPUT_EVENT
  2253. && event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
  2254. gui::IGUIElement *hovered =
  2255. Environment->getRootGUIElement()->getElementFromPoint(
  2256. core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y));
  2257. if ((hovered) && (hovered->getType() == irr::gui::EGUIET_EDIT_BOX)) {
  2258. bool retval = hovered->OnEvent(event);
  2259. if (retval) {
  2260. Environment->setFocus(hovered);
  2261. }
  2262. m_JavaDialogFieldName = getNameByID(hovered->getID());
  2263. std::string message = gettext("Enter ");
  2264. std::string label = wide_to_narrow(getLabelByID(hovered->getID()));
  2265. if (label == "") {
  2266. label = "text";
  2267. }
  2268. message += gettext(label) + ":";
  2269. /* single line text input */
  2270. int type = 2;
  2271. /* multi line text input */
  2272. if (((gui::IGUIEditBox*) hovered)->isMultiLineEnabled()) {
  2273. type = 1;
  2274. }
  2275. /* passwords are always single line */
  2276. if (((gui::IGUIEditBox*) hovered)->isPasswordBox()) {
  2277. type = 3;
  2278. }
  2279. porting::showInputDialog(gettext("ok"), "",
  2280. wide_to_narrow(((gui::IGUIEditBox*) hovered)->getText()),
  2281. type);
  2282. return retval;
  2283. }
  2284. }
  2285. if (event.EventType == EET_TOUCH_INPUT_EVENT)
  2286. {
  2287. SEvent translated;
  2288. memset(&translated, 0, sizeof(SEvent));
  2289. translated.EventType = EET_MOUSE_INPUT_EVENT;
  2290. gui::IGUIElement* root = Environment->getRootGUIElement();
  2291. if (!root) {
  2292. errorstream
  2293. << "GUIFormSpecMenu::preprocessEvent unable to get root element"
  2294. << std::endl;
  2295. return false;
  2296. }
  2297. gui::IGUIElement* hovered = root->getElementFromPoint(
  2298. core::position2d<s32>(
  2299. event.TouchInput.X,
  2300. event.TouchInput.Y));
  2301. translated.MouseInput.X = event.TouchInput.X;
  2302. translated.MouseInput.Y = event.TouchInput.Y;
  2303. translated.MouseInput.Control = false;
  2304. bool dont_send_event = false;
  2305. if (event.TouchInput.touchedCount == 1) {
  2306. switch (event.TouchInput.Event) {
  2307. case ETIE_PRESSED_DOWN:
  2308. m_pointer = v2s32(event.TouchInput.X,event.TouchInput.Y);
  2309. translated.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN;
  2310. translated.MouseInput.ButtonStates = EMBSM_LEFT;
  2311. m_down_pos = m_pointer;
  2312. break;
  2313. case ETIE_MOVED:
  2314. m_pointer = v2s32(event.TouchInput.X,event.TouchInput.Y);
  2315. translated.MouseInput.Event = EMIE_MOUSE_MOVED;
  2316. translated.MouseInput.ButtonStates = EMBSM_LEFT;
  2317. break;
  2318. case ETIE_LEFT_UP:
  2319. translated.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
  2320. translated.MouseInput.ButtonStates = 0;
  2321. hovered = root->getElementFromPoint(m_down_pos);
  2322. /* we don't have a valid pointer element use last
  2323. * known pointer pos */
  2324. translated.MouseInput.X = m_pointer.X;
  2325. translated.MouseInput.Y = m_pointer.Y;
  2326. /* reset down pos */
  2327. m_down_pos = v2s32(0,0);
  2328. break;
  2329. default:
  2330. dont_send_event = true;
  2331. //this is not supposed to happen
  2332. errorstream
  2333. << "GUIFormSpecMenu::preprocessEvent unexpected usecase Event="
  2334. << event.TouchInput.Event << std::endl;
  2335. }
  2336. } else if ( (event.TouchInput.touchedCount == 2) &&
  2337. (event.TouchInput.Event == ETIE_PRESSED_DOWN) ) {
  2338. hovered = root->getElementFromPoint(m_down_pos);
  2339. translated.MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN;
  2340. translated.MouseInput.ButtonStates = EMBSM_LEFT | EMBSM_RIGHT;
  2341. translated.MouseInput.X = m_pointer.X;
  2342. translated.MouseInput.Y = m_pointer.Y;
  2343. if (hovered) {
  2344. hovered->OnEvent(translated);
  2345. }
  2346. translated.MouseInput.Event = EMIE_RMOUSE_LEFT_UP;
  2347. translated.MouseInput.ButtonStates = EMBSM_LEFT;
  2348. if (hovered) {
  2349. hovered->OnEvent(translated);
  2350. }
  2351. dont_send_event = true;
  2352. }
  2353. /* ignore unhandled 2 touch events ... accidental moving for example */
  2354. else if (event.TouchInput.touchedCount == 2) {
  2355. dont_send_event = true;
  2356. }
  2357. else if (event.TouchInput.touchedCount > 2) {
  2358. errorstream
  2359. << "GUIFormSpecMenu::preprocessEvent to many multitouch events "
  2360. << event.TouchInput.touchedCount << " ignoring them" << std::endl;
  2361. }
  2362. if (dont_send_event) {
  2363. return true;
  2364. }
  2365. /* check if translated event needs to be preprocessed again */
  2366. if (preprocessEvent(translated)) {
  2367. return true;
  2368. }
  2369. if (hovered) {
  2370. grab();
  2371. bool retval = hovered->OnEvent(translated);
  2372. if (event.TouchInput.Event == ETIE_LEFT_UP) {
  2373. /* reset pointer */
  2374. m_pointer = v2s32(0,0);
  2375. }
  2376. drop();
  2377. return retval;
  2378. }
  2379. }
  2380. #endif
  2381. return false;
  2382. }
  2383. /******************************************************************************/
  2384. bool GUIFormSpecMenu::DoubleClickDetection(const SEvent event)
  2385. {
  2386. if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
  2387. m_doubleclickdetect[0].pos = m_doubleclickdetect[1].pos;
  2388. m_doubleclickdetect[0].time = m_doubleclickdetect[1].time;
  2389. m_doubleclickdetect[1].pos = m_pointer;
  2390. m_doubleclickdetect[1].time = getTimeMs();
  2391. }
  2392. else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
  2393. u32 delta = porting::getDeltaMs(m_doubleclickdetect[0].time, getTimeMs());
  2394. if (delta > 400) {
  2395. return false;
  2396. }
  2397. double squaredistance =
  2398. m_doubleclickdetect[0].pos
  2399. .getDistanceFromSQ(m_doubleclickdetect[1].pos);
  2400. if (squaredistance > (30*30)) {
  2401. return false;
  2402. }
  2403. SEvent* translated = new SEvent();
  2404. assert(translated != 0);
  2405. //translate doubleclick to escape
  2406. memset(translated, 0, sizeof(SEvent));
  2407. translated->EventType = irr::EET_KEY_INPUT_EVENT;
  2408. translated->KeyInput.Key = KEY_ESCAPE;
  2409. translated->KeyInput.Control = false;
  2410. translated->KeyInput.Shift = false;
  2411. translated->KeyInput.PressedDown = true;
  2412. translated->KeyInput.Char = 0;
  2413. OnEvent(*translated);
  2414. // no need to send the key up event as we're already deleted
  2415. // and no one else did notice this event
  2416. delete translated;
  2417. return true;
  2418. }
  2419. return false;
  2420. }
  2421. bool GUIFormSpecMenu::OnEvent(const SEvent& event)
  2422. {
  2423. if(event.EventType==EET_KEY_INPUT_EVENT) {
  2424. KeyPress kp(event.KeyInput);
  2425. if (event.KeyInput.PressedDown && ( (kp == EscapeKey) ||
  2426. (kp == getKeySetting("keymap_inventory")) || (kp == CancelKey))) {
  2427. if (m_allowclose) {
  2428. doPause = false;
  2429. acceptInput(quit_mode_cancel);
  2430. quitMenu();
  2431. } else {
  2432. m_text_dst->gotText(narrow_to_wide("MenuQuit"));
  2433. }
  2434. return true;
  2435. }
  2436. if (event.KeyInput.PressedDown &&
  2437. (event.KeyInput.Key==KEY_RETURN ||
  2438. event.KeyInput.Key==KEY_UP ||
  2439. event.KeyInput.Key==KEY_DOWN)
  2440. ) {
  2441. switch (event.KeyInput.Key) {
  2442. case KEY_RETURN:
  2443. current_keys_pending.key_enter = true;
  2444. break;
  2445. case KEY_UP:
  2446. current_keys_pending.key_up = true;
  2447. break;
  2448. case KEY_DOWN:
  2449. current_keys_pending.key_down = true;
  2450. break;
  2451. break;
  2452. default:
  2453. //can't happen at all!
  2454. assert("reached a source line that can't ever been reached" == 0);
  2455. break;
  2456. }
  2457. if (current_keys_pending.key_enter && m_allowclose) {
  2458. acceptInput(quit_mode_accept);
  2459. quitMenu();
  2460. } else {
  2461. acceptInput();
  2462. }
  2463. return true;
  2464. }
  2465. }
  2466. if(event.EventType==EET_MOUSE_INPUT_EVENT
  2467. && event.MouseInput.Event != EMIE_MOUSE_MOVED) {
  2468. // Mouse event other than movement
  2469. // Get selected item and hovered/clicked item (s)
  2470. m_old_tooltip_id = -1;
  2471. updateSelectedItem();
  2472. ItemSpec s = getItemAtPos(m_pointer);
  2473. Inventory *inv_selected = NULL;
  2474. Inventory *inv_s = NULL;
  2475. if(m_selected_item) {
  2476. inv_selected = m_invmgr->getInventory(m_selected_item->inventoryloc);
  2477. assert(inv_selected);
  2478. assert(inv_selected->getList(m_selected_item->listname) != NULL);
  2479. }
  2480. u32 s_count = 0;
  2481. if(s.isValid())
  2482. do { // breakable
  2483. inv_s = m_invmgr->getInventory(s.inventoryloc);
  2484. if(!inv_s) {
  2485. errorstream<<"InventoryMenu: The selected inventory location "
  2486. <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
  2487. <<std::endl;
  2488. s.i = -1; // make it invalid again
  2489. break;
  2490. }
  2491. InventoryList *list = inv_s->getList(s.listname);
  2492. if(list == NULL) {
  2493. verbosestream<<"InventoryMenu: The selected inventory list \""
  2494. <<s.listname<<"\" does not exist"<<std::endl;
  2495. s.i = -1; // make it invalid again
  2496. break;
  2497. }
  2498. if((u32)s.i >= list->getSize()) {
  2499. infostream<<"InventoryMenu: The selected inventory list \""
  2500. <<s.listname<<"\" is too small (i="<<s.i<<", size="
  2501. <<list->getSize()<<")"<<std::endl;
  2502. s.i = -1; // make it invalid again
  2503. break;
  2504. }
  2505. s_count = list->getItem(s.i).count;
  2506. } while(0);
  2507. bool identical = (m_selected_item != NULL) && s.isValid() &&
  2508. (inv_selected == inv_s) &&
  2509. (m_selected_item->listname == s.listname) &&
  2510. (m_selected_item->i == s.i);
  2511. // buttons: 0 = left, 1 = right, 2 = middle
  2512. // up/down: 0 = down (press), 1 = up (release), 2 = unknown event
  2513. int button = 0;
  2514. int updown = 2;
  2515. if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
  2516. { button = 0; updown = 0; }
  2517. else if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
  2518. { button = 1; updown = 0; }
  2519. else if(event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN)
  2520. { button = 2; updown = 0; }
  2521. else if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
  2522. { button = 0; updown = 1; }
  2523. else if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
  2524. { button = 1; updown = 1; }
  2525. else if(event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP)
  2526. { button = 2; updown = 1; }
  2527. // Set this number to a positive value to generate a move action
  2528. // from m_selected_item to s.
  2529. u32 move_amount = 0;
  2530. // Set this number to a positive value to generate a drop action
  2531. // from m_selected_item.
  2532. u32 drop_amount = 0;
  2533. // Set this number to a positive value to generate a craft action at s.
  2534. u32 craft_amount = 0;
  2535. if(updown == 0) {
  2536. // Some mouse button has been pressed
  2537. //infostream<<"Mouse button "<<button<<" pressed at p=("
  2538. // <<p.X<<","<<p.Y<<")"<<std::endl;
  2539. m_selected_dragging = false;
  2540. if(s.isValid() && s.listname == "craftpreview") {
  2541. // Craft preview has been clicked: craft
  2542. craft_amount = (button == 2 ? 10 : 1);
  2543. }
  2544. else if(m_selected_item == NULL) {
  2545. if(s_count != 0) {
  2546. // Non-empty stack has been clicked: select it
  2547. m_selected_item = new ItemSpec(s);
  2548. if(button == 1) // right
  2549. m_selected_amount = (s_count + 1) / 2;
  2550. else if(button == 2) // middle
  2551. m_selected_amount = MYMIN(s_count, 10);
  2552. else // left
  2553. m_selected_amount = s_count;
  2554. m_selected_dragging = true;
  2555. }
  2556. }
  2557. else { // m_selected_item != NULL
  2558. assert(m_selected_amount >= 1);
  2559. if(s.isValid()) {
  2560. // Clicked a slot: move
  2561. if(button == 1) // right
  2562. move_amount = 1;
  2563. else if(button == 2) // middle
  2564. move_amount = MYMIN(m_selected_amount, 10);
  2565. else // left
  2566. move_amount = m_selected_amount;
  2567. if(identical) {
  2568. if(move_amount >= m_selected_amount)
  2569. m_selected_amount = 0;
  2570. else
  2571. m_selected_amount -= move_amount;
  2572. move_amount = 0;
  2573. }
  2574. }
  2575. else if (!getAbsoluteClippingRect().isPointInside(m_pointer)) {
  2576. // Clicked outside of the window: drop
  2577. if(button == 1) // right
  2578. drop_amount = 1;
  2579. else if(button == 2) // middle
  2580. drop_amount = MYMIN(m_selected_amount, 10);
  2581. else // left
  2582. drop_amount = m_selected_amount;
  2583. }
  2584. }
  2585. }
  2586. else if(updown == 1) {
  2587. // Some mouse button has been released
  2588. //infostream<<"Mouse button "<<button<<" released at p=("
  2589. // <<p.X<<","<<p.Y<<")"<<std::endl;
  2590. if(m_selected_item != NULL && m_selected_dragging && s.isValid()) {
  2591. if(!identical) {
  2592. // Dragged to different slot: move all selected
  2593. move_amount = m_selected_amount;
  2594. }
  2595. }
  2596. else if(m_selected_item != NULL && m_selected_dragging &&
  2597. !(getAbsoluteClippingRect().isPointInside(m_pointer))) {
  2598. // Dragged outside of window: drop all selected
  2599. drop_amount = m_selected_amount;
  2600. }
  2601. m_selected_dragging = false;
  2602. }
  2603. // Possibly send inventory action to server
  2604. if(move_amount > 0)
  2605. {
  2606. // Send IACTION_MOVE
  2607. assert(m_selected_item && m_selected_item->isValid());
  2608. assert(s.isValid());
  2609. assert(inv_selected && inv_s);
  2610. InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
  2611. InventoryList *list_to = inv_s->getList(s.listname);
  2612. assert(list_from && list_to);
  2613. ItemStack stack_from = list_from->getItem(m_selected_item->i);
  2614. ItemStack stack_to = list_to->getItem(s.i);
  2615. // Check how many items can be moved
  2616. move_amount = stack_from.count = MYMIN(move_amount, stack_from.count);
  2617. ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef());
  2618. // If source stack cannot be added to destination stack at all,
  2619. // they are swapped
  2620. if ((leftover.count == stack_from.count) &&
  2621. (leftover.name == stack_from.name)) {
  2622. m_selected_amount = stack_to.count;
  2623. // In case the server doesn't directly swap them but instead
  2624. // moves stack_to somewhere else, set this
  2625. m_selected_content_guess = stack_to;
  2626. m_selected_content_guess_inventory = s.inventoryloc;
  2627. }
  2628. // Source stack goes fully into destination stack
  2629. else if(leftover.empty()) {
  2630. m_selected_amount -= move_amount;
  2631. m_selected_content_guess = ItemStack(); // Clear
  2632. }
  2633. // Source stack goes partly into destination stack
  2634. else {
  2635. move_amount -= leftover.count;
  2636. m_selected_amount -= move_amount;
  2637. m_selected_content_guess = ItemStack(); // Clear
  2638. }
  2639. infostream<<"Handing IACTION_MOVE to manager"<<std::endl;
  2640. IMoveAction *a = new IMoveAction();
  2641. a->count = move_amount;
  2642. a->from_inv = m_selected_item->inventoryloc;
  2643. a->from_list = m_selected_item->listname;
  2644. a->from_i = m_selected_item->i;
  2645. a->to_inv = s.inventoryloc;
  2646. a->to_list = s.listname;
  2647. a->to_i = s.i;
  2648. m_invmgr->inventoryAction(a);
  2649. }
  2650. else if(drop_amount > 0) {
  2651. m_selected_content_guess = ItemStack(); // Clear
  2652. // Send IACTION_DROP
  2653. assert(m_selected_item && m_selected_item->isValid());
  2654. assert(inv_selected);
  2655. InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
  2656. assert(list_from);
  2657. ItemStack stack_from = list_from->getItem(m_selected_item->i);
  2658. // Check how many items can be dropped
  2659. drop_amount = stack_from.count = MYMIN(drop_amount, stack_from.count);
  2660. assert(drop_amount > 0 && drop_amount <= m_selected_amount);
  2661. m_selected_amount -= drop_amount;
  2662. infostream<<"Handing IACTION_DROP to manager"<<std::endl;
  2663. IDropAction *a = new IDropAction();
  2664. a->count = drop_amount;
  2665. a->from_inv = m_selected_item->inventoryloc;
  2666. a->from_list = m_selected_item->listname;
  2667. a->from_i = m_selected_item->i;
  2668. m_invmgr->inventoryAction(a);
  2669. }
  2670. else if(craft_amount > 0) {
  2671. m_selected_content_guess = ItemStack(); // Clear
  2672. // Send IACTION_CRAFT
  2673. assert(s.isValid());
  2674. assert(inv_s);
  2675. infostream<<"Handing IACTION_CRAFT to manager"<<std::endl;
  2676. ICraftAction *a = new ICraftAction();
  2677. a->count = craft_amount;
  2678. a->craft_inv = s.inventoryloc;
  2679. m_invmgr->inventoryAction(a);
  2680. }
  2681. // If m_selected_amount has been decreased to zero, deselect
  2682. if(m_selected_amount == 0) {
  2683. delete m_selected_item;
  2684. m_selected_item = NULL;
  2685. m_selected_amount = 0;
  2686. m_selected_dragging = false;
  2687. m_selected_content_guess = ItemStack();
  2688. }
  2689. }
  2690. if(event.EventType==EET_GUI_EVENT) {
  2691. if(event.GUIEvent.EventType==gui::EGET_TAB_CHANGED
  2692. && isVisible()) {
  2693. // find the element that was clicked
  2694. for(unsigned int i=0; i<m_fields.size(); i++) {
  2695. FieldSpec &s = m_fields[i];
  2696. if ((s.ftype == f_TabHeader) &&
  2697. (s.fid == event.GUIEvent.Caller->getID())) {
  2698. s.send = true;
  2699. acceptInput();
  2700. s.send = false;
  2701. return true;
  2702. }
  2703. }
  2704. }
  2705. if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
  2706. && isVisible()) {
  2707. if(!canTakeFocus(event.GUIEvent.Element)) {
  2708. infostream<<"GUIFormSpecMenu: Not allowing focus change."
  2709. <<std::endl;
  2710. // Returning true disables focus change
  2711. return true;
  2712. }
  2713. }
  2714. if((event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) ||
  2715. (event.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED) ||
  2716. (event.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED) ||
  2717. (event.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED)) {
  2718. unsigned int btn_id = event.GUIEvent.Caller->getID();
  2719. if (btn_id == 257) {
  2720. if (m_allowclose) {
  2721. acceptInput(quit_mode_accept);
  2722. quitMenu();
  2723. } else {
  2724. acceptInput();
  2725. m_text_dst->gotText(narrow_to_wide("ExitButton"));
  2726. }
  2727. // quitMenu deallocates menu
  2728. return true;
  2729. }
  2730. // find the element that was clicked
  2731. for(u32 i=0; i<m_fields.size(); i++) {
  2732. FieldSpec &s = m_fields[i];
  2733. // if its a button, set the send field so
  2734. // lua knows which button was pressed
  2735. if (((s.ftype == f_Button) || (s.ftype == f_CheckBox)) &&
  2736. (s.fid == event.GUIEvent.Caller->getID())) {
  2737. s.send = true;
  2738. if(s.is_exit) {
  2739. if (m_allowclose) {
  2740. acceptInput(quit_mode_accept);
  2741. quitMenu();
  2742. } else {
  2743. m_text_dst->gotText(narrow_to_wide("ExitButton"));
  2744. }
  2745. return true;
  2746. } else {
  2747. acceptInput(quit_mode_no);
  2748. s.send = false;
  2749. return true;
  2750. }
  2751. }
  2752. else if ((s.ftype == f_DropDown) &&
  2753. (s.fid == event.GUIEvent.Caller->getID())) {
  2754. // only send the changed dropdown
  2755. for(u32 i=0; i<m_fields.size(); i++) {
  2756. FieldSpec &s2 = m_fields[i];
  2757. if (s2.ftype == f_DropDown) {
  2758. s2.send = false;
  2759. }
  2760. }
  2761. s.send = true;
  2762. acceptInput(quit_mode_no);
  2763. // revert configuration to make sure dropdowns are sent on
  2764. // regular button click
  2765. for(u32 i=0; i<m_fields.size(); i++) {
  2766. FieldSpec &s2 = m_fields[i];
  2767. if (s2.ftype == f_DropDown) {
  2768. s2.send = true;
  2769. }
  2770. }
  2771. return true;
  2772. }
  2773. else if ((s.ftype == f_ScrollBar) &&
  2774. (s.fid == event.GUIEvent.Caller->getID()))
  2775. {
  2776. s.fdefault = L"Changed";
  2777. acceptInput(quit_mode_no);
  2778. s.fdefault = L"";
  2779. }
  2780. }
  2781. }
  2782. if(event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
  2783. if(event.GUIEvent.Caller->getID() > 257) {
  2784. if (m_allowclose) {
  2785. acceptInput(quit_mode_accept);
  2786. quitMenu();
  2787. } else {
  2788. current_keys_pending.key_enter = true;
  2789. acceptInput();
  2790. }
  2791. // quitMenu deallocates menu
  2792. return true;
  2793. }
  2794. }
  2795. if(event.GUIEvent.EventType == gui::EGET_TABLE_CHANGED) {
  2796. int current_id = event.GUIEvent.Caller->getID();
  2797. if(current_id > 257) {
  2798. // find the element that was clicked
  2799. for(u32 i=0; i<m_fields.size(); i++) {
  2800. FieldSpec &s = m_fields[i];
  2801. // if it's a table, set the send field
  2802. // so lua knows which table was changed
  2803. if ((s.ftype == f_Table) && (s.fid == current_id)) {
  2804. s.send = true;
  2805. acceptInput();
  2806. s.send=false;
  2807. }
  2808. }
  2809. return true;
  2810. }
  2811. }
  2812. }
  2813. return Parent ? Parent->OnEvent(event) : false;
  2814. }
  2815. /**
  2816. * get name of element by element id
  2817. * @param id of element
  2818. * @return name string or empty string
  2819. */
  2820. std::wstring GUIFormSpecMenu::getNameByID(s32 id)
  2821. {
  2822. for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
  2823. iter != m_fields.end(); iter++) {
  2824. if (iter->fid == id) {
  2825. return iter->fname;
  2826. }
  2827. }
  2828. return L"";
  2829. }
  2830. /**
  2831. * get label of element by id
  2832. * @param id of element
  2833. * @return label string or empty string
  2834. */
  2835. std::wstring GUIFormSpecMenu::getLabelByID(s32 id)
  2836. {
  2837. for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
  2838. iter != m_fields.end(); iter++) {
  2839. if (iter->fid == id) {
  2840. return iter->flabel;
  2841. }
  2842. }
  2843. return L"";
  2844. }
  2845. bool GUIFormSpecMenu::parseColor(const std::string &value, video::SColor &color,
  2846. bool quiet)
  2847. {
  2848. const char *hexpattern = NULL;
  2849. if (value[0] == '#') {
  2850. if (value.size() == 9)
  2851. hexpattern = "#RRGGBBAA";
  2852. else if (value.size() == 7)
  2853. hexpattern = "#RRGGBB";
  2854. else if (value.size() == 5)
  2855. hexpattern = "#RGBA";
  2856. else if (value.size() == 4)
  2857. hexpattern = "#RGB";
  2858. }
  2859. if (hexpattern) {
  2860. assert(strlen(hexpattern) == value.size());
  2861. video::SColor outcolor(255, 255, 255, 255);
  2862. for (size_t pos = 0; pos < value.size(); ++pos) {
  2863. // '#' in the pattern means skip that character
  2864. if (hexpattern[pos] == '#')
  2865. continue;
  2866. // Else assume hexpattern[pos] is one of 'R' 'G' 'B' 'A'
  2867. // Read one or two digits, depending on hexpattern
  2868. unsigned char c1, c2;
  2869. if (hexpattern[pos+1] == hexpattern[pos]) {
  2870. // Two digits, e.g. hexpattern == "#RRGGBB"
  2871. if (!hex_digit_decode(value[pos], c1) ||
  2872. !hex_digit_decode(value[pos+1], c2))
  2873. goto fail;
  2874. ++pos;
  2875. }
  2876. else {
  2877. // One digit, e.g. hexpattern == "#RGB"
  2878. if (!hex_digit_decode(value[pos], c1))
  2879. goto fail;
  2880. c2 = c1;
  2881. }
  2882. u32 colorpart = ((c1 & 0x0f) << 4) | (c2 & 0x0f);
  2883. // Update outcolor with newly read color part
  2884. if (hexpattern[pos] == 'R')
  2885. outcolor.setRed(colorpart);
  2886. else if (hexpattern[pos] == 'G')
  2887. outcolor.setGreen(colorpart);
  2888. else if (hexpattern[pos] == 'B')
  2889. outcolor.setBlue(colorpart);
  2890. else if (hexpattern[pos] == 'A')
  2891. outcolor.setAlpha(colorpart);
  2892. }
  2893. color = outcolor;
  2894. return true;
  2895. }
  2896. // Optionally, named colors could be implemented here
  2897. fail:
  2898. if (!quiet)
  2899. errorstream<<"Invalid color: \""<<value<<"\""<<std::endl;
  2900. return false;
  2901. }