Re-add jungles, apple trees
[oweals/minetest.git] / src / guiFormSpecMenu.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 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_esc("["));
211                 if(type == "invsize" || type == "size")
212                 {
213                         v2f invsize;
214                         invsize.X = stof(f.next_esc(","));
215                         if(type == "size")
216                         {
217                                 invsize.Y = stof(f.next_esc("]"));
218                         }
219                         else{
220                                 invsize.Y = stof(f.next_esc(";"));
221                                 f.next_esc("]");
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_esc(";");
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_esc(";");
252                         v2s32 pos = basepos;
253                         pos.X += stof(f.next_esc(",")) * (float)spacing.X;
254                         pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
255                         v2s32 geom;
256                         geom.X = stoi(f.next_esc(","));
257                         geom.Y = stoi(f.next_esc(";"));
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_esc("]");
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_esc(",")) * (float)spacing.X;
274                         pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
275                         v2s32 geom;
276                         geom.X = stof(f.next_esc(",")) * (float)imgsize.X;
277                         geom.Y = stof(f.next_esc(";")) * (float)imgsize.Y;
278                         std::string name = f.next_esc("]");
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_esc(",")) * (float)spacing.X;
291                         pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
292                         v2s32 geom;
293                         geom.X = stof(f.next_esc(",")) * (float)imgsize.X;
294                         geom.Y = stof(f.next_esc(";")) * (float)imgsize.Y;
295                         std::string name = f.next_esc("]");
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_esc(",")) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2;
308                         pos.Y += stof(f.next_esc(";")) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2;
309                         v2s32 geom;
310                         geom.X = stof(f.next_esc(",")) * (float)spacing.X;
311                         geom.Y = stof(f.next_esc(";")) * (float)spacing.Y;
312                         std::string name = f.next_esc("]");
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" || type == "textarea")
322                 {
323                         std::string fname = f.next_esc(";");
324                         std::string flabel = f.next_esc(";");
325
326                         if(fname.find(",") == std::string::npos && flabel.find(",") == std::string::npos)
327                         {
328                                 if (type == "textarea")
329                                         errorstream<<"WARNING: Textarea connot be unpositioned"<<std::endl;
330
331                                 if(!bp_set)
332                                 {
333                                         rect = core::rect<s32>(
334                                                 screensize.X/2 - 580/2,
335                                                 screensize.Y/2 - 300/2,
336                                                 screensize.X/2 + 580/2,
337                                                 screensize.Y/2 + 300/2
338                                         );
339                                         DesiredRect = rect;
340                                         recalculateAbsolutePosition(false);
341                                         basepos = getBasePos();
342                                         bp_set = 1;
343                                 }
344                                 else if(bp_set == 2)
345                                         errorstream<<"WARNING: invalid use of unpositioned "<<type<<" in inventory"<<std::endl;
346
347                                 v2s32 pos = basepos;
348                                 pos.Y = ((m_fields.size()+2)*60);
349                                 v2s32 size = DesiredRect.getSize();
350                                 rect = core::rect<s32>(size.X/2-150, pos.Y, (size.X/2-150)+300, pos.Y+30);
351                         }
352                         else
353                         {
354                                 v2s32 pos;
355                                 pos.X = stof(fname.substr(0,fname.find(","))) * (float)spacing.X;
356                                 pos.Y = stof(fname.substr(fname.find(",")+1)) * (float)spacing.Y;
357                                 v2s32 geom;
358                                 geom.X = (stof(flabel.substr(0,flabel.find(","))) * (float)spacing.X)-(spacing.X-imgsize.X);
359                                 if (type == "textarea")
360                                 {
361                                         geom.Y = (stof(flabel.substr(flabel.find(",")+1)) * (float)imgsize.Y) - (spacing.Y-imgsize.Y);
362                                         pos.Y += 15;
363                                 }
364                                 else
365                                 {
366                                         pos.Y += (stof(flabel.substr(flabel.find(",")+1)) * (float)imgsize.Y)/2;
367                                         pos.Y -= 15;
368                                         geom.Y = 30;
369                                 }
370
371                                 rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
372
373
374                                 
375                                 fname = f.next_esc(";");
376                                 flabel = f.next_esc(";");
377                                 if(bp_set != 2)
378                                         errorstream<<"WARNING: invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
379                                 
380                         }
381
382                         std::string odefault = f.next_esc("]");
383                         std::string fdefault;
384
385                         // fdefault may contain a variable reference, which
386                         // needs to be resolved from the node metadata
387                         if(m_form_src)
388                                 fdefault = m_form_src->resolveText(odefault);
389                         else
390                                 fdefault = odefault;
391
392                         fdefault = unescape_string(fdefault);
393                         flabel = unescape_string(flabel);
394
395                         FieldSpec spec = FieldSpec(
396                                 narrow_to_wide(fname.c_str()),
397                                 narrow_to_wide(flabel.c_str()),
398                                 narrow_to_wide(fdefault.c_str()),
399                                 258+m_fields.size()
400                         );
401
402                         // three cases: field name and no label, label and field, label name and no field
403                         gui::IGUIEditBox *e;
404                         if (fname == "")
405                         {
406                                 // spec field id to 0, this stops submit searching for a value that isn't there
407                                 Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
408                         }
409                         else
410                         {
411                                 spec.send = true;
412                                 e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
413                                 Environment->setFocus(e);
414
415                                 if (type == "textarea")
416                                 {
417                                         e->setMultiLine(true);
418                                         e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_UPPERLEFT);
419                                 } else {
420                                         irr::SEvent evt;
421                                         evt.KeyInput.Key = KEY_END;
422                                         evt.EventType = EET_KEY_INPUT_EVENT;
423                                         evt.KeyInput.PressedDown = true;
424                                         e->OnEvent(evt);
425                                 }
426
427                                 if (flabel != "")
428                                 {
429                                         rect.UpperLeftCorner.Y -= 15;
430                                         rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 15;
431                                         Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
432                                 }
433                         }
434
435                         m_fields.push_back(spec);
436                 }
437                 else if(type == "label")
438                 {
439                         v2s32 pos = padding;
440                         pos.X += stof(f.next_esc(",")) * (float)spacing.X;
441                         pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
442
443                         rect = core::rect<s32>(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+300, pos.Y+((imgsize.Y/2)+15));
444                         
445                         std::string flabel = f.next_esc("]");
446                         if(bp_set != 2)
447                                 errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
448
449                         flabel = unescape_string(flabel);
450
451                         FieldSpec spec = FieldSpec(
452                                 narrow_to_wide(""),
453                                 narrow_to_wide(flabel.c_str()),
454                                 narrow_to_wide(""),
455                                 258+m_fields.size()
456                         );
457                         Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid);
458                         m_fields.push_back(spec);
459                 }
460                 else if(type == "button" || type == "button_exit")
461                 {
462                         v2s32 pos = padding;
463                         pos.X += stof(f.next_esc(",")) * (float)spacing.X;
464                         pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
465                         v2s32 geom;
466                         geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
467                         pos.Y += (stof(f.next_esc(";")) * (float)imgsize.Y)/2;
468
469                         rect = core::rect<s32>(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15);
470                         
471                         std::string fname = f.next_esc(";");
472                         std::string flabel = f.next_esc("]");
473                         if(bp_set != 2)
474                                 errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
475
476                         flabel = unescape_string(flabel);
477
478                         FieldSpec spec = FieldSpec(
479                                 narrow_to_wide(fname.c_str()),
480                                 narrow_to_wide(flabel.c_str()),
481                                 narrow_to_wide(""),
482                                 258+m_fields.size()
483                         );
484                         spec.is_button = true;
485                         if(type == "button_exit")
486                                 spec.is_exit = true;
487                         Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
488                         m_fields.push_back(spec);
489                 }
490                 else if(type == "image_button" || type == "image_button_exit")
491                 {
492                         v2s32 pos = padding;
493                         pos.X += stof(f.next_esc(",")) * (float)spacing.X;
494                         pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
495                         v2s32 geom;
496                         geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
497                         geom.Y = (stof(f.next_esc(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
498
499                         rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
500                         
501                         std::string fimage = f.next_esc(";");
502                         std::string fname = f.next_esc(";");
503                         std::string flabel = f.next_esc("]");
504                         if(bp_set != 2)
505                                 errorstream<<"WARNING: invalid use of image_button without a size[] element"<<std::endl;
506
507                         flabel = unescape_string(flabel);
508
509                         FieldSpec spec = FieldSpec(
510                                 narrow_to_wide(fname.c_str()),
511                                 narrow_to_wide(flabel.c_str()),
512                                 narrow_to_wide(fimage.c_str()),
513                                 258+m_fields.size()
514                         );
515                         spec.is_button = true;
516                         if(type == "image_button_exit")
517                                 spec.is_exit = true;
518                         
519                         video::ITexture *texture = m_gamedef->tsrc()->getTextureRaw(fimage);
520                         gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
521                         e->setUseAlphaChannel(true);
522                         e->setImage(texture);
523                         e->setPressedImage(texture);
524                         e->setScaleImage(true);
525                         
526                         m_fields.push_back(spec);
527                 }
528                 else if(type == "item_image_button")
529                 {
530                         v2s32 pos = padding;
531                         pos.X += stof(f.next_esc(",")) * (float)spacing.X;
532                         pos.Y += stof(f.next_esc(";")) * (float)spacing.Y;
533                         v2s32 geom;
534                         geom.X = (stof(f.next_esc(",")) * (float)spacing.X)-(spacing.X-imgsize.X);
535                         geom.Y = (stof(f.next_esc(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
536                         rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);               
537                         std::string fimage = f.next_esc(";");
538                         std::string fname = f.next_esc(";");
539                         std::string flabel = f.next_esc("]");
540                         if(bp_set != 2)
541                                 errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;           
542                         IItemDefManager *idef = m_gamedef->idef();
543                         ItemStack item;
544                         item.deSerialize(fimage, idef);
545                         video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
546                         std::string tooltip = item.getDefinition(idef).description;
547                         flabel = unescape_string(flabel);
548                         FieldSpec spec = FieldSpec(
549                                 narrow_to_wide(fname.c_str()),
550                                 narrow_to_wide(flabel.c_str()),
551                                 narrow_to_wide(fimage.c_str()),
552                                 258+m_fields.size()
553                         );
554                         gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
555                         e->setUseAlphaChannel(true);
556                         e->setImage(texture);
557                         e->setPressedImage(texture);
558                         e->setScaleImage(true);
559                         spec.is_button = true;
560                         rect+=basepos-padding;
561                         spec.rect=rect;         
562                         if (tooltip!="")
563                                 spec.tooltip=tooltip;
564                         m_fields.push_back(spec);
565                 }
566                 else
567                 {
568                         // Ignore others
569                         std::string ts = f.next_esc("]");
570                         infostream<<"Unknown DrawSpec: type="<<type<<", data=\""<<ts<<"\""
571                                         <<std::endl;
572                 }
573         }
574
575         // If there's inventory, put the usage string at the bottom
576         if (m_inventorylists.size())
577         {
578                 changeCtype("");
579                 core::rect<s32> rect(0, 0, size.X-padding.X*2, helptext_h);
580                 rect = rect + v2s32(size.X/2 - rect.getWidth()/2,
581                                 size.Y-rect.getHeight()-5);
582                 const wchar_t *text = wgettext("Left click: Move all items, Right click: Move single item");
583                 Environment->addStaticText(text, rect, false, true, this, 256);
584                 changeCtype("C");
585         }
586         // If there's fields, add a Proceed button
587         if (m_fields.size() && bp_set != 2) 
588         {
589                 // if the size wasn't set by an invsize[] or size[] adjust it now to fit all the fields
590                 rect = core::rect<s32>(
591                         screensize.X/2 - 580/2,
592                         screensize.Y/2 - 300/2,
593                         screensize.X/2 + 580/2,
594                         screensize.Y/2 + 240/2+(m_fields.size()*60)
595                 );
596                 DesiredRect = rect;
597                 recalculateAbsolutePosition(false);
598                 basepos = getBasePos();
599
600                 changeCtype("");
601                 {
602                         v2s32 pos = basepos;
603                         pos.Y = ((m_fields.size()+2)*60);
604
605                         v2s32 size = DesiredRect.getSize();
606                         rect = core::rect<s32>(size.X/2-70, pos.Y, (size.X/2-70)+140, pos.Y+30);
607                         Environment->addButton(rect, this, 257, wgettext("Proceed"));
608                 }
609                 changeCtype("C");
610         }
611         // Add tooltip
612         {
613                 // Note: parent != this so that the tooltip isn't clipped by the menu rectangle
614                 m_tooltip_element = Environment->addStaticText(L"",core::rect<s32>(0,0,110,18));
615                 m_tooltip_element->enableOverrideColor(true);
616                 m_tooltip_element->setBackgroundColor(video::SColor(255,110,130,60));
617                 m_tooltip_element->setDrawBackground(true);
618                 m_tooltip_element->setDrawBorder(true);
619                 m_tooltip_element->setOverrideColor(video::SColor(255,255,255,255));
620                 m_tooltip_element->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
621                 m_tooltip_element->setWordWrap(false);
622         }
623 }
624
625 GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
626 {
627         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
628         
629         for(u32 i=0; i<m_inventorylists.size(); i++)
630         {
631                 const ListDrawSpec &s = m_inventorylists[i];
632
633                 for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
634                 {
635                         s32 item_i = i + s.start_item_i;
636                         s32 x = (i%s.geom.X) * spacing.X;
637                         s32 y = (i/s.geom.X) * spacing.Y;
638                         v2s32 p0(x,y);
639                         core::rect<s32> rect = imgrect + s.pos + p0;
640                         if(rect.isPointInside(p))
641                         {
642                                 return ItemSpec(s.inventoryloc, s.listname, item_i);
643                         }
644                 }
645         }
646
647         return ItemSpec(InventoryLocation(), "", -1);
648 }
649
650 void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
651 {
652         video::IVideoDriver* driver = Environment->getVideoDriver();
653
654         // Get font
655         gui::IGUIFont *font = NULL;
656         gui::IGUISkin* skin = Environment->getSkin();
657         if (skin)
658                 font = skin->getFont();
659         
660         Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
661         if(!inv){
662                 infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
663                                 <<"The inventory location "
664                                 <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
665                                 <<std::endl;
666                 return;
667         }
668         InventoryList *ilist = inv->getList(s.listname);
669         if(!ilist){
670                 infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
671                                 <<"The inventory list \""<<s.listname<<"\" @ \""
672                                 <<s.inventoryloc.dump()<<"\" doesn't exist"
673                                 <<std::endl;
674                 return;
675         }
676         
677         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
678         
679         for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
680         {
681                 u32 item_i = i + s.start_item_i;
682                 if(item_i >= ilist->getSize())
683                         break;
684                 s32 x = (i%s.geom.X) * spacing.X;
685                 s32 y = (i/s.geom.X) * spacing.Y;
686                 v2s32 p(x,y);
687                 core::rect<s32> rect = imgrect + s.pos + p;
688                 ItemStack item;
689                 if(ilist)
690                         item = ilist->getItem(item_i);
691
692                 bool selected = m_selected_item
693                         && m_invmgr->getInventory(m_selected_item->inventoryloc) == inv
694                         && m_selected_item->listname == s.listname
695                         && m_selected_item->i == i;
696                 bool hovering = rect.isPointInside(m_pointer);
697
698                 if(phase == 0)
699                 {
700                         if(hovering && m_selected_item)
701                         {
702                                 video::SColor bgcolor(255,192,192,192);
703                                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
704                         }
705                         else
706                         {
707                                 video::SColor bgcolor(255,128,128,128);
708                                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
709                         }
710                 }
711
712                 if(phase == 1)
713                 {
714                         // Draw item stack
715                         if(selected)
716                         {
717                                 item.takeItem(m_selected_amount);
718                         }
719                         if(!item.empty())
720                         {
721                                 drawItemStack(driver, font, item,
722                                                 rect, &AbsoluteClippingRect, m_gamedef);
723                         }
724
725                         // Draw tooltip
726                         std::string tooltip_text = "";
727                         if(hovering && !m_selected_item)
728                                 tooltip_text = item.getDefinition(m_gamedef->idef()).description;
729                         if(tooltip_text != "")
730                         {
731                                 m_tooltip_element->setVisible(true);
732                                 this->bringToFront(m_tooltip_element);
733                                 m_tooltip_element->setText(narrow_to_wide(tooltip_text).c_str());
734                                 s32 tooltip_x = m_pointer.X + 15;
735                                 s32 tooltip_y = m_pointer.Y + 15;
736                                 s32 tooltip_width = m_tooltip_element->getTextWidth() + 15;
737                                 s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
738                                 m_tooltip_element->setRelativePosition(core::rect<s32>(
739                                                 core::position2d<s32>(tooltip_x, tooltip_y),
740                                                 core::dimension2d<s32>(tooltip_width, tooltip_height)));
741                         }
742                 }
743         }
744 }
745
746 void GUIFormSpecMenu::drawSelectedItem()
747 {
748         if(!m_selected_item)
749                 return;
750
751         video::IVideoDriver* driver = Environment->getVideoDriver();
752
753         // Get font
754         gui::IGUIFont *font = NULL;
755         gui::IGUISkin* skin = Environment->getSkin();
756         if (skin)
757                 font = skin->getFont();
758         
759         Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
760         assert(inv);
761         InventoryList *list = inv->getList(m_selected_item->listname);
762         assert(list);
763         ItemStack stack = list->getItem(m_selected_item->i);
764         stack.count = m_selected_amount;
765
766         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
767         core::rect<s32> rect = imgrect + (m_pointer - imgrect.getCenter());
768         drawItemStack(driver, font, stack, rect, NULL, m_gamedef);
769 }
770
771 void GUIFormSpecMenu::drawMenu()
772 {
773         if(m_form_src){
774                 std::string newform = m_form_src->getForm();
775                 if(newform != m_formspec_string){
776                         m_formspec_string = newform;
777                         regenerateGui(m_screensize_old);
778                 }
779         }
780
781         m_pointer = m_device->getCursorControl()->getPosition();
782
783         updateSelectedItem();
784
785         gui::IGUISkin* skin = Environment->getSkin();
786         if (!skin)
787                 return;
788         video::IVideoDriver* driver = Environment->getVideoDriver();
789         
790         video::SColor bgcolor(140,0,0,0);
791         driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
792
793         m_tooltip_element->setVisible(false);
794
795         /*
796                 Draw backgrounds
797         */
798         for(u32 i=0; i<m_backgrounds.size(); i++)
799         {
800                 const ImageDrawSpec &spec = m_backgrounds[i];
801                 video::ITexture *texture =
802                                 m_gamedef->tsrc()->getTextureRaw(spec.name);
803                 // Image size on screen
804                 core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
805                 // Image rectangle on screen
806                 core::rect<s32> rect = imgrect + spec.pos;
807                 const video::SColor color(255,255,255,255);
808                 const video::SColor colors[] = {color,color,color,color};
809                 driver->draw2DImage(texture, rect,
810                         core::rect<s32>(core::position2d<s32>(0,0),
811                                         core::dimension2di(texture->getOriginalSize())),
812                         NULL/*&AbsoluteClippingRect*/, colors, true);
813         }
814         
815         /*
816                 Draw images
817         */
818         for(u32 i=0; i<m_images.size(); i++)
819         {
820                 const ImageDrawSpec &spec = m_images[i];
821                 video::ITexture *texture =
822                                 m_gamedef->tsrc()->getTextureRaw(spec.name);
823                 // Image size on screen
824                 core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
825                 // Image rectangle on screen
826                 core::rect<s32> rect = imgrect + spec.pos;
827                 const video::SColor color(255,255,255,255);
828                 const video::SColor colors[] = {color,color,color,color};
829                 driver->draw2DImage(texture, rect,
830                         core::rect<s32>(core::position2d<s32>(0,0),
831                                         core::dimension2di(texture->getOriginalSize())),
832                         NULL/*&AbsoluteClippingRect*/, colors, true);
833         }
834         
835         /*
836                 Draw item images
837         */
838         for(u32 i=0; i<m_itemimages.size(); i++)
839         {
840                 const ImageDrawSpec &spec = m_itemimages[i];
841                 IItemDefManager *idef = m_gamedef->idef();
842                 ItemStack item;
843                 item.deSerialize(spec.name, idef);
844                 video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);         
845                 // Image size on screen
846                 core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
847                 // Image rectangle on screen
848                 core::rect<s32> rect = imgrect + spec.pos;
849                 const video::SColor color(255,255,255,255);
850                 const video::SColor colors[] = {color,color,color,color};
851                 driver->draw2DImage(texture, rect,
852                         core::rect<s32>(core::position2d<s32>(0,0),
853                                         core::dimension2di(texture->getOriginalSize())),
854                         NULL/*&AbsoluteClippingRect*/, colors, true);
855         }
856         
857         /*
858                 Draw items
859                 Phase 0: Item slot rectangles
860                 Phase 1: Item images; prepare tooltip
861                 If backgrounds used, do not draw Item slot rectangles
862         */
863         int start_phase=0;
864         if (m_backgrounds.size() > 0) start_phase=1;
865         for(int phase=start_phase; phase<=1; phase++)
866         for(u32 i=0; i<m_inventorylists.size(); i++)
867         {
868                 drawList(m_inventorylists[i], phase);
869         }
870
871         /*
872                 Call base class
873         */
874         gui::IGUIElement::draw();
875         
876         /*
877                 Draw fields/buttons tooltips
878         */
879         for(u32 i=0; i<m_fields.size(); i++)
880         {
881                 const FieldSpec &spec = m_fields[i];
882                 if (spec.tooltip != "")
883                 {
884                         core::rect<s32> rect = spec.rect;
885                         if (rect.isPointInside(m_pointer)) 
886                         {
887                                 m_tooltip_element->setVisible(true);
888                                 this->bringToFront(m_tooltip_element);
889                                 m_tooltip_element->setText(narrow_to_wide(spec.tooltip).c_str());
890                                 s32 tooltip_x = m_pointer.X + 15;
891                                 s32 tooltip_y = m_pointer.Y + 15;
892                                 s32 tooltip_width = m_tooltip_element->getTextWidth() + 15;
893                                 s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
894                                 m_tooltip_element->setRelativePosition(core::rect<s32>(
895                                 core::position2d<s32>(tooltip_x, tooltip_y),
896                                 core::dimension2d<s32>(tooltip_width, tooltip_height)));
897                         }
898                 }
899         }
900         
901         /*
902                 Draw dragged item stack
903         */
904         drawSelectedItem();
905 }
906
907 void GUIFormSpecMenu::updateSelectedItem()
908 {
909         // WARNING: BLACK MAGIC
910         // See if there is a stack suited for our current guess.
911         // If such stack does not exist, clear the guess.
912         if(m_selected_content_guess.name != "")
913         {
914                 bool found = false;
915                 for(u32 i=0; i<m_inventorylists.size() && !found; i++){
916                         const ListDrawSpec &s = m_inventorylists[i];
917                         Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
918                         if(!inv)
919                                 continue;
920                         InventoryList *list = inv->getList(s.listname);
921                         if(!list)
922                                 continue;
923                         for(s32 i=0; i<s.geom.X*s.geom.Y && !found; i++){
924                                 u32 item_i = i + s.start_item_i;
925                                 if(item_i >= list->getSize())
926                                         continue;
927                                 ItemStack stack = list->getItem(item_i);
928                                 if(stack.name == m_selected_content_guess.name &&
929                                                 stack.count == m_selected_content_guess.count){
930                                         found = true;
931                                         if(m_selected_item){
932                                                 // If guessed stack is already selected, all is fine
933                                                 if(m_selected_item->inventoryloc == s.inventoryloc &&
934                                                                 m_selected_item->listname == s.listname &&
935                                                                 m_selected_item->i == (s32)item_i &&
936                                                                 m_selected_amount == stack.count){
937                                                         break;
938                                                 }
939                                                 delete m_selected_item;
940                                                 m_selected_item = NULL;
941                                         }
942                                         infostream<<"Client: Changing selected content guess to "
943                                                         <<s.inventoryloc.dump()<<" "<<s.listname
944                                                         <<" "<<item_i<<std::endl;
945                                         m_selected_item = new ItemSpec(s.inventoryloc, s.listname, item_i);
946                                         m_selected_amount = stack.count;
947                                         break;
948                                 }
949                         }
950                 }
951                 if(!found){
952                         infostream<<"Client: Discarding selected content guess: "
953                                         <<m_selected_content_guess.getItemString()<<std::endl;
954                         m_selected_content_guess.name = "";
955                 }
956         }
957         // If the selected stack has become empty for some reason, deselect it.
958         // If the selected stack has become smaller, adjust m_selected_amount.
959         if(m_selected_item)
960         {
961                 bool selection_valid = false;
962                 if(m_selected_item->isValid())
963                 {
964                         Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
965                         if(inv)
966                         {
967                                 InventoryList *list = inv->getList(m_selected_item->listname);
968                                 if(list && (u32) m_selected_item->i < list->getSize())
969                                 {
970                                         ItemStack stack = list->getItem(m_selected_item->i);
971                                         if(m_selected_amount > stack.count)
972                                                 m_selected_amount = stack.count;
973                                         if(!stack.empty())
974                                                 selection_valid = true;
975                                 }
976                         }
977                 }
978                 if(!selection_valid)
979                 {
980                         delete m_selected_item;
981                         m_selected_item = NULL;
982                         m_selected_amount = 0;
983                         m_selected_dragging = false;
984                 }
985         }
986
987         // If craftresult is nonempty and nothing else is selected, select it now.
988         if(!m_selected_item)
989         {
990                 for(u32 i=0; i<m_inventorylists.size(); i++)
991                 {
992                         const ListDrawSpec &s = m_inventorylists[i];
993                         if(s.listname == "craftpreview")
994                         {
995                                 Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
996                                 InventoryList *list = inv->getList("craftresult");
997                                 if(list && list->getSize() >= 1 && !list->getItem(0).empty())
998                                 {
999                                         m_selected_item = new ItemSpec;
1000                                         m_selected_item->inventoryloc = s.inventoryloc;
1001                                         m_selected_item->listname = "craftresult";
1002                                         m_selected_item->i = 0;
1003                                         m_selected_amount = 0;
1004                                         m_selected_dragging = false;
1005                                         break;
1006                                 }
1007                         }
1008                 }
1009         }
1010
1011         // If craftresult is selected, keep the whole stack selected
1012         if(m_selected_item && m_selected_item->listname == "craftresult")
1013         {
1014                 Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
1015                 assert(inv);
1016                 InventoryList *list = inv->getList(m_selected_item->listname);
1017                 assert(list);
1018                 m_selected_amount = list->getItem(m_selected_item->i).count;
1019         }
1020 }
1021
1022 void GUIFormSpecMenu::acceptInput()
1023 {
1024         if(m_text_dst)
1025         {
1026                 std::map<std::string, std::string> fields;
1027                 gui::IGUIElement *e;
1028                 for(u32 i=0; i<m_fields.size(); i++)
1029                 {
1030                         const FieldSpec &s = m_fields[i];
1031                         if(s.send) 
1032                         {
1033                                 if(s.is_button)
1034                                 {
1035                                         fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(s.flabel.c_str());
1036                                 }
1037                                 else
1038                                 {
1039                                         e = getElementFromId(s.fid);
1040                                         if(e != NULL)
1041                                         {
1042                                                 fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(e->getText());
1043                                         }
1044                                 }
1045                         }
1046                 }
1047                 m_text_dst->gotText(fields);
1048         }
1049 }
1050
1051 bool GUIFormSpecMenu::OnEvent(const SEvent& event)
1052 {
1053         if(event.EventType==EET_KEY_INPUT_EVENT)
1054         {
1055                 KeyPress kp(event.KeyInput);
1056                 if (event.KeyInput.PressedDown && (kp == EscapeKey ||
1057                         kp == getKeySetting("keymap_inventory")))
1058                 {
1059                         quitMenu();
1060                         return true;
1061                 }
1062                 if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
1063                 {
1064                         acceptInput();
1065                         quitMenu();
1066                         return true;
1067                 }
1068         }
1069         if(event.EventType==EET_MOUSE_INPUT_EVENT
1070                         && event.MouseInput.Event != EMIE_MOUSE_MOVED)
1071         {
1072                 // Mouse event other than movement
1073
1074                 // Get selected item and hovered/clicked item (s)
1075
1076                 updateSelectedItem();
1077                 ItemSpec s = getItemAtPos(m_pointer);
1078
1079                 Inventory *inv_selected = NULL;
1080                 Inventory *inv_s = NULL;
1081
1082                 if(m_selected_item)
1083                 {
1084                         inv_selected = m_invmgr->getInventory(m_selected_item->inventoryloc);
1085                         assert(inv_selected);
1086                         assert(inv_selected->getList(m_selected_item->listname) != NULL);
1087                 }
1088
1089                 u32 s_count = 0;
1090
1091                 if(s.isValid())
1092                 do{ // breakable
1093                         inv_s = m_invmgr->getInventory(s.inventoryloc);
1094
1095                         if(!inv_s){
1096                                 errorstream<<"InventoryMenu: The selected inventory location "
1097                                                 <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
1098                                                 <<std::endl;
1099                                 s.i = -1;  // make it invalid again
1100                                 break;
1101                         }
1102
1103                         InventoryList *list = inv_s->getList(s.listname);
1104                         if(list == NULL){
1105                                 verbosestream<<"InventoryMenu: The selected inventory list \""
1106                                                 <<s.listname<<"\" does not exist"<<std::endl;
1107                                 s.i = -1;  // make it invalid again
1108                                 break;
1109                         }
1110
1111                         if((u32)s.i >= list->getSize()){
1112                                 infostream<<"InventoryMenu: The selected inventory list \""
1113                                                 <<s.listname<<"\" is too small (i="<<s.i<<", size="
1114                                                 <<list->getSize()<<")"<<std::endl;
1115                                 s.i = -1;  // make it invalid again
1116                                 break;
1117                         }
1118
1119                         s_count = list->getItem(s.i).count;
1120                 }while(0);
1121
1122                 bool identical = (m_selected_item != NULL) && s.isValid() &&
1123                         (inv_selected == inv_s) &&
1124                         (m_selected_item->listname == s.listname) &&
1125                         (m_selected_item->i == s.i);
1126
1127                 // buttons: 0 = left, 1 = right, 2 = middle
1128                 // up/down: 0 = down (press), 1 = up (release), 2 = unknown event
1129                 int button = 0;
1130                 int updown = 2;
1131                 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
1132                         { button = 0; updown = 0; }
1133                 else if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
1134                         { button = 1; updown = 0; }
1135                 else if(event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN)
1136                         { button = 2; updown = 0; }
1137                 else if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
1138                         { button = 0; updown = 1; }
1139                 else if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
1140                         { button = 1; updown = 1; }
1141                 else if(event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP)
1142                         { button = 2; updown = 1; }
1143
1144                 // Set this number to a positive value to generate a move action
1145                 // from m_selected_item to s.
1146                 u32 move_amount = 0;
1147
1148                 // Set this number to a positive value to generate a drop action
1149                 // from m_selected_item.
1150                 u32 drop_amount = 0;
1151
1152                 // Set this number to a positive value to generate a craft action at s.
1153                 u32 craft_amount = 0;
1154
1155                 if(updown == 0)
1156                 {
1157                         // Some mouse button has been pressed
1158
1159                         //infostream<<"Mouse button "<<button<<" pressed at p=("
1160                         //      <<p.X<<","<<p.Y<<")"<<std::endl;
1161
1162                         m_selected_dragging = false;
1163
1164                         if(s.isValid() && s.listname == "craftpreview")
1165                         {
1166                                 // Craft preview has been clicked: craft
1167                                 craft_amount = (button == 2 ? 10 : 1);
1168                         }
1169                         else if(m_selected_item == NULL)
1170                         {
1171                                 if(s_count != 0)
1172                                 {
1173                                         // Non-empty stack has been clicked: select it
1174                                         m_selected_item = new ItemSpec(s);
1175
1176                                         if(button == 1)  // right
1177                                                 m_selected_amount = (s_count + 1) / 2;
1178                                         else if(button == 2)  // middle
1179                                                 m_selected_amount = MYMIN(s_count, 10);
1180                                         else  // left
1181                                                 m_selected_amount = s_count;
1182
1183                                         m_selected_dragging = true;
1184                                 }
1185                         }
1186                         else  // m_selected_item != NULL
1187                         {
1188                                 assert(m_selected_amount >= 1);
1189
1190                                 if(s.isValid())
1191                                 {
1192                                         // Clicked a slot: move
1193                                         if(button == 1)  // right
1194                                                 move_amount = 1;
1195                                         else if(button == 2)  // middle
1196                                                 move_amount = MYMIN(m_selected_amount, 10);
1197                                         else  // left
1198                                                 move_amount = m_selected_amount;
1199
1200                                         if(identical)
1201                                         {
1202                                                 if(move_amount >= m_selected_amount)
1203                                                         m_selected_amount = 0;
1204                                                 else
1205                                                         m_selected_amount -= move_amount;
1206                                                 move_amount = 0;
1207                                         }
1208                                 }
1209                                 else if(getAbsoluteClippingRect().isPointInside(m_pointer))
1210                                 {
1211                                         // Clicked somewhere else: deselect
1212                                         m_selected_amount = 0;
1213                                 }
1214                                 else
1215                                 {
1216                                         // Clicked outside of the window: drop
1217                                         if(button == 1)  // right
1218                                                 drop_amount = 1;
1219                                         else if(button == 2)  // middle
1220                                                 drop_amount = MYMIN(m_selected_amount, 10);
1221                                         else  // left
1222                                                 drop_amount = m_selected_amount;
1223                                 }
1224                         }
1225                 }
1226                 else if(updown == 1)
1227                 {
1228                         // Some mouse button has been released
1229
1230                         //infostream<<"Mouse button "<<button<<" released at p=("
1231                         //      <<p.X<<","<<p.Y<<")"<<std::endl;
1232
1233                         if(m_selected_item != NULL && m_selected_dragging && s.isValid())
1234                         {
1235                                 if(!identical)
1236                                 {
1237                                         // Dragged to different slot: move all selected
1238                                         move_amount = m_selected_amount;
1239                                 }
1240                         }
1241                         else if(m_selected_item != NULL && m_selected_dragging &&
1242                                 !(getAbsoluteClippingRect().isPointInside(m_pointer)))
1243                         {
1244                                 // Dragged outside of window: drop all selected
1245                                 drop_amount = m_selected_amount;
1246                         }
1247
1248                         m_selected_dragging = false;
1249                 }
1250
1251                 // Possibly send inventory action to server
1252                 if(move_amount > 0)
1253                 {
1254                         // Send IACTION_MOVE
1255
1256                         assert(m_selected_item && m_selected_item->isValid());
1257                         assert(s.isValid());
1258
1259                         assert(inv_selected && inv_s);
1260                         InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
1261                         InventoryList *list_to = inv_s->getList(s.listname);
1262                         assert(list_from && list_to);
1263                         ItemStack stack_from = list_from->getItem(m_selected_item->i);
1264                         ItemStack stack_to = list_to->getItem(s.i);
1265
1266                         // Check how many items can be moved
1267                         move_amount = stack_from.count = MYMIN(move_amount, stack_from.count);
1268                         ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef());
1269                         // If source stack cannot be added to destination stack at all,
1270                         // they are swapped
1271                         if(leftover.count == stack_from.count && leftover.name == stack_from.name)
1272                         {
1273                                 m_selected_amount = stack_to.count;
1274                                 // In case the server doesn't directly swap them but instead
1275                                 // moves stack_to somewhere else, set this
1276                                 m_selected_content_guess = stack_to;
1277                                 m_selected_content_guess_inventory = s.inventoryloc;
1278                         }
1279                         // Source stack goes fully into destination stack
1280                         else if(leftover.empty())
1281                         {
1282                                 m_selected_amount -= move_amount;
1283                                 m_selected_content_guess = ItemStack(); // Clear
1284                         }
1285                         // Source stack goes partly into destination stack
1286                         else
1287                         {
1288                                 move_amount -= leftover.count;
1289                                 m_selected_amount -= move_amount;
1290                                 m_selected_content_guess = ItemStack(); // Clear
1291                         }
1292
1293                         infostream<<"Handing IACTION_MOVE to manager"<<std::endl;
1294                         IMoveAction *a = new IMoveAction();
1295                         a->count = move_amount;
1296                         a->from_inv = m_selected_item->inventoryloc;
1297                         a->from_list = m_selected_item->listname;
1298                         a->from_i = m_selected_item->i;
1299                         a->to_inv = s.inventoryloc;
1300                         a->to_list = s.listname;
1301                         a->to_i = s.i;
1302                         m_invmgr->inventoryAction(a);
1303                 }
1304                 else if(drop_amount > 0)
1305                 {
1306                         m_selected_content_guess = ItemStack(); // Clear
1307
1308                         // Send IACTION_DROP
1309
1310                         assert(m_selected_item && m_selected_item->isValid());
1311                         assert(inv_selected);
1312                         InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
1313                         assert(list_from);
1314                         ItemStack stack_from = list_from->getItem(m_selected_item->i);
1315
1316                         // Check how many items can be dropped
1317                         drop_amount = stack_from.count = MYMIN(drop_amount, stack_from.count);
1318                         assert(drop_amount > 0 && drop_amount <= m_selected_amount);
1319                         m_selected_amount -= drop_amount;
1320
1321                         infostream<<"Handing IACTION_DROP to manager"<<std::endl;
1322                         IDropAction *a = new IDropAction();
1323                         a->count = drop_amount;
1324                         a->from_inv = m_selected_item->inventoryloc;
1325                         a->from_list = m_selected_item->listname;
1326                         a->from_i = m_selected_item->i;
1327                         m_invmgr->inventoryAction(a);
1328                 }
1329                 else if(craft_amount > 0)
1330                 {
1331                         m_selected_content_guess = ItemStack(); // Clear
1332
1333                         // Send IACTION_CRAFT
1334
1335                         assert(s.isValid());
1336                         assert(inv_s);
1337
1338                         infostream<<"Handing IACTION_CRAFT to manager"<<std::endl;
1339                         ICraftAction *a = new ICraftAction();
1340                         a->count = craft_amount;
1341                         a->craft_inv = s.inventoryloc;
1342                         m_invmgr->inventoryAction(a);
1343                 }
1344
1345                 // If m_selected_amount has been decreased to zero, deselect
1346                 if(m_selected_amount == 0)
1347                 {
1348                         delete m_selected_item;
1349                         m_selected_item = NULL;
1350                         m_selected_amount = 0;
1351                         m_selected_dragging = false;
1352                         m_selected_content_guess = ItemStack();
1353                 }
1354         }
1355         if(event.EventType==EET_GUI_EVENT)
1356         {
1357                 if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
1358                                 && isVisible())
1359                 {
1360                         if(!canTakeFocus(event.GUIEvent.Element))
1361                         {
1362                                 infostream<<"GUIFormSpecMenu: Not allowing focus change."
1363                                                 <<std::endl;
1364                                 // Returning true disables focus change
1365                                 return true;
1366                         }
1367                 }
1368                 if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
1369                 {
1370                         switch(event.GUIEvent.Caller->getID())
1371                         {
1372                         case 257:
1373                                 acceptInput();
1374                                 quitMenu();
1375                                 // quitMenu deallocates menu
1376                                 return true;
1377                         }
1378                         // find the element that was clicked
1379                         for(u32 i=0; i<m_fields.size(); i++)
1380                         {
1381                                 FieldSpec &s = m_fields[i];
1382                                 // if its a button, set the send field so 
1383                                 // lua knows which button was pressed
1384                                 if (s.is_button && s.fid == event.GUIEvent.Caller->getID())
1385                                 {
1386                                         s.send = true;
1387                                         acceptInput();
1388                                         if(s.is_exit){
1389                                                 quitMenu();
1390                                                 return true;
1391                                         }else{
1392                                                 s.send = false;
1393                                                 // Restore focus to the full form
1394                                                 Environment->setFocus(this);
1395                                                 return true;
1396                                         }
1397                                 }
1398                         }
1399                 }
1400                 if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
1401                 {
1402                         if(event.GUIEvent.Caller->getID() > 257)
1403                         {
1404                                 acceptInput();
1405                                 quitMenu();
1406                                 // quitMenu deallocates menu
1407                                 return true;
1408                         }
1409                 }
1410         }
1411
1412         return Parent ? Parent->OnEvent(event) : false;
1413 }
1414