]> git.lizzy.rs Git - dragonfireclient.git/blob - src/guiInventoryMenu.cpp
Update Lua API documentation to include minetest.get_modnames()
[dragonfireclient.git] / src / guiInventoryMenu.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 "guiInventoryMenu.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 void drawItemStack(video::IVideoDriver *driver,
37                 gui::IGUIFont *font,
38                 const ItemStack &item,
39                 const core::rect<s32> &rect,
40                 const core::rect<s32> *clip,
41                 IGameDef *gamedef)
42 {
43         if(item.empty())
44                 return;
45         
46         const ItemDefinition &def = item.getDefinition(gamedef->idef());
47         video::ITexture *texture = def.inventory_texture;
48
49         // Draw the inventory texture
50         if(texture != NULL)
51         {
52                 const video::SColor color(255,255,255,255);
53                 const video::SColor colors[] = {color,color,color,color};
54                 driver->draw2DImage(texture, rect,
55                         core::rect<s32>(core::position2d<s32>(0,0),
56                         core::dimension2di(texture->getOriginalSize())),
57                         clip, colors, true);
58         }
59
60         if(def.type == ITEM_TOOL && item.wear != 0)
61         {
62                 // Draw a progressbar
63                 float barheight = rect.getHeight()/16;
64                 float barpad_x = rect.getWidth()/16;
65                 float barpad_y = rect.getHeight()/16;
66                 core::rect<s32> progressrect(
67                         rect.UpperLeftCorner.X + barpad_x,
68                         rect.LowerRightCorner.Y - barpad_y - barheight,
69                         rect.LowerRightCorner.X - barpad_x,
70                         rect.LowerRightCorner.Y - barpad_y);
71
72                 // Shrink progressrect by amount of tool damage
73                 float wear = item.wear / 65535.0;
74                 int progressmid =
75                         wear * progressrect.UpperLeftCorner.X +
76                         (1-wear) * progressrect.LowerRightCorner.X;
77
78                 // Compute progressbar color
79                 //   wear = 0.0: green
80                 //   wear = 0.5: yellow
81                 //   wear = 1.0: red
82                 video::SColor color(255,255,255,255);
83                 int wear_i = MYMIN(floor(wear * 600), 511);
84                 wear_i = MYMIN(wear_i + 10, 511);
85                 if(wear_i <= 255)
86                         color.set(255, wear_i, 255, 0);
87                 else
88                         color.set(255, 255, 511-wear_i, 0);
89
90                 core::rect<s32> progressrect2 = progressrect;
91                 progressrect2.LowerRightCorner.X = progressmid;
92                 driver->draw2DRectangle(color, progressrect2, clip);
93
94                 color = video::SColor(255,0,0,0);
95                 progressrect2 = progressrect;
96                 progressrect2.UpperLeftCorner.X = progressmid;
97                 driver->draw2DRectangle(color, progressrect2, clip);
98         }
99
100         if(font != NULL && item.count >= 2)
101         {
102                 // Get the item count as a string
103                 std::string text = itos(item.count);
104                 v2u32 dim = font->getDimension(narrow_to_wide(text).c_str());
105                 v2s32 sdim(dim.X,dim.Y);
106
107                 core::rect<s32> rect2(
108                         /*rect.UpperLeftCorner,
109                         core::dimension2d<u32>(rect.getWidth(), 15)*/
110                         rect.LowerRightCorner - sdim,
111                         sdim
112                 );
113
114                 video::SColor bgcolor(128,0,0,0);
115                 driver->draw2DRectangle(bgcolor, rect2, clip);
116
117                 video::SColor color(255,255,255,255);
118                 font->draw(text.c_str(), rect2, color, false, false, clip);
119         }
120 }
121
122 /*
123         GUIInventoryMenu
124 */
125
126 GUIInventoryMenu::GUIInventoryMenu(gui::IGUIEnvironment* env,
127                 gui::IGUIElement* parent, s32 id,
128                 IMenuManager *menumgr,
129                 InventoryManager *invmgr,
130                 IGameDef *gamedef
131 ):
132         GUIModalMenu(env, parent, id, menumgr),
133         m_invmgr(invmgr),
134         m_gamedef(gamedef),
135         m_form_src(NULL),
136         m_selected_item(NULL),
137         m_selected_amount(0),
138         m_selected_dragging(false),
139         m_tooltip_element(NULL)
140 {
141 }
142
143 GUIInventoryMenu::~GUIInventoryMenu()
144 {
145         removeChildren();
146
147         delete m_selected_item;
148         delete m_form_src;
149 }
150
151 void GUIInventoryMenu::removeChildren()
152 {
153         const core::list<gui::IGUIElement*> &children = getChildren();
154         core::list<gui::IGUIElement*> children_copy;
155         for(core::list<gui::IGUIElement*>::ConstIterator
156                         i = children.begin(); i != children.end(); i++)
157         {
158                 children_copy.push_back(*i);
159         }
160         for(core::list<gui::IGUIElement*>::Iterator
161                         i = children_copy.begin();
162                         i != children_copy.end(); i++)
163         {
164                 (*i)->remove();
165         }
166         /*{
167                 gui::IGUIElement *e = getElementFromId(256);
168                 if(e != NULL)
169                         e->remove();
170         }*/
171         if(m_tooltip_element)
172         {
173                 m_tooltip_element->remove();
174                 m_tooltip_element = NULL;
175         }
176 }
177
178 void GUIInventoryMenu::regenerateGui(v2u32 screensize)
179 {
180         // Remove children
181         removeChildren();
182         
183         v2s32 size(100,100);
184         s32 helptext_h = 15;
185         core::rect<s32> rect;
186         v2s32 basepos = getBasePos();
187         
188         /* Convert m_init_draw_spec to m_inventorylists */
189         
190         m_inventorylists.clear();
191         m_images.clear();
192
193         Strfnd f(m_formspec_string);
194         while(f.atend() == false)
195         {
196                 std::string type = trim(f.next("["));
197                 if(type == "invsize")
198                 {
199                         v2f invsize;
200                         invsize.X = stof(f.next(","));
201                         invsize.Y = stof(f.next(";"));
202                         infostream<<"invsize ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
203                         f.next("]");
204
205                         padding = v2s32(screensize.Y/40, screensize.Y/40);
206                         spacing = v2s32(screensize.Y/12, screensize.Y/13);
207                         imgsize = v2s32(screensize.Y/15, screensize.Y/15);
208                         size = v2s32(
209                                 padding.X*2+spacing.X*(invsize.X-1.0)+imgsize.X,
210                                 padding.Y*2+spacing.Y*(invsize.Y-1.0)+imgsize.Y + (helptext_h-5)
211                         );
212                         rect = core::rect<s32>(
213                                         screensize.X/2 - size.X/2,
214                                         screensize.Y/2 - size.Y/2,
215                                         screensize.X/2 + size.X/2,
216                                         screensize.Y/2 + size.Y/2
217                         );
218                         DesiredRect = rect;
219                         recalculateAbsolutePosition(false);
220                         basepos = getBasePos();
221                 }
222                 else if(type == "list")
223                 {
224                         std::string name = f.next(";");
225                         InventoryLocation loc;
226                         if(name == "context" || name == "current_name")
227                                 loc = m_current_inventory_location;
228                         else
229                                 loc.deSerialize(name);
230                         std::string listname = f.next(";");
231                         v2s32 pos = basepos;
232                         pos.X += stof(f.next(",")) * (float)spacing.X;
233                         pos.Y += stof(f.next(";")) * (float)spacing.Y;
234                         v2s32 geom;
235                         geom.X = stoi(f.next(","));
236                         geom.Y = stoi(f.next(";"));
237                         infostream<<"list inv="<<name<<", listname="<<listname
238                                         <<", pos=("<<pos.X<<","<<pos.Y<<")"
239                                         <<", geom=("<<geom.X<<","<<geom.Y<<")"
240                                         <<std::endl;
241                         f.next("]");
242                         m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom));
243                 }
244                 else if(type == "image")
245                 {
246                         v2s32 pos = basepos;
247                         pos.X += stof(f.next(",")) * (float)spacing.X;
248                         pos.Y += stof(f.next(";")) * (float)spacing.Y;
249                         v2s32 geom;
250                         geom.X = stof(f.next(",")) * (float)imgsize.X;
251                         geom.Y = stof(f.next(";")) * (float)imgsize.Y;
252                         std::string name = f.next("]");
253                         infostream<<"image name="<<name
254                                         <<", pos=("<<pos.X<<","<<pos.Y<<")"
255                                         <<", geom=("<<geom.X<<","<<geom.Y<<")"
256                                         <<std::endl;
257                         m_images.push_back(ImageDrawSpec(name, pos, geom));
258                 }
259                 else
260                 {
261                         // Ignore others
262                         std::string ts = f.next("]");
263                         infostream<<"Unknown DrawSpec: type="<<type<<", data=\""<<ts<<"\""
264                                         <<std::endl;
265                 }
266         }
267
268         // Add children
269         {
270                 core::rect<s32> rect(0, 0, size.X-padding.X*2, helptext_h);
271                 rect = rect + v2s32(size.X/2 - rect.getWidth()/2,
272                                 size.Y-rect.getHeight()-5);
273                 const wchar_t *text =
274                 L"Left click: Move all items, Right click: Move single item";
275                 Environment->addStaticText(text, rect, false, true, this, 256);
276
277                 // Add tooltip
278                 // Note: parent != this so that the tooltip isn't clipped by the menu rectangle
279                 m_tooltip_element = Environment->addStaticText(L"",core::rect<s32>(0,0,110,18));
280                 m_tooltip_element->enableOverrideColor(true);
281                 m_tooltip_element->setBackgroundColor(video::SColor(255,110,130,60));
282                 m_tooltip_element->setDrawBackground(true);
283                 m_tooltip_element->setDrawBorder(true);
284                 m_tooltip_element->setOverrideColor(video::SColor(255,255,255,255));
285                 m_tooltip_element->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
286                 m_tooltip_element->setWordWrap(false);
287         }
288 }
289
290 GUIInventoryMenu::ItemSpec GUIInventoryMenu::getItemAtPos(v2s32 p) const
291 {
292         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
293         
294         for(u32 i=0; i<m_inventorylists.size(); i++)
295         {
296                 const ListDrawSpec &s = m_inventorylists[i];
297
298                 for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
299                 {
300                         s32 x = (i%s.geom.X) * spacing.X;
301                         s32 y = (i/s.geom.X) * spacing.Y;
302                         v2s32 p0(x,y);
303                         core::rect<s32> rect = imgrect + s.pos + p0;
304                         if(rect.isPointInside(p))
305                         {
306                                 return ItemSpec(s.inventoryloc, s.listname, i);
307                         }
308                 }
309         }
310
311         return ItemSpec(InventoryLocation(), "", -1);
312 }
313
314 void GUIInventoryMenu::drawList(const ListDrawSpec &s, int phase)
315 {
316         video::IVideoDriver* driver = Environment->getVideoDriver();
317
318         // Get font
319         gui::IGUIFont *font = NULL;
320         gui::IGUISkin* skin = Environment->getSkin();
321         if (skin)
322                 font = skin->getFont();
323         
324         Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
325         if(!inv){
326                 infostream<<"GUIInventoryMenu::drawList(): WARNING: "
327                                 <<"The inventory location "
328                                 <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
329                                 <<std::endl;
330                 return;
331         }
332         InventoryList *ilist = inv->getList(s.listname);
333         if(!ilist){
334                 infostream<<"GUIInventoryMenu::drawList(): WARNING: "
335                                 <<"The inventory list \""<<s.listname<<"\" @ \""
336                                 <<s.inventoryloc.dump()<<"\" doesn't exist"
337                                 <<std::endl;
338                 return;
339         }
340         
341         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
342         
343         for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
344         {
345                 s32 x = (i%s.geom.X) * spacing.X;
346                 s32 y = (i/s.geom.X) * spacing.Y;
347                 v2s32 p(x,y);
348                 core::rect<s32> rect = imgrect + s.pos + p;
349                 ItemStack item;
350                 if(ilist)
351                         item = ilist->getItem(i);
352
353                 bool selected = m_selected_item
354                         && m_invmgr->getInventory(m_selected_item->inventoryloc) == inv
355                         && m_selected_item->listname == s.listname
356                         && m_selected_item->i == i;
357                 bool hovering = rect.isPointInside(m_pointer);
358
359                 if(phase == 0)
360                 {
361                         if(hovering && m_selected_item)
362                         {
363                                 video::SColor bgcolor(255,192,192,192);
364                                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
365                         }
366                         else
367                         {
368                                 video::SColor bgcolor(255,128,128,128);
369                                 driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
370                         }
371                 }
372
373                 if(phase == 1)
374                 {
375                         // Draw item stack
376                         if(selected)
377                         {
378                                 item.takeItem(m_selected_amount);
379                         }
380                         if(!item.empty())
381                         {
382                                 drawItemStack(driver, font, item,
383                                                 rect, &AbsoluteClippingRect, m_gamedef);
384                         }
385
386                         // Draw tooltip
387                         std::string tooltip_text = "";
388                         if(hovering && !m_selected_item)
389                                 tooltip_text = item.getDefinition(m_gamedef->idef()).description;
390                         if(tooltip_text != "")
391                         {
392                                 m_tooltip_element->setVisible(true);
393                                 this->bringToFront(m_tooltip_element);
394                                 m_tooltip_element->setText(narrow_to_wide(tooltip_text).c_str());
395                                 s32 tooltip_x = m_pointer.X + 15;
396                                 s32 tooltip_y = m_pointer.Y + 15;
397                                 s32 tooltip_width = m_tooltip_element->getTextWidth() + 15;
398                                 s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
399                                 m_tooltip_element->setRelativePosition(core::rect<s32>(
400                                                 core::position2d<s32>(tooltip_x, tooltip_y),
401                                                 core::dimension2d<s32>(tooltip_width, tooltip_height)));
402                         }
403                 }
404         }
405 }
406
407 void GUIInventoryMenu::drawSelectedItem()
408 {
409         if(!m_selected_item)
410                 return;
411
412         video::IVideoDriver* driver = Environment->getVideoDriver();
413
414         // Get font
415         gui::IGUIFont *font = NULL;
416         gui::IGUISkin* skin = Environment->getSkin();
417         if (skin)
418                 font = skin->getFont();
419         
420         Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
421         assert(inv);
422         InventoryList *list = inv->getList(m_selected_item->listname);
423         assert(list);
424         ItemStack stack = list->getItem(m_selected_item->i);
425         stack.count = m_selected_amount;
426
427         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
428         core::rect<s32> rect = imgrect + (m_pointer - imgrect.getCenter());
429         drawItemStack(driver, font, stack, rect, NULL, m_gamedef);
430 }
431
432 void GUIInventoryMenu::drawMenu()
433 {
434         if(m_form_src){
435                 std::string newform = m_form_src->getForm();
436                 if(newform != m_formspec_string){
437                         m_formspec_string = newform;
438                         regenerateGui(m_screensize_old);
439                 }
440         }
441
442         updateSelectedItem();
443
444         gui::IGUISkin* skin = Environment->getSkin();
445         if (!skin)
446                 return;
447         video::IVideoDriver* driver = Environment->getVideoDriver();
448         
449         video::SColor bgcolor(140,0,0,0);
450         driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
451
452         m_tooltip_element->setVisible(false);
453
454         /*
455                 Draw items
456                 Phase 0: Item slot rectangles
457                 Phase 1: Item images; prepare tooltip
458         */
459         
460         for(int phase=0; phase<=1; phase++)
461         for(u32 i=0; i<m_inventorylists.size(); i++)
462         {
463                 drawList(m_inventorylists[i], phase);
464         }
465
466         for(u32 i=0; i<m_images.size(); i++)
467         {
468                 const ImageDrawSpec &spec = m_images[i];
469                 video::ITexture *texture =
470                                 m_gamedef->tsrc()->getTextureRaw(spec.name);
471                 // Image size on screen
472                 core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
473                 // Image rectangle on screen
474                 core::rect<s32> rect = imgrect + spec.pos;
475                 const video::SColor color(255,255,255,255);
476                 const video::SColor colors[] = {color,color,color,color};
477                 driver->draw2DImage(texture, rect,
478                         core::rect<s32>(core::position2d<s32>(0,0),
479                                         core::dimension2di(texture->getOriginalSize())),
480                         NULL/*&AbsoluteClippingRect*/, colors, true);
481         }
482
483         /*
484                 Draw dragged item stack
485         */
486         drawSelectedItem();
487
488         /*
489                 Call base class
490         */
491         gui::IGUIElement::draw();
492 }
493
494 void GUIInventoryMenu::updateSelectedItem()
495 {
496         // If the selected stack has become empty for some reason, deselect it.
497         // If the selected stack has become smaller, adjust m_selected_amount.
498         if(m_selected_item)
499         {
500                 bool selection_valid = false;
501                 if(m_selected_item->isValid())
502                 {
503                         Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
504                         if(inv)
505                         {
506                                 InventoryList *list = inv->getList(m_selected_item->listname);
507                                 if(list && (u32) m_selected_item->i < list->getSize())
508                                 {
509                                         ItemStack stack = list->getItem(m_selected_item->i);
510                                         if(m_selected_amount > stack.count)
511                                                 m_selected_amount = stack.count;
512                                         if(!stack.empty())
513                                                 selection_valid = true;
514                                 }
515                         }
516                 }
517                 if(!selection_valid)
518                 {
519                         delete m_selected_item;
520                         m_selected_item = NULL;
521                         m_selected_amount = 0;
522                         m_selected_dragging = false;
523                 }
524         }
525
526         // If craftresult is nonempty and nothing else is selected, select it now.
527         if(!m_selected_item)
528         {
529                 for(u32 i=0; i<m_inventorylists.size(); i++)
530                 {
531                         const ListDrawSpec &s = m_inventorylists[i];
532                         if(s.listname == "craftpreview")
533                         {
534                                 Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
535                                 InventoryList *list = inv->getList("craftresult");
536                                 if(list && list->getSize() >= 1 && !list->getItem(0).empty())
537                                 {
538                                         m_selected_item = new ItemSpec;
539                                         m_selected_item->inventoryloc = s.inventoryloc;
540                                         m_selected_item->listname = "craftresult";
541                                         m_selected_item->i = 0;
542                                         m_selected_amount = 0;
543                                         m_selected_dragging = false;
544                                         break;
545                                 }
546                         }
547                 }
548         }
549
550         // If craftresult is selected, keep the whole stack selected
551         if(m_selected_item && m_selected_item->listname == "craftresult")
552         {
553                 Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
554                 assert(inv);
555                 InventoryList *list = inv->getList(m_selected_item->listname);
556                 assert(list);
557                 m_selected_amount = list->getItem(m_selected_item->i).count;
558         }
559 }
560
561 bool GUIInventoryMenu::OnEvent(const SEvent& event)
562 {
563         if(event.EventType==EET_KEY_INPUT_EVENT)
564         {
565                 KeyPress kp(event.KeyInput);
566                 if (event.KeyInput.PressedDown && (kp == EscapeKey ||
567                         kp == getKeySetting("keymap_inventory")))
568                 {
569                         quitMenu();
570                         return true;
571                 }
572         }
573         if(event.EventType==EET_MOUSE_INPUT_EVENT
574                         && event.MouseInput.Event == EMIE_MOUSE_MOVED)
575         {
576                 // Mouse moved
577                 m_pointer = v2s32(event.MouseInput.X, event.MouseInput.Y);
578         }
579         if(event.EventType==EET_MOUSE_INPUT_EVENT
580                         && event.MouseInput.Event != EMIE_MOUSE_MOVED)
581         {
582                 // Mouse event other than movement
583
584                 v2s32 p(event.MouseInput.X, event.MouseInput.Y);
585                 m_pointer = p;
586
587                 // Get selected item and hovered/clicked item (s)
588
589                 updateSelectedItem();
590                 ItemSpec s = getItemAtPos(p);
591
592                 Inventory *inv_selected = NULL;
593                 Inventory *inv_s = NULL;
594
595                 if(m_selected_item)
596                 {
597                         inv_selected = m_invmgr->getInventory(m_selected_item->inventoryloc);
598                         assert(inv_selected);
599                         assert(inv_selected->getList(m_selected_item->listname) != NULL);
600                 }
601
602                 u32 s_count = 0;
603
604                 if(s.isValid())
605                 do{ // breakable
606                         inv_s = m_invmgr->getInventory(s.inventoryloc);
607
608                         if(!inv_s){
609                                 errorstream<<"InventoryMenu: The selected inventory location "
610                                                 <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
611                                                 <<std::endl;
612                                 s.i = -1;  // make it invalid again
613                                 break;
614                         }
615
616                         InventoryList *list = inv_s->getList(s.listname);
617                         if(list == NULL){
618                                 errorstream<<"InventoryMenu: The selected inventory list \""
619                                                 <<s.listname<<"\" does not exist"<<std::endl;
620                                 s.i = -1;  // make it invalid again
621                                 break;
622                         }
623
624                         if((u32)s.i >= list->getSize()){
625                                 errorstream<<"InventoryMenu: The selected inventory list \""
626                                                 <<s.listname<<"\" is too small (i="<<s.i<<", size="
627                                                 <<list->getSize()<<")"<<std::endl;
628                                 s.i = -1;  // make it invalid again
629                                 break;
630                         }
631
632                         s_count = list->getItem(s.i).count;
633                 }while(0);
634
635                 bool identical = (m_selected_item != NULL) && s.isValid() &&
636                         (inv_selected == inv_s) &&
637                         (m_selected_item->listname == s.listname) &&
638                         (m_selected_item->i == s.i);
639
640                 // buttons: 0 = left, 1 = right, 2 = middle
641                 // up/down: 0 = down (press), 1 = up (release), 2 = unknown event
642                 int button = 0;
643                 int updown = 2;
644                 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
645                         { button = 0; updown = 0; }
646                 else if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
647                         { button = 1; updown = 0; }
648                 else if(event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN)
649                         { button = 2; updown = 0; }
650                 else if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
651                         { button = 0; updown = 1; }
652                 else if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
653                         { button = 1; updown = 1; }
654                 else if(event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP)
655                         { button = 2; updown = 1; }
656
657                 // Set this number to a positive value to generate a move action
658                 // from m_selected_item to s.
659                 u32 move_amount = 0;
660
661                 // Set this number to a positive value to generate a drop action
662                 // from m_selected_item.
663                 u32 drop_amount = 0;
664
665                 // Set this number to a positive value to generate a craft action at s.
666                 u32 craft_amount = 0;
667
668                 if(updown == 0)
669                 {
670                         // Some mouse button has been pressed
671
672                         //infostream<<"Mouse button "<<button<<" pressed at p=("
673                         //      <<p.X<<","<<p.Y<<")"<<std::endl;
674
675                         m_selected_dragging = false;
676
677                         if(s.isValid() && s.listname == "craftpreview")
678                         {
679                                 // Craft preview has been clicked: craft
680                                 craft_amount = (button == 2 ? 10 : 1);
681                         }
682                         else if(m_selected_item == NULL)
683                         {
684                                 if(s_count != 0)
685                                 {
686                                         // Non-empty stack has been clicked: select it
687                                         m_selected_item = new ItemSpec(s);
688
689                                         if(button == 1)  // right
690                                                 m_selected_amount = (s_count + 1) / 2;
691                                         else if(button == 2)  // middle
692                                                 m_selected_amount = MYMIN(s_count, 10);
693                                         else  // left
694                                                 m_selected_amount = s_count;
695
696                                         m_selected_dragging = true;
697                                 }
698                         }
699                         else  // m_selected_item != NULL
700                         {
701                                 assert(m_selected_amount >= 1);
702
703                                 if(s.isValid())
704                                 {
705                                         // Clicked a slot: move
706                                         if(button == 1)  // right
707                                                 move_amount = 1;
708                                         else if(button == 2)  // middle
709                                                 move_amount = MYMIN(m_selected_amount, 10);
710                                         else  // left
711                                                 move_amount = m_selected_amount;
712
713                                         if(identical)
714                                         {
715                                                 if(move_amount >= m_selected_amount)
716                                                         m_selected_amount = 0;
717                                                 else
718                                                         m_selected_amount -= move_amount;
719                                                 move_amount = 0;
720                                         }
721                                 }
722                                 else if(getAbsoluteClippingRect().isPointInside(m_pointer))
723                                 {
724                                         // Clicked somewhere else: deselect
725                                         m_selected_amount = 0;
726                                 }
727                                 else
728                                 {
729                                         // Clicked outside of the window: drop
730                                         if(button == 1)  // right
731                                                 drop_amount = 1;
732                                         else if(button == 2)  // middle
733                                                 drop_amount = MYMIN(m_selected_amount, 10);
734                                         else  // left
735                                                 drop_amount = m_selected_amount;
736                                 }
737                         }
738                 }
739                 else if(updown == 1)
740                 {
741                         // Some mouse button has been released
742
743                         //infostream<<"Mouse button "<<button<<" released at p=("
744                         //      <<p.X<<","<<p.Y<<")"<<std::endl;
745
746                         if(m_selected_item != NULL && m_selected_dragging && s.isValid())
747                         {
748                                 if(!identical)
749                                 {
750                                         // Dragged to different slot: move all selected
751                                         move_amount = m_selected_amount;
752                                 }
753                         }
754                         else if(m_selected_item != NULL && m_selected_dragging &&
755                                 !(getAbsoluteClippingRect().isPointInside(m_pointer)))
756                         {
757                                 // Dragged outside of window: drop all selected
758                                 drop_amount = m_selected_amount;
759                         }
760
761                         m_selected_dragging = false;
762                 }
763
764                 // Possibly send inventory action to server
765                 if(move_amount > 0)
766                 {
767                         // Send IACTION_MOVE
768
769                         assert(m_selected_item && m_selected_item->isValid());
770                         assert(s.isValid());
771
772                         assert(inv_selected && inv_s);
773                         InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
774                         InventoryList *list_to = inv_s->getList(s.listname);
775                         assert(list_from && list_to);
776                         ItemStack stack_from = list_from->getItem(m_selected_item->i);
777                         ItemStack stack_to = list_to->getItem(s.i);
778
779                         // Check how many items can be moved
780                         move_amount = stack_from.count = MYMIN(move_amount, stack_from.count);
781                         ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef());
782                         if(leftover.count == stack_from.count)
783                         {
784                                 // Swap the stacks
785                                 m_selected_amount -= stack_to.count;
786                         }
787                         else if(leftover.empty())
788                         {
789                                 // Item fits
790                                 m_selected_amount -= move_amount;
791                         }
792                         else
793                         {
794                                 // Item only fits partially
795                                 move_amount -= leftover.count;
796                                 m_selected_amount -= move_amount;
797                         }
798
799                         infostream<<"Handing IACTION_MOVE to manager"<<std::endl;
800                         IMoveAction *a = new IMoveAction();
801                         a->count = move_amount;
802                         a->from_inv = m_selected_item->inventoryloc;
803                         a->from_list = m_selected_item->listname;
804                         a->from_i = m_selected_item->i;
805                         a->to_inv = s.inventoryloc;
806                         a->to_list = s.listname;
807                         a->to_i = s.i;
808                         m_invmgr->inventoryAction(a);
809                 }
810                 else if(drop_amount > 0)
811                 {
812                         // Send IACTION_DROP
813
814                         assert(m_selected_item && m_selected_item->isValid());
815                         assert(inv_selected);
816                         InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
817                         assert(list_from);
818                         ItemStack stack_from = list_from->getItem(m_selected_item->i);
819
820                         // Check how many items can be dropped
821                         drop_amount = stack_from.count = MYMIN(drop_amount, stack_from.count);
822                         assert(drop_amount > 0 && drop_amount <= m_selected_amount);
823                         m_selected_amount -= drop_amount;
824
825                         infostream<<"Handing IACTION_DROP to manager"<<std::endl;
826                         IDropAction *a = new IDropAction();
827                         a->count = drop_amount;
828                         a->from_inv = m_selected_item->inventoryloc;
829                         a->from_list = m_selected_item->listname;
830                         a->from_i = m_selected_item->i;
831                         m_invmgr->inventoryAction(a);
832                 }
833                 else if(craft_amount > 0)
834                 {
835                         // Send IACTION_CRAFT
836
837                         assert(s.isValid());
838                         assert(inv_s);
839
840                         infostream<<"Handing IACTION_CRAFT to manager"<<std::endl;
841                         ICraftAction *a = new ICraftAction();
842                         a->count = craft_amount;
843                         a->craft_inv = s.inventoryloc;
844                         m_invmgr->inventoryAction(a);
845                 }
846
847                 // If m_selected_amount has been decreased to zero, deselect
848                 if(m_selected_amount == 0)
849                 {
850                         delete m_selected_item;
851                         m_selected_item = NULL;
852                         m_selected_amount = 0;
853                         m_selected_dragging = false;
854                 }
855         }
856         if(event.EventType==EET_GUI_EVENT)
857         {
858                 if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
859                                 && isVisible())
860                 {
861                         if(!canTakeFocus(event.GUIEvent.Element))
862                         {
863                                 infostream<<"GUIInventoryMenu: Not allowing focus change."
864                                                 <<std::endl;
865                                 // Returning true disables focus change
866                                 return true;
867                         }
868                 }
869                 if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
870                 {
871                         /*switch(event.GUIEvent.Caller->getID())
872                         {
873                         case 256: // continue
874                                 setVisible(false);
875                                 break;
876                         case 257: // exit
877                                 dev->closeDevice();
878                                 break;
879                         }*/
880                 }
881         }
882
883         return Parent ? Parent->OnEvent(event) : false;
884 }
885