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