]> git.lizzy.rs Git - minetest.git/blob - src/guiInventoryMenu.cpp
...Make the the server buildable again after the last commit
[minetest.git] / src / guiInventoryMenu.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20
21 #include "guiInventoryMenu.h"
22 #include "constants.h"
23 #include "keycode.h"
24 #include "strfnd.h"
25 #include <IGUICheckBox.h>
26 #include <IGUIEditBox.h>
27 #include <IGUIButton.h>
28 #include <IGUIStaticText.h>
29 #include <IGUIFont.h>
30 #include "log.h"
31
32 void drawInventoryItem(video::IVideoDriver *driver,
33                 gui::IGUIFont *font,
34                 InventoryItem *item, core::rect<s32> rect,
35                 const core::rect<s32> *clip)
36 {
37         if(item == NULL)
38                 return;
39         
40         video::ITexture *texture = NULL;
41         texture = item->getImage();
42
43         if(texture != NULL)
44         {
45                 const video::SColor color(255,255,255,255);
46                 const video::SColor colors[] = {color,color,color,color};
47                 driver->draw2DImage(texture, rect,
48                         core::rect<s32>(core::position2d<s32>(0,0),
49                         core::dimension2di(texture->getOriginalSize())),
50                         clip, colors, true);
51         }
52         else
53         {
54                 video::SColor bgcolor(255,50,50,128);
55                 driver->draw2DRectangle(bgcolor, rect, clip);
56         }
57
58         if(font != NULL)
59         {
60                 std::string text = item->getText();
61                 if(font && text != "")
62                 {
63                         v2u32 dim = font->getDimension(narrow_to_wide(text).c_str());
64                         v2s32 sdim(dim.X,dim.Y);
65
66                         core::rect<s32> rect2(
67                                 /*rect.UpperLeftCorner,
68                                 core::dimension2d<u32>(rect.getWidth(), 15)*/
69                                 rect.LowerRightCorner - sdim,
70                                 sdim
71                         );
72
73                         video::SColor bgcolor(128,0,0,0);
74                         driver->draw2DRectangle(bgcolor, rect2, clip);
75                         
76                         font->draw(text.c_str(), rect2,
77                                         video::SColor(255,255,255,255), false, false,
78                                         clip);
79                 }
80         }
81 }
82
83 /*
84         GUIInventoryMenu
85 */
86
87 GUIInventoryMenu::GUIInventoryMenu(gui::IGUIEnvironment* env,
88                 gui::IGUIElement* parent, s32 id,
89                 IMenuManager *menumgr,
90                 v2s16 menu_size,
91                 InventoryContext *c,
92                 InventoryManager *invmgr
93                 ):
94         GUIModalMenu(env, parent, id, menumgr),
95         m_menu_size(menu_size),
96         m_c(c),
97         m_invmgr(invmgr)
98 {
99         m_selected_item = NULL;
100 }
101
102 GUIInventoryMenu::~GUIInventoryMenu()
103 {
104         removeChildren();
105
106         if(m_selected_item)
107                 delete m_selected_item;
108 }
109
110 void GUIInventoryMenu::removeChildren()
111 {
112         const core::list<gui::IGUIElement*> &children = getChildren();
113         core::list<gui::IGUIElement*> children_copy;
114         for(core::list<gui::IGUIElement*>::ConstIterator
115                         i = children.begin(); i != children.end(); i++)
116         {
117                 children_copy.push_back(*i);
118         }
119         for(core::list<gui::IGUIElement*>::Iterator
120                         i = children_copy.begin();
121                         i != children_copy.end(); i++)
122         {
123                 (*i)->remove();
124         }
125         /*{
126                 gui::IGUIElement *e = getElementFromId(256);
127                 if(e != NULL)
128                         e->remove();
129         }*/
130 }
131
132 void GUIInventoryMenu::regenerateGui(v2u32 screensize)
133 {
134         // Remove children
135         removeChildren();
136         
137         /*padding = v2s32(24,24);
138         spacing = v2s32(60,56);
139         imgsize = v2s32(48,48);*/
140
141         padding = v2s32(screensize.Y/40, screensize.Y/40);
142         spacing = v2s32(screensize.Y/12, screensize.Y/13);
143         imgsize = v2s32(screensize.Y/15, screensize.Y/15);
144
145         s32 helptext_h = 15;
146
147         v2s32 size(
148                 padding.X*2+spacing.X*(m_menu_size.X-1)+imgsize.X,
149                 padding.Y*2+spacing.Y*(m_menu_size.Y-1)+imgsize.Y + helptext_h
150         );
151
152         core::rect<s32> rect(
153                         screensize.X/2 - size.X/2,
154                         screensize.Y/2 - size.Y/2,
155                         screensize.X/2 + size.X/2,
156                         screensize.Y/2 + size.Y/2
157         );
158         
159         DesiredRect = rect;
160         recalculateAbsolutePosition(false);
161
162         v2s32 basepos = getBasePos();
163         
164         m_draw_spec.clear();
165         for(u16 i=0; i<m_init_draw_spec.size(); i++)
166         {
167                 DrawSpec &s = m_init_draw_spec[i];
168                 if(s.type == "list")
169                 {
170                         m_draw_spec.push_back(ListDrawSpec(s.name, s.subname,
171                                         basepos + v2s32(spacing.X*s.pos.X, spacing.Y*s.pos.Y),
172                                         s.geom));
173                 }
174         }
175
176         /*
177         m_draw_spec.clear();
178         m_draw_spec.push_back(ListDrawSpec("main",
179                         basepos + v2s32(spacing.X*0, spacing.Y*3), v2s32(8, 4)));
180         m_draw_spec.push_back(ListDrawSpec("craft",
181                         basepos + v2s32(spacing.X*3, spacing.Y*0), v2s32(3, 3)));
182         m_draw_spec.push_back(ListDrawSpec("craftresult",
183                         basepos + v2s32(spacing.X*7, spacing.Y*1), v2s32(1, 1)));
184         */
185         
186         // Add children
187         {
188                 core::rect<s32> rect(0, 0, size.X-padding.X*2, helptext_h);
189                 rect = rect + v2s32(size.X/2 - rect.getWidth()/2,
190                                 size.Y-rect.getHeight()-15);
191                 const wchar_t *text =
192                 L"Left click: Move all items, Right click: Move single item";
193                 Environment->addStaticText(text, rect, false, true, this, 256);
194         }
195 }
196
197 GUIInventoryMenu::ItemSpec GUIInventoryMenu::getItemAtPos(v2s32 p) const
198 {
199         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
200         
201         for(u32 i=0; i<m_draw_spec.size(); i++)
202         {
203                 const ListDrawSpec &s = m_draw_spec[i];
204
205                 for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
206                 {
207                         s32 x = (i%s.geom.X) * spacing.X;
208                         s32 y = (i/s.geom.X) * spacing.Y;
209                         v2s32 p0(x,y);
210                         core::rect<s32> rect = imgrect + s.pos + p0;
211                         if(rect.isPointInside(p))
212                         {
213                                 return ItemSpec(s.inventoryname, s.listname, i);
214                         }
215                 }
216         }
217
218         return ItemSpec("", "", -1);
219 }
220
221 void GUIInventoryMenu::drawList(const ListDrawSpec &s)
222 {
223         video::IVideoDriver* driver = Environment->getVideoDriver();
224
225         // Get font
226         gui::IGUIFont *font = NULL;
227         gui::IGUISkin* skin = Environment->getSkin();
228         if (skin)
229                 font = skin->getFont();
230         
231         Inventory *inv = m_invmgr->getInventory(m_c, s.inventoryname);
232         assert(inv);
233         InventoryList *ilist = inv->getList(s.listname);
234         
235         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
236         
237         for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
238         {
239                 s32 x = (i%s.geom.X) * spacing.X;
240                 s32 y = (i/s.geom.X) * spacing.Y;
241                 v2s32 p(x,y);
242                 core::rect<s32> rect = imgrect + s.pos + p;
243                 InventoryItem *item = NULL;
244                 if(ilist)
245                         item = ilist->getItem(i);
246
247                 if(m_selected_item != NULL && m_selected_item->listname == s.listname
248                                 && m_selected_item->i == i)
249                 {
250                         /*s32 border = imgsize.X/12;
251                         driver->draw2DRectangle(video::SColor(255,192,192,192),
252                                         core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*border,
253                                                         rect.LowerRightCorner + v2s32(1,1)*border),
254                                         NULL);
255                         driver->draw2DRectangle(video::SColor(255,0,0,0),
256                                         core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*((border+1)/2),
257                                                         rect.LowerRightCorner + v2s32(1,1)*((border+1)/2)),
258                                         NULL);*/
259                         s32 border = 2;
260                         driver->draw2DRectangle(video::SColor(255,255,0,0),
261                                         core::rect<s32>(rect.UpperLeftCorner - v2s32(1,1)*border,
262                                                         rect.LowerRightCorner + v2s32(1,1)*border),
263                                         &AbsoluteClippingRect);
264                 }
265
266                 video::SColor bgcolor(255,128,128,128);
267                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
268
269                 if(item)
270                 {
271                         drawInventoryItem(driver, font, item,
272                                         rect, &AbsoluteClippingRect);
273                 }
274
275         }
276 }
277
278 void GUIInventoryMenu::drawMenu()
279 {
280         gui::IGUISkin* skin = Environment->getSkin();
281         if (!skin)
282                 return;
283         video::IVideoDriver* driver = Environment->getVideoDriver();
284         
285         video::SColor bgcolor(140,0,0,0);
286         driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
287
288         /*
289                 Draw items
290         */
291         
292         for(u32 i=0; i<m_draw_spec.size(); i++)
293         {
294                 ListDrawSpec &s = m_draw_spec[i];
295                 drawList(s);
296         }
297
298         /*
299                 Call base class
300         */
301         gui::IGUIElement::draw();
302 }
303
304 bool GUIInventoryMenu::OnEvent(const SEvent& event)
305 {
306         if(event.EventType==EET_KEY_INPUT_EVENT)
307         {
308                 KeyPress kp(event.KeyInput);
309                 if (event.KeyInput.PressedDown && (kp == EscapeKey ||
310                         kp == getKeySetting("keymap_inventory")))
311                 {
312                         quitMenu();
313                         return true;
314                 }
315         }
316         if(event.EventType==EET_MOUSE_INPUT_EVENT)
317         {
318                 char amount = -1;
319                 
320                 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
321                         amount = 0;
322                 else if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
323                         amount = 1;
324                 else if(event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN)
325                         amount = 10;
326                 
327                 if(amount >= 0)
328                 {
329                         v2s32 p(event.MouseInput.X, event.MouseInput.Y);
330                         //infostream<<"Mouse down at p=("<<p.X<<","<<p.Y<<")"<<std::endl;
331                         ItemSpec s = getItemAtPos(p);
332                         if(s.isValid())
333                         {
334                                 infostream<<"Mouse down on "<<s.inventoryname
335                                                 <<"/"<<s.listname<<" "<<s.i<<std::endl;
336                                 if(m_selected_item)
337                                 {
338                                         Inventory *inv_from = m_invmgr->getInventory(m_c,
339                                                         m_selected_item->inventoryname);
340                                         Inventory *inv_to = m_invmgr->getInventory(m_c,
341                                                         s.inventoryname);
342                                         assert(inv_from);
343                                         assert(inv_to);
344                                         InventoryList *list_from =
345                                                         inv_from->getList(m_selected_item->listname);
346                                         InventoryList *list_to =
347                                                         inv_to->getList(s.listname);
348                                         if(list_from == NULL)
349                                                 infostream<<"from list doesn't exist"<<std::endl;
350                                         if(list_to == NULL)
351                                                 infostream<<"to list doesn't exist"<<std::endl;
352                                         // Indicates whether source slot completely empties
353                                         bool source_empties = false;
354                                         if(list_from && list_to
355                                                         && list_from->getItem(m_selected_item->i) != NULL)
356                                         {
357                                                 infostream<<"Handing IACTION_MOVE to manager"<<std::endl;
358                                                 IMoveAction *a = new IMoveAction();
359                                                 a->count = amount;
360                                                 a->from_inv = m_selected_item->inventoryname;
361                                                 a->from_list = m_selected_item->listname;
362                                                 a->from_i = m_selected_item->i;
363                                                 a->to_inv = s.inventoryname;
364                                                 a->to_list = s.listname;
365                                                 a->to_i = s.i;
366                                                 //ispec.actions->push_back(a);
367                                                 m_invmgr->inventoryAction(a);
368                                                 
369                                                 if(list_from->getItem(m_selected_item->i)->getCount()==1)
370                                                         source_empties = true;
371                                         }
372                                         // Remove selection if target was left-clicked or source
373                                         // slot was emptied
374                                         if(amount == 0 || source_empties)
375                                         {
376                                                 delete m_selected_item;
377                                                 m_selected_item = NULL;
378                                         }
379                                 }
380                                 else
381                                 {
382                                         /*
383                                                 Select if non-NULL
384                                         */
385                                         Inventory *inv = m_invmgr->getInventory(m_c,
386                                                         s.inventoryname);
387                                         assert(inv);
388                                         InventoryList *list = inv->getList(s.listname);
389                                         if(list->getItem(s.i) != NULL)
390                                         {
391                                                 m_selected_item = new ItemSpec(s);
392                                         }
393                                 }
394                         }
395                         else
396                         {
397                                 if(m_selected_item)
398                                 {
399                                         delete m_selected_item;
400                                         m_selected_item = NULL;
401                                 }
402                         }
403                 }
404         }
405         if(event.EventType==EET_GUI_EVENT)
406         {
407                 if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
408                                 && isVisible())
409                 {
410                         if(!canTakeFocus(event.GUIEvent.Element))
411                         {
412                                 infostream<<"GUIInventoryMenu: Not allowing focus change."
413                                                 <<std::endl;
414                                 // Returning true disables focus change
415                                 return true;
416                         }
417                 }
418                 if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
419                 {
420                         /*switch(event.GUIEvent.Caller->getID())
421                         {
422                         case 256: // continue
423                                 setVisible(false);
424                                 break;
425                         case 257: // exit
426                                 dev->closeDevice();
427                                 break;
428                         }*/
429                 }
430         }
431
432         return Parent ? Parent->OnEvent(event) : false;
433 }
434
435 /*
436         Here is an example traditional set-up sequence for a DrawSpec list:
437
438         std::string furnace_inv_id = "nodemetadata:0,1,2";
439         core::array<GUIInventoryMenu::DrawSpec> draw_spec;
440         draw_spec.push_back(GUIInventoryMenu::DrawSpec(
441                         "list", furnace_inv_id, "fuel",
442                         v2s32(2, 3), v2s32(1, 1)));
443         draw_spec.push_back(GUIInventoryMenu::DrawSpec(
444                         "list", furnace_inv_id, "src",
445                         v2s32(2, 1), v2s32(1, 1)));
446         draw_spec.push_back(GUIInventoryMenu::DrawSpec(
447                         "list", furnace_inv_id, "dst",
448                         v2s32(5, 1), v2s32(2, 2)));
449         draw_spec.push_back(GUIInventoryMenu::DrawSpec(
450                         "list", "current_player", "main",
451                         v2s32(0, 5), v2s32(8, 4)));
452         setDrawSpec(draw_spec);
453
454         Here is the string for creating the same DrawSpec list (a single line,
455         spread to multiple lines here):
456         
457         GUIInventoryMenu::makeDrawSpecArrayFromString(
458                         draw_spec,
459                         "nodemetadata:0,1,2",
460                         "invsize[8,9;]"
461                         "list[current_name;fuel;2,3;1,1;]"
462                         "list[current_name;src;2,1;1,1;]"
463                         "list[current_name;dst;5,1;2,2;]"
464                         "list[current_player;main;0,5;8,4;]");
465         
466         Returns inventory menu size defined by invsize[].
467 */
468 v2s16 GUIInventoryMenu::makeDrawSpecArrayFromString(
469                 core::array<GUIInventoryMenu::DrawSpec> &draw_spec,
470                 const std::string &data,
471                 const std::string &current_name)
472 {
473         v2s16 invsize(8,9);
474         Strfnd f(data);
475         while(f.atend() == false)
476         {
477                 std::string type = trim(f.next("["));
478                 //infostream<<"type="<<type<<std::endl;
479                 if(type == "list")
480                 {
481                         std::string name = f.next(";");
482                         if(name == "current_name")
483                                 name = current_name;
484                         std::string subname = f.next(";");
485                         s32 pos_x = stoi(f.next(","));
486                         s32 pos_y = stoi(f.next(";"));
487                         s32 geom_x = stoi(f.next(","));
488                         s32 geom_y = stoi(f.next(";"));
489                         infostream<<"list name="<<name<<", subname="<<subname
490                                         <<", pos=("<<pos_x<<","<<pos_y<<")"
491                                         <<", geom=("<<geom_x<<","<<geom_y<<")"
492                                         <<std::endl;
493                         draw_spec.push_back(GUIInventoryMenu::DrawSpec(
494                                         type, name, subname,
495                                         v2s32(pos_x,pos_y),v2s32(geom_x,geom_y)));
496                         f.next("]");
497                 }
498                 else if(type == "invsize")
499                 {
500                         invsize.X = stoi(f.next(","));
501                         invsize.Y = stoi(f.next(";"));
502                         infostream<<"invsize ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
503                         f.next("]");
504                 }
505                 else
506                 {
507                         // Ignore others
508                         std::string ts = f.next("]");
509                         infostream<<"Unknown DrawSpec: type="<<type<<", data=\""<<ts<<"\""
510                                         <<std::endl;
511                 }
512         }
513
514         return invsize;
515 }
516