diff -abBdpuNPr --exclude='*.svn' irrlicht-svn-ss/trunk/include/IGUITable.h Irrlicht_starsonata/include/IGUITable.h --- irrlicht-svn-ss/trunk/include/IGUITable.h 1970-01-01 01:00:00.000000000 +0100 +++ Irrlicht_starsonata/include/IGUITable.h 2008-06-17 23:06:34.000000000 +0200 @@ -0,0 +1,285 @@ +// Copyright (C) 2003-2007 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#ifndef __I_GUI_TABLE_H_INCLUDED__ +#define __I_GUI_TABLE_H_INCLUDED__ + +#include "IGUIElement.h" +#include "irrTypes.h" +#include "SColor.h" +#include "IGUISkin.h" + +namespace irr +{ +namespace gui +{ + + //! modes for ordering used when a column header is clicked + enum EGUI_COLUMN_ORDERING + { + //! Do not use ordering + EGCO_NONE, + + //! Send a EGET_TABLE_HEADER_CHANGED message when a column header is clicked. + EGCO_CUSTOM, + + //! Sort it ascending by it's ascii value like: a,b,c,... + EGCO_ASCENDING, + + //! Sort it descending by it's ascii value like: z,x,y,... + EGCO_DESCENDING, + + //! Sort it ascending on first click, descending on next, etc + EGCO_FLIP_ASCENDING_DESCENDING, + + //! Not used as mode, only to get maximum value for this enum + EGCO_COUNT + }; + + //! Names for EGUI_COLUMN_ORDERING types + const c8* const GUIColumnOrderingNames[] = + { + "none", + "custom", + "ascend", + "descend", + "ascend_descend", + 0, + }; + + enum EGUI_ORDERING_MODE + { + //! No element ordering + EGOM_NONE, + + //! Elements are ordered from the smallest to the largest. + EGOM_ASCENDING, + + //! Elements are ordered from the largest to the smallest. + EGOM_DESCENDING, + + //! this value is not used, it only specifies the amount of default ordering types + //! available. + EGOM_COUNT + }; + + const c8* const GUIOrderingModeNames[] = + { + "none", + "ascending", + "descending", + 0 + }; + + enum EGUI_TABLE_DRAW_FLAGS + { + EGTDF_ROWS = 1, + EGTDF_COLUMNS = 2, + EGTDF_ACTIVE_ROW = 4, + EGTDF_HIDE_HEADER = 8, + + EGTDF_COUNT, + }; + + enum EGUI_TABLE_COLUMN_RESIZE + { + //! Make sure all texts, including headers and padding, do fit. + //! If there's room to the right, do fill it with the most-right column. + ETCR_FIT_TEXTS, + }; + + class IGUIFont; + + //! Default list box GUI element. + class IGUITable : public IGUIElement + { + public: + //! constructor + IGUITable(IGUIEnvironment* environment, IGUIElement* parent, s32 id, core::rect rectangle) + : IGUIElement(EGUIET_TABLE, environment, parent, id, rectangle) {} + + //! Adds a column + //! If columnIndex is outside the current range, do push new colum at the end + virtual void addColumn(const wchar_t* caption, s32 columnIndex=-1) = 0; + + //! Set the name for the given column. + //! Column must exist, otherwise nothing will happen. + virtual void setColumnName(u32 columnIndex, const wchar_t* name) = 0; + + //! remove a column from the table + virtual void removeColumn(u32 columnIndex) = 0; + + //! Returns the number of columns in the table control + virtual s32 getColumnCount() const = 0; + + //! Makes a column active. This will trigger an ordering process. + /** \param idx: The id of the column to make active. + //! \param doOrder: Do also the ordering which depending on mode for active column + \return Returns true if successful. */ + virtual bool setActiveColumn(s32 idx, bool doOrder=false) = 0; + + //! Returns which header is currently active + virtual s32 getActiveColumn() const = 0; + + //! Returns the ordering used by the currently active column + virtual EGUI_ORDERING_MODE getActiveColumnOrdering() const = 0; + + //! Set the width of a column + virtual void setColumnWidth(u32 columnIndex, u32 width) = 0; + + //! Get the size which the widest text within this column would need + //! \param includePadding_: add the usual padding on both sides + //! \param includeHeader_: Also regard the width of the header text + virtual u32 getColumnTextWidth(u32 columnIndex, bool includePadding=true, bool includeHeader=true) const = 0; + + //! columns can be resized by drag 'n drop + virtual void setResizableColumns(bool resizable) = 0; + + //! can columns be resized by drag 'n drop? + virtual bool hasResizableColumns() const = 0; + + // micha, starsonata + //! some comfortable resizing of column widths + virtual void resizeAllColumns(EGUI_TABLE_COLUMN_RESIZE style=ETCR_FIT_TEXTS) = 0; + + //! This tells the table control which ordering mode should be used when + //! a column header is clicked. + /** \param columnIndex: The index of the column header. + \param state: If true, a EGET_TABLE_HEADER_CHANGED message will be sent and you can order the table data as you whish.*/ + //! \param mode: One of the modes defined in EGUI_COLUMN_ORDERING + virtual void setColumnOrdering(u32 columnIndex, EGUI_COLUMN_ORDERING mode) = 0; + + //! Returns which row is currently selected + virtual s32 getSelected() const = 0; + + //! Select the row or comletely remove selection by using a rowIndex of -1 + //! if you set triggerEvent to true an EGET_TABLE_CHANGED or EGET_TABLE_SELECTED_AGAIN event will get triggered + virtual void setSelected(s32 rowIndex, bool triggerEvent=false)= 0; + + //! Select the row by the y coordinate of a mouse position + //! If the mousepos is above the top-row the selection won't be changed + //! If the mousepos is below the guielement rectangle the selection won't be changed + //! If the mousepos is below the bottom row, but within the guielement rectangle then the selection will be set to -1 (no selection) + virtual void selectRowByPosY(s32 ypos, bool triggerEvent=false) = 0; + + //! scroll to the row if it's not visible currently + virtual void ensureRowVisibility(s32 rowIndex) = 0; + + //! Returns amount of rows in the tabcontrol + virtual u32 getRowCount() const = 0; + + //! adds a row to the table + /* \param rowIndex: zero based index of rows. The row will be inserted at this + position or pushed at the end for indices beyond the current tablesize */ + virtual void addRow(u32 rowIndex) = 0; + + //! sets all cell texts in a row (or at least as many as contained in cellTexts) + /* \param rowIndex: zero based index of rows. It must exit or nothing will happen*/ + //! \param cellTexts: contains the text for all the cells in a row. Each string will be filled in one column. + virtual void setRowTexts(u32 rowIndex, const irr::core::array &cellTexts ) = 0; + + //! set some custom data for the given row + //! \param data: custom data pointer + virtual void setRowData(u32 rowIndex, void * data) = 0; + + //! get the custom row data + virtual void* getRowData(u32 rowIndex) const = 0; + + //! Remove a row from the table + //! returns true on success and false if the index did not exist + virtual bool removeRow(u32 rowIndex) = 0; + + //! clears the table rows, but keeps the columns intact + virtual void clearRows() = 0; + + //! Swap two row positions. This is useful for a custom ordering algo. + virtual void swapRows(u32 rowIndexA, u32 rowIndexB) = 0; + + //! find the index of the row which contains the same custom data value + //! returns -1 when it did not find it + virtual s32 findRowIndexByData(void *data) const = 0; + + //! This tells the table to start ordering all the rows. You need to explicitly + //! tell the table to re order the rows when a new row is added or the cells data is + //! changed. This makes the system more flexible and doesn't make you pay the cost of + //! ordering when adding a lot of rows. + //! \param columnIndex: When set to -1 the active column is used. + virtual void orderRows(s32 columnIndex=-1, EGUI_ORDERING_MODE mode=EGOM_NONE) = 0; + + //! This tells the table to start ordering all the rows with a custom compare function. + //! \param compareFn: The comparison should return negative numbers if idx1 should be above idx2. See example below. + //! \param data: custom data. Often used to pass the this pointer of class objects + //! Example for compareFn which sort from small to larger numbers from top to bottom (ignoring data): + //! static int MyCompare(u32 idx1, u32 idx2, void * data) + //! { return idx1-idx2; } + virtual void orderRows( s32 (*compareFn)(u32 idx1, u32 idx2, void * data), void * data=NULL ) = 0; + + //! This tells the table to start ordering all the rows with a custom compare function which works with the rowdata + //! \param compareFn: The comparison should return negative numbers if rowdata1 should be above rowdata2. + //! \param customdata: Often used to pass the this pointer of class objects + //! Example for compareFn which sort by the pointer value, so that rows with smaller rowdata pointers will be on top. + //! static int MyCompare(void * rowdata1, void * rowdata2, void * customdata) + //! { return rowdata1-rowdata2; } + virtual void orderRows( s32 (*compareFn)(void * rowdata1, void * rowdata2, void * customdata), void * customdata=NULL ) = 0; + + //! override the background color of given row + virtual void setRowOverrideBackground(u32 rowIndex, const video::SColor &color) = 0; + + //! clear override of the background color of given row + virtual void clearRowOverrideBackground(u32 rowIndex) = 0; + + //! Set the text of a cell + virtual void setCellText(u32 rowIndex, u32 columnIndex, const wchar_t* text) = 0; + + //! Set the text of a cell, and set a color of this cell. + virtual void setCellText(u32 rowIndex, u32 columnIndex, const wchar_t* text, video::SColor color) = 0; + + //! Set the data of a cell + virtual void setCellData(u32 rowIndex, u32 columnIndex, void *data) = 0; + + //! Set the color of a cell text + virtual void setCellColor(u32 rowIndex, u32 columnIndex, video::SColor color) = 0; + + //! Set the text of all cells in the given row + virtual void setCellColorsInRow(u32 rowIndex, video::SColor color) = 0; + + //! Get the text of a cell + virtual const wchar_t* getCellText(u32 rowIndex, u32 columnIndex ) const = 0; + + //! Get the data of a cell + virtual void* getCellData(u32 rowIndex, u32 columnIndex ) const = 0; + + //! \param icon Sprite index of the Icon within the current sprite bank + virtual void setCellIcon(u32 rowIndex, u32 columnIndex, s32 icon) = 0; + + //! get the sprite index of the icon in that cell or -1 for no icon + virtual s32 getCellIcon(u32 rowIndex, u32 columnIndex) const = 0; + + //! set the spritebank used for icons + virtual void setSpriteBank(const c8 *bankName) = 0; + + //! clears the table, deletes all items in the table + virtual void clear() = 0; + + // StarSonata, Micha + //! override the background color + virtual void setBackgroundOverrideColor(const video::SColor &color) = 0; + + // StarSonata, Micha + //! clear the background color override + virtual void clearBackgroundOverrideColor() = 0; + + //! Set flags, as defined in EGUI_TABLE_DRAW_FLAGS, which influence the layout + virtual void setDrawFlags(s32 flags) = 0; + + //! Get the flags, as defined in EGUI_TABLE_DRAW_FLAGS, which influence the layout + virtual s32 getDrawFlags() const = 0; + }; + + +} // end namespace gui +} // end namespace irr + +#endif + diff -abBdpuNPr --exclude='*.svn' irrlicht-svn-ss/trunk/source/Irrlicht/CGUITable.cpp Irrlicht_starsonata/source/Irrlicht/CGUITable.cpp --- irrlicht-svn-ss/trunk/source/Irrlicht/CGUITable.cpp 1970-01-01 01:00:00.000000000 +0100 +++ Irrlicht_starsonata/source/Irrlicht/CGUITable.cpp 2008-08-20 14:21:24.000000000 +0200 @@ -0,0 +1,1555 @@ +// Copyright (C) 2002-2007 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +// 07.10.2005 - Multicolor-Listbox added by A. Buschhueter (Acki) +// A_Buschhueter@gmx.de + +#include "CGUITable.h" +#include "IGUISkin.h" +#include "IGUIEnvironment.h" +#include "IVideoDriver.h" +#include "IGUIFont.h" +#include "IGUISpriteBank.h" +#include "CGUIScrollBar.h" +#include "os.h" +#include "profiler.h" +#include "profile_ids.h" + +#define ARROW_PAD 15 + +namespace irr +{ +namespace gui +{ + +//! constructor +CGUITable::CGUITable(IGUIEnvironment* environment, IGUIElement* parent, + s32 id, core::rect rectangle, bool clip, + bool drawBack, bool moveOverSelect) +: IGUITable(environment, parent, id, rectangle), Font(0), + VerticalScrollBar(0), HorizontalScrollBar(0), + Clip(clip), DrawBack(drawBack), MoveOverSelect(moveOverSelect), + Selecting(false), CurrentResizedColumn(-1), ResizeStart(0), ResizableColumns(true), + EnsureRowVisibility(-1), UseOverrideBgColor(false), + ItemHeight(0), TotalItemHeight(0), TotalItemWidth(0), Selected(-1), + CellHeightPadding(2), CellWidthPadding(5), ActiveTab(-1), + CurrentOrdering(EGOM_NONE), DrawFlags(EGTDF_ROWS | EGTDF_COLUMNS | EGTDF_ACTIVE_ROW ), + ContinuationWidth(0), IconBank(0) +{ + #ifdef _DEBUG + setDebugName("CGUITable"); + #endif + + VerticalScrollBar = Environment->addScrollBar(false, core::rect(0, 0, 100, 100), this, -1); + if (VerticalScrollBar) + { + VerticalScrollBar->grab(); + VerticalScrollBar->setNotClipped(false); + VerticalScrollBar->setSubElement(true); + } + + HorizontalScrollBar = Environment->addScrollBar(true, core::rect(0, 0, 100, 100), this, -1); + if ( HorizontalScrollBar ) + { + HorizontalScrollBar->grab(); + HorizontalScrollBar->setNotClipped(false); + HorizontalScrollBar->setSubElement(true); + } + + refreshControls(); +} + + +//! destructor +CGUITable::~CGUITable() +{ + if (VerticalScrollBar) + VerticalScrollBar->drop(); + if ( HorizontalScrollBar ) + HorizontalScrollBar->drop(); + + if (IconBank) + IconBank->drop(); + + if (Font) + Font->drop(); +} + + +void CGUITable::addColumn(const wchar_t* caption, s32 columnIndex) +{ + Column tabHeader; + tabHeader.Name = caption; + tabHeader.Width = Font ? Font->getDimension(caption).Width + (CellWidthPadding * 2) + ARROW_PAD : (CellWidthPadding * 2) + ARROW_PAD; + tabHeader.OrderingMode = EGCO_NONE; + + IGUISkin* skin = Environment->getSkin(); + if (skin) + { + tabHeader.TextColor = skin->getColor(EGDC_BUTTON_TEXT); + } + + if ( columnIndex < 0 || columnIndex >= (s32)Columns.size() ) + { + Columns.push_back(tabHeader); + for ( u32 i=0; i < Rows.size(); ++i ) + { + Cell cell; + Rows[i].Items.push_back(cell); + } + } + else + { + Columns.insert(tabHeader, columnIndex); + for ( u32 i=0; i < Rows.size(); ++i ) + { + Cell cell; + Rows[i].Items.insert(cell, columnIndex); + } + } + + if (ActiveTab == -1) + ActiveTab = 0; + + recalculateWidths(); +} + +//! Set the name for the given column. +//! Column must exist, otherwise nothing will happen. +void CGUITable::setColumnName(u32 columnIndex, const wchar_t* name) +{ + if ( columnIndex >= Columns.size() ) + return; + + Columns[columnIndex].Name = name; +} + +//! remove a column from the table +void CGUITable::removeColumn(u32 columnIndex) +{ + if ( columnIndex < Columns.size() ) + { + Columns.erase(columnIndex); + for ( u32 i=0; i < Rows.size(); ++i ) + { + Rows[i].Items.erase(columnIndex); + } + } + if ( (s32)columnIndex <= ActiveTab ) + ActiveTab = Columns.size() ? 0 : -1; + + recalculateWidths(); +} + +s32 CGUITable::getColumnCount() const +{ + return Columns.size(); +} + +u32 CGUITable::getRowCount() const +{ + return Rows.size(); +} + +bool CGUITable::setActiveColumn(s32 idx, bool doOrder ) +{ + if (idx < 0 || idx >= (s32)Columns.size()) + return false; + + bool changed = (ActiveTab != idx); + + ActiveTab = idx; + if ( ActiveTab < 0 ) + return false; + + if ( doOrder ) + { + switch ( Columns[idx].OrderingMode ) + { + case EGCO_NONE: + CurrentOrdering = EGOM_NONE; + break; + + case EGCO_CUSTOM: + CurrentOrdering = EGOM_NONE; + if (Parent) + { + SEvent event; + event.EventType = EET_GUI_EVENT; + event.GUIEvent.Caller = this; + event.GUIEvent.EventType = EGET_TABLE_HEADER_CHANGED; + Parent->OnEvent(event); + } + + break; + + case EGCO_ASCENDING: + CurrentOrdering = EGOM_ASCENDING; + break; + + case EGCO_DESCENDING: + CurrentOrdering = EGOM_DESCENDING; + break; + + case EGCO_FLIP_ASCENDING_DESCENDING: + CurrentOrdering = EGOM_ASCENDING == CurrentOrdering ? EGOM_DESCENDING : EGOM_ASCENDING; + break; + default: + CurrentOrdering = EGOM_NONE; + } + + orderRows(getActiveColumn(), CurrentOrdering); + } + + if (changed) + { + SEvent event; + event.EventType = EET_GUI_EVENT; + event.GUIEvent.Caller = this; + event.GUIEvent.EventType = EGET_TABLE_HEADER_CHANGED; + Parent->OnEvent(event); + } + + return true; +} + +s32 CGUITable::getActiveColumn() const +{ + return ActiveTab; +} + +EGUI_ORDERING_MODE CGUITable::getActiveColumnOrdering() const +{ + return CurrentOrdering; +} + +void CGUITable::setColumnWidth(u32 columnIndex, u32 width) +{ + if ( columnIndex < Columns.size() ) + { + const u32 MIN_WIDTH = Font ? Font->getDimension(Columns[columnIndex].Name.c_str() ).Width + (CellWidthPadding * 2) : CellWidthPadding * 2; + if ( width < MIN_WIDTH ) + width = MIN_WIDTH; + + Columns[columnIndex].Width = width; + + for ( u32 i=0; i < Rows.size(); ++i ) + { + breakText( Rows[i].Items[columnIndex].Text, Rows[i].Items[columnIndex].BrokenText, Columns[columnIndex].Width ); + } + } + recalculateWidths(); +} + + +u32 CGUITable::getColumnTextWidth(u32 columnIndex, bool includePadding, bool includeHeader) const +{ + if ( columnIndex < Columns.size() ) + { + u32 largestWidth = 0; + if ( Font ) + { + if ( includeHeader ) + { + largestWidth = Font->getDimension( Columns[columnIndex].Name.c_str() ).Width; + } + + for ( u32 i=0; i < Rows.size(); ++i ) + { + core::dimension2d dim = Font->getDimension( Rows[i].Items[columnIndex].Text.c_str() ); + if ( (u32)dim.Width > largestWidth ) + { + largestWidth = dim.Width; + } + } + } + + if ( includePadding ) + largestWidth += CellWidthPadding * 2; + + return largestWidth; + } + return 0; +} + + +void CGUITable::setResizableColumns(bool resizable) +{ + ResizableColumns = resizable; +} + + +bool CGUITable::hasResizableColumns() const +{ + return ResizableColumns; +} + +void CGUITable::resizeAllColumns(EGUI_TABLE_COLUMN_RESIZE style) +{ + if ( style == ETCR_FIT_TEXTS ) + { + u32 widthSum = 0; + for (u32 i=0;igetSkin(); + if ( VerticalScrollBar && VerticalScrollBar->isVisible() ) + itemClipWith -= skin->getSize(EGDS_SCROLLBAR_SIZE); + + setColumnWidth(idx, core::max_(itemClipWith-widthSum, width) ); + } + } +} + +void CGUITable::addRow(u32 rowIndex) +{ + Row row; + + if ( rowIndex >= Rows.size() ) + Rows.push_back(row); + else + Rows.insert(row, rowIndex); + + for ( u32 i = 0 ; i < Columns.size() ; ++i ) + { + Cell cell; + Rows[rowIndex].Items.push_back(cell); + } + + recalculateHeights(); +} + +void CGUITable::setRowTexts(u32 rowIndex, const irr::core::array &cellTexts ) +{ + if ( rowIndex < Rows.size() ) + { + for ( u32 i = 0 ; i < cellTexts.size() ; ++i ) + { + setCellText(rowIndex, i, cellTexts[i].c_str() ); + } + + recalculateHeights(); + } +} + +//! set some custom data for the given row +//! \param data: custom data pointer +void CGUITable::setRowData(u32 rowIndex, void * data) +{ + if ( rowIndex < Rows.size() ) + { + Rows[rowIndex].Data = data; + } +} + +//! get the custom row data +void* CGUITable::getRowData(u32 rowIndex) const +{ + if ( rowIndex < Rows.size() ) + return Rows[rowIndex].Data; + return NULL; +} + +bool CGUITable::removeRow(u32 rowIndex) +{ + if ( rowIndex < Rows.size() ) + { + Rows.erase( rowIndex ); + + if ( !(Selected < s32(Rows.size())) ) + Selected = Rows.size() - 1; + + recalculateHeights(); + return true; + } + return false; +} + +//! adds an list item, returns id of item +void CGUITable::setCellText(u32 rowIndex, u32 columnIndex, const wchar_t* text) +{ + if ( rowIndex < Rows.size() && columnIndex < Columns.size() ) + { + Rows[rowIndex].Items[columnIndex].Text = text; + breakText( Rows[rowIndex].Items[columnIndex].Text, Rows[rowIndex].Items[columnIndex].BrokenText, Columns[columnIndex].Width ); + + IGUISkin* skin = Environment->getSkin(); + if ( skin ) + Rows[rowIndex].Items[columnIndex].Color = skin->getColor(EGDC_BUTTON_TEXT); + } +} + +void CGUITable::setCellText(u32 rowIndex, u32 columnIndex, const wchar_t* text, video::SColor color) +{ + if ( rowIndex < Rows.size() && columnIndex < Columns.size() ) + { + Rows[rowIndex].Items[columnIndex].Text = text; + breakText( Rows[rowIndex].Items[columnIndex].Text, Rows[rowIndex].Items[columnIndex].BrokenText, Columns[columnIndex].Width ); + Rows[rowIndex].Items[columnIndex].Color = color; + } +} + +void CGUITable::setCellColor(u32 rowIndex, u32 columnIndex, video::SColor color) +{ + if ( rowIndex < Rows.size() && columnIndex < Columns.size() ) + { + Rows[rowIndex].Items[columnIndex].Color = color; + } +} + +//! Set the text of all cells in the given row +void CGUITable::setCellColorsInRow(u32 rowIndex, video::SColor color) +{ + if ( rowIndex < Rows.size() ) + { + for ( u32 i=0; i < Columns.size(); ++i ) + { + Rows[rowIndex].Items[i].Color = color; + } + } +} + +void CGUITable::setCellData(u32 rowIndex, u32 columnIndex, void *data) +{ + if ( rowIndex < Rows.size() && columnIndex < Columns.size() ) + { + Rows[rowIndex].Items[columnIndex].Data = data; + } +} + +const wchar_t* CGUITable::getCellText(u32 rowIndex, u32 columnIndex ) const +{ + if ( rowIndex < Rows.size() && columnIndex < Columns.size() ) + { + return Rows[rowIndex].Items[columnIndex].Text.c_str(); + } + + return 0; +} + +void* CGUITable::getCellData(u32 rowIndex, u32 columnIndex ) const +{ + if ( rowIndex < Rows.size() && columnIndex < Columns.size() ) + { + return Rows[rowIndex].Items[columnIndex].Data; + } + + return 0; +} + +void CGUITable::setCellIcon(u32 rowIndex, u32 columnIndex, s32 icon) +{ + if ( rowIndex < Rows.size() && columnIndex < Columns.size() ) + { + Rows[rowIndex].Items[columnIndex].Icon = icon; + } +} + +s32 CGUITable::getCellIcon(u32 rowIndex, u32 columnIndex) const +{ + if ( rowIndex < Rows.size() && columnIndex < Columns.size() ) + { + return Rows[rowIndex].Items[columnIndex].Icon; + } + return -1; +} + +void CGUITable::setSpriteBank(const c8 *bankName) +{ + IconBankName = core::stringc(bankName); + IGUISpriteBank* bank = IconBankName.size() ? Environment->getSpriteBank(IconBankName.c_str()) : NULL; + if ( bank != IconBank ) + { + if (IconBank) + IconBank->drop(); + IconBank = bank; + if (IconBank) + IconBank->grab(); + } +} + +//! clears the list +void CGUITable::clear() +{ + Selected = -1; + Rows.clear(); + Columns.clear(); + + if (VerticalScrollBar) + VerticalScrollBar->setPos(0); + if ( HorizontalScrollBar ) + HorizontalScrollBar->setPos(0); + + recalculateHeights(); + recalculateWidths(); +} + +void CGUITable::clearRows() +{ + Selected = -1; + Rows.clear(); + + if (VerticalScrollBar) + VerticalScrollBar->setPos(0); + + recalculateHeights(); +} + +s32 CGUITable::getSelected() const +{ + return Selected; +} + +void CGUITable::setSelected(s32 rowIndex, bool triggerEvent) +{ + s32 oldSelected = Selected; + + Selected = rowIndex; + if ( Selected >= (s32)Rows.size() ) + Selected = -1; + + // post the news + if (Parent && triggerEvent) + { + SEvent event; + event.EventType = EET_GUI_EVENT; + event.GUIEvent.Caller = this; + event.GUIEvent.EventType = (Selected != oldSelected) ? EGET_TABLE_CHANGED : EGET_TABLE_SELECTED_AGAIN; + + Parent->OnEvent(event); + } +} + +void CGUITable::ensureRowVisibility(s32 rowIndex) +{ + if ( rowIndex >= (s32)Rows.size() ) + { + rowIndex = (s32)Rows.size()-1; + } + + EnsureRowVisibility = rowIndex; +} + +void CGUITable::recalculateWidths() +{ + TotalItemWidth=0; + for ( u32 i=0; i < Columns.size(); ++i ) + { + TotalItemWidth += Columns[i].Width; + } + checkScrollbars(); +} + +void CGUITable::recalculateHeights() +{ + TotalItemHeight = 0; + IGUISkin* skin = Environment->getSkin(); + if (Font != skin->getFont()) + { + if (Font) + Font->drop(); + + Font = skin->getFont(); + + ItemHeight = 0; + ContinuationWidth = 0; + + if(Font) + { + ItemHeight = Font->getDimension(L"A").Height + (CellHeightPadding * 2); + Font->grab(); + + if ( ContinuationText.size() ) + { + ContinuationWidth = Font->getDimension(ContinuationText.c_str()).Width; + } + } + } + TotalItemHeight = ItemHeight * Rows.size(); // header is not counted, because we only want items + checkScrollbars(); +} + +// automatic enabled/disabling and resizing of scrollbars +void CGUITable::checkScrollbars() +{ + IGUISkin* skin = Environment->getSkin(); + if ( !HorizontalScrollBar || !VerticalScrollBar || !skin) + return; + + s32 scrollBarSize = skin->getSize(EGDS_SCROLLBAR_SIZE); + bool wasHorizontalScrollBarVisible = HorizontalScrollBar->isVisible(); + bool wasVerticalScrollBarVisible = VerticalScrollBar->isVisible(); + HorizontalScrollBar->setVisible(false); + VerticalScrollBar->setVisible(false); + + // CAREFUL: near identical calculations for tableRect and clientClip are also done in draw + // area of table used for drawing without scrollbars + core::rect tableRect(AbsoluteRect); + tableRect.UpperLeftCorner.X += 1; + tableRect.UpperLeftCorner.Y += 1; + s32 headerBottom = getHeaderBottom(); + + // area of for the items (without header and without scrollbars) + core::rect clientClip(tableRect); + clientClip.UpperLeftCorner.Y = headerBottom ; + + // needs horizontal scroll be visible? + if( TotalItemWidth > clientClip.getWidth() ) + { + clientClip.LowerRightCorner.Y -= scrollBarSize; + HorizontalScrollBar->setVisible(true); + HorizontalScrollBar->setMax(TotalItemWidth - clientClip.getWidth()); + s32 pageSize = TotalItemHeight ? core::round32((f32)HorizontalScrollBar->getMax()*((f32)clientClip.getHeight() / (f32)TotalItemWidth)) : 1; + HorizontalScrollBar->setPageSize( pageSize ); + } + + // needs vertical scroll be visible? + if( TotalItemHeight > clientClip.getHeight() ) + { + clientClip.LowerRightCorner.X -= scrollBarSize; + VerticalScrollBar->setVisible(true); + VerticalScrollBar->setMax(TotalItemHeight - clientClip.getHeight()); + s32 pageSize = TotalItemHeight ? core::round32((f32)VerticalScrollBar->getMax()*((f32)clientClip.getHeight() / (f32)TotalItemHeight)) : 1; + VerticalScrollBar->setPageSize(pageSize); + + // check horizontal again because we have now smaller clientClip + if ( !HorizontalScrollBar->isVisible() ) + { + if( TotalItemWidth > clientClip.getWidth() ) + { + clientClip.LowerRightCorner.Y -= scrollBarSize; + HorizontalScrollBar->setVisible(true); + HorizontalScrollBar->setMax(TotalItemWidth - clientClip.getWidth()); + s32 pageSize = TotalItemHeight ? core::round32((f32)HorizontalScrollBar->getMax()*((f32)clientClip.getWidth() / (f32)TotalItemWidth)) : 1; + HorizontalScrollBar->setPageSize( pageSize ); + } + } + } + + // find the correct size for the vertical scrollbar + if ( VerticalScrollBar->isVisible() ) + { + if (!wasVerticalScrollBarVisible ) + VerticalScrollBar->setPos(0); + + if ( HorizontalScrollBar->isVisible() ) + { + VerticalScrollBar->setRelativePosition( + core::rect(RelativeRect.getWidth() - scrollBarSize, 1, + RelativeRect.getWidth()-1, RelativeRect.getHeight()-(1+scrollBarSize) ) ); + } + else + { + VerticalScrollBar->setRelativePosition( + core::rect(RelativeRect.getWidth() - scrollBarSize, 1, + RelativeRect.getWidth()-1, RelativeRect.getHeight()-1) ); + } + } + + // find the correct size for the horizontal scrollbar + if ( HorizontalScrollBar->isVisible() ) + { + if ( !wasHorizontalScrollBarVisible ) + HorizontalScrollBar->setPos(0); + + if ( VerticalScrollBar->isVisible() ) + { + HorizontalScrollBar->setRelativePosition( core::rect(1, RelativeRect.getHeight() - scrollBarSize, RelativeRect.getWidth()-(1+scrollBarSize), RelativeRect.getHeight()-1) ); + } + else + { + HorizontalScrollBar->setRelativePosition( core::rect(1, RelativeRect.getHeight() - scrollBarSize, RelativeRect.getWidth()-1, RelativeRect.getHeight()-1) ); + } + } +} + +void CGUITable::refreshControls() +{ + updateAbsolutePosition(); + + if ( VerticalScrollBar ) + VerticalScrollBar->setVisible(false); + + if ( HorizontalScrollBar ) + HorizontalScrollBar->setVisible(false); + + recalculateHeights(); + recalculateWidths(); +} + + +//! called if an event happened. +bool CGUITable::OnEvent(SEvent event) +{ + if (!IsEnabled) + return IGUIElement::OnEvent(event); + + switch(event.EventType) + { + case EET_GUI_EVENT: + switch(event.GUIEvent.EventType) + { + case gui::EGET_SCROLL_BAR_CHANGED: + if (event.GUIEvent.Caller == VerticalScrollBar) + { + // current position will get read out in draw + return true; + } + if (event.GUIEvent.Caller == HorizontalScrollBar) + { + // current position will get read out in draw + return true; + } + break; + case gui::EGET_ELEMENT_FOCUS_LOST: + { + CurrentResizedColumn = -1; + Selecting = false; + } + break; + default: + break; + } + break; + case EET_MOUSE_INPUT_EVENT: + { + if ( !IsEnabled ) + return false; + + core::position2d p(event.MouseInput.X, event.MouseInput.Y); + + switch(event.MouseInput.Event) + { + case EMIE_MOUSE_WHEEL: + VerticalScrollBar->setPos(VerticalScrollBar->getPos() + (s32)event.MouseInput.Wheel*-10); + return true; + + case EMIE_LMOUSE_PRESSED_DOWN: + + if (Environment->hasFocus(this) && + VerticalScrollBar->isVisible() && + VerticalScrollBar->getAbsolutePosition().isPointInside(p) && + VerticalScrollBar->OnEvent(event)) + return true; + + if (Environment->hasFocus(this) && + HorizontalScrollBar->isVisible() && + HorizontalScrollBar->getAbsolutePosition().isPointInside(p) && + HorizontalScrollBar->OnEvent(event)) + return true; + + if ( dragColumnStart( event.MouseInput.X, event.MouseInput.Y ) ) + { + Environment->setFocus(this); + return true; + } + + if ( selectColumnHeader( event.MouseInput.X, event.MouseInput.Y ) ) + { + return true; + } + + Selecting = true; + Environment->setFocus(this); + return true; + + case EMIE_LMOUSE_LEFT_UP: + + CurrentResizedColumn = -1; + Selecting = false; + if (!getAbsolutePosition().isPointInside(p)) + { + Environment->removeFocus(this); + } + + if (Environment->hasFocus(this) && + VerticalScrollBar->isVisible() && + VerticalScrollBar->getAbsolutePosition().isPointInside(p) && + VerticalScrollBar->OnEvent(event)) + { + return true; + } + + if (Environment->hasFocus(this) && + HorizontalScrollBar->isVisible() && + HorizontalScrollBar->getAbsolutePosition().isPointInside(p) && + HorizontalScrollBar->OnEvent(event)) + { + return true; + } + + selectRowByPosY(event.MouseInput.Y, true); + return true; + + case EMIE_LMOUSE_DOUBLE_CLICK: + { + if ( !getAbsolutePosition().isPointInside(p) ) + return false; + + if ( VerticalScrollBar->isVisible() + && VerticalScrollBar->getAbsolutePosition().isPointInside(p) + ) + { + if ( Environment->hasFocus(this) ) + VerticalScrollBar->OnEvent(event); + return true; + } + + if ( HorizontalScrollBar->isVisible() + && HorizontalScrollBar->getAbsolutePosition().isPointInside(p) + ) + { + if ( Environment->hasFocus(this) ) + HorizontalScrollBar->OnEvent(event); + return true; + } + + if (Parent) + { + SEvent event; + event.EventType = EET_GUI_EVENT; + event.GUIEvent.Caller = this; + event.GUIEvent.EventType = EGET_ELEMENT_DOUBLE_CLICKED; + Parent->OnEvent(event); + } + + return false; + } + + case EMIE_MOUSE_MOVED: + if ( CurrentResizedColumn >= 0 ) + { + if ( dragColumnUpdate(event.MouseInput.X) ) + { + return true; + } + } + if (Selecting || MoveOverSelect) + { + if (getAbsolutePosition().isPointInside(p)) + { + selectRowByPosY(event.MouseInput.Y, true); + return true; + } + } + break; + default: + break; + } + } + break; + default: + break; + } + + return IGUIElement::OnEvent(event); +} + + +void CGUITable::setColumnOrdering(u32 columnIndex, EGUI_COLUMN_ORDERING mode) +{ + if ( columnIndex < Columns.size() ) + Columns[columnIndex].OrderingMode = mode; +} + + +void CGUITable::swapRows(u32 rowIndexA, u32 rowIndexB) +{ + if ( rowIndexA >= Rows.size() ) + return; + + if ( rowIndexB >= Rows.size() ) + return; + + Row swap = Rows[rowIndexA]; + Rows[rowIndexA] = Rows[rowIndexB]; + Rows[rowIndexB] = swap; + + if ( Selected == s32(rowIndexA) ) + Selected = rowIndexB; + else if( Selected == s32(rowIndexB) ) + Selected = rowIndexA; + +} + +s32 CGUITable::findRowIndexByData(void *data) const +{ + for ( u32 i=0; i ( AbsoluteRect.UpperLeftCorner.Y + ItemHeight ) ) + return false; + + const s32 CLICK_AREA = 3; // to left and right of line which can be dragged + s32 pos = AbsoluteRect.UpperLeftCorner.X+1; + + if ( HorizontalScrollBar && HorizontalScrollBar->isVisible() ) + pos -= HorizontalScrollBar->getPos(); + + pos += TotalItemWidth; + + // have to search from the right as otherwise lines could no longer be resized when a column width is 0 + for ( s32 i = (s32)Columns.size()-1; i >= 0 ; --i ) + { + u32 colWidth = Columns[i].Width; + + if ( xpos >= (pos - CLICK_AREA) && xpos < ( pos + CLICK_AREA ) ) + { + CurrentResizedColumn = i; + ResizeStart = xpos; + return true; + } + + pos -= colWidth; + } + + return false; +} + +bool CGUITable::dragColumnUpdate(s32 xpos) +{ + if ( !ResizableColumns || CurrentResizedColumn < 0 || CurrentResizedColumn >= s32(Columns.size()) ) + { + CurrentResizedColumn = -1; + return false; + } + + s32 width = s32(Columns[CurrentResizedColumn].Width) + (xpos-ResizeStart); + if ( width < 0 ) + width = 0; + setColumnWidth(CurrentResizedColumn, u32(width)); + ResizeStart = xpos; + + return false; +} + +bool CGUITable::selectColumnHeader(s32 xpos, s32 ypos) +{ + if( DrawFlags & EGTDF_HIDE_HEADER ) + return false; + + if ( ypos >= getHeaderBottom() ) + return false; + + s32 pos = AbsoluteRect.UpperLeftCorner.X+1; + + if ( HorizontalScrollBar && HorizontalScrollBar->isVisible() ) + pos -= HorizontalScrollBar->getPos(); + + for ( u32 i = 0 ; i < Columns.size() ; ++i ) + { + u32 colWidth = Columns[i].Width; + + if ( xpos >= pos && xpos < ( pos + s32(colWidth) ) ) + { + setActiveColumn( i, true ); + + return true; + } + + pos += colWidth; + } + + return false; +} + +void CGUITable::orderRows( s32 (*compareFn)(u32 idx1, u32 idx2, void * data), void * data ) +{ + if ( !compareFn ) + return; + + Row swap; + + for ( s32 i = 0 ; i < s32(Rows.size()) - 1 ; ++i ) + { + for ( s32 j = 0 ; j < s32(Rows.size()) - i - 1 ; ++j ) + { + if ( compareFn(j+1, j, data) < 0 ) + { + swap = Rows[j]; + Rows[j] = Rows[j+1]; + Rows[j+1] = swap; + + if ( Selected == j ) + Selected = j+1; + else if( Selected == j+1 ) + Selected = j; + } + } + } +} + +void CGUITable::orderRows( s32 (*compareFn)(void * rowdata1, void * rowdata2, void * customdata), void * customdata ) +{ + if ( !compareFn ) + return; + + CGUITableCompareFunctor myCmp(compareFn, customdata); + + core::heapsort(Rows.pointer(), Rows.size(), myCmp); + Selected = -1; +} + +// This doublicates some code which can be removed if some1 has fun. +// (because the callback orderRows was added later) +void CGUITable::orderRows(s32 columnIndex, EGUI_ORDERING_MODE mode) +{ + Row swap; + + if ( columnIndex == -1 ) + columnIndex = getActiveColumn(); + if ( columnIndex < 0 ) + return; + + if ( mode == EGOM_ASCENDING ) + { + for ( s32 i = 0 ; i < s32(Rows.size()) - 1 ; ++i ) + { + for ( s32 j = 0 ; j < s32(Rows.size()) - i - 1 ; ++j ) + { + if ( Rows[j+1].Items[columnIndex].Text < Rows[j].Items[columnIndex].Text ) + { + swap = Rows[j]; + Rows[j] = Rows[j+1]; + Rows[j+1] = swap; + + if ( Selected == j ) + Selected = j+1; + else if( Selected == j+1 ) + Selected = j; + } + } + } + } + else if ( mode == EGOM_DESCENDING ) + { + for ( s32 i = 0 ; i < s32(Rows.size()) - 1 ; ++i ) + { + for ( s32 j = 0 ; j < s32(Rows.size()) - i - 1 ; ++j ) + { + if ( Rows[j].Items[columnIndex].Text < Rows[j+1].Items[columnIndex].Text) + { + swap = Rows[j]; + Rows[j] = Rows[j+1]; + Rows[j+1] = swap; + + if ( Selected == j ) + Selected = j+1; + else if( Selected == j+1 ) + Selected = j; + } + } + } + } +} + +//! override the background color of given row +void CGUITable::setRowOverrideBackground(u32 rowIndex, const video::SColor &color) +{ + if ( rowIndex >= Rows.size() ) + return; + + Rows[rowIndex].UseOverrideBackCol = true; + Rows[rowIndex].OverrideBackCol = color; +} + +//! clear override of the background color of given row +void CGUITable::clearRowOverrideBackground(u32 rowIndex) +{ + if ( rowIndex >= Rows.size() ) + return; + Rows[rowIndex].UseOverrideBackCol = false; +} + +s32 CGUITable::getHeaderBottom() +{ + s32 headerBottom = AbsoluteRect.UpperLeftCorner.Y; + if( !(DrawFlags & EGTDF_HIDE_HEADER) ) + headerBottom += ItemHeight; + return headerBottom; +} + +void CGUITable::selectRowByPosY(s32 ypos, bool triggerEvent) +{ + if ( ypos < getHeaderBottom() ) + return; + + if ( ypos >= AbsoluteRect.LowerRightCorner.Y ) + return; + + // find new selected item. + s32 newSelected = (ypos - AbsoluteRect.UpperLeftCorner.Y - 1) + VerticalScrollBar->getPos(); + if( !(DrawFlags & EGTDF_HIDE_HEADER) ) + newSelected -= ItemHeight; + if (ItemHeight!=0) + newSelected /= ItemHeight; + + if (newSelected >= (s32)Rows.size()) + newSelected = -1; + else if (newSelected<0) + newSelected = 0; + + setSelected(newSelected, triggerEvent); +} + + +//! draws the element and its children +void CGUITable::draw() +{ + if (!IsVisible) + return; + + irr::video::IVideoDriver* driver = Environment->getVideoDriver(); + + IGUISkin* skin = Environment->getSkin(); + if (!skin) + return; + + if (!Font) + return; + + // CAREFUL: Near identical calculations for tableRect and clientClip are also done in checkScrollbars and selectColumnHeader + // Also selectRowByPosY has some calculations which need to be checked on changes here. + // Area of table used for drawing without scrollbars + core::rect tableRect(AbsoluteRect); + tableRect.UpperLeftCorner.X += 1; + tableRect.UpperLeftCorner.Y += 1; + if ( VerticalScrollBar && VerticalScrollBar->isVisible() ) + tableRect.LowerRightCorner.X -= skin->getSize(EGDS_SCROLLBAR_SIZE); + if ( HorizontalScrollBar && HorizontalScrollBar->isVisible() ) + tableRect.LowerRightCorner.Y -= skin->getSize(EGDS_SCROLLBAR_SIZE); + --tableRect.LowerRightCorner.Y; + + s32 headerBottom = getHeaderBottom(); + + // area of for the items (without header and without scrollbars) + core::rect clientClip(tableRect); + clientClip.UpperLeftCorner.Y = headerBottom; + + core::rect* clipRect = 0; + if (Clip) + clipRect = &AbsoluteClippingRect; + + // draw background for whole element + skin->draw3DSunkenPane(this, UseOverrideBgColor ? OverrideBgColor : skin->getColor(EGDC_3D_FACE), true, DrawBack, AbsoluteRect, clipRect); + + // scrolledTableClient is the area where the table items would be if it could be drawn completely + core::rect scrolledTableClient(clientClip); + scrolledTableClient.LowerRightCorner.Y = scrolledTableClient.UpperLeftCorner.Y + TotalItemHeight; + scrolledTableClient.LowerRightCorner.X = scrolledTableClient.UpperLeftCorner.X + TotalItemWidth; + + if ( VerticalScrollBar && VerticalScrollBar->isVisible() ) + { + // if VerticalScrollBar is visible then not all elements do fit and we might have + // to scroll it to the right place + if ( EnsureRowVisibility >= 0 ) + { + core::rect rowRect(scrolledTableClient); + rowRect.UpperLeftCorner.Y += EnsureRowVisibility * ItemHeight - VerticalScrollBar->getPos(); + rowRect.LowerRightCorner.Y = rowRect.UpperLeftCorner.Y + ItemHeight - VerticalScrollBar->getPos(); + + // is the row is completely visible? + if ( rowRect.UpperLeftCorner.Y < clientClip.UpperLeftCorner.Y + || rowRect.LowerRightCorner.Y > clientClip.LowerRightCorner.Y + ) + { + // scroll so that the row is now at the top + VerticalScrollBar->setPos( EnsureRowVisibility * ItemHeight ); + } + } + + scrolledTableClient.UpperLeftCorner.Y -= VerticalScrollBar->getPos(); + scrolledTableClient.LowerRightCorner.Y -= VerticalScrollBar->getPos(); + } + if ( HorizontalScrollBar && HorizontalScrollBar->isVisible() ) + { + scrolledTableClient.UpperLeftCorner.X -= HorizontalScrollBar->getPos(); + scrolledTableClient.LowerRightCorner.X -= HorizontalScrollBar->getPos(); + } + EnsureRowVisibility = -1; + + // rowRect is around the scrolled row + core::rect rowRect(scrolledTableClient); + rowRect.LowerRightCorner.Y = rowRect.UpperLeftCorner.Y + ItemHeight; + + u32 pos; + for ( u32 i = 0 ; i < Rows.size() ; ++i ) + { + if (rowRect.LowerRightCorner.Y >= clientClip.UpperLeftCorner.Y && + rowRect.UpperLeftCorner.Y <= clientClip.LowerRightCorner.Y) + { + // draw row seperator + if ( DrawFlags & EGTDF_ROWS ) + { + core::rect lineRect(rowRect); + lineRect.UpperLeftCorner.Y = lineRect.LowerRightCorner.Y - 1; + driver->draw2DRectangle(skin->getColor(EGDC_3D_SHADOW), lineRect, &clientClip); + } + + core::rect textRect(rowRect); + pos = rowRect.UpperLeftCorner.X; + + // draw override background color + if ((s32)i == Selected && DrawFlags & EGTDF_ACTIVE_ROW ) + { + // draw selected row background highlighted + driver->draw2DRectangle(skin->getColor(EGDC_HIGH_LIGHT), rowRect, &clientClip); + } + else if ( Rows[i].UseOverrideBackCol ) + { + driver->draw2DRectangle(Rows[i].OverrideBackCol, rowRect, &clientClip); + } + + for ( u32 j = 0 ; j < Columns.size() ; ++j ) + { + textRect.UpperLeftCorner.X = pos + CellWidthPadding; + textRect.LowerRightCorner.X = pos + Columns[j].Width - CellWidthPadding; + + // draw icons + if ( Rows[i].Items[j].Icon >= 0 && IconBank) + { + IconBank->draw2DSprite(Rows[i].Items[j].Icon, + textRect.getCenter(), + &clientClip, + video::SColor(255,255,255,255), + /*u32 starttime=*/0, + /*u32 currenttime=*/0, + /*bool loop=*/true, + /*bool center=*/true); + } + + // draw item text + if ((s32)i == Selected) + { + Font->draw(Rows[i].Items[j].BrokenText.c_str(), textRect, skin->getColor(IsEnabled ? EGDC_HIGH_LIGHT_TEXT : EGDC_GRAY_TEXT), false, true, &clientClip); + } + else + { + Font->draw(Rows[i].Items[j].BrokenText.c_str(), textRect, IsEnabled ? Rows[i].Items[j].Color : skin->getColor(EGDC_GRAY_TEXT), false, true, &clientClip); + } + + pos += Columns[j].Width; + } + } + + rowRect.UpperLeftCorner.Y += ItemHeight; + rowRect.LowerRightCorner.Y += ItemHeight; + } + + core::rect columnSeparator(clientClip); + pos = scrolledTableClient.UpperLeftCorner.X; + + for (u32 i = 0 ; i < Columns.size() ; ++i ) + { + const wchar_t* text = Columns[i].Name.c_str(); + u32 colWidth = Columns[i].Width; + + core::rect columnrect(pos, tableRect.UpperLeftCorner.Y, pos + colWidth, headerBottom); + + // draw column background + skin->draw3DButtonPaneStandard(this, columnrect, &tableRect); + + // draw column seperator + if ( DrawFlags & EGTDF_COLUMNS ) + { + columnSeparator.UpperLeftCorner.X = pos; + columnSeparator.LowerRightCorner.X = pos + 1; + driver->draw2DRectangle(skin->getColor(EGDC_3D_SHADOW), columnSeparator, &tableRect); + } + + if( !(DrawFlags & EGTDF_HIDE_HEADER) ) + { + // draw header column text + columnrect.UpperLeftCorner.X += CellWidthPadding; + Font->draw(text, columnrect, skin->getColor( IsEnabled ? EGDC_BUTTON_TEXT : EGDC_GRAY_TEXT), false, true, &tableRect); + + // draw icon for active column tab + if ( (s32)i == ActiveTab ) + { + if ( CurrentOrdering == EGOM_ASCENDING ) + { + columnrect.UpperLeftCorner.X = columnrect.LowerRightCorner.X - CellWidthPadding - ARROW_PAD / 2 + 2; + columnrect.UpperLeftCorner.Y += 7; + skin->drawIcon(this,EGDI_SORTED_ASCENDING,columnrect.UpperLeftCorner,0,0,false,&tableRect); + } + else + { + columnrect.UpperLeftCorner.X = columnrect.LowerRightCorner.X - CellWidthPadding - ARROW_PAD / 2 + 2; + columnrect.UpperLeftCorner.Y += 7; + skin->drawIcon(this,EGDI_SORTED_DESCENDING,columnrect.UpperLeftCorner,0,0,false,&tableRect); + } + } + } + + pos += colWidth; + } + + // fill up header background up to the right side + core::rect columnrect(pos, tableRect.UpperLeftCorner.Y, tableRect.LowerRightCorner.X , headerBottom); + skin->draw3DButtonPaneStandard(this, columnrect, &tableRect); + + IGUIElement::draw(); +} + +void CGUITable::breakText(const core::stringw &text, core::stringw & brokenText, u32 cellWidth) +{ + if ( !Font ) + return; + + bool useContinuations = false; + u32 maxLength = cellWidth - CellWidthPadding * 2; + if ( ContinuationWidth && maxLength > ContinuationWidth ) + { + maxLength -= ContinuationWidth; + useContinuations = true; + } + s32 index = Font->getCharacterFromPos(text.c_str(), maxLength); + if ( index >= 0 && index < (s32)text.size() ) + { + if ( useContinuations ) + { + // not perfect, but the old solution was just too slow (often freezed for seconds) + brokenText = text.subString(0, index+1) + ContinuationText; + } + else + { + brokenText = text.subString(0, index+1); + } + } + else + { + brokenText = text; + } +} + +// StarSonata, Micha +//! override the background color +void CGUITable::setBackgroundOverrideColor(const video::SColor &color) +{ + UseOverrideBgColor = true; + OverrideBgColor = color; +} + +// StarSonata, Micha +//! clear the background color override +void CGUITable::clearBackgroundOverrideColor() +{ + UseOverrideBgColor = false; +} + +//! Set some flags influencing the layout of the table +void CGUITable::setDrawFlags(s32 flags) +{ + DrawFlags = flags; +} + +//! Get the flags which influence the layout of the table +s32 CGUITable::getDrawFlags() const +{ + return DrawFlags; +} + + +//! Writes attributes of the element. +void CGUITable::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) +{ + IGUITable::serializeAttributes(out, options); + + out->addInt("ColumnCount", Columns.size()); + u32 i; + for (i=0;iaddString(label.c_str(), Columns[i].Name.c_str() ); + label = "Column"; label += i; label += "color"; + out->addColor(label.c_str(), Columns[i].TextColor ); + label = "Column"; label += i; label += "width"; + out->addInt(label.c_str(), Columns[i].Width ); + label = "Column"; label += i; label += "OrderingMode"; + out->addEnum(label.c_str(), Columns[i].OrderingMode, GUIColumnOrderingNames); + } + + out->addInt("RowCount", Rows.size()); + for (i=0;iaddBool(label.c_str(), Rows[i].UseOverrideBackCol); + if ( Rows[i].UseOverrideBackCol ) + { + label = "Row"; label += i; label += "OverrideBackCol"; + out->addColor(label.c_str(), Rows[i].OverrideBackCol ); + } + + label = "Row"; label += i; label += "ItemCount"; + out->addInt(label.c_str(), Rows[i].Items.size()); + + u32 c; + for ( c=0; c < Rows[i].Items.size(); ++c ) + { + label = "Row"; label += i; label += "cell"; label += c; label += "text"; + out->addString(label.c_str(), Rows[i].Items[c].Text.c_str() ); + // core::stringw BrokenText; // can be recalculated + label = "Row"; label += i; label += "cell"; label += c; label += "color"; + out->addColor(label.c_str(), Rows[i].Items[c].Color ); + label = "Row"; label += i; label += "cell"; label += c; label += "icon"; + out->addInt(label.c_str(), Rows[i].Items[c].Icon ); + // void *data; // can't be serialized + } + } + + // s32 ItemHeight; // can be calculated + // TotalItemHeight // calculated + // TotalItemWidth // calculated + // gui::IGUIFont* Font; // font is just the current font from environment + // gui::IGUIScrollBar* VerticalScrollBar; // not serialized + // gui::IGUIScrollBar* HorizontalScrollBar; // not serialized + + out->addBool ("Clip", Clip); + out->addBool ("DrawBack", DrawBack); + out->addBool ("MoveOverSelect", MoveOverSelect); + + // s32 CurrentResizedColumn; // runtime info - depends on user action + out->addBool ("ResizableColumns", ResizableColumns); + + // s32 Selected; // runtime info - depends on user action + out->addInt("CellWidthPadding", CellWidthPadding ); + out->addInt("CellHeightPadding", CellHeightPadding ); + // s32 ActiveTab; // runtime info - depends on user action + // bool Selecting; // runtime info - depends on user action + out->addEnum("CurrentOrdering", CurrentOrdering, GUIOrderingModeNames); + out->addInt("DrawFlags", DrawFlags); + out->addString("IconBankName", IconBankName.c_str() ); + out->addBool ("UseOverrideBgColor", UseOverrideBgColor); + out->addColor ("OverrideBgColor", OverrideBgColor); +} + +//! Reads attributes of the element +void CGUITable::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options) +{ + IGUITable::deserializeAttributes(in, options); + + Columns.clear(); + u32 columnCount = in->getAttributeAsInt("ColumnCount"); + u32 i; + for (i=0;igetAttributeAsString(label.c_str()).c_str()); + label = "Column"; label += i; label += "color"; + column.TextColor = in->getAttributeAsColor(label.c_str()); + label = "Column"; label += i; label += "width"; + column.Width = in->getAttributeAsInt(label.c_str()); + label = "Column"; label += i; label += "OrderingMode"; + column.OrderingMode = (EGUI_COLUMN_ORDERING) in->getAttributeAsEnumeration(label.c_str(), GUIColumnOrderingNames); + + Columns.push_back(column); + } + + Rows.clear(); + u32 rowCount = in->getAttributeAsInt("RowCount"); + for (i=0;igetAttributeAsBool(label.c_str()); + if ( row.UseOverrideBackCol ) + { + label = "Row"; label += i; label += "OverrideBackCol"; + row.OverrideBackCol = in->getAttributeAsColor(label.c_str()); + } + + Rows.push_back(row); + + label = "Row"; label += i; label += "ItemCount"; + u32 itemCount = in->getAttributeAsInt(label.c_str()); + u32 c; + for ( c=0; c < itemCount; ++c ) + { + Cell cell; + + label = "Row"; label += i; label += "cell"; label += c; label += "text"; + cell.Text = core::stringw(in->getAttributeAsString(label.c_str()).c_str()); + breakText( cell.Text, cell.BrokenText, Columns[c].Width ); + label = "Row"; label += i; label += "cell"; label += c; label += "color"; + cell.Color = in->getAttributeAsColor(label.c_str()); + label = "Row"; label += i; label += "cell"; label += c; label += "icon"; + cell.Icon = in->getAttributeAsInt(label.c_str()); + cell.Data = NULL; + + Rows[Rows.size()-1].Items.push_back(cell); + } + } + + ItemHeight = 0; // calculated + TotalItemHeight = 0; // calculated + TotalItemWidth = 0; // calculated + + // force font recalculation + if ( Font ) + { + Font->drop(); + Font = 0; + } + + Clip = in->getAttributeAsBool("Clip"); + DrawBack = in->getAttributeAsBool("DrawBack"); + MoveOverSelect = in->getAttributeAsBool("MoveOverSelect"); + + CurrentResizedColumn = -1; + ResizeStart = 0; + ResizableColumns = in->getAttributeAsBool("ResizableColumns"); + + Selected = -1; + CellWidthPadding = in->getAttributeAsInt("CellWidthPadding"); + CellHeightPadding = in->getAttributeAsInt("CellHeightPadding"); + ActiveTab = -1; + Selecting = false; + + CurrentOrdering = (EGUI_ORDERING_MODE) in->getAttributeAsEnumeration("CurrentOrdering", GUIOrderingModeNames); + DrawFlags = in->getAttributeAsInt("DrawFlags"); + + IconBankName = in->getAttributeAsString("IconBankName"); + setSpriteBank(IconBankName.c_str()); + + UseOverrideBgColor = in->getAttributeAsBool("UseOverrideBgColor"); + OverrideBgColor = in->getAttributeAsColor("OverrideBgColor"); + + refreshControls(); +} + +} // end namespace gui +} // end namespace irr diff -abBdpuNPr --exclude='*.svn' irrlicht-svn-ss/trunk/source/Irrlicht/CGUITable.h Irrlicht_starsonata/source/Irrlicht/CGUITable.h --- irrlicht-svn-ss/trunk/source/Irrlicht/CGUITable.h 1970-01-01 01:00:00.000000000 +0100 +++ Irrlicht_starsonata/source/Irrlicht/CGUITable.h 2008-08-20 14:21:36.000000000 +0200 @@ -0,0 +1,327 @@ +// Copyright (C) 2002-2005 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +// 07.10.2005 - Multicolor-Listbox addet by A. Buschhueter (Acki) +// A_Buschhueter@gmx.de + +#ifndef __C_GUI_TABLE_BAR_H_INCLUDED__ +#define __C_GUI_TABLE_BAR_H_INCLUDED__ + +#include "IGUITable.h" +#include "irrArray.h" + +namespace irr +{ +namespace gui +{ + + class IGUIFont; + class IGUIScrollBar; + + class CGUITable : public IGUITable + { + public: + //! constructor + CGUITable(IGUIEnvironment* environment, IGUIElement* parent, + s32 id, core::rect rectangle, bool clip=true, + bool drawBack=false, bool moveOverSelect=true); + + //! destructor + ~CGUITable(); + + //! Adds a column + //! If columnIndex is outside the current range, do push new colum at the end + virtual void addColumn(const wchar_t* name, s32 columnIndex=-1); + + //! Set the name for the given column. + //! Column must exist, otherwise nothing will happen. + virtual void setColumnName(u32 columnIndex, const wchar_t* name); + + //! remove a column from the table + virtual void removeColumn(u32 columnIndex); + + //! Returns the number of columns in the table control + virtual s32 getColumnCount() const; + + //! Makes a column active. This will trigger an ordering process. + /** \param idx: The id of the column to make active. + \return Returns true if successful. */ + virtual bool setActiveColumn(s32 columnIndex, bool doOrder=false); + + //! Returns which header is currently active + virtual s32 getActiveColumn() const; + + //! Returns the ordering used by the currently active column + virtual EGUI_ORDERING_MODE getActiveColumnOrdering() const; + + //! set a column width + virtual void setColumnWidth(u32 columnIndex, u32 width); + + //! Get the size which the widest text within this column would need + //! \param includePadding_: add the usual padding on both sides + //! \param includeHeader_: Also regard the width of the header text + virtual u32 getColumnTextWidth(u32 columnIndex, bool includePadding=true, bool includeHeader=true) const; + + //! columns can be resized by drag 'n drop + virtual void setResizableColumns(bool resizable); + + //! can columns be resized by drag 'n drop? + virtual bool hasResizableColumns() const; + + // micha, starsonata + //! some comfortable resizing of column widths + virtual void resizeAllColumns(EGUI_TABLE_COLUMN_RESIZE style); + + //! This tells the table control which ordering mode should be used when + //! a column header is clicked. + /** \param columnIndex: The index of the column header. + \param state: If true, a EGET_TABLE_HEADER_CHANGED message will be sent and you can order the table data as you whish.*/ + //! \param mode: One of the modes defined in EGUI_COLUMN_ORDERING + virtual void setColumnOrdering(u32 columnIndex, EGUI_COLUMN_ORDERING mode); + + + //! Returns which row is currently selected + virtual s32 getSelected() const; + + //! Select the row or comletely remove selection by using a rowIndex of -1 + //! if you set triggerEvent to true an EGET_TABLE_CHANGED or EGET_TABLE_SELECTED_AGAIN event will get triggered + virtual void setSelected(s32 rowIndex, bool triggerEvent=false); + + //! Select the row by the y coordinate of a mouse position + //! If the mousepos is above the top-row the selection won't be changed + //! If the mousepos is below the guielement rectangle the selection won't be changed + //! If the mousepos is below the bottom row, but within the guielement rectangle then the selection will be set to -1 (no selection) + virtual void selectRowByPosY(s32 ypos, bool triggerEvent=false); + + //! scroll to the row if it's not visible currently + virtual void ensureRowVisibility(s32 rowIndex); + + //! Returns amount of rows in the tabcontrol + virtual u32 getRowCount() const; + + //! adds a row to the table + /* \param rowIndex: zero based index of rows. The row will be inserted at this + position or pushed at the end for indices beyond the current tablesize */ + virtual void addRow(u32 rowIndex); + + //! sets all cell texts in a row (or at least as many as contained in cellTexts) + /* \param rowIndex: zero based index of rows. It must exit or nothing will happen*/ + //! \param cellTexts: contains the text for all the cells in a row. Each string will be filled in one column. + virtual void setRowTexts(u32 rowIndex, const irr::core::array &cellTexts ); + + //! set some custom data for the given row + //! \param data: custom data pointer + virtual void setRowData(u32 rowIndex, void * data); + + //! get the custom row data + virtual void* getRowData(u32 rowIndex) const; + + //! Remove a row from the table + //! returns true on success and false if the index did not exist + virtual bool removeRow(u32 rowIndex); + + //! clear the table rows, but keep the columns intact + virtual void clearRows(); + + //! Swap two row positions. This is useful for a custom ordering algo. + virtual void swapRows(u32 rowIndexA, u32 rowIndexB); + + //! find the index of the row which contains the same custom data value + //! returns -1 when it did not find it + virtual s32 findRowIndexByData(void *data) const; + + //! This tells the table to start ordering all the rows. You + //! need to explicitly tell the table to reorder the rows when + //! a new row is added or the cells data is changed. This makes + //! the system more flexible and doesn't make you pay the cost + //! of ordering when adding a lot of rows. + //! \param columnIndex: When set to -1 the active column is used. + virtual void orderRows(s32 columnIndex=-1, EGUI_ORDERING_MODE mode=EGOM_NONE); + + //! This tells the table to start ordering all the rows with a custom compare function. + //! \param compareFn: The comparison should return negative numbers if idx1 should be above idx2. See example below. + //! \param customdata: Often used to pass the this pointer of class objects + //! Example for compareFn which sort from small to larger numbers from top to bottom (so nothing will change...): + //! static int MyCompare(u32 idx1, u32 idx2, void * data) + //! { return idx1-idx2; } + virtual void orderRows( s32 (*compareFn)(u32 idx1, u32 idx2, void * customdata), void * customdata=NULL ); + + //! This tells the table to start ordering all the rows with a custom compare function which works with the rowdata + //! \param compareFn: The comparison should return negative numbers if rowdata1 should be above rowdata2. + //! \param customdata: Often used to pass the this pointer of class objects + //! Example for compareFn which sort by the pointer value, so that rows with smaller rowdata pointers will be on top. + //! static int MyCompare(void * rowdata1, void * rowdata2, void * customdata) + //! { return rowdata1-rowdata2; } + virtual void orderRows( s32 (*compareFn)(void * rowdata1, void * rowdata2, void * customdata), void * customdata=NULL ); + + //! override the background color of given row + virtual void setRowOverrideBackground(u32 rowIndex, const video::SColor &color); + + //! clear override of the background color of given row + virtual void clearRowOverrideBackground(u32 rowIndex); + + //! Set the text of a cell + virtual void setCellText(u32 rowIndex, u32 columnIndex, const wchar_t* text); + + //! Set the text of a cell, and set a color of this cell. + virtual void setCellText(u32 rowIndex, u32 columnIndex, const wchar_t* text, video::SColor color); + + //! Set the data of a cell + //! data will not be serialized. + virtual void setCellData(u32 rowIndex, u32 columnIndex, void *data); + + //! Set the color of a cell text + virtual void setCellColor(u32 rowIndex, u32 columnIndex, video::SColor color); + + //! Set the text of all cells in the given row + virtual void setCellColorsInRow(u32 rowIndex, video::SColor color); + + //! Get the text of a cell + virtual const wchar_t* getCellText(u32 rowIndex, u32 columnIndex ) const; + + //! Get the data of a cell + virtual void* getCellData(u32 rowIndex, u32 columnIndex ) const; + + //! \param icon Sprite index of the Icon within the current sprite bank + virtual void setCellIcon(u32 rowIndex, u32 columnIndex, s32 icon); + + //! get the sprite index of the icon in that cell or -1 for no icon + virtual s32 getCellIcon(u32 rowIndex, u32 columnIndex) const; + + //! set the spritebank used for icons + virtual void setSpriteBank(const c8 *bankName); + + //! clears the table, deletes all items in the table + virtual void clear(); + + //! called if an event happened. + virtual bool OnEvent(SEvent event); + + //! draws the element and its children + virtual void draw(); + + // StarSonata, Micha + //! override the background color + virtual void setBackgroundOverrideColor(const video::SColor &color); + + // StarSonata, Micha + //! clear the background color override + virtual void clearBackgroundOverrideColor(); + + //! Set flags, as defined in EGUI_TABLE_DRAW_FLAGS, which influence the layout + virtual void setDrawFlags(s32 flags); + + //! Get the flags, as defined in EGUI_TABLE_DRAW_FLAGS, which influence the layout + virtual s32 getDrawFlags() const; + + //! Writes attributes of the object. + //! Implement this to expose the attributes of your scene node animator for + //! scripting languages, editors, debuggers or xml serialization purposes. + virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options); + + //! Reads attributes of the object. + //! Implement this to set the attributes of your scene node animator for + //! scripting languages, editors, debuggers or xml deserialization purposes. + virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options); + + protected: + virtual void refreshControls(); + virtual void checkScrollbars(); + s32 getHeaderBottom(); + + private: + + struct Cell + { + Cell() : Icon(-1), Data(0) {} + core::stringw Text; + core::stringw BrokenText; + video::SColor Color; + s32 Icon; + void *Data; + }; + + struct Row + { + Row() : Data(0), UseOverrideBackCol(false) + {} + + core::array Items; + void *Data; + + bool UseOverrideBackCol; + video::SColor OverrideBackCol; + }; + + struct Column + { + Column() : Width(0), OrderingMode(EGCO_NONE) {} + core::stringw Name; + video::SColor TextColor; + u32 Width; + EGUI_COLUMN_ORDERING OrderingMode; + }; + + class CGUITableCompareFunctor + { + public: + CGUITableCompareFunctor( s32 (*compareFn)(void * , void * , void * ), void * data) + : CompareFn(compareFn) + , CustomData(data) + { + } + + bool operator()(const Row& a, const Row& b) + { + return CompareFn( a.Data, b.Data, CustomData ) < 0 ? true : false; + } + + s32 (*CompareFn)(void * , void * , void * ); + void * CustomData; + }; + + void breakText(const core::stringw &text, core::stringw & brokenText, u32 cellWidth); + bool selectColumnHeader(s32 xpos, s32 ypos); + bool dragColumnStart(s32 xpos, s32 ypos); + bool dragColumnUpdate(s32 xpos); + void recalculateHeights(); + void recalculateWidths(); + + core::array< Column > Columns; + core::array< Row > Rows; + gui::IGUIFont* Font; + gui::IGUIScrollBar* VerticalScrollBar; + gui::IGUIScrollBar* HorizontalScrollBar; + bool Clip; + bool DrawBack; + bool MoveOverSelect; + bool Selecting; + s32 CurrentResizedColumn; + s32 ResizeStart; + bool ResizableColumns; + s32 EnsureRowVisibility; // ensure row with given index is visible on next draw + bool UseOverrideBgColor; + video::SColor OverrideBgColor; + + s32 ItemHeight; + s32 TotalItemHeight; + s32 TotalItemWidth; + s32 Selected; + s32 CellHeightPadding; + s32 CellWidthPadding; + s32 ActiveTab; + EGUI_ORDERING_MODE CurrentOrdering; + s32 DrawFlags; + + core::stringw ContinuationText; + u32 ContinuationWidth; + + core::stringc IconBankName; + gui::IGUISpriteBank* IconBank; + }; +} // end namespace gui +} // end namespace irr + +#endif +