// Copyright (C) 2002-2009 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" #ifdef _IRR_COMPILE_WITH_GUI_ #include "IGUISkin.h" #include "IGUIEnvironment.h" #include "IVideoDriver.h" #include "IGUIFont.h" #include "CGUIScrollBar.h" #include "os.h" #define ARROW_PAD 15 namespace irr { namespace gui { //! constructor CGUITable::CGUITable(IGUIEnvironment* environment, IGUIElement* parent, s32 id, const 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), 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 ) { #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 (Font) Font->drop(); } void CGUITable::addColumn(const wchar_t* caption, s32 columnIndex) { Column tabHeader; tabHeader.Name = caption; tabHeader.Width = Font->getDimension(caption).Width + (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(); } //! 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(); } s32 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->getDimension(Columns[columnIndex].Name.c_str() ).Width + (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(); } void CGUITable::setResizableColumns(bool resizable) { ResizableColumns = resizable; } bool CGUITable::hasResizableColumns() const { return ResizableColumns; } u32 CGUITable::addRow(u32 rowIndex) { if ( rowIndex > Rows.size() ) { rowIndex = Rows.size(); } Row row; if ( rowIndex == Rows.size() ) Rows.push_back(row); else Rows.insert(row, rowIndex); Rows[rowIndex].Items.reallocate(Columns.size()); for ( u32 i = 0 ; i < Columns.size() ; ++i ) { Rows[rowIndex].Items.push_back(Cell()); } recalculateHeights(); return rowIndex; } void CGUITable::removeRow(u32 rowIndex) { if ( rowIndex > Rows.size() ) return; Rows.erase( rowIndex ); if ( !(Selected < s32(Rows.size())) ) Selected = Rows.size() - 1; recalculateHeights(); } //! adds an list item, returns id of item void CGUITable::setCellText(u32 rowIndex, u32 columnIndex, const core::stringw& 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 core::stringw& 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; } } 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; } //! 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; } //! set wich row is currently selected void CGUITable::setSelected( s32 index ) { Selected = -1; if ( index >= 0 && index < (s32) Rows.size() ) Selected = index; } 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; if(Font) { ItemHeight = Font->getDimension(L"A").Height + (CellHeightPadding * 2); Font->grab(); } } 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 = tableRect.UpperLeftCorner.Y + ItemHeight; // area of for the items (without header and without scrollbars) core::rect clientClip(tableRect); clientClip.UpperLeftCorner.Y = headerBottom + 1; // needs horizontal scroll be visible? if( TotalItemWidth > clientClip.getWidth() ) { clientClip.LowerRightCorner.Y -= scrollBarSize; HorizontalScrollBar->setVisible(true); HorizontalScrollBar->setMax(TotalItemWidth - clientClip.getWidth()); } // needs vertical scroll be visible? if( TotalItemHeight > clientClip.getHeight() ) { clientClip.LowerRightCorner.X -= scrollBarSize; VerticalScrollBar->setVisible(true); VerticalScrollBar->setMax(TotalItemHeight - clientClip.getHeight()); // 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()); } } } // 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(const SEvent &event) { if (IsEnabled) { 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; } selectNew(event.MouseInput.Y); return true; case EMIE_MOUSE_MOVED: if ( CurrentResizedColumn >= 0 ) { if ( dragColumnUpdate(event.MouseInput.X) ) { return true; } } if (Selecting || MoveOverSelect) { if (getAbsolutePosition().isPointInside(p)) { selectNew(event.MouseInput.Y); 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; } bool CGUITable::dragColumnStart(s32 xpos, s32 ypos) { if ( !ResizableColumns ) return false; if ( ypos > ( AbsoluteRect.UpperLeftCorner.Y + ItemHeight ) ) return false; const s32 CLICK_AREA = 12; // 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 ( ypos > ( AbsoluteRect.UpperLeftCorner.Y + ItemHeight ) ) 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 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; } } } } } void CGUITable::selectNew(s32 ypos, bool onlyHover) { IGUISkin* skin = Environment->getSkin(); if (!skin) return; s32 oldSelected = Selected; if ( ypos < ( AbsoluteRect.UpperLeftCorner.Y + ItemHeight ) ) return; // find new selected item. if (ItemHeight!=0) Selected = ((ypos - AbsoluteRect.UpperLeftCorner.Y - ItemHeight - 1) + VerticalScrollBar->getPos()) / ItemHeight; if (Selected >= (s32)Rows.size()) Selected = Rows.size() - 1; else if (Selected<0) Selected = 0; // post the news if (Parent && !onlyHover) { 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); } } //! 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; IGUIFont* font = skin->getFont(); if (!font) return; // CAREFUL: near identical calculations for tableRect and clientClip are also done in checkScrollbars and selectColumnHeader // 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); s32 headerBottom = tableRect.UpperLeftCorner.Y + ItemHeight; // area of for the items (without header and without scrollbars) core::rect clientClip(tableRect); clientClip.UpperLeftCorner.Y = headerBottom + 1; core::rect* clipRect = 0; if (Clip) clipRect = &AbsoluteClippingRect; // draw background for whole element skin->draw3DSunkenPane(this, skin->getColor(EGDC_3D_HIGH_LIGHT), true, DrawBack, AbsoluteRect, clipRect); // scrolledTableClient is the area where the table items would be if it could be drawn completely core::rect scrolledTableClient(tableRect); scrolledTableClient.UpperLeftCorner.Y = headerBottom + 1; scrolledTableClient.LowerRightCorner.Y = scrolledTableClient.UpperLeftCorner.Y + TotalItemHeight; scrolledTableClient.LowerRightCorner.X = scrolledTableClient.UpperLeftCorner.X + TotalItemWidth; if ( VerticalScrollBar && VerticalScrollBar->isVisible() ) { scrolledTableClient.UpperLeftCorner.Y -= VerticalScrollBar->getPos(); scrolledTableClient.LowerRightCorner.Y -= VerticalScrollBar->getPos(); } if ( HorizontalScrollBar && HorizontalScrollBar->isVisible() ) { scrolledTableClient.UpperLeftCorner.X -= HorizontalScrollBar->getPos(); scrolledTableClient.LowerRightCorner.X -= HorizontalScrollBar->getPos(); } // 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 >= AbsoluteRect.UpperLeftCorner.Y && rowRect.UpperLeftCorner.Y <= AbsoluteRect.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 selected row background highlighted if ((s32)i == Selected && DrawFlags & EGTDF_ACTIVE_ROW ) driver->draw2DRectangle(skin->getColor(EGDC_HIGH_LIGHT), rowRect, &clientClip); for ( u32 j = 0 ; j < Columns.size() ; ++j ) { textRect.UpperLeftCorner.X = pos + CellWidthPadding; textRect.LowerRightCorner.X = pos + Columns[j].Width - CellWidthPadding; // 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::dimension2d dim = font->getDimension(text); 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); } // 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_CURSOR_UP,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_CURSOR_DOWN,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) { IGUISkin* skin = Environment->getSkin(); if (!skin) return; if (!Font) return; IGUIFont* font = skin->getFont(); if (!font) return; core::stringw line, lineDots; wchar_t c[2]; c[1] = L'\0'; const u32 maxLength = cellWidth - (CellWidthPadding * 2); const u32 maxLengthDots = cellWidth - (CellWidthPadding * 2) - font->getDimension(L"...").Width; const u32 size = text.size(); u32 pos = 0; u32 i; for (i=0; igetDimension(c).Width; if ( pos > maxLength ) break; if ( font->getDimension( (line + c).c_str() ).Width > maxLengthDots ) lineDots = line; line += c[0]; } if ( i < size ) brokenText = lineDots + L"..."; else brokenText = line; } //! 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) const { 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;iaddInt(label.c_str(), Rows[i].Height ); //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 ); // 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); } //! 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 = EGCO_NONE; s32 co = in->getAttributeAsEnumeration(label.c_str(), GUIColumnOrderingNames); if (co > 0) column.OrderingMode = EGUI_COLUMN_ORDERING(co); Columns.push_back(column); } Rows.clear(); u32 rowCount = in->getAttributeAsInt("RowCount"); for (i=0; igetAttributeAsInt(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 < columnCount; ++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()); 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"); refreshControls(); } } // end namespace gui } // end namespace irr #endif