doc updates; CMake works reasonably well now.
[oweals/minetest.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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
14
15 You should have received a copy of the GNU 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
24 void drawInventoryItem(gui::IGUIEnvironment* env,
25                 InventoryItem *item, core::rect<s32> rect,
26                 const core::rect<s32> *clip)
27 {
28         gui::IGUISkin* skin = env->getSkin();
29         if (!skin)
30                 return;
31         video::IVideoDriver* driver = env->getVideoDriver();
32         
33         video::ITexture *texture = NULL;
34         
35         if(item != NULL)
36         {
37                 texture = item->getImage();
38         }
39
40         if(texture != NULL)
41         {
42                 const video::SColor color(255,255,255,255);
43                 const video::SColor colors[] = {color,color,color,color};
44                 driver->draw2DImage(texture, rect,
45                         core::rect<s32>(core::position2d<s32>(0,0),
46                         core::dimension2di(texture->getOriginalSize())),
47                         clip, colors, false);
48         }
49         else
50         {
51                 video::SColor bgcolor(128,128,128,128);
52                 driver->draw2DRectangle(bgcolor, rect, clip);
53         }
54
55         if(item != NULL)
56         {
57                 gui::IGUIFont *font = skin->getFont();
58                 std::string text = item->getText();
59                 if(font && text != "")
60                 {
61                         core::rect<s32> rect2(rect.UpperLeftCorner,
62                                         (core::dimension2d<u32>(rect.getWidth(), 15)));
63
64                         video::SColor bgcolor(128,0,0,0);
65                         driver->draw2DRectangle(bgcolor, rect2, clip);
66
67                         font->draw(text.c_str(), rect2,
68                                         video::SColor(255,255,255,255), false, false,
69                                         clip);
70                 }
71         }
72 }
73
74 /*
75         GUIInventoryMenu
76 */
77
78 GUIInventoryMenu::GUIInventoryMenu(gui::IGUIEnvironment* env,
79                 gui::IGUIElement* parent, s32 id,
80                 Inventory *inventory,
81                 Queue<InventoryAction*> *actions,
82                 int *active_menu_count):
83         GUIModalMenu(env, parent, id, active_menu_count)
84 {
85         m_inventory = inventory;
86         m_selected_item = NULL;
87         m_actions = actions;
88
89         /*m_selected_item = new ItemSpec;
90         m_selected_item->listname = "main";
91         m_selected_item->i = 3;*/
92 }
93
94 GUIInventoryMenu::~GUIInventoryMenu()
95 {
96         removeChildren();
97
98         if(m_selected_item)
99                 delete m_selected_item;
100 }
101
102 void GUIInventoryMenu::removeChildren()
103 {
104         {
105                 gui::IGUIElement *e = getElementFromId(256);
106                 if(e != NULL)
107                         e->remove();
108         }
109 }
110
111 void GUIInventoryMenu::regenerateGui(v2u32 screensize)
112 {
113         // Remove children
114         removeChildren();
115         
116         padding = v2s32(24,24);
117         spacing = v2s32(60,56);
118         imgsize = v2s32(48,48);
119
120         s32 helptext_h = 15;
121
122         v2s32 size(
123                 padding.X*2+spacing.X*(8-1)+imgsize.X,
124                 padding.Y*2+spacing.Y*(7-1)+imgsize.Y + helptext_h
125         );
126
127         core::rect<s32> rect(
128                         screensize.X/2 - size.X/2,
129                         screensize.Y/2 - size.Y/2,
130                         screensize.X/2 + size.X/2,
131                         screensize.Y/2 + size.Y/2
132         );
133         
134         DesiredRect = rect;
135         recalculateAbsolutePosition(false);
136
137         v2s32 basepos = getBasePos();
138         
139         m_draw_positions.clear();
140         m_draw_positions.push_back(ListDrawSpec("main",
141                         basepos + v2s32(spacing.X*0, spacing.Y*3), v2s32(8, 4)));
142         m_draw_positions.push_back(ListDrawSpec("craft",
143                         basepos + v2s32(spacing.X*3, spacing.Y*0), v2s32(3, 3)));
144         m_draw_positions.push_back(ListDrawSpec("craftresult",
145                         basepos + v2s32(spacing.X*7, spacing.Y*1), v2s32(1, 1)));
146         
147         // Add children
148         {
149                 core::rect<s32> rect(0, 0, size.X-padding.X*2, helptext_h);
150                 rect = rect + v2s32(size.X/2 - rect.getWidth()/2,
151                                 size.Y-rect.getHeight()-15);
152                 const wchar_t *text =
153                 L"Left click: Move all items, Right click: Move single item";
154                 Environment->addStaticText(text, rect, false, true, this, 256);
155         }
156 }
157
158 GUIInventoryMenu::ItemSpec GUIInventoryMenu::getItemAtPos(v2s32 p) const
159 {
160         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
161         
162         for(u32 i=0; i<m_draw_positions.size(); i++)
163         {
164                 const ListDrawSpec &s = m_draw_positions[i];
165
166                 for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
167                 {
168                         s32 x = (i%s.geom.X) * spacing.X;
169                         s32 y = (i/s.geom.X) * spacing.Y;
170                         v2s32 p0(x,y);
171                         core::rect<s32> rect = imgrect + s.pos + p0;
172                         if(rect.isPointInside(p))
173                         {
174                                 return ItemSpec(s.listname, i);
175                         }
176                 }
177         }
178
179         return ItemSpec("", -1);
180 }
181
182 //void GUIInventoryMenu::drawList(const std::string &name, v2s32 pos, v2s32 geom)
183 void GUIInventoryMenu::drawList(const ListDrawSpec &s)
184 {
185         video::IVideoDriver* driver = Environment->getVideoDriver();
186
187         InventoryList *ilist = m_inventory->getList(s.listname);
188         
189         core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
190
191         for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
192         {
193                 s32 x = (i%s.geom.X) * spacing.X;
194                 s32 y = (i/s.geom.X) * spacing.Y;
195                 v2s32 p(x,y);
196                 core::rect<s32> rect = imgrect + s.pos + p;
197                 InventoryItem *item = NULL;
198                 if(ilist)
199                         item = ilist->getItem(i);
200
201                 if(m_selected_item != NULL && m_selected_item->listname == s.listname
202                                 && m_selected_item->i == i)
203                 {
204                         driver->draw2DRectangle(video::SColor(255,255,0,0),
205                                         core::rect<s32>(rect.UpperLeftCorner - v2s32(2,2),
206                                                         rect.LowerRightCorner + v2s32(2,2)),
207                                                         &AbsoluteClippingRect);
208                 }
209                 drawInventoryItem(Environment, item,
210                                 rect, &AbsoluteClippingRect);
211         }
212 }
213
214 void GUIInventoryMenu::drawMenu()
215 {
216         gui::IGUISkin* skin = Environment->getSkin();
217         if (!skin)
218                 return;
219         video::IVideoDriver* driver = Environment->getVideoDriver();
220         
221         video::SColor bgcolor(140,0,0,0);
222         driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
223
224         /*
225                 Draw items
226         */
227         
228         for(u32 i=0; i<m_draw_positions.size(); i++)
229         {
230                 ListDrawSpec &s = m_draw_positions[i];
231                 drawList(s);
232         }
233
234         /*
235                 Call base class
236         */
237         gui::IGUIElement::draw();
238 }
239
240 bool GUIInventoryMenu::OnEvent(const SEvent& event)
241 {
242         if(event.EventType==EET_KEY_INPUT_EVENT)
243         {
244                 if(event.KeyInput.Key==KEY_ESCAPE && event.KeyInput.PressedDown)
245                 {
246                         quitMenu();
247                         return true;
248                 }
249                 if(event.KeyInput.Key==KEY_KEY_I && event.KeyInput.PressedDown)
250                 {
251                         quitMenu();
252                         return true;
253                 }
254         }
255         if(event.EventType==EET_MOUSE_INPUT_EVENT)
256         {
257                 if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN
258                                 || event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
259                 {
260                         bool right = (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN);
261                         v2s32 p(event.MouseInput.X, event.MouseInput.Y);
262                         //dstream<<"Mouse down at p=("<<p.X<<","<<p.Y<<")"<<std::endl;
263                         ItemSpec s = getItemAtPos(p);
264                         if(s.isValid())
265                         {
266                                 //dstream<<"Mouse down on "<<s.listname<<" "<<s.i<<std::endl;
267                                 if(m_selected_item)
268                                 {
269                                         InventoryList *list_from =
270                                                         m_inventory->getList(m_selected_item->listname);
271                                         InventoryList *list_to =
272                                                         m_inventory->getList(s.listname);
273                                         // Indicates whether source slot completely empties
274                                         bool source_empties = false;
275                                         if(list_from && list_to
276                                                         && list_from->getItem(m_selected_item->i) != NULL)
277                                         {
278                                                 dstream<<"Queueing IACTION_MOVE"<<std::endl;
279                                                 IMoveAction *a = new IMoveAction();
280                                                 a->count = right ? 1 : 0;
281                                                 a->from_name = m_selected_item->listname;
282                                                 a->from_i = m_selected_item->i;
283                                                 a->to_name = s.listname;
284                                                 a->to_i = s.i;
285                                                 m_actions->push_back(a);
286                                                 
287                                                 if(list_from->getItem(m_selected_item->i)->getCount()==1)
288                                                         source_empties = true;
289                                         }
290                                         // Remove selection if target was left-clicked or source
291                                         // slot was emptied
292                                         if(right == false || source_empties)
293                                         {
294                                                 delete m_selected_item;
295                                                 m_selected_item = NULL;
296                                         }
297                                 }
298                                 else
299                                 {
300                                         /*
301                                                 Select if non-NULL
302                                         */
303                                         InventoryList *list = m_inventory->getList(s.listname);
304                                         if(list->getItem(s.i) != NULL)
305                                         {
306                                                 m_selected_item = new ItemSpec(s);
307                                         }
308                                 }
309                         }
310                         else
311                         {
312                                 if(m_selected_item)
313                                 {
314                                         delete m_selected_item;
315                                         m_selected_item = NULL;
316                                 }
317                         }
318                 }
319         }
320         if(event.EventType==EET_GUI_EVENT)
321         {
322                 if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST
323                                 && isVisible())
324                 {
325                         if(!canTakeFocus(event.GUIEvent.Element))
326                         {
327                                 dstream<<"GUIInventoryMenu: Not allowing focus change."
328                                                 <<std::endl;
329                                 // Returning true disables focus change
330                                 return true;
331                         }
332                 }
333                 if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
334                 {
335                         /*switch(event.GUIEvent.Caller->getID())
336                         {
337                         case 256: // continue
338                                 setVisible(false);
339                                 break;
340                         case 257: // exit
341                                 dev->closeDevice();
342                                 break;
343                         }*/
344                 }
345         }
346
347         return Parent ? Parent->OnEvent(event) : false;
348 }
349
350
351