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