summaryrefslogtreecommitdiff
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/popup_box.cpp139
-rw-r--r--src/gui/popup_box.h71
-rw-r--r--src/gui/popupmenu.cpp52
-rw-r--r--src/gui/setup.cpp10
-rw-r--r--src/gui/setup.h2
-rw-r--r--src/gui/setup_players.cpp342
-rw-r--r--src/gui/setup_players.h70
-rw-r--r--src/gui/table.cpp419
-rw-r--r--src/gui/table.h150
-rw-r--r--src/gui/table_model.cpp149
-rw-r--r--src/gui/table_model.h137
11 files changed, 1537 insertions, 4 deletions
diff --git a/src/gui/popup_box.cpp b/src/gui/popup_box.cpp
new file mode 100644
index 00000000..4cac2169
--- /dev/null
+++ b/src/gui/popup_box.cpp
@@ -0,0 +1,139 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World 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.
+ *
+ * The Mana World 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 The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "popup_box.h"
+#include "window.h"
+#include "browserbox.h"
+
+#include <iostream>
+#include <sstream>
+
+class PoppedSelectionWindow : public Window
+{
+public:
+ PoppedSelectionWindow(PopupBox *owner):
+ mOwner(owner)
+ {
+ setResizable(false);
+ setTitleBarHeight(0);
+ mShowTitle = false;
+
+ mBrowserBox = new BrowserBox();
+ mBrowserBox->setPosition(4, 4);
+ mBrowserBox->setHighlightMode(BrowserBox::BACKGROUND);
+ mBrowserBox->setOpaque(false);
+ add(mBrowserBox);
+ mBrowserBox->setLinkHandler(owner);
+
+ initModel();
+ }
+
+ void initModel(void)
+ {
+ mBrowserBox->clearRows();
+ gcn::ListModel *model = mOwner->getListModel();
+ for (int i = 0; i < model->getNumberOfElements(); i++) {
+ std::stringstream s;
+ s << "@@" << i << "|" << model->getElementAt(i) << "@@";
+ mBrowserBox->addRow(s.str());
+ }
+
+ setContentSize(mBrowserBox->getWidth() + 8, mBrowserBox->getHeight() + 8);
+ }
+
+protected:
+ BrowserBox *mBrowserBox;
+ PopupBox *mOwner;
+};
+
+PopupBox::PopupBox(gcn::ListModel *list_model) :
+ DropDown(list_model),
+ mWindow(NULL)
+{
+}
+
+PopupBox::~PopupBox(void)
+{
+ if (mWindow)
+ delete mWindow;
+}
+
+////////////////////////////////////////
+// Widget ops
+
+void
+PopupBox::draw(gcn::Graphics *graphics)
+{
+ // fill background
+ graphics->setColor(gcn::Color(0xd0, 0xd0, 0xd0));
+ graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight()));
+
+ // draw frame-ish object
+ graphics->setColor(gcn::Color(0xc0, 0xc0, 0xc0));
+ graphics->drawRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight()));
+ graphics->setColor(gcn::Color(0xe0, 0xe0, 0xe0));
+ graphics->drawLine(0, getHeight(), getWidth(), getHeight());
+ graphics->drawLine(getWidth(), 0, getWidth(), getHeight());
+
+ graphics->drawText(getListModel()->getElementAt(getSelected()), 0, 0);
+}
+
+////////////////////////////////////////
+// MouseListener ops
+
+void
+PopupBox::mousePressed(gcn::MouseEvent& mouseEvent)
+{
+ if (0 <= mouseEvent.getY()
+ && mouseEvent.getY() < getHeight()
+ && mouseEvent.getX() >= 0
+ && mouseEvent.getX() < getWidth()
+ && mouseEvent.getButton() == gcn::MouseEvent::LEFT
+ && mouseEvent.getSource() == this) {
+ if (mWindow == NULL) {
+ mWindow = new PoppedSelectionWindow(this);
+ mWindow->resizeToContent();
+ }
+
+ int x, y;
+ getAbsolutePosition(x, y);
+ mWindow->setPosition(mouseEvent.getX() + x,
+ mouseEvent.getY() + y);
+ mWindow->setVisible(true);
+ mWindow->requestMoveToTop();
+ }
+}
+
+////////////////////////////////////////
+// LinkHandler ops
+
+void
+PopupBox::handleLink(const std::string &link)
+{
+ if (mWindow)
+ mWindow->setVisible(false);
+
+ std::stringstream s;
+ s << link;
+ int index;
+ s >> index;
+ setSelected(index);
+}
diff --git a/src/gui/popup_box.h b/src/gui/popup_box.h
new file mode 100644
index 00000000..1e3b155e
--- /dev/null
+++ b/src/gui/popup_box.h
@@ -0,0 +1,71 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World 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.
+ *
+ * The Mana World 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 The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _TMW_POPUP_BOX_H
+#define _TMW_POPUP_BOX_H
+
+#include <guichan/widgets/dropdown.hpp>
+
+#include "../guichanfwd.h"
+#include "linkhandler.h"
+
+class PoppedSelectionWindow;
+
+/**
+ * Popup box widget. Appears as a box that `pops out' into a separate floating window when selected.
+ *
+ * API adapted from guichan's listbox API. Typewise
+ */
+class PopupBox : public gcn::DropDown,
+ public LinkHandler
+{
+public:
+ /**
+ * Constructor.
+ *
+ * \param listModel the list model to use.
+ */
+ PopupBox(gcn::ListModel *list_model = NULL);
+
+ /**
+ * Destructor.
+ */
+ virtual ~PopupBox(void);
+
+ // Overriding Widget
+
+ virtual void draw(gcn::Graphics* graphics);
+
+
+ // Overriding MouseListener
+
+ virtual void mousePressed(gcn::MouseEvent& mouseEvent);
+
+
+ // Implementing Linkhandler
+ void handleLink(const std::string& link);
+
+protected:
+ PoppedSelectionWindow *mWindow;
+};
+
+
+#endif /* !defined(_TMW_POPUP_BOX_H) */
diff --git a/src/gui/popupmenu.cpp b/src/gui/popupmenu.cpp
index 8f93cb1e..f9bfe56d 100644
--- a/src/gui/popupmenu.cpp
+++ b/src/gui/popupmenu.cpp
@@ -38,6 +38,7 @@
#include "../item.h"
#include "../localplayer.h"
#include "../npc.h"
+#include "../player_relations.h"
#include "../resources/iteminfo.h"
#include "../resources/itemdb.h"
@@ -75,8 +76,29 @@ void PopupMenu::showPopup(int x, int y, Being *being)
// add as buddy will be options in this menu.
const std::string &name = mBeing->getName();
mBrowserBox->addRow("@@trade|Trade With " + name + "@@");
-
mBrowserBox->addRow("@@attack|Attack " + name + "@@");
+
+ mBrowserBox->addRow("##3---");
+
+ switch (player_relations.getRelation(name)) {
+ case PlayerRelation::NEUTRAL:
+ mBrowserBox->addRow("@@friend|Befriend " + name + "@@");
+
+ case PlayerRelation::FRIEND:
+ mBrowserBox->addRow("@@disregard|Disregard " + name + "@@");
+ mBrowserBox->addRow("@@ignore|Ignore " + name + "@@");
+ break;
+
+ case PlayerRelation::DISREGARDED:
+ mBrowserBox->addRow("@@unignore|Un-Ignore " + name + "@@");
+ mBrowserBox->addRow("@@ignore|Completely ignore " + name + "@@");
+ break;
+
+ case PlayerRelation::IGNORED:
+ mBrowserBox->addRow("@@unignore|Un-Ignore " + name + "@@");
+ break;
+ }
+
//mBrowserBox->addRow("@@follow|Follow " + name + "@@");
//mBrowserBox->addRow("@@buddy|Add " + name + " to Buddy List@@");
}
@@ -144,6 +166,34 @@ void PopupMenu::handleLink(const std::string& link)
player_node->attack(mBeing, true);
}
+ else if (link == "unignore" &&
+ mBeing != NULL &&
+ mBeing->getType() == Being::PLAYER)
+ {
+ player_relations.setRelation(mBeing->getName(), PlayerRelation::NEUTRAL);
+ }
+
+ else if (link == "ignore" &&
+ mBeing != NULL &&
+ mBeing->getType() == Being::PLAYER)
+ {
+ player_relations.setRelation(mBeing->getName(), PlayerRelation::IGNORED);
+ }
+
+ else if (link == "disregard" &&
+ mBeing != NULL &&
+ mBeing->getType() == Being::PLAYER)
+ {
+ player_relations.setRelation(mBeing->getName(), PlayerRelation::DISREGARDED);
+ }
+
+ else if (link == "friend" &&
+ mBeing != NULL &&
+ mBeing->getType() == Being::PLAYER)
+ {
+ player_relations.setRelation(mBeing->getName(), PlayerRelation::FRIEND);
+ }
+
/*
// Follow Player action
else if (link == "follow")
diff --git a/src/gui/setup.cpp b/src/gui/setup.cpp
index 3089e54a..54bf01f4 100644
--- a/src/gui/setup.cpp
+++ b/src/gui/setup.cpp
@@ -30,9 +30,11 @@
#include "setup_joystick.h"
#include "setup_video.h"
#include "setup_keyboard.h"
+#include "setup_players.h"
#include "tabbedcontainer.h"
#include "../utils/dtor.h"
+#include <iostream>
extern Window *statusWindow;
extern Window *minimap;
@@ -46,7 +48,7 @@ Setup::Setup():
Window("Setup")
{
setCloseButton(true);
- int width = 250;
+ int width = 310;
int height = 245;
setContentSize(width, height);
@@ -62,7 +64,7 @@ Setup::Setup():
}
TabbedContainer *panel = new TabbedContainer();
- panel->setDimension(gcn::Rectangle(5, 5, 250, 205));
+ panel->setDimension(gcn::Rectangle(5, 5, width, 205));
panel->setOpaque(false);
SetupTab *tab;
@@ -83,6 +85,10 @@ Setup::Setup():
panel->addTab(tab, "Keyboard");
mTabs.push_back(tab);
+ tab = new Setup_Players();
+ panel->addTab(tab, "Players");
+ mTabs.push_back(tab);
+
add(panel);
setLocationRelativeTo(getParent());
diff --git a/src/gui/setup.h b/src/gui/setup.h
index 77173367..543cab2c 100644
--- a/src/gui/setup.h
+++ b/src/gui/setup.h
@@ -48,7 +48,7 @@ class Setup : public Window, public gcn::ActionListener
/**
* Destructor.
*/
- ~Setup();
+ virtual ~Setup();
/**
* Event handling method.
diff --git a/src/gui/setup_players.cpp b/src/gui/setup_players.cpp
new file mode 100644
index 00000000..16c916b4
--- /dev/null
+++ b/src/gui/setup_players.cpp
@@ -0,0 +1,342 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World 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.
+ *
+ * The Mana World 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 The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * $Id$
+ */
+
+#include "setup_players.h"
+
+#include <vector>
+#include <guichan/widgets/label.hpp>
+#include "popup_box.h"
+#include "button.h"
+
+#include "checkbox.h"
+#include "ok_dialog.h"
+
+#include "../player_relations.h"
+#include "../configuration.h"
+#include "../log.h"
+#include "../sound.h"
+
+#define COLUMNS_NR 2 // name plus listbox
+#define NAME_COLUMN 0
+#define RELATION_CHOICE_COLUMN 1
+
+#define ROW_HEIGHT 12
+// The following column widths really shouldn't be hardcoded but should scale with the size of the widget... except
+// that, right now, the widget doesn't exactly scale either.
+#define NAME_COLUMN_WIDTH 120
+#define RELATION_CHOICE_COLUMN_WIDTH 80
+
+#define WIDGET_AT(row, column) (((row) * COLUMNS_NR) + column)
+
+static std::string table_titles[COLUMNS_NR] = {"name", "relation"};
+
+static const std::string RELATION_NAMES[PlayerRelation::RELATIONS_NR] = {
+ "neutral", "friend", "disregarded", "ignored"
+};
+
+class PlayerRelationListModel : public gcn::ListModel
+{
+public:
+ virtual ~PlayerRelationListModel(void) { }
+
+ virtual int getNumberOfElements(void)
+ {
+ return PlayerRelation::RELATIONS_NR;
+ }
+
+ virtual std::string getElementAt(int i)
+ {
+ if (i >= getNumberOfElements() || i < 0)
+ return "";
+ return RELATION_NAMES[i];
+ }
+};
+
+class PlayerTableModel : public TableModel
+{
+public:
+ PlayerTableModel(void) :
+ mPlayers(NULL)
+ {
+ playerRelationsUpdated();
+ }
+
+ virtual ~PlayerTableModel(void)
+ {
+ freeWidgets();
+ if (mPlayers)
+ delete mPlayers;
+ }
+
+ virtual int getRows(void)
+ {
+ return mPlayers->size();
+ }
+
+ virtual int getColumns(void)
+ {
+ return COLUMNS_NR;
+ }
+
+ virtual int getRowHeight(void)
+ {
+ return ROW_HEIGHT;
+ }
+
+ virtual int getColumnWidth(int index)
+ {
+ if (index == NAME_COLUMN)
+ return NAME_COLUMN_WIDTH;
+ else
+ return RELATION_CHOICE_COLUMN_WIDTH;
+ }
+
+ virtual void playerRelationsUpdated(void)
+ {
+ signalBeforeUpdate();
+
+ freeWidgets();
+ std::vector<std::string> *player_names = player_relations.getPlayers();
+ if (mPlayers)
+ delete mPlayers;
+ mPlayers = player_names;
+
+ // set up widgets
+ for (unsigned int r = 0; r < player_names->size(); ++r) {
+ std::string name = (*player_names)[r];
+ gcn::Widget *widget = new gcn::Label(name);
+ mWidgets.push_back(widget);
+
+ PopupBox *choicebox = new PopupBox(new PlayerRelationListModel());
+ choicebox->setSelected(player_relations.getRelation(name));
+ mWidgets.push_back(choicebox);
+ }
+
+ signalAfterUpdate();
+ }
+
+ virtual void updateModelInRow(int row)
+ {
+ PopupBox *choicebox = dynamic_cast<PopupBox *>(getElementAt(row, RELATION_CHOICE_COLUMN));
+ player_relations.setRelation(getPlayerAt(row),
+ static_cast<PlayerRelation::relation>(choicebox->getSelected()));
+ }
+
+
+ virtual gcn::Widget *getElementAt(int row, int column)
+ {
+ return mWidgets[WIDGET_AT(row, column)];
+ }
+
+ virtual void freeWidgets(void)
+ {
+ if (mPlayers)
+ delete mPlayers;
+ mPlayers = NULL;
+
+ for (std::vector<gcn::Widget *>::const_iterator it = mWidgets.begin(); it != mWidgets.end(); it++) {
+ delete *it;
+ }
+
+ mWidgets.clear();
+ }
+
+ std::string getPlayerAt(int index)
+ {
+ return (*mPlayers)[index];
+ }
+
+protected:
+ std::vector<std::string> *mPlayers;
+ std::vector<gcn::Widget *> mWidgets;
+};
+
+/**
+ * Class for choosing one of the various `what to do when ignoring a player' options
+ */
+class IgnoreChoicesListModel : public gcn::ListModel
+{
+public:
+ virtual ~IgnoreChoicesListModel(void) { }
+
+ virtual int getNumberOfElements(void)
+ {
+ return player_relations.getPlayerIgnoreStrategies()->size();
+ }
+
+ virtual std::string getElementAt(int i)
+ {
+ if (i >= getNumberOfElements()) {
+ return "???";
+ }
+ return (*player_relations.getPlayerIgnoreStrategies())[i]->mDescription;
+ }
+};
+
+#define ACTION_DELETE "delete"
+#define ACTION_TABLE "table"
+#define ACTION_STRATEGY "strategy"
+
+Setup_Players::Setup_Players():
+ mPlayerTableTitleModel(new StaticTableModel(1, COLUMNS_NR)),
+ mPlayerTableModel(new PlayerTableModel()),
+ mPlayerTable(new GuiTable(mPlayerTableModel)),
+ mPlayerTitleTable(new GuiTable(mPlayerTableTitleModel)),
+ mPlayerScrollArea(new ScrollArea(mPlayerTable)),
+ mPersistIgnores(new CheckBox("save player list", player_relations.getPersistIgnores())),
+ mDefaultTrading(new CheckBox("allow trading", player_relations.getDefault() & PlayerRelation::TRADE)),
+ mDefaultWhisper(new CheckBox("allow whispers", player_relations.getDefault() & PlayerRelation:: WHISPER)),
+ mDeleteButton(new Button("Delete", ACTION_DELETE, this)),
+ mIgnoreActionChoicesBox(new PopupBox(new IgnoreChoicesListModel()))
+{
+ setOpaque(false);
+
+ int table_width = NAME_COLUMN_WIDTH + RELATION_CHOICE_COLUMN_WIDTH;
+ mPlayerTableTitleModel->fixColumnWidth(NAME_COLUMN, NAME_COLUMN_WIDTH);
+ mPlayerTableTitleModel->fixColumnWidth(RELATION_CHOICE_COLUMN, RELATION_CHOICE_COLUMN_WIDTH);
+ mPlayerTitleTable->setDimension(gcn::Rectangle(10, 10, table_width, 10));
+ mPlayerTitleTable->setBackgroundColor(gcn::Color(0xbf, 0xbf, 0xbf));
+ for (int i = 0; i < COLUMNS_NR; i++)
+ mPlayerTableTitleModel->set(0, i, new gcn::Label(table_titles[i]));
+ mPlayerTitleTable->setLinewiseSelection(true);
+
+ mPlayerScrollArea->setDimension(gcn::Rectangle(10, 25, table_width + COLUMNS_NR, 90));
+ mPlayerScrollArea->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER);
+ mPlayerTable->setActionEventId(ACTION_TABLE);
+ mPlayerTable->setLinewiseSelection(true);
+ mPlayerTable->addActionListener(this);
+
+ mDeleteButton->setPosition(10, 118);
+
+ gcn::Label *ignore_action_label = new gcn::Label("When ignoring:");
+
+ ignore_action_label->setPosition(80, 118);
+ mIgnoreActionChoicesBox->setDimension(gcn::Rectangle(80, 132, 120, 12));
+ mIgnoreActionChoicesBox->setActionEventId(ACTION_STRATEGY);
+ mIgnoreActionChoicesBox->addActionListener(this);
+ int ignore_strategy_index = 0; // safe default
+ if (player_relations.getPlayerIgnoreStrategy()) {
+ ignore_strategy_index = player_relations.getPlayerIgnoreStrategyIndex(
+ player_relations.getPlayerIgnoreStrategy()->mShortName);
+ if (ignore_strategy_index < 0)
+ ignore_strategy_index = 0;
+ }
+ mIgnoreActionChoicesBox->setSelected(ignore_strategy_index);
+ mIgnoreActionChoicesBox->adjustHeight();
+
+ mPersistIgnores->setPosition(80, 148);
+ mDefaultTrading->setPosition(80, 160);
+ mDefaultWhisper->setPosition(80, 172);
+
+ reset();
+
+ add(ignore_action_label);
+ add(mDefaultTrading);
+ add(mDefaultWhisper);
+ add(mIgnoreActionChoicesBox);
+ add(mDeleteButton);
+ add(mPlayerScrollArea);
+ add(mPlayerTitleTable);
+ add(mPersistIgnores);
+
+ player_relations.addListener(this);
+}
+
+Setup_Players::~Setup_Players(void)
+{
+ player_relations.removeListener(this);
+}
+
+
+void
+Setup_Players::reset()
+{
+ // We now have to search through the list of ignore choices to find the current
+ // selection. We could use an index into the table of config options in
+ // player_relations instead of strategies to sidestep this.
+ int selection = 0;
+ for (unsigned int i = 0; i < player_relations.getPlayerIgnoreStrategies()->size(); ++i)
+ if ((*player_relations.getPlayerIgnoreStrategies())[i] ==
+ player_relations.getPlayerIgnoreStrategy()) {
+
+ selection = i;
+ break;
+ }
+
+ mIgnoreActionChoicesBox->setSelected(selection);
+}
+
+void
+Setup_Players::apply()
+{
+ player_relations.setPersistIgnores(mPersistIgnores->isSelected());
+ player_relations.store();
+
+ unsigned int old_default_relations =
+ player_relations.getDefault() & ~(PlayerRelation::TRADE | PlayerRelation::WHISPER);
+ player_relations.setDefault(old_default_relations
+ | (mDefaultTrading->isSelected()? PlayerRelation::TRADE : 0)
+ | (mDefaultWhisper->isSelected()? PlayerRelation::WHISPER : 0));
+}
+
+void
+Setup_Players::cancel()
+{
+}
+
+void
+Setup_Players::action(const gcn::ActionEvent &event)
+{
+ if (event.getId() == ACTION_TABLE) {
+ // temporarily eliminate ourselves: we are fully aware of this change, so there is no
+ // need for asynchronous updates. (In fact, thouse might destroy the widet that
+ // triggered them, which would be rather embarrassing.)
+ player_relations.removeListener(this);
+
+ int row = mPlayerTable->getSelectedRow();
+ if (row >= 0)
+ mPlayerTableModel->updateModelInRow(row);
+
+ player_relations.addListener(this);
+
+ } else if (event.getId() == ACTION_DELETE) {
+ std::string name = mPlayerTableModel->getPlayerAt(mPlayerTable->getSelectedRow());
+
+ player_relations.removePlayer(name);
+
+ } else if (event.getId() == ACTION_STRATEGY) {
+ PlayerIgnoreStrategy *s =
+ (*player_relations.getPlayerIgnoreStrategies())[
+ mIgnoreActionChoicesBox->getSelected()];
+
+ player_relations.setPlayerIgnoreStrategy(s);
+ }
+}
+
+void
+Setup_Players::updatedPlayer(const std::string &name)
+{
+ mPlayerTableModel->playerRelationsUpdated();
+ mDefaultTrading->setSelected(player_relations.getDefault() & PlayerRelation::TRADE);
+ mDefaultWhisper->setSelected(player_relations.getDefault() & PlayerRelation::WHISPER);
+}
diff --git a/src/gui/setup_players.h b/src/gui/setup_players.h
new file mode 100644
index 00000000..2c6ccc82
--- /dev/null
+++ b/src/gui/setup_players.h
@@ -0,0 +1,70 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World 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.
+ *
+ * The Mana World 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 The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _TMW_GUI_SETUP_PLAYERS_H
+#define _TMW_GUI_SETUP_PLAYERS_H
+
+#include "setuptab.h"
+
+#include "scrollarea.h"
+#include "button.h"
+#include "table.h"
+#include "popup_box.h"
+#include <guichan/actionlistener.hpp>
+
+#include "../guichanfwd.h"
+
+#include "../player_relations.h"
+
+class PlayerTableModel;
+
+class Setup_Players : public SetupTab, public gcn::ActionListener, public PlayerRelationsListener
+{
+public:
+ Setup_Players();
+ virtual ~Setup_Players();
+
+ void apply();
+ void cancel();
+
+ void reset();
+
+ void action(const gcn::ActionEvent &event);
+
+ virtual void updatedPlayer(const std::string &name);
+
+private:
+
+ StaticTableModel *mPlayerTableTitleModel;
+ PlayerTableModel *mPlayerTableModel;
+ GuiTable *mPlayerTable;
+ GuiTable *mPlayerTitleTable;
+ ScrollArea *mPlayerScrollArea;
+
+ gcn::CheckBox *mPersistIgnores;
+ gcn::CheckBox *mDefaultTrading;
+ gcn::CheckBox *mDefaultWhisper;
+
+ Button *mDeleteButton;
+ PopupBox *mIgnoreActionChoicesBox;
+};
+
+#endif
diff --git a/src/gui/table.cpp b/src/gui/table.cpp
new file mode 100644
index 00000000..07f40f76
--- /dev/null
+++ b/src/gui/table.cpp
@@ -0,0 +1,419 @@
+/*
+ * The Mana World
+ * Copyright 2004 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World 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.
+ *
+ * The Mana World 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 The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <guichan/graphics.hpp>
+#include <guichan/actionlistener.hpp>
+#include "table.h"
+#include <cassert>
+
+
+
+class GuiTableActionListener : public gcn::ActionListener
+{
+public:
+ GuiTableActionListener(GuiTable *_table, gcn::Widget *_widget, int _row, int _column);
+
+ virtual ~GuiTableActionListener(void);
+
+ virtual void action(const gcn::ActionEvent& actionEvent);
+
+protected:
+ GuiTable *mTable;
+ int mRow;
+ int mColumn;
+ gcn::Widget *mWidget;
+};
+
+
+GuiTableActionListener::GuiTableActionListener(GuiTable *table, gcn::Widget *widget, int row, int column) :
+ mTable(table),
+ mRow(row),
+ mColumn(column),
+ mWidget(widget)
+{
+ if (widget) {
+ widget->addActionListener(this);
+ widget->_setParent(table);
+ }
+}
+
+GuiTableActionListener::~GuiTableActionListener(void)
+{
+ if (mWidget) {
+ mWidget->removeActionListener(this);
+ mWidget->_setParent(NULL);
+ }
+}
+
+void
+GuiTableActionListener::action(const gcn::ActionEvent& actionEvent)
+{
+ mTable->setSelected(mRow, mColumn);
+ mTable->distributeActionEvent();
+}
+
+
+GuiTable::GuiTable(TableModel *initial_model) :
+ mLinewiseMode(false),
+ mModel(NULL),
+ mSelectedRow(0),
+ mSelectedColumn(0),
+ mTopWidget(NULL)
+{
+ setModel(initial_model);
+ addMouseListener(this);
+ addKeyListener(this);
+}
+
+GuiTable::~GuiTable(void)
+{
+ delete mModel;
+}
+
+TableModel *
+GuiTable::getModel(void) const
+{
+ return mModel;
+}
+
+void
+GuiTable::setModel(TableModel *new_model)
+{
+ if (mModel) {
+ uninstallActionListeners();
+ mModel->removeListener(this);
+ }
+
+ mModel = new_model;
+ installActionListeners();
+
+ new_model->installListener(this);
+ recomputeDimensions();
+}
+
+void
+GuiTable::recomputeDimensions(void)
+{
+ int rows_nr = mModel->getRows();
+ int columns_nr = mModel->getColumns();
+ int width = 0;
+ int height = 0;
+
+ if (mSelectedRow >= rows_nr)
+ mSelectedRow = rows_nr - 1;
+
+ if (mSelectedColumn >= columns_nr)
+ mSelectedColumn = columns_nr - 1;
+
+ for (int i = 0; i < columns_nr; i++)
+ width += getColumnWidth(i);
+
+ height = getRowHeight() * rows_nr;
+
+ setWidth(width);
+ setHeight(height);
+}
+
+void
+GuiTable::setSelected(int row, int column)
+{
+ mSelectedColumn = column;
+ mSelectedRow = row;
+}
+
+int
+GuiTable::getSelectedRow(void)
+{
+ return mSelectedRow;
+}
+
+int
+GuiTable::getSelectedColumn(void)
+{
+ return mSelectedColumn;
+}
+
+void
+GuiTable::setLinewiseSelection(bool linewise)
+{
+ mLinewiseMode = linewise;
+}
+
+int
+GuiTable::getRowHeight(void)
+{
+ if (mModel)
+ return mModel->getRowHeight() + 1; // border
+ else
+ return 0;
+}
+
+int
+GuiTable::getColumnWidth(int i)
+{
+ if (mModel)
+ return mModel->getColumnWidth(i) + 1; // border
+ else
+ return 0;
+}
+
+void
+GuiTable::uninstallActionListeners(void)
+{
+ for (std::vector<GuiTableActionListener *>::const_iterator it = action_listeners.begin(); it != action_listeners.end(); it++)
+ delete *it;
+ action_listeners.clear();
+}
+
+void
+GuiTable::installActionListeners(void)
+{
+ int rows = mModel->getRows();
+ int columns = mModel->getColumns();
+
+ for (int row = 0; row < rows; ++row)
+ for (int column = 0; column < columns; ++column) {
+ gcn::Widget *widget = mModel->getElementAt(row, column);
+ action_listeners.push_back(new GuiTableActionListener(this, widget,
+ row, column));
+ }
+
+ _setFocusHandler(_getFocusHandler()); // propagate focus handler to widgets
+}
+
+// -- widget ops
+void
+GuiTable::draw(gcn::Graphics* graphics)
+{
+ graphics->setColor(getBackgroundColor());
+ graphics->fillRectangle(gcn::Rectangle(0, 0, getWidth(), getHeight()));
+
+ if (!mModel)
+ return;
+
+ // First, determine how many rows we need to draw, and where we should start.
+ int first_row = -(getY() / getRowHeight());
+
+ if (first_row < 0)
+ first_row = 0;
+
+ int rows_nr = 1 + (getHeight() / getRowHeight()); // May overestimate by one.
+
+ int max_rows_nr = mModel->getRows() - first_row; // clip if neccessary:
+ if (max_rows_nr < rows_nr)
+ rows_nr = max_rows_nr;
+
+ // Now determine the first and last column
+ // Take the easy way out; these are usually bounded and all visible.
+ int first_column = 0;
+ int last_column = mModel->getColumns() - 1;
+
+ // Set up everything for drawing
+ int height = getRowHeight();
+ int y_offset = first_row * height;
+
+ for (int r = first_row; r < first_row + rows_nr; ++r) {
+ int x_offset = 0;
+
+ for (int c = first_column; c <= last_column; ++c) {
+ gcn::Widget *widget = mModel->getElementAt(r, c);
+ int width = getColumnWidth(c);
+ if (widget) {
+ gcn::Rectangle bounds(x_offset, y_offset, width, height);
+
+ if (widget == mTopWidget) {
+ bounds.height = widget->getHeight();
+ bounds.width = widget->getWidth();
+ }
+
+ widget->setDimension(bounds);
+
+ graphics->pushClipArea(bounds);
+ widget->draw(graphics);
+ graphics->popClipArea();
+
+ if (!mLinewiseMode
+ && c == mSelectedColumn
+ && r == mSelectedRow)
+ graphics->drawRectangle(bounds);
+ }
+
+ x_offset += width;
+ }
+
+ if (mLinewiseMode
+ && r == mSelectedRow)
+ graphics->drawRectangle(gcn::Rectangle(0, y_offset,
+ x_offset, height));
+
+ y_offset += height;
+ }
+
+ if (mTopWidget) {
+ gcn::Rectangle bounds = mTopWidget->getDimension();
+ graphics->pushClipArea(bounds);
+ mTopWidget->draw(graphics);
+ graphics->popClipArea();
+ }
+}
+
+void
+GuiTable::logic(void)
+{
+}
+
+void
+GuiTable::moveToTop(gcn::Widget *widget)
+{
+ gcn::Widget::moveToTop(widget);
+ this->mTopWidget = widget;
+}
+
+void
+GuiTable::moveToBottom(gcn::Widget *widget)
+{
+ gcn::Widget::moveToBottom(widget);
+ if (widget == this->mTopWidget)
+ this->mTopWidget = NULL;
+}
+
+gcn::Rectangle
+GuiTable::getChildrenArea(void)
+{
+ return gcn::Rectangle(0, 0, getWidth(), getHeight());
+}
+
+// -- KeyListener notifications
+void
+GuiTable::keyPressed(gcn::KeyEvent& keyEvent)
+{
+}
+
+
+// -- MouseListener notifications
+void
+GuiTable::mousePressed(gcn::MouseEvent& mouseEvent)
+{
+ if (mouseEvent.getButton() == gcn::MouseEvent::LEFT) {
+ int row = getRowForY(mouseEvent.getY());
+ int column = getColumnForX(mouseEvent.getX());
+
+ if (row > -1 && column > -1) {
+ mSelectedColumn = column;
+ mSelectedRow = row;
+ }
+
+ distributeActionEvent();
+ }
+}
+
+void
+GuiTable::mouseWheelMovedUp(gcn::MouseEvent& mouseEvent)
+{
+}
+
+void
+GuiTable::mouseWheelMovedDown(gcn::MouseEvent& mouseEvent)
+{
+}
+
+void
+GuiTable::mouseDragged(gcn::MouseEvent& mouseEvent)
+{
+}
+
+// -- TableModelListener notifications
+void
+GuiTable::modelUpdated(bool completed)
+{
+ if (completed) {
+ recomputeDimensions();
+ installActionListeners();
+ } else // before the update?
+ uninstallActionListeners();
+}
+
+gcn::Widget *
+GuiTable::getWidgetAt(int x, int y)
+{
+ int row = getRowForY(y);
+ int column = getColumnForX(x);
+
+ if (mTopWidget
+ && mTopWidget->getDimension().isPointInRect(x, y))
+ return mTopWidget;
+
+ if (row > -1
+ && column > -1) {
+ gcn::Widget *w = mModel->getElementAt(row, column);
+ if (w->isFocusable())
+ return w;
+ else
+ return NULL; // Grab the event locally
+ } else
+ return NULL;
+}
+
+int
+GuiTable::getRowForY(int y)
+{
+ int row = y / getRowHeight();
+
+ if (row < 0
+ || row >= mModel->getRows())
+ return -1;
+ else
+ return row;
+}
+
+int
+GuiTable::getColumnForX(int x)
+{
+ int column;
+ int delta = 0;
+
+ for (column = 0; column < mModel->getColumns(); column++) {
+ delta += getColumnWidth(column);
+ if (x <= delta)
+ break;
+ }
+
+ if (column < 0
+ || column >= mModel->getColumns())
+ return -1;
+ else
+ return column;
+}
+
+
+void
+GuiTable::_setFocusHandler(gcn::FocusHandler* focusHandler)
+{
+ gcn::Widget::_setFocusHandler(focusHandler);
+
+ if (mModel)
+ for (int r = 0; r < mModel->getRows(); ++r)
+ for (int c = 0; c < mModel->getColumns(); ++c) {
+ gcn::Widget *w = mModel->getElementAt(r, c);
+ if (w)
+ w->_setFocusHandler(focusHandler);
+ }
+}
diff --git a/src/gui/table.h b/src/gui/table.h
new file mode 100644
index 00000000..cef82d5d
--- /dev/null
+++ b/src/gui/table.h
@@ -0,0 +1,150 @@
+/*
+ * The Mana World
+ * Copyright 2008 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World 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.
+ *
+ * The Mana World 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 The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TMW_TABLE_H_
+#define TMW_TABLE_H_
+
+#include <vector>
+
+#include <guichan/gui.hpp>
+#include <guichan/keylistener.hpp>
+#include <guichan/mouselistener.hpp>
+#include <guichan/platform.hpp>
+#include <guichan/widget.hpp>
+
+#include "table_model.h"
+#include "../guichanfwd.h"
+
+class GuiTableActionListener;
+
+/**
+ * A table, with rows and columns made out of sub-widgets. Largely inspired by (and can be thought of as a generalisation of)
+ * the guichan listbox implementation.
+ *
+ * Normally you want this within a ScrollArea.
+ *
+ * \ingroup GUI
+ */
+class GuiTable : public gcn::Widget,
+ public gcn::MouseListener,
+ public gcn::KeyListener,
+ public TableModelListener
+{
+ friend class GuiTableActionListener; // so that the action listener can call distributeActionEvent
+public:
+ GuiTable(TableModel * initial_model = NULL);
+
+ virtual ~GuiTable(void);
+
+ /**
+ * Retrieves the active table model
+ */
+ TableModel *getModel(void) const;
+
+ /**
+ * Sets the table model
+ *
+ * Note that actions issued by widgets returned from the model will update the table
+ * selection, but only AFTER any event handlers installed within the widget have been
+ *triggered. To be notified after such an update, add an action listener to the table
+ * instead.
+ */
+ void setModel(TableModel *m);
+
+ void setSelected(int row, int column);
+
+ int getSelectedRow(void);
+
+ int getSelectedColumn(void);
+
+ gcn::Rectangle getChildrenArea(void);
+
+ /**
+ * Toggle whether to use linewise selection mode, in which the table selects an entire
+ * line at a time, rather than a single cell.
+ *
+ * Note that column information is tracked even in linewise selection mode; this mode
+ * therefore only affects visualisation.
+ *
+ * Disabled by default.
+ *
+ * \param linewise: Whether to enable linewise selection mode
+ */
+ void setLinewiseSelection(bool linewise);
+
+ // Inherited from Widget
+ virtual void draw(gcn::Graphics* graphics);
+
+ virtual void logic(void);
+
+ virtual gcn::Widget *getWidgetAt(int x, int y);
+
+ virtual void moveToTop(gcn::Widget *child);
+
+ virtual void moveToBottom(gcn::Widget *child);
+
+ virtual void _setFocusHandler(gcn::FocusHandler* focusHandler);
+
+ // Inherited from KeyListener
+ virtual void keyPressed(gcn::KeyEvent& keyEvent);
+
+
+ // Inherited from MouseListener
+ virtual void mousePressed(gcn::MouseEvent& mouseEvent);
+
+ virtual void mouseWheelMovedUp(gcn::MouseEvent& mouseEvent);
+
+ virtual void mouseWheelMovedDown(gcn::MouseEvent& mouseEvent);
+
+ virtual void mouseDragged(gcn::MouseEvent& mouseEvent);
+
+ // Constraints inherited from TableModelListener
+ virtual void modelUpdated(bool);
+
+protected:
+
+ virtual void uninstallActionListeners(void); // frees all action listeners on inner widgets
+ virtual void installActionListeners(void); // installs all action listeners on inner widgets
+
+ virtual int getRowHeight(void);
+ virtual int getColumnWidth(int i);
+
+private:
+
+ int getRowForY(int y); // -1 on error
+ int getColumnForX(int x); // -1 on error
+ void recomputeDimensions(void);
+ bool mLinewiseMode;
+
+ TableModel *mModel;
+
+ int mSelectedRow;
+ int mSelectedColumn;
+
+ int mPopFramesNr; // Number of frames to skip upwards when drawing the selected widget
+
+ gcn::Widget *mTopWidget; // If someone moves a fresh widget to the top, we must display it
+
+ std::vector<GuiTableActionListener *> action_listeners; // Vector for compactness; used as a list in practice.
+};
+
+
+#endif /* !defined(TMW_TABLE_H_) */
diff --git a/src/gui/table_model.cpp b/src/gui/table_model.cpp
new file mode 100644
index 00000000..78df0636
--- /dev/null
+++ b/src/gui/table_model.cpp
@@ -0,0 +1,149 @@
+/*
+ * The Mana World
+ * Copyright 2008 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World 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.
+ *
+ * The Mana World 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 The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <guichan/widget.hpp>
+#include <cstdlib>
+#include "table_model.h"
+
+void
+TableModel::installListener(TableModelListener *listener)
+{
+ listeners.insert(listener);
+}
+
+void
+TableModel::removeListener(TableModelListener *listener)
+{
+ listeners.erase(listener);
+}
+
+void
+TableModel::signalBeforeUpdate(void)
+{
+ for (std::set<TableModelListener *>::const_iterator it = listeners.begin(); it != listeners.end(); it++)
+ (*it)->modelUpdated(false);
+}
+
+void
+TableModel::signalAfterUpdate(void)
+{
+ for (std::set<TableModelListener *>::const_iterator it = listeners.begin(); it != listeners.end(); it++)
+ (*it)->modelUpdated(true);
+}
+
+
+
+#define WIDGET_AT(row, column) (((row) * mColumns) + (column))
+#define DYN_SIZE(h) ((h) >= 0) // determines whether this size is tagged for auto-detection
+
+StaticTableModel::StaticTableModel(int row, int column) :
+ mRows(row),
+ mColumns(column),
+ mHeight(1)
+{
+ mTableModel.resize(row * column, NULL);
+ mWidths.resize(column, 1);
+}
+
+StaticTableModel::~StaticTableModel(void)
+{
+ for (std::vector<gcn::Widget *>::const_iterator it = mTableModel.begin(); it != mTableModel.end(); it++)
+ if (*it)
+ delete *it;
+}
+
+void
+StaticTableModel::set(int row, int column, gcn::Widget *widget)
+{
+ if (row >= mRows || row < 0
+ || column >= mColumns || row > 0)
+ // raise exn?
+ return;
+
+ if (DYN_SIZE(mHeight)
+ && widget->getHeight() > mHeight)
+ mHeight = widget->getHeight();
+
+ if (DYN_SIZE(mWidths[column])
+ && widget->getWidth() > mWidths[column])
+ mWidths[column] = widget->getWidth();
+
+ signalBeforeUpdate();
+
+ if (mTableModel[WIDGET_AT(row, column)])
+ delete mTableModel[WIDGET_AT(row, column)];
+
+ mTableModel[WIDGET_AT(row, column)] = widget;
+
+ signalAfterUpdate();
+}
+
+gcn::Widget *
+StaticTableModel::getElementAt(int row, int column)
+{
+ return mTableModel[WIDGET_AT(row, column)];
+}
+
+void
+StaticTableModel::fixColumnWidth(int column, int width)
+{
+ if (width < 0
+ || column < 0 || column >= mColumns)
+ return;
+
+ mWidths[column] = -width; // Negate to tag as fixed
+}
+
+void
+StaticTableModel::fixRowHeight(int height)
+{
+ if (height < 0)
+ return;
+
+ mHeight = -height;
+}
+
+int
+StaticTableModel::getRowHeight(void)
+{
+ return abs(mHeight);
+}
+
+int
+StaticTableModel::getColumnWidth(int column)
+{
+ if (column < 0 || column >= mColumns)
+ return 0;
+
+ return abs(mWidths[column]);
+}
+
+int
+StaticTableModel::getRows(void)
+{
+ return mRows;
+}
+
+int
+StaticTableModel::getColumns(void)
+{
+ return mColumns;
+}
diff --git a/src/gui/table_model.h b/src/gui/table_model.h
new file mode 100644
index 00000000..48ef1c0d
--- /dev/null
+++ b/src/gui/table_model.h
@@ -0,0 +1,137 @@
+/*
+ * The Mana World
+ * Copyright 2008 The Mana World Development Team
+ *
+ * This file is part of The Mana World.
+ *
+ * The Mana World 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.
+ *
+ * The Mana World 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 The Mana World; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef TMW_TABLE_MODEL_H_
+#define TMW_TABLE_MODEL_H_
+
+#include <guichan/gui.hpp>
+#include <set>
+#include <vector>
+#include "../guichanfwd.h"
+
+class TableModelListener
+{
+public:
+ /**
+ * Must be invoked by the TableModel whenever a global change is about to occur or
+ * has occurred (e.g., when a row or column is being removed or added).
+ *
+ * This method is triggered twice, once before and once after the update.
+ *
+ * \param completed whether we are signalling the end of the update
+ */
+ virtual void modelUpdated(bool completed) = 0;
+};
+
+/**
+ * A model for a regular table of widgets.
+ */
+class TableModel
+{
+public:
+ virtual ~TableModel(void) { }
+
+ /**
+ * Determines the number of rows (lines) in the table
+ */
+ virtual int getRows(void) = 0;
+
+ /**
+ * Determines the number of columns in each row
+ */
+ virtual int getColumns(void) = 0;
+
+ /**
+ * Determines the height for each row
+ */
+ virtual int getRowHeight(void) = 0;
+
+ /**
+ * Determines the width of each individual column
+ */
+ virtual int getColumnWidth(int index) = 0;
+
+ /**
+ * Retrieves the widget stored at the specified location within the table.
+ */
+ virtual gcn::Widget *getElementAt(int row, int column) = 0;
+
+ virtual void installListener(TableModelListener *listener);
+
+ virtual void removeListener(TableModelListener *listener);
+
+protected:
+ /**
+ * Tells all listeners that the table is about to see an update
+ */
+ virtual void signalBeforeUpdate(void);
+
+ /**
+ * Tells all listeners that the table has seen an update
+ */
+ virtual void signalAfterUpdate(void);
+
+private:
+ std::set<TableModelListener *> listeners;
+};
+
+
+class StaticTableModel : public TableModel
+{
+public:
+ StaticTableModel(int width, int height);
+ virtual ~StaticTableModel(void);
+
+ /**
+ * Inserts a widget into the table model.
+ * The model is resized to accomodate the widget's width and height, unless column width / row height have been fixed.
+ */
+ virtual void set(int row, int column, gcn::Widget *widget);
+
+ /**
+ * Fixes the column width for a given column; this overrides dynamic width inference.
+ *
+ * Semantics are undefined for width 0.
+ */
+ virtual void fixColumnWidth(int column, int width);
+
+ /**
+ * Fixes the row height; this overrides dynamic height inference.
+ *
+ * Semantics are undefined for width 0.
+ */
+ virtual void fixRowHeight(int height);
+
+ virtual int getRows();
+ virtual int getColumns();
+ virtual int getRowHeight();
+ virtual int getColumnWidth(int index);
+ virtual gcn::Widget *getElementAt(int row, int column);
+
+
+protected:
+ int mRows, mColumns;
+ int mHeight;
+ std::vector<gcn::Widget *> mTableModel;
+ std::vector<int> mWidths;
+};
+
+#endif /* !defined(TMW_TABLE_MODEL_H_) */