class Menu is template now / bunch of code clean-ups.

This commit is contained in:
unK
2008-09-03 01:16:26 +02:00
parent 451f2e112e
commit 723de0687e
10 changed files with 1021 additions and 954 deletions

View File

@@ -22,15 +22,17 @@
#define HAVE_MENU_H
#include "window.h"
#include "misc.h"
#include <stdexcept>
enum Location { lLeft, lCenter, lRight };
template <class T>
struct Option
{
Option() : is_static(0), is_bold(0), selected(0), have_separator(0) { }
string content;
T item;
bool is_static;
bool is_bold;
bool selected;
@@ -38,56 +40,73 @@ struct Option
Location location;
};
template <class T>
class Menu : public Window
{
typedef typename vector<Option<T> *>::iterator T_iterator;
typedef typename vector<Option<T> *>::const_iterator T_const_iterator;
typedef string (*ItemDisplayer) (const T &, void *);
public:
Menu(int startx, int starty, int width, int height, string title, Color color, Border border) : Window(startx, starty, width, height, title, color, border), itsSelectedPrefix("[r]"), itsSelectedSuffix("[/r]"), itsStaticsNumber(0), itsBeginning(0), itsHighlight(0), itsHighlightColor(itsBaseColor), itsHighlightEnabled(1) { SetColor(color); }
Menu(int startx, int starty, int width, int height, string title, Color color, Border border) : itsItemDisplayer(0), itsUserdata(0), Window(startx, starty, width, height, title, color, border), itsSelectedPrefix("[r]"), itsSelectedSuffix("[/r]"), itsStaticsNumber(0), itsBeginning(0), itsHighlight(0), itsHighlightColor(itsBaseColor), itsHighlightEnabled(1) { SetColor(color); }
Menu(const Menu &);
virtual ~Menu();
virtual void Add(string str) { AddOption(str); }
void AddOption(const string &, Location = lLeft, bool separator = 0);
void AddBoldOption(const string &str, Location location = lLeft, bool separator = 0);
void AddStaticOption(const string &str, Location location = lLeft, bool separator = 0);
void AddStaticBoldOption(const string &str, Location location = lLeft, bool separator = 0);
void SetItemDisplayer(ItemDisplayer ptr) { itsItemDisplayer = ptr; }
void SetItemDisplayerUserData(void *data) { itsUserdata = data; }
void AddOption(const T &, Location = lLeft, bool separator = 0);
void AddBoldOption(const T &item, Location location = lLeft, bool separator = 0);
void AddStaticOption(const T &item, Location location = lLeft, bool separator = 0);
void AddStaticBoldOption(const T &item, Location location = lLeft, bool separator = 0);
void AddSeparator();
void UpdateOption(int, string, Location = lLeft, bool separator = 0);
void UpdateOption(int, const T &, Location = lLeft, bool separator = 0);
void BoldOption(int, bool);
void MakeStatic(int, bool);
void DeleteOption(int);
void Swap(int, int);
string GetCurrentOption() const;
string GetOption(int i) const;
virtual string GetOption(int i) const;
virtual void Display(bool redraw_whole_window = 0);
virtual void Refresh(bool redraw_whole_window = 0);
virtual void Go(Where);
void Highlight(int);
virtual void Highlight(int);
virtual void Reset();
virtual void Clear(bool clear_screen = 1);
void Select(int, bool);
bool Selected(int);
bool IsAnySelected();
virtual void Select(int, bool);
virtual bool Selected(int);
virtual bool IsAnySelected();
virtual void GetSelectedList(vector<int> &);
void SetSelectPrefix(string str) { itsSelectedPrefix = str; }
void SetSelectSuffix(string str) { itsSelectedSuffix = str; }
void GetSelectedList(vector<int> &);
void HighlightColor(Color col) { itsHighlightColor = col; NeedsRedraw.push_back(itsHighlight); }
void Highlighting(bool hl) { itsHighlightEnabled = hl; NeedsRedraw.push_back(itsHighlight); Refresh(); }
virtual void Highlighting(bool hl) { itsHighlightEnabled = hl; NeedsRedraw.push_back(itsHighlight); Refresh(); }
int GetRealChoice() const;
virtual int GetChoice() const { return itsHighlight+1; }
int MaxChoice() const { return itsOptions.size(); }
virtual int Size() const { return itsOptions.size(); }
int MaxRealChoice() const { return itsOptions.size()-itsStaticsNumber; }
bool Empty() { return itsOptions.empty(); }
bool IsStatic(int);
virtual bool IsStatic(int);
virtual Window * Clone() { return new Menu(*this); }
virtual Window * EmptyClone();
T & at(int i) { return itsOptions.at(i)->item; }
const T & at(int i) const { return itsOptions.at(i)->item; }
const T & operator[](int i) const { return itsOptions[i]->item; }
T & operator[](int i) { return itsOptions[i]->item; }
protected:
vector<Option *> itsOptions;
string DisplayOption(const T &t) const;
ItemDisplayer itsItemDisplayer;
void *itsUserdata;
vector<Option<T> *> itsOptions;
vector<int> NeedsRedraw;
string itsSelectedPrefix;
@@ -107,5 +126,677 @@ class Menu : public Window
bool itsHighlightEnabled;
};
template <class T>
Menu<T>::Menu(const Menu &m) : Window(m)
{
for (T_const_iterator it = m.itsOptions.begin(); it != m.itsOptions.end(); it++)
{
Option<T> *new_option = new Option<T>(**it);
itsOptions.push_back(new_option);
}
itsItemDisplayer = m.itsItemDisplayer;
itsUserdata = m.itsUserdata;
NeedsRedraw = m.NeedsRedraw;
itsSelectedPrefix = m.itsSelectedPrefix;
itsSelectedSuffix = m.itsSelectedSuffix;
itsStaticsNumber = m.itsStaticsNumber;
itsBeginning = m.itsBeginning;
itsHighlight = m.itsHighlight;
itsHighlightColor = m.itsHighlightColor;
itsHighlightEnabled = m.itsHighlightEnabled;
}
template <class T>
Menu<T>::~Menu()
{
for (T_iterator it = itsOptions.begin(); it != itsOptions.end(); it++)
delete *it;
}
template <class T>
int Menu<T>::count_length(string str)
{
if (str.empty())
return 0;
bool collect = false;
int length = 0;
#ifdef UTF8_ENABLED
wstring str2 = ToWString(str);
wstring tmp;
#else
string &str2 = str;
string tmp;
#endif
for (int i = 0; i < str2.length(); i++, length++)
{
if (str2[i] == '[')
collect = 1;
if (collect)
tmp += str2[i];
if (str2[i] == ']')
collect = 0;
if (!collect && !tmp.empty())
{
if (is_valid_color(TO_STRING(tmp)))
length -= tmp.length();
tmp.clear();
}
}
return length;
}
template <class T>
void Menu<T>::AddOption(const T &item, Location location, bool separator)
{
Option<T> *new_option = new Option<T>;
new_option->item = item;
new_option->location = location;
new_option->have_separator = separator;
itsOptions.push_back(new_option);
if (itsOptions.size() > itsBeginning && itsOptions.size() <= itsBeginning+itsHeight)
NeedsRedraw.push_back(itsOptions.size()-1);
}
template <class T>
void Menu<T>::AddBoldOption(const T &item, Location location, bool separator)
{
Option<T> *new_option = new Option<T>;
new_option->item = item;
new_option->location = location;
new_option->have_separator = separator;
new_option->is_bold = 1;
itsOptions.push_back(new_option);
if (itsOptions.size() > itsBeginning && itsOptions.size() <= itsBeginning+itsHeight)
NeedsRedraw.push_back(itsOptions.size()-1);
}
template <class T>
void Menu<T>::AddStaticOption(const T &item, Location location, bool separator)
{
Option<T> *new_option = new Option<T>;
new_option->item = item;
new_option->location = location;
new_option->have_separator = separator;
new_option->is_static = 1;
itsOptions.push_back(new_option);
itsStaticsNumber++;
if (itsOptions.size() > itsBeginning && itsOptions.size() <= itsBeginning+itsHeight)
NeedsRedraw.push_back(itsOptions.size()-1);
}
template <class T>
void Menu<T>::AddStaticBoldOption(const T &item, Location location, bool separator)
{
Option<T> *new_option = new Option<T>;
new_option->item = item;
new_option->location = location;
new_option->have_separator = separator;
new_option->is_static = 1;
new_option->is_bold = 1;
itsOptions.push_back(new_option);
itsStaticsNumber++;
if (itsOptions.size() > itsBeginning && itsOptions.size() <= itsBeginning+itsHeight)
NeedsRedraw.push_back(itsOptions.size()-1);
}
template <class T>
void Menu<T>::AddSeparator()
{
AddStaticOption("", lLeft, 1);
}
template <class T>
void Menu<T>::UpdateOption(int index, const T &item, Location location, bool separator)
{
index--;
try
{
itsOptions.at(index)->location = location;
itsOptions.at(index)->item = item;
itsOptions.at(index)->have_separator = separator;
if (index >= itsBeginning && index < itsBeginning+itsHeight)
NeedsRedraw.push_back(index);
}
catch (std::out_of_range)
{
}
}
template <class T>
void Menu<T>::BoldOption(int index, bool bold)
{
index--;
try
{
itsOptions.at(index)->is_bold = bold;
if (index >= itsBeginning && index < itsBeginning+itsHeight)
NeedsRedraw.push_back(index);
}
catch (std::out_of_range)
{
}
}
template <class T>
void Menu<T>::MakeStatic(int index, bool stat)
{
index--;
try
{
if (stat && !itsOptions.at(index)->is_static)
itsStaticsNumber++;
if (!stat && itsOptions.at(index)->is_static)
itsStaticsNumber--;
itsOptions.at(index)->is_static = stat;
}
catch (std::out_of_range)
{
}
}
template <class T>
string Menu<T>::GetCurrentOption() const
{
try
{
return OmitBBCodes(DisplayOption(itsOptions.at(itsHighlight)->item));
}
catch (std::out_of_range)
{
return "";
}
}
template <class T>
string Menu<T>::GetOption(int i) const
{
try
{
return OmitBBCodes(DisplayOption(itsOptions.at(i-1)->item));
}
catch (std::out_of_range)
{
return "";
}
}
template <class T>
void Menu<T>::DeleteOption(int no)
{
no--;
try
{
if (itsOptions.at(no)->is_static)
itsStaticsNumber--;
}
catch (std::out_of_range)
{
return;
}
delete itsOptions[no];
itsOptions.erase(itsOptions.begin()+no);
if (itsHighlight > itsOptions.size()-1)
itsHighlight = itsOptions.size()-1;
idlok(itsWindow, 1);
scrollok(itsWindow, 1);
int MaxBeginning = itsOptions.size() < itsHeight ? 0 : itsOptions.size()-itsHeight;
if (itsBeginning > MaxBeginning)
{
itsBeginning = MaxBeginning;
wmove(itsWindow, no-itsBeginning, 0);
wdeleteln(itsWindow);
wscrl(itsWindow, -1);
NeedsRedraw.push_back(itsBeginning);
}
else
{
wmove(itsWindow, no-itsBeginning, 0);
wdeleteln(itsWindow);
}
NeedsRedraw.push_back(itsHighlight);
NeedsRedraw.push_back(itsBeginning+itsHeight-1);
scrollok(itsWindow, 0);
idlok(itsWindow, 0);
}
template <class T>
void Menu<T>::Swap(int one, int two)
{
try
{
std::swap<Option<T> *>(itsOptions.at(one), itsOptions.at(two));
NeedsRedraw.push_back(one);
NeedsRedraw.push_back(two);
}
catch (std::out_of_range)
{
}
}
template <class T>
void Menu<T>::redraw_screen()
{
NeedsRedraw.clear();
T_const_iterator it = itsOptions.begin()+itsBeginning;
NeedsRedraw.reserve(itsHeight);
for (int i = itsBeginning; i < itsBeginning+itsHeight && it != itsOptions.end(); i++, it++)
NeedsRedraw.push_back(i);
}
template <class T>
void Menu<T>::Display(bool redraw_whole_window)
{
Window::show_border();
Refresh(redraw_whole_window);
}
template <class T>
void Menu<T>::Refresh(bool redraw_whole_window)
{
if (!itsOptions.empty() && is_static())
itsHighlight == 0 ? Go(wDown) : Go(wUp);
int MaxBeginning = itsOptions.size() < itsHeight ? 0 : itsOptions.size()-itsHeight;
if (itsBeginning > MaxBeginning)
itsBeginning = MaxBeginning;
if (itsHighlight >= itsOptions.size()-1)
Highlight(itsOptions.size());
if (redraw_whole_window)
{
Window::Clear();
redraw_screen();
}
for (vector<int>::const_iterator it = NeedsRedraw.begin(); it != NeedsRedraw.end(); it++)
{
try
{
itsOptions.at(*it);
}
catch (std::out_of_range)
{
break;
}
int line = *it-itsBeginning;
if (line < 0 || line+1 > itsHeight) // do not draw if line should be invisible anyway
continue;
Color old_basecolor = itsBaseColor;
if (*it == itsHighlight && itsHighlightEnabled)
{
Reverse(1);
SetBaseColor(itsHighlightColor);
SetColor(itsHighlightColor);
}
if (itsOptions[*it]->is_bold)
Bold(1);
int ch = itsOptions[*it]->have_separator ? 0 : 32;
mvwhline(itsWindow,line, 0, ch, itsWidth);
string option = DisplayOption(itsOptions[*it]->item);
int strlength = itsOptions[*it]->location != lLeft && BBEnabled ? count_length(option) : option.length();
if (strlength)
{
int x = 0;
if (itsOptions[*it]->location == lCenter)
{
for (; x < (itsWidth-strlength-(!ch ? 4 : 0))/2; x++);
if (!ch)
{
AltCharset(1);
mvwaddstr(itsWindow, line, x, "u ");
AltCharset(0);
x += 2;
}
}
if (itsOptions[*it]->location == lRight)
{
for (; x < (itsWidth-strlength); x++)
if (!ch)
{
AltCharset(1);
mvwaddstr(itsWindow, line, x, "u ");
AltCharset(0);
x += 2;
}
}
# ifdef UTF8_ENABLED
if (itsOptions[*it]->selected)
WriteXY(x, line, itsWidth, ToWString(itsSelectedPrefix + option + itsSelectedSuffix), 0);
else
WriteXY(x, line, itsWidth, ToWString(option), 0);
# else
if (itsOptions[*it]->selected)
WriteXY(x, line, itsWidth, itsSelectedPrefix + option + itsSelectedSuffix, 0);
else
WriteXY(x, line, itsWidth, option, 0);
# endif
if (!ch && (itsOptions[*it]->location == lCenter || itsOptions[*it]->location == lLeft))
{
x += strlength;
AltCharset(1);
mvwaddstr(itsWindow, line, x, " t");
AltCharset(0);
}
}
SetBaseColor(old_basecolor);
SetColor(old_basecolor);
while (!itsColors.empty()) // clear color stack and disable bold and reverse as
itsColors.pop(); // some items are too long to close all tags properly
AltCharset(0);
Reverse(0);
Bold(0);
}
NeedsRedraw.clear();
wrefresh(itsWindow);
}
template <class T>
void Menu<T>::Go(Where where)
{
if (Empty()) return;
int MaxHighlight = itsOptions.size()-1;
int MaxBeginning = itsOptions.size() < itsHeight ? 0 : itsOptions.size()-itsHeight;
int MaxCurrentHighlight = itsBeginning+itsHeight-1;
idlok(itsWindow, 1);
scrollok(itsWindow, 1);
switch (where)
{
case wUp:
{
if (itsHighlight <= itsBeginning && itsHighlight > 0)
{
itsBeginning--; // for scrolling
wscrl(itsWindow, -1);
}
if (itsHighlight == 0)
break;
else
{
NeedsRedraw.push_back(itsHighlight--);
NeedsRedraw.push_back(itsHighlight);
}
if (is_static())
{
if (itsHighlight == 0)
Go(wDown);
else
Go(wUp);
}
break;
}
case wDown:
{
if (itsHighlight >= MaxCurrentHighlight && itsHighlight < MaxHighlight)
{
itsBeginning++; // scroll
wscrl(itsWindow, 1);
}
if (itsHighlight == MaxHighlight)
break;
else
{
NeedsRedraw.push_back(itsHighlight++);
NeedsRedraw.push_back(itsHighlight);
}
if (is_static())
{
if (itsHighlight == MaxHighlight)
Go(wUp);
else
Go(wDown);
}
break;
}
case wPageUp:
{
itsHighlight -= itsHeight;
itsBeginning -= itsHeight;
if (itsBeginning < 0)
{
itsBeginning = 0;
if (itsHighlight < 0) itsHighlight = 0;
}
if (is_static())
{
if (itsHighlight == 0)
Go(wDown);
else
Go(wUp);
}
redraw_screen();
break;
}
case wPageDown:
{
itsHighlight += itsHeight;
itsBeginning += itsHeight;
if (itsBeginning > MaxBeginning)
{
itsBeginning = MaxBeginning;
if (itsHighlight > MaxHighlight) itsHighlight = MaxHighlight;
}
if (is_static())
{
if (itsHighlight == MaxHighlight)
Go(wUp);
else
Go(wDown);
}
redraw_screen();
break;
}
case wHome:
{
itsHighlight = 0;
itsBeginning = 0;
if (is_static())
{
if (itsHighlight == 0)
Go(wDown);
else
Go(wUp);
}
redraw_screen();
break;
}
case wEnd:
{
itsHighlight = MaxHighlight;
itsBeginning = MaxBeginning;
if (is_static())
{
if (itsHighlight == MaxHighlight)
Go(wUp);
else
Go(wDown);
}
redraw_screen();
break;
}
}
idlok(itsWindow, 0);
scrollok(itsWindow, 0);
}
template <class T>
void Menu<T>::Highlight(int which)
{
which--;
int old_highlight = itsHighlight;
int old_beginning = itsBeginning;
if (which < itsOptions.size())
itsHighlight = which;
else
return;
if (which >= itsHeight/2 && itsOptions.size() > itsHeight)
{
itsBeginning = itsHighlight-itsHeight/2;
if (itsBeginning > itsOptions.size()-itsHeight)
itsBeginning = itsOptions.size()-itsHeight;
}
else
itsBeginning = 0;
int howmuch = itsBeginning-old_beginning;
if (Abs(howmuch) > itsHeight)
redraw_screen();
else
{
idlok(itsWindow, 1);
scrollok(itsWindow, 1);
if (old_highlight >= itsBeginning && old_highlight < itsBeginning+itsHeight)
NeedsRedraw.push_back(old_highlight);
wscrl(itsWindow, howmuch);
if (howmuch < 0)
for (int i = 0; i < Abs(howmuch); i++)
NeedsRedraw.push_back(itsBeginning+i);
else
for (int i = 0; i < Abs(howmuch); i++)
NeedsRedraw.push_back(itsBeginning+itsHeight-1-i);
NeedsRedraw.push_back(itsHighlight);
scrollok(itsWindow, 0);
idlok(itsWindow, 0);
}
}
template <class T>
void Menu<T>::Reset()
{
NeedsRedraw.clear();
itsHighlight = 0;
itsBeginning = 0;
}
template <class T>
void Menu<T>::Clear(bool clear_screen)
{
for (T_iterator it = itsOptions.begin(); it != itsOptions.end(); it++)
delete *it;
itsOptions.clear();
NeedsRedraw.clear();
itsStaticsNumber = 0;
if (clear_screen)
Window::Clear();
}
template <class T>
void Menu<T>::Select(int option, bool selected)
{
option--;
try
{
if (itsOptions.at(option)->selected != selected)
NeedsRedraw.push_back(option);
itsOptions.at(option)->selected = selected;
}
catch (std::out_of_range)
{
}
}
template <class T>
bool Menu<T>::Selected(int option)
{
option--;
try
{
return itsOptions.at(option)->selected;
}
catch (std::out_of_range)
{
return false;
}
}
template <class T>
bool Menu<T>::IsAnySelected()
{
bool result = 0;
for (T_const_iterator it = itsOptions.begin(); it != itsOptions.end(); it++)
{
if ((*it)->selected)
{
result = 1;
break;
}
}
return result;
}
template <class T>
void Menu<T>::GetSelectedList(vector<int> &v)
{
int i = 1;
for (T_const_iterator it = itsOptions.begin(); it != itsOptions.end(); it++, i++)
if ((*it)->selected)
v.push_back(i);
}
template <class T>
int Menu<T>::GetRealChoice() const
{
int real_choice = 0;
T_const_iterator it = itsOptions.begin();
for (int i = 0; i < itsHighlight; it++, i++)
if (!(*it)->is_static) real_choice++;
return real_choice+1;
}
template <class T>
bool Menu<T>::IsStatic(int option)
{
try
{
return itsOptions.at(option-1)->is_static;
}
catch (std::out_of_range)
{
return 1;
}
}
template <class T>
Window * Menu<T>::EmptyClone()
{
return new Menu(GetStartX(),GetStartY(),GetWidth(),GetHeight(),itsTitle,itsBaseColor,itsBorder);
}
template <class T>
string Menu<T>::DisplayOption(const T &t) const
{
return itsItemDisplayer ? itsItemDisplayer(t, itsUserdata) : "";
}
#endif