]> git.lizzy.rs Git - dragonfireclient.git/blob - src/guiFormSpecMenu.cpp
Bugfixes to item_image formspec method
[dragonfireclient.git] / src / guiFormSpecMenu.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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser 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 "guiFormSpecMenu.h"
22 #include "constants.h"
23 #include "gamedef.h"
24 #include "keycode.h"
25 #include "strfnd.h"
26 #include <IGUICheckBox.h>
27 #include <IGUIEditBox.h>
28 #include <IGUIButton.h>
29 #include <IGUIStaticText.h>
30 #include <IGUIFont.h>
31 #include "log.h"
32 #include "tile.h" // ITextureSource
33 #include "util/string.h"
34 #include "util/numeric.h"
35
36 #include "gettext.h"
37
38 void drawItemStack(video::IVideoDriver *driver,
39                 gui::IGUIFont *font,
40                 const ItemStack &item,
41                 const core::rect<s32> &rect,
42                 const core::rect<s32> *clip,
43                 IGameDef *gamedef)
44 {
45         if(item.empty())
46                 return;
47         
48         const ItemDefinition &def = item.getDefinition(gamedef->idef());
49         video::ITexture *texture = gamedef->idef()->getInventoryTexture(def.name, gamedef);
50
51         // Draw the inventory texture
52         if(texture != NULL)
53         {
54                 const video::SColor color(255,255,255,255);
55                 const video::SColor colors[] = {color,color,color,color};
56                 driver->draw2DImage(texture, rect,
57                         core::rect<s32>(core::position2d<s32>(0,0),
58                         core::dimension2di(texture->getOriginalSize())),
59                         clip, colors, true);
60         }
61
62         if(def.type == ITEM_TOOL && item.wear != 0)
63         {
64                 // Draw a progressbar
65                 float barheight = rect.getHeight()/16;
66                 float barpad_x = rect.getWidth()/16;
67                 float barpad_y = rect.getHeight()/16;
68                 core::rect<s32> progressrect(
69                         rect.UpperLeftCorner.X + barpad_x,
70                         rect.LowerRightCorner.Y - barpad_y - barheight,
71                         rect.LowerRightCorner.X - barpad_x,
72                         rect.LowerRightCorner.Y - barpad_y);
73
74                 // Shrink progressrect by amount of tool damage
75                 float wear = item.wear / 65535.0;
76                 int progressmid =
77                         wear * progressrect.UpperLeftCorner.X +
78                         (1-wear) * progressrect.LowerRightCorner.X;
79
80                 // Compute progressbar color
81                 //   wear = 0.0: green
82                 //   wear = 0.5: yellow
83                 //   wear = 1.0: red
84                 video::SColor color(255,255,255,255);
85                 int wear_i = MYMIN(floor(wear * 600), 511);
86                 wear_i = MYMIN(wear_i + 10, 511);
87                 if(wear_i <= 255)
88                         color.set(255, wear_i, 255, 0);
89                 else
90                         color.set(255, 255, 511-wear_i, 0);
91
92                 core::rect<s32> progressrect2 = progressrect;
93                 progressrect2.LowerRightCorner.X = progressmid;
94                 driver->draw2DRectangle(color, progressrect2, clip);
95
96                 color = video::SColor(255,0,0,0);
97                 progressrect2 = progressrect;
98                 progressrect2.UpperLeftCorner.X = progressmid;
99                 driver->draw2DRectangle(color, progressrect2, clip);
100         }
101
102         if(font != NULL && item.count >= 2)
103         {
104                 // Get the item count as a string
105                 std::string text = itos(item.count);
106                 v2u32 dim = font->getDimension(narrow_to_wide(text).c_str());
107                 v2s32 sdim(dim.X,dim.Y);
108
109                 core::rect<s32> rect2(
110                         /*rect.UpperLeftCorner,
111                         core::dimension2d<u32>(rect.getWidth(), 15)*/
112                         rect.LowerRightCorner - sdim,
113                         sdim
114                 );
115
116                 video::SColor bgcolor(128,0,0,0);
117                 driver->draw2DRectangle(bgcolor, rect2, clip);
118
119                 video::SColor color(255,255,255,255);
120                 font->draw(text.c_str(), rect2, color, false, false, clip);
121         }
122 }
123
124 /*
125         GUIFormSpecMenu
126 */
127
128 GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev,
129                 gui::IGUIElement* parent, s32 id,
130                 IMenuManager *menumgr,
131                 InventoryManager *invmgr,
132                 IGameDef *gamedef
133 ):
134         GUIModalMenu(dev->getGUIEnvironment(), parent, id, menumgr),
135         m_device(dev),
136         m_invmgr(invmgr),
137         m_gamedef(gamedef),
138         m_form_src(NULL),
139         m_text_dst(NULL),
140         m_selected_item(NULL),
141         m_selected_amount(0),
142         m_selected_dragging(false),
143         m_tooltip_element(NULL)
144 {
145 }
146
147 GUIFormSpecMenu::~GUIFormSpecMenu()
148 {
149         removeChildren();
150
151         delete m_selected_item;
152         delete m_form_src;
153         delete m_text_dst;
154 }
155
156 void GUIFormSpecMenu::removeChildren()
157 {
158         const core::list<gui::IGUIElement*> &children = getChildren();
159         core::list<gui::IGUIElement*> children_copy;
160         for(core::list<gui::IGUIElement*>::ConstIterator
161                         i = children.begin(); i != children.end(); i++)
162         {
163                 children_copy.push_back(*i);
164         }
165         for(core::list<gui::IGUIElement*>::Iterator
166                         i = children_copy.begin();
167                         i != children_copy.end(); i++)
168         {
169                 (*i)->remove();
170         }
171         /*{
172                 gui::IGUIElement *e = getElementFromId(256);
173                 if(e != NULL)
174                         e->remove();
175         }*/
176         if(m_tooltip_element)
177         {
178                 m_tooltip_element->remove();
179                 m_tooltip_element = NULL;
180         }
181 }
182
183 void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
184 {
185         // Remove children
186         removeChildren();
187         
188         v2s32 size(100,100);
189         s32 helptext_h = 15;
190         core::rect<s32> rect;
191
192         // Base position of contents of form
193         v2s32 basepos = getBasePos();
194         // State of basepos, 0 = not set, 1= set by formspec, 2 = set by size[] element
195         // Used to adjust form size automatically if needed
196         // A proceed button is added if there is no size[] element
197         int bp_set = 0;
198         
199         /* Convert m_init_draw_spec to m_inventorylists */
200         
201         m_inventorylists.clear();
202         m_images.clear();
203         m_backgrounds.clear();
204         m_itemimages.clear();
205         m_fields.clear();
206
207         Strfnd f(m_formspec_string);
208         while(f.atend() == false)
209         {
210                 std::string type = trim(f.next("["));
211                 if(type == "invsize" || type == "size")
212                 {
213                         v2f invsize;
214                         invsize.X = stof(f.next(","));
215                         if(type == "size")
216                         {
217                                 invsize.Y = stof(f.next("]"));
218                         }
219                         else{
220                                 invsize.Y = stof(f.next(";"));
221                                 f.next("]");
222                         }
223                         infostream<<"Form size ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
224
225                         padding = v2s32(screensize.Y/40, screensize.Y/40);
226                         spacing = v2s32(screensize.Y/12, screensize.Y/13);
227                         imgsize = v2s32(screensize.Y/15, screensize.Y/15);
228                         size = v2s32(
229                                 padding.X*2+spacing.X*(invsize.X-1.0)+imgsize.X,
230                                 padding.Y*2+spacing.Y*(invsize.Y-1.0)+imgsize.Y + (helptext_h-5)
231                         );
232                         rect = core::rect<s32>(
233                                         screensize.X/2 - size.X/2,
234                                         screensize.Y/2 - size.Y/2,
235                                         screensize.X/2 + size.X/2,
236                                         screensize.Y/2 + size.Y/2
237                         );
238                         DesiredRect = rect;
239                         recalculateAbsolutePosition(false);
240                         basepos = getBasePos();
241                         bp_set = 2;
242                 }
243                 else if(type == "list")
244                 {
245                         std::string name = f.next(";");
246                         InventoryLocation loc;
247                         if(name == "context" || name == "current_name")
248                                 loc = m_current_inventory_location;
249                         else
250                                 loc.deSerialize(name);
251                         std::string listname = f.next(";");
252                         v2s32 pos = basepos;
253                         pos.X += stof(f.next(",")) * (float)spacing.X;
254                         pos.Y += stof(f.next(";")) * (float)spacing.Y;
255                         v2s32 geom;
256                         geom.X = stoi(f.next(","));
257                         geom.Y = stoi(f.next(";"));
258                         infostream<<"list inv="<<name<<", listname="<<listname
259                                         <<", pos=("<<pos.X<<","<<pos.Y<<")"
260                                         <<", geom=("<<geom.X<<","<<geom.Y<<")"
261                                         <<std::endl;
262                         std::string start_i_s = f.next("]");
263                         s32 start_i = 0;
264                         if(start_i_s != "")
265                                 start_i = stoi(start_i_s);
266                         if(bp_set != 2)
267                                 errorstream<<"WARNING: invalid use of list without a size[] element"<<std::endl;
268                         m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom, start_i));
269                 }
270                 else if(type == "image")
271                 {
272                         v2s32 pos = basepos;
273                         pos.X += stof(f.next(",")) * (float)spacing.X;
274                         pos.Y += stof(f.next(";")) * (float)spacing.Y;
275                         v2s32 geom;
276                         geom.X = stof(f.next(",")) * (float)imgsize.X;
277                         geom.Y = stof(f.next(";")) * (float)imgsize.Y;
278                         std::string name = f.next("]");
279                         infostream<<"image name="<<name
280                                         <<", pos=("<<pos.X<<","<<pos.Y<<")"
281                                         <<", geom=("<<geom.X<<","<<geom.Y<<")"
282                                         <<std::endl;
283                         if(bp_set != 2)
284                                 errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
285                         m_images.push_back(ImageDrawSpec(name, pos, geom));
286                 }
287                 else if(type == "item_image")
288                 {
289                         v2s32 pos = basepos;
290                         pos.X += stof(f.next(",")) * (float)spacing.X;
291                         pos.Y += stof(f.next(";")) * (float)spacing.Y;
292                         v2s32 geom;
293                         geom.X = stof(f.next(",")) * (float)imgsize.X;
294                         geom.Y = stof(f.next(";")) * (float)imgsize.Y;
295                         std::string name = f.next("]");
296                         infostream<<"item name="<<name
297                                         <<", pos=("<<pos.X<<","<<pos.Y<<")"
298                                         <<", geom=("<<geom.X<<","<<geom.Y<<")"
299                                         <<std::endl;
300                         if(bp_set != 2)
301                                 errorstream<<"WARNING: invalid use of item_image without a size[] element"<<std::endl;
302                         m_itemimages.push_back(ImageDrawSpec(name, pos, geom));
303                 }
304                 else if(type == "background")
305                 {
306                         v2s32 pos = basepos;
307                         pos.X += stof(f.next(",")) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2;
308                         pos.Y += stof(f.next(";")) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2;
309                         v2s32 geom;
310                         geom.X = stof(f.next(",")) * (float)spacing.X;
311                         geom.Y = stof(f.next(";")) * (float)spacing.Y;
312                         std::string name = f.next("]");
313                         infostream<<"image name="<<name
314                                         <<", pos=("<<pos.X<<","<<pos.Y<<")"
315                                         <<", geom=("<<geom.X<<","<<geom.Y<<")"
316                                         <<std::endl;
317                         if(bp_set != 2)
318                                 errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl;
319                         m_backgrounds.push_back(ImageDrawSpec(name, pos, geom));
320                 }
321                 else if(type == "field")
322                 {
323                         std::string fname = f.next(";");
324                         std::string flabel = f.next(";");
325                         
326                         if(fname.find(",") == std::string::npos && flabel.find(",") == std::string::npos)
327                         {
328                                 if(!bp_set)
329                                 {
330                                         rect = core::rect<s32>(
331                                                 screensize.X/2 - 580/2,
332                                                 screensize.Y/2 - 300/2,
333                                                 screensize.X/2 + 580/2,
334                                                 screensize.Y/2 + 300/2
335                                         );
336                                         DesiredRect = rect;
337                                         recalculateAbsolutePosition(false);
338                                         basepos = getBasePos();
339                                         bp_set = 1;
340                                 }
341                                 else if(bp_set == 2)
342                                         errorstream<<"WARNING: invalid use of unpositioned field in inventory"<<std::endl;
343
344                                 v2s32 pos = basepos;
345                                 pos.Y = ((m_fields.size()+2)*60);
346                                 v2s32 size = DesiredRect.getSize();
347                                 rect = core::rect<s32>(size.X/2-150, pos.Y, (size.X/2-150)+300, pos.Y+30);
348                         }
349                         else
350                         {
351                                 v2s32 pos;
352                                 pos.X = stof(fname.substr(0,fname.find(","))) * (float)spacing.X;
353                                 pos.Y = stof(fname.substr(fname.find(",")+1)) * (float)spacing.Y;
354                                 v2s32 geom;
355                                 geom.X = (stof(flabel.substr(0,flabel.find(","))) * (float)spacing.X)-(spacing.X-imgsize.X);
356                                 pos.Y += (stof(flabel.substr(flabel.find(",")+1)) * (float)imgsize.Y)/2;
357
358                                 rect = core::rect<s32>(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15);
359                                 
360                                 fname = f.next(";");
361                                 flabel = f.next(";");
362                                 if(bp_set != 2)
363                                         errorstream<<"WARNING: invalid use of positioned field without a size[] element"<<std::endl;
364                                 
365                         }
366
367                         std::string odefault = f.next("]");
368                         std::string fdefault;
369                         
370                         // fdefault may contain a variable reference, which
371                         // needs to be resolved from the node metadata
372                         if(m_form_src)
373                                 fdefault = m_form_src->resolveText(odefault);
374                         else
375                                 fdefault = odefault;
376
377                         FieldSpec spec = FieldSpec(
378                                 narrow_to_wide(fname.c_str()),
379                                 narrow_to_wide(flabel.c_str()),
380                                 narrow_to_wide(fdefault.c_str()),
381                                 258+m_fields.size()
382                         );
383                         
384                         // three cases: field and no label, label and no field, label and field
385                         if (flabel == "") 
386                         {
387                                 spec.send = true;
388                                 gui::IGUIElement *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
389                                 Environment->setFocus(e);
390
391                                 irr::SEvent evt;
392                                 evt.EventType = EET_KEY_INPUT_EVENT;
393                                 evt.KeyInput.Key = KEY_END;
394                                 evt.KeyInput.PressedDown = true;
395                                 e->OnEvent(evt);
396                         }
397                         else if (fname == "")
398                         {
399                                 // set spec field id to 0, this stops submit searching for a value that isn't there
400                                 Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
401                         }
402                         else
403                         {
404                                 spec.send = true;
405                                 gui::IGUIElement *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
406                                 Environment->setFocus(e);
407                                 rect.UpperLeftCorner.Y -= 15;
408                                 rect.LowerRightCorner.Y -= 15;
409                                 Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
410
411                                 irr::SEvent evt;
412                                 evt.EventType = EET_KEY_INPUT_EVENT;
413                                 evt.KeyInput.Key = KEY_END;
414                                 evt.KeyInput.PressedDown = true;
415                                 e->OnEvent(evt);
416                         }
417                         
418                         m_fields.push_back(spec);
419                 }
420                 else if(type == "label")
421                 {
422                         v2s32 pos = padding;
423                         pos.X += stof(f.next(",")) * (float)spacing.X;
424                         pos.Y += stof(f.next(";")) * (float)spacing.Y;
425
426                         rect = core::rect<s32>(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+300, pos.Y+((imgsize.Y/2)+15));
427                         
428                         std::string flabel = f.next("]");
429                         if(bp_set != 2)
430                                 errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
431
432                         FieldSpec spec = FieldSpec(
433                                 narrow_to_wide(""),
434                                 narrow_to_wide(flabel.c_str()),
435                                 narrow_to_wide(""),
436                                 258+m_fields.size()
437                         );
438                         Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
439                         m_fields.push_back(spec);
440                 }
441                 else if(type == "button" || type == "button_exit")
442                 {
443                         v2s32 pos = padding;
444                         pos.X += stof(f.next(",")) * (float)spacing.X;
445                         pos.Y += stof(f.next(";")) * (float)spacing.Y;
446                         v2s32 geom;
447                         geom.X = (stof(f.next(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
448                         pos.Y += (stof(f.next(";")) * (float)imgsize.Y)/2;
449
450                         rect = core::rect<s32>(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15);
451                         
452                         std::string fname = f.next(";");
453                         std::string flabel = f.next("]");
454                         if(bp_set != 2)
455                                 errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
456
457                         FieldSpec spec = FieldSpec(
458                                 narrow_to_wide(fname.c_str()),
459                                 narrow_to_wide(flabel.c_str()),
460                                 narrow_to_wide(""),
461                                 258+m_fields.size()
462                         );
463                         spec.is_button = true;
464                         if(type == "button_exit")
465                                 spec.is_exit = true;
466                         Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
467                         m_fields.push_back(spec);
468                 }
469                 else if(type == "image_button" || type == "image_button_exit")
470                 {
471                         v2s32 pos = padding;
472                         pos.X += stof(f.next(",")) * (float)spacing.X;
473                         pos.Y += stof(f.next(";")) * (float)spacing.Y;
474                         v2s32 geom;
475                         geom.X = (stof(f.next(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
476                         geom.Y = (stof(f.next(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
477
478                         rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
479                         
480                         std::string fimage = f.next(";");
481                         std::string fname = f.next(";");
482                         std::string flabel = f.next("]");
483                         if(bp_set != 2)
484                                 errorstream<<"WARNING: invalid use of image_button without a size[] element"<<std::endl;
485
486                         FieldSpec spec = FieldSpec(
487                                 narrow_to_wide(fname.c_str()),
488                                 narrow_to_wide(flabel.c_str()),
489                                 narrow_to_wide(fimage.c_str()),
490                                 258+m_fields.size()
491                         );
492                         spec.is_button = true;
493                         if(type == "image_button_exit")
494                                 spec.is_exit = true;
495                         
496                         video::ITexture *texture = m_gamedef->tsrc()->getTextureRaw(fimage);
497                         gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
498                         e->setUseAlphaChannel(true);
499                         e->setImage(texture);
500                         e->setPressedImage(texture);
501                         e->setScaleImage(true);
502                         
503                         m_fields.push_back(spec);
504                 }
505                 else if(type == "item_image_button")
506                 {
507                         v2s32 pos = padding;
508                         pos.X += stof(f.next(",")) * (float)spacing.X;
509                         pos.Y += stof(f.next(";")) * (float)spacing.Y;
510                         v2s32 geom;
511                         geom.X = (stof(f.next(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
512                         geom.Y = (stof(f.next(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
513                         rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);               
514                         std::string fimage = f.next(";");
515                         std::string fname = f.next(";");
516                         std::string flabel = f.next("]");
517                         if(bp_set != 2)
518                                 errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;           
519                         IItemDefManager *idef = m_gamedef->idef();
520                         ItemStack item;
521                         item.deSerialize(fimage, idef);
522                         video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
523                         std::string tooltip = item.getDefinition(idef).description;
524                         FieldSpec spec = FieldSpec(
525                                 narrow_to_wide(fname.c_str()),
526                                 narrow_to_wide(flabel.c_str()),
527                                 narrow_to_wide(fimage.c_str()),
528                                 258+m_fields.size()
529                         );
530                         gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
531                         e->setUseAlphaChannel(true);
532                         e->setImage(texture);
533                         e->setPressedImage(texture);
534                         e->setScaleImage(true);
535                         spec.is_button = true;
536                         rect+=basepos-padding;
537                         spec.rect=rect;         
538                         if (tooltip!="")
539                                 spec.tooltip=tooltip;
540                         m_fields.push_back(spec);
541                 }
542                 else
543                 {
544                         // Ignore others
545                         std::string ts = f.next("]");
546                         infostream<<"Unknown DrawSpec: type="<<type<<", data=\""<<ts<<"\""
547                                         <<std::endl;
548                 }
549         }
550
551         // If there's inventory, put the usage string at the bottom
552         if (m_inventorylists.size())
553         {
554                 changeCtype("");
555                 core::rect<s32> rect(0, 0, size.X-padding.X*2, helptext_h);
556                 rect = rect + v2s32(size.X/2 - rect.getWidth()/2,
557                                 size.Y-rect.getHeight()-5);
558                 const wchar_t *text = wgettext("Left click: Move all items, Right click: Move single item");
559                 Environment->addStaticText(text, rect, false, true, this, 256);
560                 changeCtype("C");
561         }
562         // If there's fields, add a Proceed button
563         if (m_fields.size() && bp_set != 2) 
564         {
565                 // if the size wasn't set by an invsize[] or size[] adjust it now to fit all the fields
566                 rect = core::rect<s32>(
567                         screensize.X/2 - 580/2,
568                         screensize.Y/2 - 300/2,
569                         screensize.X/2 + 580/2,
570                         screensize.Y/2 + 240/2+(m_fields.size()*60)
571                 );
572                 DesiredRect = rect;
573                 recalculateAbsolutePosition(false);
574                 basepos = getBasePos();
575
576                 changeCtype("");
577                 {
578                         v2s32 pos = basepos;
579                         pos.Y = ((m_fields.size()+2)*60);
580
581                         v2s32 size = DesiredRect.getSize();
582                         rect = core::rect<s32>(size.X/2-70, pos.Y, (size.X/2-70)+140, pos.Y+30);
583                         Environment->addButton(rect, this, 257, wgettext("Proceed"));
584                 }
585                 changeCtype("C");
586         }
587         // Add tooltip
588         {
589                 // Note: parent != this so that the tooltip isn't clipped by the menu rectangle
590                 m_tooltip_element = Environment->addStaticText(L"",core::rect<s32>(0,0,110,18));
591                 m_tooltip_element->enableOverrideColor(true);
592                 m_tooltip_element->setBackgroundColor(video::SColor(255,110,130,60));
593                 m_tooltip_element->setDrawBackground(true);
594                 m_tooltip_element->setDrawBorder(true);
595                 m_tooltip_element->setOverrideColor(video::SColor(255,255,255,255));
596                 m_tooltip_element->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
597                 m_tooltip_element->setWordWrap(false);
598         }
599 }
600
601 GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
602 {
603         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
604         
605         for(u32 i=0; i<m_inventorylists.size(); i++)
606         {
607                 const ListDrawSpec &s = m_inventorylists[i];
608
609                 for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
610                 {
611                         s32 item_i = i + s.start_item_i;
612                         s32 x = (i%s.geom.X) * spacing.X;
613                         s32 y = (i/s.geom.X) * spacing.Y;
614                         v2s32 p0(x,y);
615                         core::rect<s32> rect = imgrect + s.pos + p0;
616                         if(rect.isPointInside(p))
617                         {
618                                 return ItemSpec(s.inventoryloc, s.listname, item_i);
619                         }
620                 }
621         }
622
623         return ItemSpec(InventoryLocation(), "", -1);
624 }
625
626 void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
627 {
628         video::IVideoDriver* driver = Environment->getVideoDriver();
629
630         // Get font
631         gui::IGUIFont *font = NULL;
632         gui::IGUISkin* skin = Environment->getSkin();
633         if (skin)
634                 font = skin->getFont();
635         
636         Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
637         if(!inv){
638                 infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
639                                 <<"The inventory location "
640                                 <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
641                                 <<std::endl;
642                 return;
643         }
644         InventoryList *ilist = inv->getList(s.listname);
645         if(!ilist){
646                 infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
647                                 <<"The inventory list \""<<s.listname<<"\" @ \""
648                                 <<s.inventoryloc.dump()<<"\" doesn't exist"
649                                 <<std::endl;
650                 return;
651         }
652         
653         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
654         
655         for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
656         {
657                 u32 item_i = i + s.start_item_i;
658                 if(item_i >= ilist->getSize())
659                         break;
660                 s32 x = (i%s.geom.X) * spacing.X;
661                 s32 y = (i/s.geom.X) * spacing.Y;
662                 v2s32 p(x,y);
663                 core::rect<s32> rect = imgrect + s.pos + p;
664                 ItemStack item;
665                 if(ilist)
666                         item = ilist->getItem(item_i);
667
668                 bool selected = m_selected_item
669                         && m_invmgr->getInventory(m_selected_item->inventoryloc) == inv
670                         && m_selected_item->listname == s.listname
671                         && m_selected_item->i == i;
672                 bool hovering = rect.isPointInside(m_pointer);
673
674                 if(phase == 0)
675                 {
676                         if(hovering && m_selected_item)
677                         {
678                                 video::SColor bgcolor(255,192,192,192);
679                                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
680                         }
681                         else
682                         {
683                                 video::SColor bgcolor(255,128,128,128);
684                                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
685                         }
686                 }
687
688                 if(phase == 1)
689                 {
690                         // Draw item stack
691                         if(selected)
692                         {
693                                 item.takeItem(m_selected_amount);
694                         }
695                         if(!item.empty())
696                         {
697                                 drawItemStack(driver, font, item,
698                                                 rect, &AbsoluteClippingRect, m_gamedef);
699                         }
700
701                         // Draw tooltip
702                         std::string tooltip_text = "";
703                         if(hovering && !m_selected_item)
704                                 tooltip_text = item.getDefinition(m_gamedef->idef()).description;
705                         if(tooltip_text != "")
706                         {
707                                 m_tooltip_element->setVisible(true);
708                                 this->bringToFront(m_tooltip_element);
709                                 m_tooltip_element->setText(narrow_to_wide(tooltip_text).c_str());
710                                 s32 tooltip_x = m_pointer.X + 15;
711                                 s32 tooltip_y = m_pointer.Y + 15;
712                                 s32 tooltip_width = m_tooltip_element->getTextWidth() + 15;
713                                 s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
714                                 m_tooltip_element->setRelativePosition(core::rect<s32>(
715                                                 core::position2d<s32>(tooltip_x, tooltip_y),
716                                                 core::dimension2d<s32>(tooltip_width, tooltip_height)));
717                         }
718                 }
719         }
720 }
721
722 void GUIFormSpecMenu::drawSelectedItem()
723 {
724         if(!m_selected_item)
725                 return;
726
727         video::IVideoDriver* driver = Environment->getVideoDriver();
728
729         // Get font
730         gui::IGUIFont *font = NULL;
731         gui::IGUISkin* skin = Environment->getSkin();
732         if (skin)
733                 font = skin->getFont();
734         
735         Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
736         assert(inv);
737         InventoryList *list = inv->getList(m_selected_item->listname);
738         assert(list);
739         ItemStack stack = list->getItem(m_selected_item->i);
740         stack.count = m_selected_amount;
741
742         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
743         core::rect<s32> rect = imgrect + (m_pointer - imgrect.getCenter());
744         drawItemStack(driver, font, stack, rect, NULL, m_gamedef);
745 }
746
747 void GUIFormSpecMenu::drawMenu()
748 {
749         if(m_form_src){
750                 std::string newform = m_form_src->getForm();
751                 if(newform != m_formspec_string){
752                         m_formspec_string = newform;
753                         regenerateGui(m_screensize_old);
754                 }
755         }
756
757         m_pointer = m_device->getCursorControl()->getPosition();
758
759         updateSelectedItem();
760
761         gui::IGUISkin* skin = Environment->getSkin();
762         if (!skin)
763                 return;
764         video::IVideoDriver* driver = Environment->getVideoDriver();
765         
766         video::SColor bgcolor(140,0,0,0);
767         driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
768
769         m_tooltip_element->setVisible(false);
770
771         /*
772                 Draw backgrounds
773         */
774         for(u32 i=0; i<m_backgrounds.size(); i++)
775         {
776                 const ImageDrawSpec &spec = m_backgrounds[i];
777                 video::ITexture *texture =
778                                 m_gamedef->tsrc()->getTextureRaw(spec.name);
779                 // Image size on screen
780                 core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
781                 // Image rectangle on screen
782                 core::rect<s32> rect = imgrect + spec.pos;
783                 const video::SColor color(255,255,255,255);
784                 const video::SColor colors[] = {color,color,color,color};
785                 driver->draw2DImage(texture, rect,
786                         core::rect<s32>(core::position2d<s32>(0,0),
787                                         core::dimension2di(texture->getOriginalSize())),
788                         NULL/*&AbsoluteClippingRect*/, colors, true);
789         }
790         
791         /*
792                 Draw images
793         */
794         for(u32 i=0; i<m_images.size(); i++)
795         {
796                 const ImageDrawSpec &spec = m_images[i];
797                 video::ITexture *texture =
798                                 m_gamedef->tsrc()->getTextureRaw(spec.name);
799                 // Image size on screen
800                 core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
801                 // Image rectangle on screen
802                 core::rect<s32> rect = imgrect + spec.pos;
803                 const video::SColor color(255,255,255,255);
804                 const video::SColor colors[] = {color,color,color,color};
805                 driver->draw2DImage(texture, rect,
806                         core::rect<s32>(core::position2d<s32>(0,0),
807                                         core::dimension2di(texture->getOriginalSize())),
808                         NULL/*&AbsoluteClippingRect*/, colors, true);
809         }
810         
811         /*
812                 Draw item images
813         */
814         for(u32 i=0; i<m_itemimages.size(); i++)
815         {
816                 const ImageDrawSpec &spec = m_itemimages[i];
817                 IItemDefManager *idef = m_gamedef->idef();
818                 ItemStack item;
819                 item.deSerialize(spec.name, idef);
820                 video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);         
821                 // Image size on screen
822                 core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
823                 // Image rectangle on screen
824                 core::rect<s32> rect = imgrect + spec.pos;
825                 const video::SColor color(255,255,255,255);
826                 const video::SColor colors[] = {color,color,color,color};
827                 driver->draw2DImage(texture, rect,
828                         core::rect<s32>(core::position2d<s32>(0,0),
829                                         core::dimension2di(texture->getOriginalSize())),
830                         NULL/*&AbsoluteClippingRect*/, colors, true);
831         }
832         
833         /*
834                 Draw items
835                 Phase 0: Item slot rectangles
836                 Phase 1: Item images; prepare tooltip
837                 If backgrounds used, do not draw Item slot rectangles
838         */
839         int start_phase=0;
840         if (m_backgrounds.size() > 0) start_phase=1;
841         for(int phase=start_phase; phase<=1; phase++)
842         for(u32 i=0; i<m_inventorylists.size(); i++)
843         {
844                 drawList(m_inventorylists[i], phase);
845         }
846
847         /*
848                 Call base class
849         */
850         gui::IGUIElement::draw();
851         
852         /*
853                 Draw fields/buttons tooltips
854         */
855         for(u32 i=0; i<m_fields.size(); i++)
856         {
857                 const FieldSpec &spec = m_fields[i];
858                 if (spec.tooltip != "")
859                 {
860                         core::rect<s32> rect = spec.rect;
861                         if (rect.isPointInside(m_pointer)) 
862                         {
863                                 m_tooltip_element->setVisible(true);
864                                 this->bringToFront(m_tooltip_element);
865                                 m_tooltip_element->setText(narrow_to_wide(spec.tooltip).c_str());
866                                 s32 tooltip_x = m_pointer.X + 15;
867                                 s32 tooltip_y = m_pointer.Y + 15;
868                                 s32 tooltip_width = m_tooltip_element->getTextWidth() + 15;
869                                 s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
870                                 m_tooltip_element->setRelativePosition(core::rect<s32>(
871                                 core::position2d<s32>(tooltip_x, tooltip_y),
872                                 core::dimension2d<s32>(tooltip_width, tooltip_height)));
873                         }
874                 }
875         }
876         
877         /*
878                 Draw dragged item stack
879         */
880         drawSelectedItem();
881 }
882
883 void GUIFormSpecMenu::updateSelectedItem()
884 {
885         // WARNING: BLACK MAGIC
886         // See if there is a stack suited for our current guess.
887         // If such stack does not exist, clear the guess.
888         if(m_selected_content_guess.name != "")
889         {
890                 bool found = false;
891                 for(u32 i=0; i<m_inventorylists.size() && !found; i++){
892                         const ListDrawSpec &s = m_inventorylists[i];
893                         Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
894                         if(!inv)
895                                 continue;
896                         InventoryList *list = inv->getList(s.listname);
897                         if(!list)
898                                 continue;
899                         for(s32 i=0; i<s.geom.X*s.geom.Y && !found; i++){
900                                 u32 item_i = i + s.start_item_i;
901                                 if(item_i >= list->getSize())
902                                         continue;
903                                 ItemStack stack = list->getItem(item_i);
904                                 if(stack.name == m_selected_content_guess.name &&
905                                                 stack.count == m_selected_content_guess.count){
906                                         found = true;
907                                         if(m_selected_item){
908                                                 // If guessed stack is already selected, all is fine
909                                                 if(m_selected_item->inventoryloc == s.inventoryloc &&
910                                                                 m_selected_item->listname == s.listname &&
911                                                                 m_selected_item->i == (s32)item_i &&
912                                                                 m_selected_amount == stack.count){
913                                                         break;
914                                                 }
915                                                 delete m_selected_item;
916                                                 m_selected_item = NULL;
917                                         }
918                                         infostream<<"Client: Changing selected content guess to "
919                                                         <<s.inventoryloc.dump()<<" "<<s.listname
920                                                         <<" "<<item_i<<std::endl;
921                                         m_selected_item = new ItemSpec(s.inventoryloc, s.listname, item_i);
922                                         m_selected_amount = stack.count;
923                                         break;
924                                 }
925                         }
926                 }
927                 if(!found){
928                         infostream<<"Client: Discarding selected content guess: "
929                                         <<m_selected_content_guess.getItemString()<<std::endl;
930                         m_selected_content_guess.name = "";
931                 }
932         }
933         // If the selected stack has become empty for some reason, deselect it.
934         // If the selected stack has become smaller, adjust m_selected_amount.
935         if(m_selected_item)
936         {
937                 bool selection_valid = false;
938                 if(m_selected_item->isValid())
939                 {
940                         Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
941                         if(inv)
942                         {
943                                 InventoryList *list = inv->getList(m_selected_item->listname);
944                                 if(list && (u32) m_selected_item->i < list->getSize())
945                                 {
946                                         ItemStack stack = list->getItem(m_selected_item->i);
947                                         if(m_selected_amount > stack.count)
948                                                 m_selected_amount = stack.count;
949                                         if(!stack.empty())
950                                                 selection_valid = true;
951                                 }
952                         }
953                 }
954                 if(!selection_valid)
955                 {
956                         delete m_selected_item;
957                         m_selected_item = NULL;
958                         m_selected_amount = 0;
959                         m_selected_dragging = false;
960                 }
961         }
962
963         // If craftresult is nonempty and nothing else is selected, select it now.
964         if(!m_selected_item)
965         {
966                 for(u32 i=0; i<m_inventorylists.size(); i++)
967                 {
968                         const ListDrawSpec &s = m_inventorylists[i];
969                         if(s.listname == "craftpreview")
970                         {
971                                 Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
972                                 InventoryList *list = inv->getList("craftresult");
973                                 if(list && list->getSize() >= 1 && !list->getItem(0).empty())
974                                 {
975                                         m_selected_item = new ItemSpec;
976                                         m_selected_item->inventoryloc = s.inventoryloc;
977                                         m_selected_item->listname = "craftresult";
978                                         m_selected_item->i = 0;
979                                         m_selected_amount = 0;
980                                         m_selected_dragging = false;
981                                         break;
982                                 }
983                         }
984                 }
985         }
986
987         // If craftresult is selected, keep the whole stack selected
988         if(m_selected_item && m_selected_item->listname == "craftresult")
989         {
990                 Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
991                 assert(inv);
992                 InventoryList *list = inv->getList(m_selected_item->listname);
993                 assert(list);
994                 m_selected_amount = list->getItem(m_selected_item->i).count;
995         }
996 }
997
998 void GUIFormSpecMenu::acceptInput()
999 {
1000         if(m_text_dst)
1001         {
1002                 std::map<std::string, std::string> fields;
1003                 gui::IGUIElement *e;
1004                 for(u32 i=0; i<m_fields.size(); i++)
1005                 {
1006                         const FieldSpec &s = m_fields[i];
1007                         if(s.send) 
1008                         {
1009                                 if(s.is_button)
1010                                 {
1011                                         fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(s.flabel.c_str());
1012                                 }
1013                                 else
1014                                 {
1015                                         e = getElementFromId(s.fid);
1016                                         if(e != NULL)
1017                                         {
1018                                                 fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(e->getText());
1019                                         }
1020                                 }
1021                         }
1022                 }
1023                 m_text_dst->gotText(fields);
1024         }
1025 }
1026
1027 bool GUIFormSpecMenu::OnEvent(const SEvent& event)
1028 {
1029         if(event.EventType==EET_KEY_INPUT_EVENT)
1030         {
1031                 KeyPress kp(event.KeyInput);
1032                 if (event.KeyInput.PressedDown && (kp == EscapeKey ||
1033                         kp == getKeySetting("keymap_inventory")))
1034                 {
1035                         quitMenu();
1036                         return true;
1037                 }
1038                 if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
1039                 {
1040                         acceptInput();
1041                         quitMenu();
1042                         return true;
1043                 }
1044         }
1045         if(event.EventType==EET_MOUSE_INPUT_EVENT
1046                         && event.MouseInput.Event != EMIE_MOUSE_MOVED)
1047         {
1048                 // Mouse event other than movement
1049
1050                 // Get selected item and hovered/clicked item (s)
1051
1052                 updateSelectedItem();
1053                 ItemSpec s = getItemAtPos(m_pointer);
1054
1055                 Inventory *inv_selected = NULL;
1056                 Inventory *inv_s = NULL;
1057
1058                 if(m_selected_item)
1059                 {
1060                         inv_selected = m_invmgr->getInventory(m_selected_item->inventoryloc);
1061                         assert(inv_selected);
1062                         assert(inv_selected->getList(m_selected_item->listname) != NULL);
1063                 }
1064
1065                 u32 s_count = 0;
1066
1067                 if(s.isValid())
1068                 do{ // breakable
1069                         inv_s = m_invmgr->getInventory(s.inventoryloc);
1070
1071                         if(!inv_s){
1072                                 errorstream<<"InventoryMenu: The selected inventory location "
1073                                                 <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
1074                                                 <<std::endl;
1075                                 s.i = -1;  // make it invalid again
1076                                 break;
1077                         }
1078
1079                         InventoryList *list = inv_s->getList(s.listname);
1080                         if(list == NULL){
1081                                 verbosestream<<"InventoryMenu: The selected inventory list \""
1082                                                 <<s.listname<<"\" does not exist"<<std::endl;
1083                                 s.i = -1;  // make it invalid again
1084                                 break;
1085                         }
1086
1087                         if((u32)s.i >= list->getSize()){
1088                                 infostream<<"InventoryMenu: The selected inventory list \""
1089                                                 <<s.listname<<"\" is too small (i="<<s.i<<", size="
1090                                                 <<list->getSize()<<")"<<std::endl;
1091                                 s.i = -1;  // make it invalid again
1092                                 break;
1093                         }
1094
1095                         s_count = list->getItem(s.i).count;
1096                 }while(0);
1097
1098                 bool identical = (m_selected_item != NULL) && s.isValid() &&
1099                         (inv_selected == inv_s) &&
1100                         (m_selected_item->listname == s.listname) &&
1101                         (m_selected_item->i == s.i);
1102
1103                 // buttons: 0 = left, 1 = right, 2 = middle
1104                 // up/down: 0 = down (press), 1 = up (release), 2 = unknown event
1105                 int button = 0;
1106                 int updown = 2;
1107                 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
1108                         { button = 0; updown = 0; }
1109                 else if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
1110                         { button = 1; updown = 0; }
1111                 else if(event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN)
1112                         { button = 2; updown = 0; }
1113                 else if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
1114                         { button = 0; updown = 1; }
1115                 else if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
1116                         { button = 1; updown = 1; }
1117                 else if(event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP)
1118                         { button = 2; updown = 1; }
1119
1120                 // Set this number to a positive value to generate a move action
1121                 // from m_selected_item to s.
1122                 u32 move_amount = 0;
1123
1124                 // Set this number to a positive value to generate a drop action
1125                 // from m_selected_item.
1126                 u32 drop_amount = 0;
1127
1128                 // Set this number to a positive value to generate a craft action at s.
1129                 u32 craft_amount = 0;
1130
1131                 if(updown == 0)
1132                 {
1133                         // Some mouse button has been pressed
1134
1135                         //infostream<<"Mouse button "<<button<<" pressed at p=("
1136                         //      <<p.X<<","<<p.Y<<")"<<std::endl;
1137
1138                         m_selected_dragging = false;
1139
1140                         if(s.isValid() && s.listname == "craftpreview")
1141                         {
1142                                 // Craft preview has been clicked: craft
1143                                 craft_amount = (button == 2 ? 10 : 1);
1144                         }
1145                         else if(m_selected_item == NULL)
1146                         {
1147                                 if(s_count != 0)
1148                                 {
1149                                         // Non-empty stack has been clicked: select it
1150                                         m_selected_item = new ItemSpec(s);
1151
1152                                         if(button == 1)  // right
1153                                                 m_selected_amount = (s_count + 1) / 2;
1154                                         else if(button == 2)  // middle
1155                                                 m_selected_amount = MYMIN(s_count, 10);
1156                                         else  // left
1157                                                 m_selected_amount = s_count;
1158
1159                                         m_selected_dragging = true;
1160                                 }
1161                         }
1162                         else  // m_selected_item != NULL
1163                         {
1164                                 assert(m_selected_amount >= 1);
1165
1166                                 if(s.isValid())
1167                                 {
1168                                         // Clicked a slot: move
1169                                         if(button == 1)  // right
1170                                                 move_amount = 1;
1171                                         else if(button == 2)  // middle
1172                                                 move_amount = MYMIN(m_selected_amount, 10);
1173                                         else  // left
1174                                                 move_amount = m_selected_amount;
1175
1176                                         if(identical)
1177                                         {
1178                                                 if(move_amount >= m_selected_amount)
1179                                                         m_selected_amount = 0;
1180                                                 else
1181                                                         m_selected_amount -= move_amount;
1182                                                 move_amount = 0;
1183                                         }
1184                                 }
1185                                 else if(getAbsoluteClippingRect().isPointInside(m_pointer))
1186                                 {
1187                                         // Clicked somewhere else: deselect
1188                                         m_selected_amount = 0;
1189                                 }
1190                                 else
1191                                 {
1192                                         // Clicked outside of the window: drop
1193                                         if(button == 1)  // right
1194                                                 drop_amount = 1;
1195                                         else if(button == 2)  // middle
1196                                                 drop_amount = MYMIN(m_selected_amount, 10);
1197                                         else  // left
1198                                                 drop_amount = m_selected_amount;
1199                                 }
1200                         }
1201                 }
1202                 else if(updown == 1)
1203                 {
1204                         // Some mouse button has been released
1205
1206                         //infostream<<"Mouse button "<<button<<" released at p=("
1207                         //      <<p.X<<","<<p.Y<<")"<<std::endl;
1208
1209                         if(m_selected_item != NULL && m_selected_dragging && s.isValid())
1210                         {
1211                                 if(!identical)
1212                                 {
1213                                         // Dragged to different slot: move all selected
1214                                         move_amount = m_selected_amount;
1215                                 }
1216                         }
1217                         else if(m_selected_item != NULL && m_selected_dragging &&
1218                                 !(getAbsoluteClippingRect().isPointInside(m_pointer)))
1219                         {
1220                                 // Dragged outside of window: drop all selected
1221                                 drop_amount = m_selected_amount;
1222                         }
1223
1224                         m_selected_dragging = false;
1225                 }
1226
1227                 // Possibly send inventory action to server
1228                 if(move_amount > 0)
1229                 {
1230                         // Send IACTION_MOVE
1231
1232                         assert(m_selected_item && m_selected_item->isValid());
1233                         assert(s.isValid());
1234
1235                         assert(inv_selected && inv_s);
1236                         InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
1237                         InventoryList *list_to = inv_s->getList(s.listname);
1238                         assert(list_from && list_to);
1239                         ItemStack stack_from = list_from->getItem(m_selected_item->i);
1240                         ItemStack stack_to = list_to->getItem(s.i);
1241
1242                         // Check how many items can be moved
1243                         move_amount = stack_from.count = MYMIN(move_amount, stack_from.count);
1244                         ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef());
1245                         // If source stack cannot be added to destination stack at all,
1246                         // they are swapped
1247                         if(leftover.count == stack_from.count && leftover.name == stack_from.name)
1248                         {
1249                                 m_selected_amount = stack_to.count;
1250                                 // In case the server doesn't directly swap them but instead
1251                                 // moves stack_to somewhere else, set this
1252                                 m_selected_content_guess = stack_to;
1253                                 m_selected_content_guess_inventory = s.inventoryloc;
1254                         }
1255                         // Source stack goes fully into destination stack
1256                         else if(leftover.empty())
1257                         {
1258                                 m_selected_amount -= move_amount;
1259                                 m_selected_content_guess = ItemStack(); // Clear
1260                         }
1261                         // Source stack goes partly into destination stack
1262                         else
1263                         {
1264                                 move_amount -= leftover.count;
1265                                 m_selected_amount -= move_amount;
1266                                 m_selected_content_guess = ItemStack(); // Clear
1267                         }
1268
1269                         infostream<<"Handing IACTION_MOVE to manager"<<std::endl;
1270                         IMoveAction *a = new IMoveAction();
1271                         a->count = move_amount;
1272                         a->from_inv = m_selected_item->inventoryloc;
1273                         a->from_list = m_selected_item->listname;
1274                         a->from_i = m_selected_item->i;
1275                         a->to_inv = s.inventoryloc;
1276                         a->to_list = s.listname;
1277                         a->to_i = s.i;
1278                         m_invmgr->inventoryAction(a);
1279                 }
1280                 else if(drop_amount > 0)
1281                 {
1282                         m_selected_content_guess = ItemStack(); // Clear
1283
1284                         // Send IACTION_DROP
1285
1286                         assert(m_selected_item && m_selected_item->isValid());
1287                         assert(inv_selected);
1288                         InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
1289                         assert(list_from);
1290                         ItemStack stack_from = list_from->getItem(m_selected_item->i);
1291
1292                         // Check how many items can be dropped
1293                         drop_amount = stack_from.count = MYMIN(drop_amount, stack_from.count);
1294                         assert(drop_amount > 0 && drop_amount <= m_selected_amount);
1295                         m_selected_amount -= drop_amount;
1296
1297                         infostream<<"Handing IACTION_DROP to manager"<<std::endl;
1298                         IDropAction *a = new IDropAction();
1299                         a->count = drop_amount;
1300                         a->from_inv = m_selected_item->inventoryloc;
1301                         a->from_list = m_selected_item->listname;
1302                         a->from_i = m_selected_item->i;
1303                         m_invmgr->inventoryAction(a);
1304                 }
1305                 else if(craft_amount > 0)
1306                 {
1307                         m_selected_content_guess = ItemStack(); // Clear
1308
1309                         // Send IACTION_CRAFT
1310
1311                         assert(s.isValid());
1312                         assert(inv_s);
1313
1314                         infostream<<"Handing IACTION_CRAFT to manager"<<std::endl;
1315                         ICraftAction *a = new ICraftAction();
1316                         a->count = craft_amount;
1317                         a->craft_inv = s.inventoryloc;
1318                         m_invmgr->inventoryAction(a);
1319                 }
1320
1321                 // If m_selected_amount has been decreased to zero, deselect
1322                 if(m_selected_amount == 0)
1323                 {
1324                         delete m_selected_item;
1325                         m_selected_item = NULL;
1326                         m_selected_amount = 0;
1327                         m_selected_dragging = false;
1328                         m_selected_content_guess = ItemStack();
1329                 }
1330         }
1331         if(event.EventType==EET_GUI_EVENT)
1332         {
1333                 if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
1334                                 && isVisible())
1335                 {
1336                         if(!canTakeFocus(event.GUIEvent.Element))
1337                         {
1338                                 infostream<<"GUIFormSpecMenu: Not allowing focus change."
1339                                                 <<std::endl;
1340                                 // Returning true disables focus change
1341                                 return true;
1342                         }
1343                 }
1344                 if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
1345                 {
1346                         switch(event.GUIEvent.Caller->getID())
1347                         {
1348                         case 257:
1349                                 acceptInput();
1350                                 quitMenu();
1351                                 // quitMenu deallocates menu
1352                                 return true;
1353                         }
1354                         // find the element that was clicked
1355                         for(u32 i=0; i<m_fields.size(); i++)
1356                         {
1357                                 FieldSpec &s = m_fields[i];
1358                                 // if its a button, set the send field so 
1359                                 // lua knows which button was pressed
1360                                 if (s.is_button && s.fid == event.GUIEvent.Caller->getID())
1361                                 {
1362                                         s.send = true;
1363                                         acceptInput();
1364                                         if(s.is_exit){
1365                                                 quitMenu();
1366                                                 return true;
1367                                         }else{
1368                                                 s.send = false;
1369                                                 // Restore focus to the full form
1370                                                 Environment->setFocus(this);
1371                                                 return true;
1372                                         }
1373                                 }
1374                         }
1375                 }
1376                 if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
1377                 {
1378                         if(event.GUIEvent.Caller->getID() > 257)
1379                         {
1380                                 acceptInput();
1381                                 quitMenu();
1382                                 // quitMenu deallocates menu
1383                                 return true;
1384                         }
1385                 }
1386         }
1387
1388         return Parent ? Parent->OnEvent(event) : false;
1389 }
1390