On-demand item meshes and textures
[oweals/minetest.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                         errorstream<<"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 items
813                 Phase 0: Item slot rectangles
814                 Phase 1: Item images; prepare tooltip
815                 If backgrounds used, do not draw Item slot rectangles
816         */
817         int start_phase=0;
818         if (m_backgrounds.size() > 0) start_phase=1;
819         for(int phase=start_phase; phase<=1; phase++)
820         for(u32 i=0; i<m_inventorylists.size(); i++)
821         {
822                 drawList(m_inventorylists[i], phase);
823         }
824
825         /*
826                 Call base class
827         */
828         gui::IGUIElement::draw();
829         
830         /*
831                 Draw fields/buttons tooltips
832         */
833         for(u32 i=0; i<m_fields.size(); i++)
834         {
835                 const FieldSpec &spec = m_fields[i];
836                 if (spec.tooltip != "")
837                 {
838                         core::rect<s32> rect = spec.rect;
839                         if (rect.isPointInside(m_pointer)) 
840                         {
841                                 m_tooltip_element->setVisible(true);
842                                 this->bringToFront(m_tooltip_element);
843                                 m_tooltip_element->setText(narrow_to_wide(spec.tooltip).c_str());
844                                 s32 tooltip_x = m_pointer.X + 15;
845                                 s32 tooltip_y = m_pointer.Y + 15;
846                                 s32 tooltip_width = m_tooltip_element->getTextWidth() + 15;
847                                 s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
848                                 m_tooltip_element->setRelativePosition(core::rect<s32>(
849                                 core::position2d<s32>(tooltip_x, tooltip_y),
850                                 core::dimension2d<s32>(tooltip_width, tooltip_height)));
851                         }
852                 }
853         }
854         
855         /*
856                 Draw dragged item stack
857         */
858         drawSelectedItem();
859 }
860
861 void GUIFormSpecMenu::updateSelectedItem()
862 {
863         // WARNING: BLACK MAGIC
864         // See if there is a stack suited for our current guess.
865         // If such stack does not exist, clear the guess.
866         if(m_selected_content_guess.name != "")
867         {
868                 bool found = false;
869                 for(u32 i=0; i<m_inventorylists.size() && !found; i++){
870                         const ListDrawSpec &s = m_inventorylists[i];
871                         Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
872                         if(!inv)
873                                 continue;
874                         InventoryList *list = inv->getList(s.listname);
875                         if(!list)
876                                 continue;
877                         for(s32 i=0; i<s.geom.X*s.geom.Y && !found; i++){
878                                 u32 item_i = i + s.start_item_i;
879                                 if(item_i >= list->getSize())
880                                         continue;
881                                 ItemStack stack = list->getItem(item_i);
882                                 if(stack.name == m_selected_content_guess.name &&
883                                                 stack.count == m_selected_content_guess.count){
884                                         found = true;
885                                         if(m_selected_item){
886                                                 // If guessed stack is already selected, all is fine
887                                                 if(m_selected_item->inventoryloc == s.inventoryloc &&
888                                                                 m_selected_item->listname == s.listname &&
889                                                                 m_selected_item->i == (s32)item_i &&
890                                                                 m_selected_amount == stack.count){
891                                                         break;
892                                                 }
893                                                 delete m_selected_item;
894                                                 m_selected_item = NULL;
895                                         }
896                                         infostream<<"Client: Changing selected content guess to "
897                                                         <<s.inventoryloc.dump()<<" "<<s.listname
898                                                         <<" "<<item_i<<std::endl;
899                                         m_selected_item = new ItemSpec(s.inventoryloc, s.listname, item_i);
900                                         m_selected_amount = stack.count;
901                                         break;
902                                 }
903                         }
904                 }
905                 if(!found){
906                         infostream<<"Client: Discarding selected content guess: "
907                                         <<m_selected_content_guess.getItemString()<<std::endl;
908                         m_selected_content_guess.name = "";
909                 }
910         }
911         // If the selected stack has become empty for some reason, deselect it.
912         // If the selected stack has become smaller, adjust m_selected_amount.
913         if(m_selected_item)
914         {
915                 bool selection_valid = false;
916                 if(m_selected_item->isValid())
917                 {
918                         Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
919                         if(inv)
920                         {
921                                 InventoryList *list = inv->getList(m_selected_item->listname);
922                                 if(list && (u32) m_selected_item->i < list->getSize())
923                                 {
924                                         ItemStack stack = list->getItem(m_selected_item->i);
925                                         if(m_selected_amount > stack.count)
926                                                 m_selected_amount = stack.count;
927                                         if(!stack.empty())
928                                                 selection_valid = true;
929                                 }
930                         }
931                 }
932                 if(!selection_valid)
933                 {
934                         delete m_selected_item;
935                         m_selected_item = NULL;
936                         m_selected_amount = 0;
937                         m_selected_dragging = false;
938                 }
939         }
940
941         // If craftresult is nonempty and nothing else is selected, select it now.
942         if(!m_selected_item)
943         {
944                 for(u32 i=0; i<m_inventorylists.size(); i++)
945                 {
946                         const ListDrawSpec &s = m_inventorylists[i];
947                         if(s.listname == "craftpreview")
948                         {
949                                 Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
950                                 InventoryList *list = inv->getList("craftresult");
951                                 if(list && list->getSize() >= 1 && !list->getItem(0).empty())
952                                 {
953                                         m_selected_item = new ItemSpec;
954                                         m_selected_item->inventoryloc = s.inventoryloc;
955                                         m_selected_item->listname = "craftresult";
956                                         m_selected_item->i = 0;
957                                         m_selected_amount = 0;
958                                         m_selected_dragging = false;
959                                         break;
960                                 }
961                         }
962                 }
963         }
964
965         // If craftresult is selected, keep the whole stack selected
966         if(m_selected_item && m_selected_item->listname == "craftresult")
967         {
968                 Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
969                 assert(inv);
970                 InventoryList *list = inv->getList(m_selected_item->listname);
971                 assert(list);
972                 m_selected_amount = list->getItem(m_selected_item->i).count;
973         }
974 }
975
976 void GUIFormSpecMenu::acceptInput()
977 {
978         if(m_text_dst)
979         {
980                 std::map<std::string, std::string> fields;
981                 gui::IGUIElement *e;
982                 for(u32 i=0; i<m_fields.size(); i++)
983                 {
984                         const FieldSpec &s = m_fields[i];
985                         if(s.send) 
986                         {
987                                 if(s.is_button)
988                                 {
989                                         fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(s.flabel.c_str());
990                                 }
991                                 else
992                                 {
993                                         e = getElementFromId(s.fid);
994                                         if(e != NULL)
995                                         {
996                                                 fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(e->getText());
997                                         }
998                                 }
999                         }
1000                 }
1001                 m_text_dst->gotText(fields);
1002         }
1003 }
1004
1005 bool GUIFormSpecMenu::OnEvent(const SEvent& event)
1006 {
1007         if(event.EventType==EET_KEY_INPUT_EVENT)
1008         {
1009                 KeyPress kp(event.KeyInput);
1010                 if (event.KeyInput.PressedDown && (kp == EscapeKey ||
1011                         kp == getKeySetting("keymap_inventory")))
1012                 {
1013                         quitMenu();
1014                         return true;
1015                 }
1016                 if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
1017                 {
1018                         acceptInput();
1019                         quitMenu();
1020                         return true;
1021                 }
1022         }
1023         if(event.EventType==EET_MOUSE_INPUT_EVENT
1024                         && event.MouseInput.Event != EMIE_MOUSE_MOVED)
1025         {
1026                 // Mouse event other than movement
1027
1028                 // Get selected item and hovered/clicked item (s)
1029
1030                 updateSelectedItem();
1031                 ItemSpec s = getItemAtPos(m_pointer);
1032
1033                 Inventory *inv_selected = NULL;
1034                 Inventory *inv_s = NULL;
1035
1036                 if(m_selected_item)
1037                 {
1038                         inv_selected = m_invmgr->getInventory(m_selected_item->inventoryloc);
1039                         assert(inv_selected);
1040                         assert(inv_selected->getList(m_selected_item->listname) != NULL);
1041                 }
1042
1043                 u32 s_count = 0;
1044
1045                 if(s.isValid())
1046                 do{ // breakable
1047                         inv_s = m_invmgr->getInventory(s.inventoryloc);
1048
1049                         if(!inv_s){
1050                                 errorstream<<"InventoryMenu: The selected inventory location "
1051                                                 <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
1052                                                 <<std::endl;
1053                                 s.i = -1;  // make it invalid again
1054                                 break;
1055                         }
1056
1057                         InventoryList *list = inv_s->getList(s.listname);
1058                         if(list == NULL){
1059                                 verbosestream<<"InventoryMenu: The selected inventory list \""
1060                                                 <<s.listname<<"\" does not exist"<<std::endl;
1061                                 s.i = -1;  // make it invalid again
1062                                 break;
1063                         }
1064
1065                         if((u32)s.i >= list->getSize()){
1066                                 infostream<<"InventoryMenu: The selected inventory list \""
1067                                                 <<s.listname<<"\" is too small (i="<<s.i<<", size="
1068                                                 <<list->getSize()<<")"<<std::endl;
1069                                 s.i = -1;  // make it invalid again
1070                                 break;
1071                         }
1072
1073                         s_count = list->getItem(s.i).count;
1074                 }while(0);
1075
1076                 bool identical = (m_selected_item != NULL) && s.isValid() &&
1077                         (inv_selected == inv_s) &&
1078                         (m_selected_item->listname == s.listname) &&
1079                         (m_selected_item->i == s.i);
1080
1081                 // buttons: 0 = left, 1 = right, 2 = middle
1082                 // up/down: 0 = down (press), 1 = up (release), 2 = unknown event
1083                 int button = 0;
1084                 int updown = 2;
1085                 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
1086                         { button = 0; updown = 0; }
1087                 else if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
1088                         { button = 1; updown = 0; }
1089                 else if(event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN)
1090                         { button = 2; updown = 0; }
1091                 else if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
1092                         { button = 0; updown = 1; }
1093                 else if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
1094                         { button = 1; updown = 1; }
1095                 else if(event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP)
1096                         { button = 2; updown = 1; }
1097
1098                 // Set this number to a positive value to generate a move action
1099                 // from m_selected_item to s.
1100                 u32 move_amount = 0;
1101
1102                 // Set this number to a positive value to generate a drop action
1103                 // from m_selected_item.
1104                 u32 drop_amount = 0;
1105
1106                 // Set this number to a positive value to generate a craft action at s.
1107                 u32 craft_amount = 0;
1108
1109                 if(updown == 0)
1110                 {
1111                         // Some mouse button has been pressed
1112
1113                         //infostream<<"Mouse button "<<button<<" pressed at p=("
1114                         //      <<p.X<<","<<p.Y<<")"<<std::endl;
1115
1116                         m_selected_dragging = false;
1117
1118                         if(s.isValid() && s.listname == "craftpreview")
1119                         {
1120                                 // Craft preview has been clicked: craft
1121                                 craft_amount = (button == 2 ? 10 : 1);
1122                         }
1123                         else if(m_selected_item == NULL)
1124                         {
1125                                 if(s_count != 0)
1126                                 {
1127                                         // Non-empty stack has been clicked: select it
1128                                         m_selected_item = new ItemSpec(s);
1129
1130                                         if(button == 1)  // right
1131                                                 m_selected_amount = (s_count + 1) / 2;
1132                                         else if(button == 2)  // middle
1133                                                 m_selected_amount = MYMIN(s_count, 10);
1134                                         else  // left
1135                                                 m_selected_amount = s_count;
1136
1137                                         m_selected_dragging = true;
1138                                 }
1139                         }
1140                         else  // m_selected_item != NULL
1141                         {
1142                                 assert(m_selected_amount >= 1);
1143
1144                                 if(s.isValid())
1145                                 {
1146                                         // Clicked a slot: move
1147                                         if(button == 1)  // right
1148                                                 move_amount = 1;
1149                                         else if(button == 2)  // middle
1150                                                 move_amount = MYMIN(m_selected_amount, 10);
1151                                         else  // left
1152                                                 move_amount = m_selected_amount;
1153
1154                                         if(identical)
1155                                         {
1156                                                 if(move_amount >= m_selected_amount)
1157                                                         m_selected_amount = 0;
1158                                                 else
1159                                                         m_selected_amount -= move_amount;
1160                                                 move_amount = 0;
1161                                         }
1162                                 }
1163                                 else if(getAbsoluteClippingRect().isPointInside(m_pointer))
1164                                 {
1165                                         // Clicked somewhere else: deselect
1166                                         m_selected_amount = 0;
1167                                 }
1168                                 else
1169                                 {
1170                                         // Clicked outside of the window: drop
1171                                         if(button == 1)  // right
1172                                                 drop_amount = 1;
1173                                         else if(button == 2)  // middle
1174                                                 drop_amount = MYMIN(m_selected_amount, 10);
1175                                         else  // left
1176                                                 drop_amount = m_selected_amount;
1177                                 }
1178                         }
1179                 }
1180                 else if(updown == 1)
1181                 {
1182                         // Some mouse button has been released
1183
1184                         //infostream<<"Mouse button "<<button<<" released at p=("
1185                         //      <<p.X<<","<<p.Y<<")"<<std::endl;
1186
1187                         if(m_selected_item != NULL && m_selected_dragging && s.isValid())
1188                         {
1189                                 if(!identical)
1190                                 {
1191                                         // Dragged to different slot: move all selected
1192                                         move_amount = m_selected_amount;
1193                                 }
1194                         }
1195                         else if(m_selected_item != NULL && m_selected_dragging &&
1196                                 !(getAbsoluteClippingRect().isPointInside(m_pointer)))
1197                         {
1198                                 // Dragged outside of window: drop all selected
1199                                 drop_amount = m_selected_amount;
1200                         }
1201
1202                         m_selected_dragging = false;
1203                 }
1204
1205                 // Possibly send inventory action to server
1206                 if(move_amount > 0)
1207                 {
1208                         // Send IACTION_MOVE
1209
1210                         assert(m_selected_item && m_selected_item->isValid());
1211                         assert(s.isValid());
1212
1213                         assert(inv_selected && inv_s);
1214                         InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
1215                         InventoryList *list_to = inv_s->getList(s.listname);
1216                         assert(list_from && list_to);
1217                         ItemStack stack_from = list_from->getItem(m_selected_item->i);
1218                         ItemStack stack_to = list_to->getItem(s.i);
1219
1220                         // Check how many items can be moved
1221                         move_amount = stack_from.count = MYMIN(move_amount, stack_from.count);
1222                         ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef());
1223                         // If source stack cannot be added to destination stack at all,
1224                         // they are swapped
1225                         if(leftover.count == stack_from.count && leftover.name == stack_from.name)
1226                         {
1227                                 m_selected_amount = stack_to.count;
1228                                 // In case the server doesn't directly swap them but instead
1229                                 // moves stack_to somewhere else, set this
1230                                 m_selected_content_guess = stack_to;
1231                                 m_selected_content_guess_inventory = s.inventoryloc;
1232                         }
1233                         // Source stack goes fully into destination stack
1234                         else if(leftover.empty())
1235                         {
1236                                 m_selected_amount -= move_amount;
1237                                 m_selected_content_guess = ItemStack(); // Clear
1238                         }
1239                         // Source stack goes partly into destination stack
1240                         else
1241                         {
1242                                 move_amount -= leftover.count;
1243                                 m_selected_amount -= move_amount;
1244                                 m_selected_content_guess = ItemStack(); // Clear
1245                         }
1246
1247                         infostream<<"Handing IACTION_MOVE to manager"<<std::endl;
1248                         IMoveAction *a = new IMoveAction();
1249                         a->count = move_amount;
1250                         a->from_inv = m_selected_item->inventoryloc;
1251                         a->from_list = m_selected_item->listname;
1252                         a->from_i = m_selected_item->i;
1253                         a->to_inv = s.inventoryloc;
1254                         a->to_list = s.listname;
1255                         a->to_i = s.i;
1256                         m_invmgr->inventoryAction(a);
1257                 }
1258                 else if(drop_amount > 0)
1259                 {
1260                         m_selected_content_guess = ItemStack(); // Clear
1261
1262                         // Send IACTION_DROP
1263
1264                         assert(m_selected_item && m_selected_item->isValid());
1265                         assert(inv_selected);
1266                         InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
1267                         assert(list_from);
1268                         ItemStack stack_from = list_from->getItem(m_selected_item->i);
1269
1270                         // Check how many items can be dropped
1271                         drop_amount = stack_from.count = MYMIN(drop_amount, stack_from.count);
1272                         assert(drop_amount > 0 && drop_amount <= m_selected_amount);
1273                         m_selected_amount -= drop_amount;
1274
1275                         infostream<<"Handing IACTION_DROP to manager"<<std::endl;
1276                         IDropAction *a = new IDropAction();
1277                         a->count = drop_amount;
1278                         a->from_inv = m_selected_item->inventoryloc;
1279                         a->from_list = m_selected_item->listname;
1280                         a->from_i = m_selected_item->i;
1281                         m_invmgr->inventoryAction(a);
1282                 }
1283                 else if(craft_amount > 0)
1284                 {
1285                         m_selected_content_guess = ItemStack(); // Clear
1286
1287                         // Send IACTION_CRAFT
1288
1289                         assert(s.isValid());
1290                         assert(inv_s);
1291
1292                         infostream<<"Handing IACTION_CRAFT to manager"<<std::endl;
1293                         ICraftAction *a = new ICraftAction();
1294                         a->count = craft_amount;
1295                         a->craft_inv = s.inventoryloc;
1296                         m_invmgr->inventoryAction(a);
1297                 }
1298
1299                 // If m_selected_amount has been decreased to zero, deselect
1300                 if(m_selected_amount == 0)
1301                 {
1302                         delete m_selected_item;
1303                         m_selected_item = NULL;
1304                         m_selected_amount = 0;
1305                         m_selected_dragging = false;
1306                         m_selected_content_guess = ItemStack();
1307                 }
1308         }
1309         if(event.EventType==EET_GUI_EVENT)
1310         {
1311                 if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
1312                                 && isVisible())
1313                 {
1314                         if(!canTakeFocus(event.GUIEvent.Element))
1315                         {
1316                                 infostream<<"GUIFormSpecMenu: Not allowing focus change."
1317                                                 <<std::endl;
1318                                 // Returning true disables focus change
1319                                 return true;
1320                         }
1321                 }
1322                 if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
1323                 {
1324                         switch(event.GUIEvent.Caller->getID())
1325                         {
1326                         case 257:
1327                                 acceptInput();
1328                                 quitMenu();
1329                                 // quitMenu deallocates menu
1330                                 return true;
1331                         }
1332                         // find the element that was clicked
1333                         for(u32 i=0; i<m_fields.size(); i++)
1334                         {
1335                                 FieldSpec &s = m_fields[i];
1336                                 // if its a button, set the send field so 
1337                                 // lua knows which button was pressed
1338                                 if (s.is_button && s.fid == event.GUIEvent.Caller->getID())
1339                                 {
1340                                         s.send = true;
1341                                         acceptInput();
1342                                         if(s.is_exit){
1343                                                 quitMenu();
1344                                                 return true;
1345                                         }else{
1346                                                 s.send = false;
1347                                                 // Restore focus to the full form
1348                                                 Environment->setFocus(this);
1349                                                 return true;
1350                                         }
1351                                 }
1352                         }
1353                 }
1354                 if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
1355                 {
1356                         if(event.GUIEvent.Caller->getID() > 257)
1357                         {
1358                                 acceptInput();
1359                                 quitMenu();
1360                                 // quitMenu deallocates menu
1361                                 return true;
1362                         }
1363                 }
1364         }
1365
1366         return Parent ? Parent->OnEvent(event) : false;
1367 }
1368