Predict param2 of facedir nodes and attachment of attached_node nodes
[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                 delete[] text;
585                 changeCtype("C");
586         }
587         // If there's fields, add a Proceed button
588         if (m_fields.size() && bp_set != 2) 
589         {
590                 // if the size wasn't set by an invsize[] or size[] adjust it now to fit all the fields
591                 rect = core::rect<s32>(
592                         screensize.X/2 - 580/2,
593                         screensize.Y/2 - 300/2,
594                         screensize.X/2 + 580/2,
595                         screensize.Y/2 + 240/2+(m_fields.size()*60)
596                 );
597                 DesiredRect = rect;
598                 recalculateAbsolutePosition(false);
599                 basepos = getBasePos();
600
601                 changeCtype("");
602                 {
603                         v2s32 pos = basepos;
604                         pos.Y = ((m_fields.size()+2)*60);
605
606                         v2s32 size = DesiredRect.getSize();
607                         rect = core::rect<s32>(size.X/2-70, pos.Y, (size.X/2-70)+140, pos.Y+30);
608                         wchar_t* text = wgettext("Proceed");
609                         Environment->addButton(rect, this, 257, text);
610                         delete[] text;
611                 }
612                 changeCtype("C");
613         }
614         // Add tooltip
615         {
616                 // Note: parent != this so that the tooltip isn't clipped by the menu rectangle
617                 m_tooltip_element = Environment->addStaticText(L"",core::rect<s32>(0,0,110,18));
618                 m_tooltip_element->enableOverrideColor(true);
619                 m_tooltip_element->setBackgroundColor(video::SColor(255,110,130,60));
620                 m_tooltip_element->setDrawBackground(true);
621                 m_tooltip_element->setDrawBorder(true);
622                 m_tooltip_element->setOverrideColor(video::SColor(255,255,255,255));
623                 m_tooltip_element->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
624                 m_tooltip_element->setWordWrap(false);
625         }
626 }
627
628 GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
629 {
630         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
631         
632         for(u32 i=0; i<m_inventorylists.size(); i++)
633         {
634                 const ListDrawSpec &s = m_inventorylists[i];
635
636                 for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
637                 {
638                         s32 item_i = i + s.start_item_i;
639                         s32 x = (i%s.geom.X) * spacing.X;
640                         s32 y = (i/s.geom.X) * spacing.Y;
641                         v2s32 p0(x,y);
642                         core::rect<s32> rect = imgrect + s.pos + p0;
643                         if(rect.isPointInside(p))
644                         {
645                                 return ItemSpec(s.inventoryloc, s.listname, item_i);
646                         }
647                 }
648         }
649
650         return ItemSpec(InventoryLocation(), "", -1);
651 }
652
653 void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
654 {
655         video::IVideoDriver* driver = Environment->getVideoDriver();
656
657         // Get font
658         gui::IGUIFont *font = NULL;
659         gui::IGUISkin* skin = Environment->getSkin();
660         if (skin)
661                 font = skin->getFont();
662         
663         Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
664         if(!inv){
665                 infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
666                                 <<"The inventory location "
667                                 <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
668                                 <<std::endl;
669                 return;
670         }
671         InventoryList *ilist = inv->getList(s.listname);
672         if(!ilist){
673                 infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
674                                 <<"The inventory list \""<<s.listname<<"\" @ \""
675                                 <<s.inventoryloc.dump()<<"\" doesn't exist"
676                                 <<std::endl;
677                 return;
678         }
679         
680         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
681         
682         for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
683         {
684                 u32 item_i = i + s.start_item_i;
685                 if(item_i >= ilist->getSize())
686                         break;
687                 s32 x = (i%s.geom.X) * spacing.X;
688                 s32 y = (i/s.geom.X) * spacing.Y;
689                 v2s32 p(x,y);
690                 core::rect<s32> rect = imgrect + s.pos + p;
691                 ItemStack item;
692                 if(ilist)
693                         item = ilist->getItem(item_i);
694
695                 bool selected = m_selected_item
696                         && m_invmgr->getInventory(m_selected_item->inventoryloc) == inv
697                         && m_selected_item->listname == s.listname
698                         && m_selected_item->i == i;
699                 bool hovering = rect.isPointInside(m_pointer);
700
701                 if(phase == 0)
702                 {
703                         if(hovering && m_selected_item)
704                         {
705                                 video::SColor bgcolor(255,192,192,192);
706                                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
707                         }
708                         else
709                         {
710                                 video::SColor bgcolor(255,128,128,128);
711                                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
712                         }
713                 }
714
715                 if(phase == 1)
716                 {
717                         // Draw item stack
718                         if(selected)
719                         {
720                                 item.takeItem(m_selected_amount);
721                         }
722                         if(!item.empty())
723                         {
724                                 drawItemStack(driver, font, item,
725                                                 rect, &AbsoluteClippingRect, m_gamedef);
726                         }
727
728                         // Draw tooltip
729                         std::string tooltip_text = "";
730                         if(hovering && !m_selected_item)
731                                 tooltip_text = item.getDefinition(m_gamedef->idef()).description;
732                         if(tooltip_text != "")
733                         {
734                                 m_tooltip_element->setVisible(true);
735                                 this->bringToFront(m_tooltip_element);
736                                 m_tooltip_element->setText(narrow_to_wide(tooltip_text).c_str());
737                                 s32 tooltip_x = m_pointer.X + 15;
738                                 s32 tooltip_y = m_pointer.Y + 15;
739                                 s32 tooltip_width = m_tooltip_element->getTextWidth() + 15;
740                                 s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
741                                 m_tooltip_element->setRelativePosition(core::rect<s32>(
742                                                 core::position2d<s32>(tooltip_x, tooltip_y),
743                                                 core::dimension2d<s32>(tooltip_width, tooltip_height)));
744                         }
745                 }
746         }
747 }
748
749 void GUIFormSpecMenu::drawSelectedItem()
750 {
751         if(!m_selected_item)
752                 return;
753
754         video::IVideoDriver* driver = Environment->getVideoDriver();
755
756         // Get font
757         gui::IGUIFont *font = NULL;
758         gui::IGUISkin* skin = Environment->getSkin();
759         if (skin)
760                 font = skin->getFont();
761         
762         Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
763         assert(inv);
764         InventoryList *list = inv->getList(m_selected_item->listname);
765         assert(list);
766         ItemStack stack = list->getItem(m_selected_item->i);
767         stack.count = m_selected_amount;
768
769         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
770         core::rect<s32> rect = imgrect + (m_pointer - imgrect.getCenter());
771         drawItemStack(driver, font, stack, rect, NULL, m_gamedef);
772 }
773
774 void GUIFormSpecMenu::drawMenu()
775 {
776         if(m_form_src){
777                 std::string newform = m_form_src->getForm();
778                 if(newform != m_formspec_string){
779                         m_formspec_string = newform;
780                         regenerateGui(m_screensize_old);
781                 }
782         }
783
784         m_pointer = m_device->getCursorControl()->getPosition();
785
786         updateSelectedItem();
787
788         gui::IGUISkin* skin = Environment->getSkin();
789         if (!skin)
790                 return;
791         video::IVideoDriver* driver = Environment->getVideoDriver();
792         
793         video::SColor bgcolor(140,0,0,0);
794         driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
795
796         m_tooltip_element->setVisible(false);
797
798         /*
799                 Draw backgrounds
800         */
801         for(u32 i=0; i<m_backgrounds.size(); i++)
802         {
803                 const ImageDrawSpec &spec = m_backgrounds[i];
804                 video::ITexture *texture =
805                                 m_gamedef->tsrc()->getTextureRaw(spec.name);
806                 // Image size on screen
807                 core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
808                 // Image rectangle on screen
809                 core::rect<s32> rect = imgrect + spec.pos;
810                 const video::SColor color(255,255,255,255);
811                 const video::SColor colors[] = {color,color,color,color};
812                 driver->draw2DImage(texture, rect,
813                         core::rect<s32>(core::position2d<s32>(0,0),
814                                         core::dimension2di(texture->getOriginalSize())),
815                         NULL/*&AbsoluteClippingRect*/, colors, true);
816         }
817         
818         /*
819                 Draw images
820         */
821         for(u32 i=0; i<m_images.size(); i++)
822         {
823                 const ImageDrawSpec &spec = m_images[i];
824                 video::ITexture *texture =
825                                 m_gamedef->tsrc()->getTextureRaw(spec.name);
826                 // Image size on screen
827                 core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
828                 // Image rectangle on screen
829                 core::rect<s32> rect = imgrect + spec.pos;
830                 const video::SColor color(255,255,255,255);
831                 const video::SColor colors[] = {color,color,color,color};
832                 driver->draw2DImage(texture, rect,
833                         core::rect<s32>(core::position2d<s32>(0,0),
834                                         core::dimension2di(texture->getOriginalSize())),
835                         NULL/*&AbsoluteClippingRect*/, colors, true);
836         }
837         
838         /*
839                 Draw item images
840         */
841         for(u32 i=0; i<m_itemimages.size(); i++)
842         {
843                 const ImageDrawSpec &spec = m_itemimages[i];
844                 IItemDefManager *idef = m_gamedef->idef();
845                 ItemStack item;
846                 item.deSerialize(spec.name, idef);
847                 video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);         
848                 // Image size on screen
849                 core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
850                 // Image rectangle on screen
851                 core::rect<s32> rect = imgrect + spec.pos;
852                 const video::SColor color(255,255,255,255);
853                 const video::SColor colors[] = {color,color,color,color};
854                 driver->draw2DImage(texture, rect,
855                         core::rect<s32>(core::position2d<s32>(0,0),
856                                         core::dimension2di(texture->getOriginalSize())),
857                         NULL/*&AbsoluteClippingRect*/, colors, true);
858         }
859         
860         /*
861                 Draw items
862                 Phase 0: Item slot rectangles
863                 Phase 1: Item images; prepare tooltip
864                 If backgrounds used, do not draw Item slot rectangles
865         */
866         int start_phase=0;
867         if (m_backgrounds.size() > 0) start_phase=1;
868         for(int phase=start_phase; phase<=1; phase++)
869         for(u32 i=0; i<m_inventorylists.size(); i++)
870         {
871                 drawList(m_inventorylists[i], phase);
872         }
873
874         /*
875                 Call base class
876         */
877         gui::IGUIElement::draw();
878         
879         /*
880                 Draw fields/buttons tooltips
881         */
882         for(u32 i=0; i<m_fields.size(); i++)
883         {
884                 const FieldSpec &spec = m_fields[i];
885                 if (spec.tooltip != "")
886                 {
887                         core::rect<s32> rect = spec.rect;
888                         if (rect.isPointInside(m_pointer)) 
889                         {
890                                 m_tooltip_element->setVisible(true);
891                                 this->bringToFront(m_tooltip_element);
892                                 m_tooltip_element->setText(narrow_to_wide(spec.tooltip).c_str());
893                                 s32 tooltip_x = m_pointer.X + 15;
894                                 s32 tooltip_y = m_pointer.Y + 15;
895                                 s32 tooltip_width = m_tooltip_element->getTextWidth() + 15;
896                                 s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
897                                 m_tooltip_element->setRelativePosition(core::rect<s32>(
898                                 core::position2d<s32>(tooltip_x, tooltip_y),
899                                 core::dimension2d<s32>(tooltip_width, tooltip_height)));
900                         }
901                 }
902         }
903         
904         /*
905                 Draw dragged item stack
906         */
907         drawSelectedItem();
908 }
909
910 void GUIFormSpecMenu::updateSelectedItem()
911 {
912         // WARNING: BLACK MAGIC
913         // See if there is a stack suited for our current guess.
914         // If such stack does not exist, clear the guess.
915         if(m_selected_content_guess.name != "")
916         {
917                 bool found = false;
918                 for(u32 i=0; i<m_inventorylists.size() && !found; i++){
919                         const ListDrawSpec &s = m_inventorylists[i];
920                         Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
921                         if(!inv)
922                                 continue;
923                         InventoryList *list = inv->getList(s.listname);
924                         if(!list)
925                                 continue;
926                         for(s32 i=0; i<s.geom.X*s.geom.Y && !found; i++){
927                                 u32 item_i = i + s.start_item_i;
928                                 if(item_i >= list->getSize())
929                                         continue;
930                                 ItemStack stack = list->getItem(item_i);
931                                 if(stack.name == m_selected_content_guess.name &&
932                                                 stack.count == m_selected_content_guess.count){
933                                         found = true;
934                                         if(m_selected_item){
935                                                 // If guessed stack is already selected, all is fine
936                                                 if(m_selected_item->inventoryloc == s.inventoryloc &&
937                                                                 m_selected_item->listname == s.listname &&
938                                                                 m_selected_item->i == (s32)item_i &&
939                                                                 m_selected_amount == stack.count){
940                                                         break;
941                                                 }
942                                                 delete m_selected_item;
943                                                 m_selected_item = NULL;
944                                         }
945                                         infostream<<"Client: Changing selected content guess to "
946                                                         <<s.inventoryloc.dump()<<" "<<s.listname
947                                                         <<" "<<item_i<<std::endl;
948                                         m_selected_item = new ItemSpec(s.inventoryloc, s.listname, item_i);
949                                         m_selected_amount = stack.count;
950                                         break;
951                                 }
952                         }
953                 }
954                 if(!found){
955                         infostream<<"Client: Discarding selected content guess: "
956                                         <<m_selected_content_guess.getItemString()<<std::endl;
957                         m_selected_content_guess.name = "";
958                 }
959         }
960         // If the selected stack has become empty for some reason, deselect it.
961         // If the selected stack has become smaller, adjust m_selected_amount.
962         if(m_selected_item)
963         {
964                 bool selection_valid = false;
965                 if(m_selected_item->isValid())
966                 {
967                         Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
968                         if(inv)
969                         {
970                                 InventoryList *list = inv->getList(m_selected_item->listname);
971                                 if(list && (u32) m_selected_item->i < list->getSize())
972                                 {
973                                         ItemStack stack = list->getItem(m_selected_item->i);
974                                         if(m_selected_amount > stack.count)
975                                                 m_selected_amount = stack.count;
976                                         if(!stack.empty())
977                                                 selection_valid = true;
978                                 }
979                         }
980                 }
981                 if(!selection_valid)
982                 {
983                         delete m_selected_item;
984                         m_selected_item = NULL;
985                         m_selected_amount = 0;
986                         m_selected_dragging = false;
987                 }
988         }
989
990         // If craftresult is nonempty and nothing else is selected, select it now.
991         if(!m_selected_item)
992         {
993                 for(u32 i=0; i<m_inventorylists.size(); i++)
994                 {
995                         const ListDrawSpec &s = m_inventorylists[i];
996                         if(s.listname == "craftpreview")
997                         {
998                                 Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
999                                 InventoryList *list = inv->getList("craftresult");
1000                                 if(list && list->getSize() >= 1 && !list->getItem(0).empty())
1001                                 {
1002                                         m_selected_item = new ItemSpec;
1003                                         m_selected_item->inventoryloc = s.inventoryloc;
1004                                         m_selected_item->listname = "craftresult";
1005                                         m_selected_item->i = 0;
1006                                         m_selected_amount = 0;
1007                                         m_selected_dragging = false;
1008                                         break;
1009                                 }
1010                         }
1011                 }
1012         }
1013
1014         // If craftresult is selected, keep the whole stack selected
1015         if(m_selected_item && m_selected_item->listname == "craftresult")
1016         {
1017                 Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
1018                 assert(inv);
1019                 InventoryList *list = inv->getList(m_selected_item->listname);
1020                 assert(list);
1021                 m_selected_amount = list->getItem(m_selected_item->i).count;
1022         }
1023 }
1024
1025 void GUIFormSpecMenu::acceptInput()
1026 {
1027         if(m_text_dst)
1028         {
1029                 std::map<std::string, std::string> fields;
1030                 gui::IGUIElement *e;
1031                 for(u32 i=0; i<m_fields.size(); i++)
1032                 {
1033                         const FieldSpec &s = m_fields[i];
1034                         if(s.send) 
1035                         {
1036                                 if(s.is_button)
1037                                 {
1038                                         fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(s.flabel.c_str());
1039                                 }
1040                                 else
1041                                 {
1042                                         e = getElementFromId(s.fid);
1043                                         if(e != NULL)
1044                                         {
1045                                                 fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(e->getText());
1046                                         }
1047                                 }
1048                         }
1049                 }
1050                 m_text_dst->gotText(fields);
1051         }
1052 }
1053
1054 bool GUIFormSpecMenu::OnEvent(const SEvent& event)
1055 {
1056         if(event.EventType==EET_KEY_INPUT_EVENT)
1057         {
1058                 KeyPress kp(event.KeyInput);
1059                 if (event.KeyInput.PressedDown && (kp == EscapeKey ||
1060                         kp == getKeySetting("keymap_inventory")))
1061                 {
1062                         quitMenu();
1063                         return true;
1064                 }
1065                 if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
1066                 {
1067                         acceptInput();
1068                         quitMenu();
1069                         return true;
1070                 }
1071         }
1072         if(event.EventType==EET_MOUSE_INPUT_EVENT
1073                         && event.MouseInput.Event != EMIE_MOUSE_MOVED)
1074         {
1075                 // Mouse event other than movement
1076
1077                 // Get selected item and hovered/clicked item (s)
1078
1079                 updateSelectedItem();
1080                 ItemSpec s = getItemAtPos(m_pointer);
1081
1082                 Inventory *inv_selected = NULL;
1083                 Inventory *inv_s = NULL;
1084
1085                 if(m_selected_item)
1086                 {
1087                         inv_selected = m_invmgr->getInventory(m_selected_item->inventoryloc);
1088                         assert(inv_selected);
1089                         assert(inv_selected->getList(m_selected_item->listname) != NULL);
1090                 }
1091
1092                 u32 s_count = 0;
1093
1094                 if(s.isValid())
1095                 do{ // breakable
1096                         inv_s = m_invmgr->getInventory(s.inventoryloc);
1097
1098                         if(!inv_s){
1099                                 errorstream<<"InventoryMenu: The selected inventory location "
1100                                                 <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
1101                                                 <<std::endl;
1102                                 s.i = -1;  // make it invalid again
1103                                 break;
1104                         }
1105
1106                         InventoryList *list = inv_s->getList(s.listname);
1107                         if(list == NULL){
1108                                 verbosestream<<"InventoryMenu: The selected inventory list \""
1109                                                 <<s.listname<<"\" does not exist"<<std::endl;
1110                                 s.i = -1;  // make it invalid again
1111                                 break;
1112                         }
1113
1114                         if((u32)s.i >= list->getSize()){
1115                                 infostream<<"InventoryMenu: The selected inventory list \""
1116                                                 <<s.listname<<"\" is too small (i="<<s.i<<", size="
1117                                                 <<list->getSize()<<")"<<std::endl;
1118                                 s.i = -1;  // make it invalid again
1119                                 break;
1120                         }
1121
1122                         s_count = list->getItem(s.i).count;
1123                 }while(0);
1124
1125                 bool identical = (m_selected_item != NULL) && s.isValid() &&
1126                         (inv_selected == inv_s) &&
1127                         (m_selected_item->listname == s.listname) &&
1128                         (m_selected_item->i == s.i);
1129
1130                 // buttons: 0 = left, 1 = right, 2 = middle
1131                 // up/down: 0 = down (press), 1 = up (release), 2 = unknown event
1132                 int button = 0;
1133                 int updown = 2;
1134                 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
1135                         { button = 0; updown = 0; }
1136                 else if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
1137                         { button = 1; updown = 0; }
1138                 else if(event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN)
1139                         { button = 2; updown = 0; }
1140                 else if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
1141                         { button = 0; updown = 1; }
1142                 else if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
1143                         { button = 1; updown = 1; }
1144                 else if(event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP)
1145                         { button = 2; updown = 1; }
1146
1147                 // Set this number to a positive value to generate a move action
1148                 // from m_selected_item to s.
1149                 u32 move_amount = 0;
1150
1151                 // Set this number to a positive value to generate a drop action
1152                 // from m_selected_item.
1153                 u32 drop_amount = 0;
1154
1155                 // Set this number to a positive value to generate a craft action at s.
1156                 u32 craft_amount = 0;
1157
1158                 if(updown == 0)
1159                 {
1160                         // Some mouse button has been pressed
1161
1162                         //infostream<<"Mouse button "<<button<<" pressed at p=("
1163                         //      <<p.X<<","<<p.Y<<")"<<std::endl;
1164
1165                         m_selected_dragging = false;
1166
1167                         if(s.isValid() && s.listname == "craftpreview")
1168                         {
1169                                 // Craft preview has been clicked: craft
1170                                 craft_amount = (button == 2 ? 10 : 1);
1171                         }
1172                         else if(m_selected_item == NULL)
1173                         {
1174                                 if(s_count != 0)
1175                                 {
1176                                         // Non-empty stack has been clicked: select it
1177                                         m_selected_item = new ItemSpec(s);
1178
1179                                         if(button == 1)  // right
1180                                                 m_selected_amount = (s_count + 1) / 2;
1181                                         else if(button == 2)  // middle
1182                                                 m_selected_amount = MYMIN(s_count, 10);
1183                                         else  // left
1184                                                 m_selected_amount = s_count;
1185
1186                                         m_selected_dragging = true;
1187                                 }
1188                         }
1189                         else  // m_selected_item != NULL
1190                         {
1191                                 assert(m_selected_amount >= 1);
1192
1193                                 if(s.isValid())
1194                                 {
1195                                         // Clicked a slot: move
1196                                         if(button == 1)  // right
1197                                                 move_amount = 1;
1198                                         else if(button == 2)  // middle
1199                                                 move_amount = MYMIN(m_selected_amount, 10);
1200                                         else  // left
1201                                                 move_amount = m_selected_amount;
1202
1203                                         if(identical)
1204                                         {
1205                                                 if(move_amount >= m_selected_amount)
1206                                                         m_selected_amount = 0;
1207                                                 else
1208                                                         m_selected_amount -= move_amount;
1209                                                 move_amount = 0;
1210                                         }
1211                                 }
1212                                 else if(getAbsoluteClippingRect().isPointInside(m_pointer))
1213                                 {
1214                                         // Clicked somewhere else: deselect
1215                                         m_selected_amount = 0;
1216                                 }
1217                                 else
1218                                 {
1219                                         // Clicked outside of the window: drop
1220                                         if(button == 1)  // right
1221                                                 drop_amount = 1;
1222                                         else if(button == 2)  // middle
1223                                                 drop_amount = MYMIN(m_selected_amount, 10);
1224                                         else  // left
1225                                                 drop_amount = m_selected_amount;
1226                                 }
1227                         }
1228                 }
1229                 else if(updown == 1)
1230                 {
1231                         // Some mouse button has been released
1232
1233                         //infostream<<"Mouse button "<<button<<" released at p=("
1234                         //      <<p.X<<","<<p.Y<<")"<<std::endl;
1235
1236                         if(m_selected_item != NULL && m_selected_dragging && s.isValid())
1237                         {
1238                                 if(!identical)
1239                                 {
1240                                         // Dragged to different slot: move all selected
1241                                         move_amount = m_selected_amount;
1242                                 }
1243                         }
1244                         else if(m_selected_item != NULL && m_selected_dragging &&
1245                                 !(getAbsoluteClippingRect().isPointInside(m_pointer)))
1246                         {
1247                                 // Dragged outside of window: drop all selected
1248                                 drop_amount = m_selected_amount;
1249                         }
1250
1251                         m_selected_dragging = false;
1252                 }
1253
1254                 // Possibly send inventory action to server
1255                 if(move_amount > 0)
1256                 {
1257                         // Send IACTION_MOVE
1258
1259                         assert(m_selected_item && m_selected_item->isValid());
1260                         assert(s.isValid());
1261
1262                         assert(inv_selected && inv_s);
1263                         InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
1264                         InventoryList *list_to = inv_s->getList(s.listname);
1265                         assert(list_from && list_to);
1266                         ItemStack stack_from = list_from->getItem(m_selected_item->i);
1267                         ItemStack stack_to = list_to->getItem(s.i);
1268
1269                         // Check how many items can be moved
1270                         move_amount = stack_from.count = MYMIN(move_amount, stack_from.count);
1271                         ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef());
1272                         // If source stack cannot be added to destination stack at all,
1273                         // they are swapped
1274                         if(leftover.count == stack_from.count && leftover.name == stack_from.name)
1275                         {
1276                                 m_selected_amount = stack_to.count;
1277                                 // In case the server doesn't directly swap them but instead
1278                                 // moves stack_to somewhere else, set this
1279                                 m_selected_content_guess = stack_to;
1280                                 m_selected_content_guess_inventory = s.inventoryloc;
1281                         }
1282                         // Source stack goes fully into destination stack
1283                         else if(leftover.empty())
1284                         {
1285                                 m_selected_amount -= move_amount;
1286                                 m_selected_content_guess = ItemStack(); // Clear
1287                         }
1288                         // Source stack goes partly into destination stack
1289                         else
1290                         {
1291                                 move_amount -= leftover.count;
1292                                 m_selected_amount -= move_amount;
1293                                 m_selected_content_guess = ItemStack(); // Clear
1294                         }
1295
1296                         infostream<<"Handing IACTION_MOVE to manager"<<std::endl;
1297                         IMoveAction *a = new IMoveAction();
1298                         a->count = move_amount;
1299                         a->from_inv = m_selected_item->inventoryloc;
1300                         a->from_list = m_selected_item->listname;
1301                         a->from_i = m_selected_item->i;
1302                         a->to_inv = s.inventoryloc;
1303                         a->to_list = s.listname;
1304                         a->to_i = s.i;
1305                         m_invmgr->inventoryAction(a);
1306                 }
1307                 else if(drop_amount > 0)
1308                 {
1309                         m_selected_content_guess = ItemStack(); // Clear
1310
1311                         // Send IACTION_DROP
1312
1313                         assert(m_selected_item && m_selected_item->isValid());
1314                         assert(inv_selected);
1315                         InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
1316                         assert(list_from);
1317                         ItemStack stack_from = list_from->getItem(m_selected_item->i);
1318
1319                         // Check how many items can be dropped
1320                         drop_amount = stack_from.count = MYMIN(drop_amount, stack_from.count);
1321                         assert(drop_amount > 0 && drop_amount <= m_selected_amount);
1322                         m_selected_amount -= drop_amount;
1323
1324                         infostream<<"Handing IACTION_DROP to manager"<<std::endl;
1325                         IDropAction *a = new IDropAction();
1326                         a->count = drop_amount;
1327                         a->from_inv = m_selected_item->inventoryloc;
1328                         a->from_list = m_selected_item->listname;
1329                         a->from_i = m_selected_item->i;
1330                         m_invmgr->inventoryAction(a);
1331                 }
1332                 else if(craft_amount > 0)
1333                 {
1334                         m_selected_content_guess = ItemStack(); // Clear
1335
1336                         // Send IACTION_CRAFT
1337
1338                         assert(s.isValid());
1339                         assert(inv_s);
1340
1341                         infostream<<"Handing IACTION_CRAFT to manager"<<std::endl;
1342                         ICraftAction *a = new ICraftAction();
1343                         a->count = craft_amount;
1344                         a->craft_inv = s.inventoryloc;
1345                         m_invmgr->inventoryAction(a);
1346                 }
1347
1348                 // If m_selected_amount has been decreased to zero, deselect
1349                 if(m_selected_amount == 0)
1350                 {
1351                         delete m_selected_item;
1352                         m_selected_item = NULL;
1353                         m_selected_amount = 0;
1354                         m_selected_dragging = false;
1355                         m_selected_content_guess = ItemStack();
1356                 }
1357         }
1358         if(event.EventType==EET_GUI_EVENT)
1359         {
1360                 if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
1361                                 && isVisible())
1362                 {
1363                         if(!canTakeFocus(event.GUIEvent.Element))
1364                         {
1365                                 infostream<<"GUIFormSpecMenu: Not allowing focus change."
1366                                                 <<std::endl;
1367                                 // Returning true disables focus change
1368                                 return true;
1369                         }
1370                 }
1371                 if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
1372                 {
1373                         switch(event.GUIEvent.Caller->getID())
1374                         {
1375                         case 257:
1376                                 acceptInput();
1377                                 quitMenu();
1378                                 // quitMenu deallocates menu
1379                                 return true;
1380                         }
1381                         // find the element that was clicked
1382                         for(u32 i=0; i<m_fields.size(); i++)
1383                         {
1384                                 FieldSpec &s = m_fields[i];
1385                                 // if its a button, set the send field so 
1386                                 // lua knows which button was pressed
1387                                 if (s.is_button && s.fid == event.GUIEvent.Caller->getID())
1388                                 {
1389                                         s.send = true;
1390                                         acceptInput();
1391                                         if(s.is_exit){
1392                                                 quitMenu();
1393                                                 return true;
1394                                         }else{
1395                                                 s.send = false;
1396                                                 // Restore focus to the full form
1397                                                 Environment->setFocus(this);
1398                                                 return true;
1399                                         }
1400                                 }
1401                         }
1402                 }
1403                 if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER)
1404                 {
1405                         if(event.GUIEvent.Caller->getID() > 257)
1406                         {
1407                                 acceptInput();
1408                                 quitMenu();
1409                                 // quitMenu deallocates menu
1410                                 return true;
1411                         }
1412                 }
1413         }
1414
1415         return Parent ? Parent->OnEvent(event) : false;
1416 }
1417