summaryrefslogblamecommitdiff
path: root/src/gui/widgets/itemcontainer.cpp
blob: adf1188e4f254b084d284e344775fcdbabdd2f1d (plain) (tree)
1
2
3
4
5
6
7
  
                       
                                                            
                                                    
  
                                             




















                                                                         
 
                           
                    











                                 
 
                                        
 
                  

                                 
                      
           
                                                    
                                
         




                    
                                
           
                                                             







                                                         
                             
           
                                                             






                                                                 
                                 
           
                                                             













                                                               
                                 
           
                                                             









                                                             
                           

                   
                               
           
                                                             









                                                             
                           

                 
                                                        
                  
              

                          

                          
                                                                   




                                  
                      
                 
            
                 
                              
               
                                                            
 
                       








                               
                          
                      
                         
                         


















                                                           
                                    
               
                                                               

                          
                                       
     
                                             
                                              
                                      
                              

                                           
                                                         


                                            
                                                  
                      
                                                             















                                                                            



















                                                         






                                                          
                                           
                
                                             



                                                                        








                                          
                                           
 
                                                        











                                                   
                       


                                                 
                                                                   








                                            
                               
                                      
 
                                                             
 
 
                                                              
 






                                                        
                                    




                                                                            
                                                      



                                                               
                                                    






                                               
                                                              
                                                                  
                                      
                                                             
                             
                                                    
                                                    
                                                    




















                                                           

                     









                                            
                          


                   
                                                               













                                                                
                                                  











                                                                           
                                                                


                                  
                                                                   













                                                                 




                                  

                    
                         
                                                    
 
                                         

              

                             
                                                               
     
                                                    


                                                                  







                                                             






                      
                                                                               
                  
                                                                            
                  
               
                                                             
                  
                                                             
                  
                                                                              
                  
     
                                                 
                                                                             
                                                                 
     
                             
                  
                                              




                              
                                  
         
     
 
                                        
                               
     
                              
     
 
                                                                   
                                
 
                                                               
 

                                        
                                          
     
                                                                          

                                                                    



































                                                                           
                                                            










































                                                                      
 
                                            

                   
 
                                                   
                         
                   
/*
 *  The ManaPlus Client
 *  Copyright (C) 2004-2009  The Mana World Development Team
 *  Copyright (C) 2009-2010  The Mana Developers
 *  Copyright (C) 2011-2012  The ManaPlus Developers
 *
 *  This file is part of The ManaPlus Client.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "gui/widgets/itemcontainer.h"

#include "graphics.h"
#include "inventory.h"
#include "item.h"
#include "itemshortcut.h"
#include "dropshortcut.h"

#include "gui/chatwindow.h"
#include "gui/gui.h"
#include "gui/itempopup.h"
#include "gui/outfitwindow.h"
#include "gui/palette.h"
#include "gui/shopwindow.h"
#include "gui/shortcutwindow.h"
#include "gui/sdlinput.h"
#include "gui/theme.h"
#include "gui/viewport.h"

#include "net/net.h"
#include "net/inventoryhandler.h"

#include "resources/image.h"

#include <guichan/mouseinput.hpp>
#include <guichan/selectionlistener.hpp>

#include "debug.h"

static const int BOX_WIDTH = 35;
static const int BOX_HEIGHT = 43;

class ItemIdPair final
{
    public:
        ItemIdPair(const int id, Item *const item) :
            mId(id), mItem(item)
        {
        }

        int mId;
        Item* mItem;
};

class SortItemAlphaFunctor final
{
    public:
        bool operator() (const ItemIdPair *const pair1,
                         const ItemIdPair *const pair2) const
        {
            if (!pair1 || !pair2)
                return false;

            return (pair1->mItem->getInfo().getName()
                    < pair2->mItem->getInfo().getName());
        }
} itemAlphaSorter;

class SortItemIdFunctor final
{
    public:
        bool operator() (const ItemIdPair *const pair1,
                         const ItemIdPair *const pair2) const
        {
            if (!pair1 || !pair2)
                return false;

            return pair1->mItem->getId() < pair2->mItem->getId();
        }
} itemIdSorter;

class SortItemWeightFunctor final
{
    public:
        bool operator() (const ItemIdPair *const pair1,
                         const ItemIdPair *const pair2) const
        {
            if (!pair1 || !pair2)
                return false;

            const int w1 = pair1->mItem->getInfo().getWeight();
            const int w2 = pair2->mItem->getInfo().getWeight();
            if (w1 == w2)
            {
                return (pair1->mItem->getInfo().getName()
                        < pair2->mItem->getInfo().getName());
            }
            return w1 < w2;
        }
} itemWeightSorter;

class SortItemAmountFunctor final
{
    public:
        bool operator() (const ItemIdPair *const pair1,
                         const ItemIdPair *const pair2) const
        {
            if (!pair1 || !pair2)
                return false;

            const int c1 = pair1->mItem->getQuantity();
            const int c2 = pair2->mItem->getQuantity();
            if (c1 == c2)
            {
                return (pair1->mItem->getInfo().getName()
                        < pair2->mItem->getInfo().getName());
            }
            return c1 < c2;
        }
} itemAmountSorter;

class SortItemTypeFunctor final
{
    public:
        bool operator() (const ItemIdPair *const pair1,
                         const ItemIdPair *const pair2) const
        {
            if (!pair1 || !pair2)
                return false;

            const int t1 = pair1->mItem->getInfo().getType();
            const int t2 = pair2->mItem->getInfo().getType();
            if (t1 == t2)
            {
                return (pair1->mItem->getInfo().getName()
                        < pair2->mItem->getInfo().getName());
            }
            return t1 < t2;
        }
} itemTypeSorter;

ItemContainer::ItemContainer(Inventory *const inventory,
                             const bool forceQuantity) :
    gcn::Widget(),
    Widget2(),
    gcn::KeyListener(),
    gcn::MouseListener(),
    gcn::WidgetListener(),
    mInventory(inventory),
    mGridColumns(1),
    mGridRows(1),
    mSelImg(Theme::getImageFromThemeXml("item_selection.xml", "")),
    mSelectedIndex(-1),
    mHighlightedIndex(-1),
    mLastUsedSlot(-1),
    mSelectionStatus(SEL_NONE),
    mForceQuantity(forceQuantity),
    mSwapItems(false),
    mDescItems(false),
    mDragPosX(0),
    mDragPosY(0),
    mTag(0),
    mSortType(0),
    mItemPopup(new ItemPopup),
    mShowMatrix(nullptr),
    mClicks(1),
    mEquipedColor(getThemeColor(Theme::ITEM_EQUIPPED)),
    mUnEquipedColor(getThemeColor(Theme::ITEM_NOT_EQUIPPED))
{
    setFocusable(true);
    addKeyListener(this);
    addMouseListener(this);
    addWidgetListener(this);
}

ItemContainer::~ItemContainer()
{
    if (mSelImg)
    {
        mSelImg->decRef();
        mSelImg = nullptr;
    }
    delete mItemPopup;
    mItemPopup = nullptr;
    delete []mShowMatrix;
}

void ItemContainer::logic()
{
    gcn::Widget::logic();

    if (!mInventory)
        return;

    const int lastUsedSlot = mInventory->getLastUsedSlot();

    if (lastUsedSlot != mLastUsedSlot)
    {
        mLastUsedSlot = lastUsedSlot;
        adjustHeight();
    }
}

void ItemContainer::draw(gcn::Graphics *graphics)
{
    if (!mInventory || !mShowMatrix)
        return;

    Graphics *const g = static_cast<Graphics *const>(graphics);

    g->setFont(getFont());

    for (int j = 0; j < mGridRows; j++)
    {
        const int intY0 = j * BOX_HEIGHT;
        int itemIndex = j * mGridColumns - 1;
        for (int i = 0; i < mGridColumns; i++)
        {
            int itemX = i * BOX_WIDTH;
            int itemY = intY0;
            itemIndex ++;
            if (mShowMatrix[itemIndex] < 0)
                continue;

            const Item *const item = mInventory->getItem(
                mShowMatrix[itemIndex]);

            if (!item || item->getId() == 0)
                continue;

            Image *const image = item->getImage();
            if (image)
            {
                if (mShowMatrix[itemIndex] == mSelectedIndex)
                {
                    if (mSelectionStatus == SEL_DRAGGING)
                    {
                        // Reposition the coords to that of the cursor.
                        itemX = mDragPosX - (BOX_WIDTH / 2);
                        itemY = mDragPosY - (BOX_HEIGHT / 2);
                    }
                    else
                    {
                        // Draw selection border image.
                        if (mSelImg)
                            g->drawImage(mSelImg, itemX, itemY);
                    }
                }
                image->setAlpha(1.0f); // ensure the image if fully drawn...
                g->drawImage(image, itemX, itemY);
            }
        }
    }

    for (int j = 0; j < mGridRows; j++)
    {
        const int intY0 = j * BOX_HEIGHT;
        int itemIndex = j * mGridColumns - 1;
        for (int i = 0; i < mGridColumns; i++)
        {
            int itemX = i * BOX_WIDTH;
            int itemY = intY0;
            itemIndex ++;
            if (mShowMatrix[itemIndex] < 0)
                continue;

            const Item *const item = mInventory->getItem(
                mShowMatrix[itemIndex]);

            if (!item || item->getId() == 0)
                continue;

            // Draw item caption
            std::string caption;
            if (item->getQuantity() > 1 || mForceQuantity)
                caption = toString(item->getQuantity());
            else if (item->isEquipped())
                caption = "Eq.";

            if (item->isEquipped())
                g->setColor(mEquipedColor);
            else
                g->setColor(mUnEquipedColor);

            g->drawText(caption, itemX + BOX_WIDTH / 2,
                        itemY + BOX_HEIGHT - 14, gcn::Graphics::CENTER);
        }
    }
}

void ItemContainer::selectNone()
{
    setSelectedIndex(-1);
    mSelectionStatus = SEL_NONE;
    if (outfitWindow)
        outfitWindow->setItemSelected(-1);
    if (shopWindow)
        shopWindow->setItemSelected(-1);
//    if (skillDialog)
//        skillDialog->setItemSelected(-1);
}

void ItemContainer::setSelectedIndex(const int newIndex)
{
    if (mSelectedIndex != newIndex)
    {
        mSelectedIndex = newIndex;
        distributeValueChangedEvent();
    }
}

Item *ItemContainer::getSelectedItem() const
{
    if (mInventory)
        return mInventory->getItem(mSelectedIndex);
    else
        return nullptr;
}

void ItemContainer::distributeValueChangedEvent()
{
    for (SelectionListenerIterator i = mSelectionListeners.begin(),
         i_end = mSelectionListeners.end();
         i != i_end; ++i)
    {
        if (*i)
        {
            gcn::SelectionEvent event(this);
            (*i)->valueChanged(event);
        }
    }
}

void ItemContainer::hidePopup()
{
    if (mItemPopup)
        mItemPopup->setVisible(false);
}

void ItemContainer::keyPressed(gcn::KeyEvent &event A_UNUSED)
{
}

void ItemContainer::keyReleased(gcn::KeyEvent &event A_UNUSED)
{
}

void ItemContainer::mousePressed(gcn::MouseEvent &event)
{
    if (!mInventory)
        return;

    const int button = event.getButton();
    mClicks = event.getClickCount();

    if (button == gcn::MouseEvent::LEFT || button == gcn::MouseEvent::RIGHT)
    {
        const int index = getSlotIndex(event.getX(), event.getY());
        if (index == Inventory::NO_SLOT_INDEX)
            return;

        Item *const item = mInventory->getItem(index);

        // put item name into chat window
        if (item && mDescItems && chatWindow)
            chatWindow->addItemText(item->getInfo().getName());

        if (mSelectedIndex == index && mClicks != 2)
        {
            mSelectionStatus = SEL_DESELECTING;
        }
        else if (item && item->getId())
        {
            setSelectedIndex(index);
            mSelectionStatus = SEL_SELECTING;

            const int num = itemShortcutWindow->getTabIndex();
            if (num >= 0 && num < static_cast<int>(SHORTCUT_TABS))
            {
                if (itemShortcut[num])
                    itemShortcut[num]->setItemSelected(item);
            }
            if (dropShortcut)
                dropShortcut->setItemSelected(item);
            if (item->isEquipment() && outfitWindow)
                outfitWindow->setItemSelected(item);
            if (shopWindow)
                shopWindow->setItemSelected(item->getId());
        }
        else
        {
            selectNone();
        }
    }
}

void ItemContainer::mouseDragged(gcn::MouseEvent &event)
{
    if (mSelectionStatus != SEL_NONE)
    {
        mSelectionStatus = SEL_DRAGGING;
        mDragPosX = event.getX();
        mDragPosY = event.getY();
    }
}

void ItemContainer::mouseReleased(gcn::MouseEvent &event)
{
    if (mClicks == 2)
        return;

    switch (mSelectionStatus)
    {
        case SEL_SELECTING:
            mSelectionStatus = SEL_SELECTED;
            return;
        case SEL_DESELECTING:
            selectNone();
            return;
        case SEL_DRAGGING:
            mSelectionStatus = SEL_SELECTED;
            break;
        case SEL_NONE:
        case SEL_SELECTED:
        default:
            return;
    };

    const int index = getSlotIndex(event.getX(), event.getY());
    if (index == Inventory::NO_SLOT_INDEX)
        return;
    if (index == mSelectedIndex || mSelectedIndex == -1)
        return;
    Net::getInventoryHandler()->moveItem(mSelectedIndex, index);
    selectNone();
}


// Show ItemTooltip
void ItemContainer::mouseMoved(gcn::MouseEvent &event)
{
    if (!mInventory)
        return;

    const Item *const item = mInventory->getItem(
        getSlotIndex(event.getX(), event.getY()));

    if (item && viewport)
    {
        mItemPopup->setItem(item);
        mItemPopup->position(viewport->getMouseX(), viewport->getMouseY());
    }
    else
    {
        mItemPopup->setVisible(false);
    }
}

// Hide ItemTooltip
void ItemContainer::mouseExited(gcn::MouseEvent &event A_UNUSED)
{
    mItemPopup->setVisible(false);
}

void ItemContainer::widgetResized(const gcn::Event &event A_UNUSED)
{
    mGridColumns = std::max(1, getWidth() / BOX_WIDTH);
    adjustHeight();
}

void ItemContainer::adjustHeight()
{
    if (!mGridColumns)
        return;

    mGridRows = (mLastUsedSlot + 1) / mGridColumns;
    if (mGridRows == 0 || (mLastUsedSlot + 1) % mGridColumns > 0)
        ++mGridRows;

    setHeight(mGridRows * BOX_HEIGHT);

    updateMatrix();
}

void ItemContainer::updateMatrix()
{
    if (!mInventory)
        return;

    delete []mShowMatrix;
    mShowMatrix = new int[mGridRows * mGridColumns];

    std::vector<ItemIdPair*> sortedItems;
    int i = 0;
    int j = 0;

    std::string temp = mName;
    toLower(temp);

    for (unsigned idx = 0; idx < mInventory->getSize(); idx ++)
    {
        Item *const item = mInventory->getItem(idx);

        if (!item || item->getId() == 0 || !item->isHaveTag(mTag))
            continue;

        if (mName.empty())
        {
            sortedItems.push_back(new ItemIdPair(idx, item));
            continue;
        }
        std::string name = item->getInfo().getName();
        toLower(name);
        if (name.find(temp) != std::string::npos)
            sortedItems.push_back(new ItemIdPair(idx, item));
    }

    switch (mSortType)
    {
        case 0:
        default:
            break;
        case 1:
            std::sort(sortedItems.begin(), sortedItems.end(), itemAlphaSorter);
            break;
        case 2:
            std::sort(sortedItems.begin(), sortedItems.end(), itemIdSorter);
            break;
        case 3:
            std::sort(sortedItems.begin(), sortedItems.end(),
                itemWeightSorter);
            break;
        case 4:
            std::sort(sortedItems.begin(), sortedItems.end(),
                itemAmountSorter);
            break;
        case 5:
            std::sort(sortedItems.begin(), sortedItems.end(), itemTypeSorter);
            break;
    }

    int jMult = j * mGridColumns;
    const int maxSize = mGridRows * mGridColumns;
    for (std::vector<ItemIdPair*>::const_iterator iter = sortedItems.begin(),
         iter_end = sortedItems.end(); iter != iter_end; ++ iter)
    {
        if (jMult >= maxSize)
            break;

        mShowMatrix[jMult + i] = (*iter)->mId;

        i ++;
        if (i >= mGridColumns)
        {
            i = 0;
            j ++;
            jMult += mGridColumns;
        }
    }

    for (int idx = j * mGridColumns + i;
         idx < maxSize; idx ++)
    {
        mShowMatrix[idx] = -1;
    }

    for (size_t idx = 0, sz = sortedItems.size(); idx < sz; idx ++)
        delete sortedItems[idx];
}

int ItemContainer::getSlotIndex(const int x, const int y) const
{
    if (!mShowMatrix)
        return Inventory::NO_SLOT_INDEX;

    if (x < getWidth() && y < getHeight())
    {
        const int idx = (y / BOX_HEIGHT) * mGridColumns + (x / BOX_WIDTH);
        if (idx < mGridRows * mGridColumns && mShowMatrix[idx] >= 0)
            return mShowMatrix[idx];
    }

    return Inventory::NO_SLOT_INDEX;
}

void ItemContainer::keyAction()
{
    // If there is no highlight then return.
    if (mHighlightedIndex == -1)
        return;

    // If the highlight is on the selected item, then deselect it.
    if (mHighlightedIndex == mSelectedIndex)
    {
         selectNone();
    }
    // Check and swap items if necessary.
    else if (mSwapItems && mSelectedIndex != -1 && mHighlightedIndex != -1)
    {
        Net::getInventoryHandler()->moveItem(
            mSelectedIndex, mHighlightedIndex);
        setSelectedIndex(mHighlightedIndex);
    }
    // If the highlight is on an item then select it.
    else if (mHighlightedIndex != -1)
    {
        setSelectedIndex(mHighlightedIndex);
        mSelectionStatus = SEL_SELECTED;
    }
    // If the highlight is on a blank space then move it.
    else if (mSelectedIndex != -1)
    {
        Net::getInventoryHandler()->moveItem(
            mSelectedIndex, mHighlightedIndex);
        selectNone();
    }
}

void ItemContainer::moveHighlight(const Direction direction)
{
    if (mHighlightedIndex == -1)
    {
        if (mSelectedIndex != -1)
            mHighlightedIndex = mSelectedIndex;
        else
            mHighlightedIndex = 0;
        return;
    }

    switch (direction)
    {
        case Left:
            if (mHighlightedIndex % mGridColumns == 0)
                mHighlightedIndex += mGridColumns;
            mHighlightedIndex--;
            break;
        case Right:
            if ((mHighlightedIndex % mGridColumns) ==
                (mGridColumns - 1))
            {
                mHighlightedIndex -= mGridColumns;
            }
            mHighlightedIndex++;
            break;
        case Up:
            if (mHighlightedIndex / mGridColumns == 0)
                mHighlightedIndex += (mGridColumns * mGridRows);
            mHighlightedIndex -= mGridColumns;
            break;
        case Down:
            if ((mHighlightedIndex / mGridColumns) ==
                (mGridRows - 1))
            {
                mHighlightedIndex -= (mGridColumns * mGridRows);
            }
            mHighlightedIndex += mGridColumns;
            break;
        default:
            logger->log("warning moveHighlight unknown direction:"
                        + toString(static_cast<unsigned>(direction)));
            break;
    }
}

void ItemContainer::setFilter(const int tag)
{
    mTag = tag;
    updateMatrix();
}

void ItemContainer::setSortType(const int sortType)
{
    mSortType = sortType;
    updateMatrix();
}