|
need help with a console menu system
well I've been grinding on this and another project all day for three days straight and needless to say, I'm shot.
what I need to do is create a menu system in console, that draws in it's options using file input. the file layout is in the following format.
Title
# of Options
The names of the options, one per line
in the options section, a sub-menu is indicated by the following
MENU "OptionName" "filename.mnu"
an option with no functionality is listed in the format as follows
OPTION "OptionName"
The menu is required to be fullscreen, the options must be scrollable with the arrow keys, and the current option must be highlited in some way. In addition, an exit option must be added to the list of every menu. When exit is selected it must return to the previous menu via a singly linked list. Pressing ENTER on an option with no submenu is to yield no result.
if you can't make sense of what is happening in a specific part of my code, or are unsure of some of my classes please feel free to ask. I'm aware that I may have to start from scratch, but I'm completely out of ideas, and at the very least i need a gameplan for a new system if nothing here is salvageable.
#include <iostream>
using namespace std;
#include <fstream>
#include <string>
#include "SLList.h"
#include "DynArray.h"
#include <conio.h>
#include <stdio.h>
#include <vector>
#include "Console.h"
#define UP 0x48
#define DOWN 0x50
#define ENTER 13
#define ESC 27
struct menu
{
string filename;
string title;
int numOptions;
bool subMenu;
menu() {};
void menuFill(string fileName, DynArray<menu>& menuList)
{
if(menuList.capacity() > 0)
{
menuList.clear();
}
string temp;
string temp2;
string temp3;
menu a;
a.filename = fileName;
if(a.filename[0] == '\"')
{
a.filename.erase(a.filename.begin());
a.filename.erase(--a.filename.end());
}
ifstream fin(a.filename.c_str());
getline(fin, a.title);
getline(fin, temp);
a.numOptions = atoi(temp.c_str());
for(int i = 0; i < a.numOptions; i++)
{
fin >> temp2;
if(temp2 == "MENU")
{
a.subMenu = 1;
fin >> a.title;
while(a.title[(int)(a.title.length()) - 1] != '\"')
{
fin >> temp3;
a.title.push_back(' ');
a.title += temp3;
}
if(a.title[0] == ' ')
{
a.title.erase(a.title.begin());
}
if(a.title[0] == '\"')
{
a.title.erase(a.title.begin());
a.title.erase(--a.title.end());
}
fin >> a.filename;
if(a.filename[0] == '\"')
{
a.filename.erase(a.filename.begin());
a.filename.erase(--a.filename.end());
}
menuList.append(a);
}
else if(temp2 == "OPTION")
{
a.subMenu = 0;
getline(fin, a.title);
if(a.title[0] == ' ')
{
a.title.erase(a.title.begin());
}
if(a.title[0] == '\"')
{
a.title.erase(a.title.begin());
a.title.erase(--a.title.end());
}
menuList.append(a);
}
}
fin.close();
}
void show(DynArray<menu>& menuList)
{
for(int i = 0; i < menuList[0].numOptions; i++)
cout << menuList[i].title << '\n';
}
menu& operator=(const menu& m)
{
if(this == &m)
return *this;
filename = m.filename;
numOptions = m.numOptions;
title = m.title;
subMenu = m.subMenu;
return *this;
}
menu(menu& m)
{
filename = m.filename;
numOptions = m.numOptions;
title = m.title;
subMenu = m.subMenu;
}
};
int menuDraw(DynArray<menu>& list, SLList<menu>& sList, SLLIter<menu>& iter);
int main(void)
{
DynArray<menu> menuList;
DynArray<string> menuItems;
SLList<menu> menuSLL;
SLLIter<menu> menuIter(menuSLL);
menu mainMenu;
mainMenu.menuFill("main.mnu", menuList);
mainMenu.show(menuList);
int menuChoice = 0;
menuChoice = menuDraw(menuList, menuSLL, menuIter);
menuSLL.addHead(mainMenu);
cout << menuChoice << '\n';
while(menuChoice != menuList.size() && menuList[menuChoice].subMenu)
{
menu otherMenu;
otherMenu.menuFill(menuList[menuChoice].filename.c_str(), menuList);
otherMenu.show(menuList);
menuChoice = menuDraw(menuList, menuSLL, menuIter);
}
return 0;
}
int menuDraw(DynArray<menu>& list, SLList<menu>& sList, SLLIter<menu>& iter)
{
int choice = 0;
char charVal = '*';
while(true)
{
if (_kbhit ())
{
// show stuff for each specific move
if(FS::con.keyPressed(VK_UP))
{
if(choice > 0)
choice--;
}
else if(FS::con.keyPressed(VK_DOWN))
{
if(choice < list.size())
choice++;
}
else if(FS::con.keyPressed(VK_RETURN))
{
if(choice == list.size())
{
menu sbMnu;
sList.addHead(sbMnu);
list.clear();
list.append(iter.current());
list[0].show(list);
sbMnu.menuFill(list[0].filename.c_str(), list);
sbMnu.show(list);
choice = menuDraw(list, sList, iter);*/
}
else if(choice == list.size() and sList.head == NULL)
return -1;
else if(list[choice].subMenu)
return choice;
}
}
}
}
DynArray.h
#ifndef DYNARRAY_H_
#define DYNARRAY_H_
template <typename Type>
class DynArray
{
private:
int arrSize;
int cap;
int index;
Type* arr;
public:
explicit DynArray(int initialSizeAndCap = 0) {arrSize = initialSizeAndCap; cap = initialSizeAndCap; arr = new Type[initialSizeAndCap];}
virtual ~DynArray() {clear();}
Type& operator[](int index) {return arr[index];}
int size() {return arrSize;}
void append(Type& item)
{
if(size() == 0)
{
Type* temp = new Type[1];
arr = temp;
cap = 1;
arrSize = 0;
arr[arrSize] = item;
arrSize++;
}
else if(size() < cap)
{
arr[arrSize] = item;
arrSize++;
}
else
{
Type* temp = new Type[2 * arrSize];
for(int i = 0; i < arrSize; i++)
temp[i] = arr[i];
delete [] arr;
arr = temp;
cap = arrSize * 2;
arr[arrSize] = item;
arrSize++;
}
}
int capacity() {return cap;}
void clear(){arrSize = 0; cap = 0; delete [] arr;}
DynArray& operator=(DynArray& d)
{
if(this == &d)
return *this;
this->arrSize = d.arrSize;
this->cap = d.cap;
this->index = d.index;
for(int i = 0; i < d.arrSize; i++)
this->append((d[i]));
return *this;
}
DynArray(DynArray& d)
{
this->arrSize = d.arrSize;
this->cap = d.cap;
this->index = d.index;
for(int i = 0; i < d.arrSize; i++)
this->append(d[i]);
}
};
#endif
SLList.h
template<typename Type>
class SLLIter;
template<typename Type>
class SLList
{
friend SLLIter<Type>;
private:
struct Node
{
Type element;
Node* next;
};
Node* head;
public:
// SLList : constructor.
SLList() {head = 0;}
// ~SLList : destructor.
virtual ~SLList() {};
// operator = : assignment operator.
// NOTE : invalidates all iterators to this list.
SLList<Type> &operator=(const SLList<Type> &list)
{
clear();
if(!list.head)
return *this;
else
recursive(list.head);
return *this;
}
// addHead : append a new item to the front of the list.
void addHead(const Type &v)
{
Node* temp = new Node;
temp->element = v;
//if(head)
//{
temp->next = head;
head = temp;
//}
//else
//{
// temp->next = 0;
// head = temp;
//}
}
// clear : clear out the list
void clear()
{
while(head)
{
Node* temp = head;
head = head->next;
delete temp;
}
}
// insert : place a value before the given iterator
// location. Don’t insert at a bad location.
void insert(SLLIter<Type> &index, const Type &v)
{
Node* temp = new Node;
temp->element = v;
temp->next = index.location;
if(index.previous)
index.previous->next = temp;
else
head = temp;
index.location = temp;
/*if(!head)
{
addHead(v);
index.location = head;
}
else
{
Node* temp = new Node;
temp->element = v;
index.previous->next = temp;
temp->next = index.location;
index.location = temp;
}*/
}
// remove : remove the value at the given iterator
// location. Don’t remove at a bad location.
void remove(SLLIter<Type> &index)
{
Node* temp = index.location;
if(index.previous)
index.previous->next = temp->next;
else
head = temp->next;
index.location = temp->next;
delete temp;
/*if(head == 0)
{
clear();
return;
}
if(index.location == head)
{
Node* temp = head;
head = head->next;
delete temp;
index.location = head;
}
else
{
Node* temp = index.location;
index.previous->next = index.location->next;
index.location = index.location->next;
delete temp;
}*/
}
void recursive(Node* n)
{
if(n == NULL)
return;
recursive(n->next);
addHead(n->element);
}
};
template<typename Type>
class SLLIter
{
friend SLList<Type>;
private:
SLList<Type> *list;
typename SLList<Type>::Node* previous;
typename SLList<Type>::Node* location;
public:
//template<typename Type>
// SLLIter : constructor.
SLLIter(SLList<Type> &listToIterate)
{
list = &listToIterate;
previous = 0;
location = list->head;
}
// ~SLLIter : destructor.
virtual ~SLLIter () {};
// begin : move the iterator to the front of the list
void begin ()
{ location = list->head;
previous = 0;
}
// end : only return true when iterator is beyond the last // item
bool end ()
{
if(location == NULL)
return true;
else
return false;
}
//template<typename Type>
// operator++ : prefix (only) ++ operator
SLLIter<Type> &operator++ ()
{
previous = location;
location = location->next;
return *this;
}
//template<typename Type>
// current : extract the element at the current location.
Type ¤t ()
{
return location->element;
}
};
Console.h
// Console.h : Add support for some interesting console tricks.
#ifndef CONSOLE_H_
#define CONSOLE_H_
// Link the appropriate Console library.
#ifdef _DEBUG
#pragma comment(lib, "ConsoleD")
#else
#pragma comment(lib, "ConsoleR")
#endif
// Don't bring in quite so much stuff.
#define WIN32_LEAN_AND_MEAN
// The following is so we can use "GetConsoleWindow()".
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <ostream>
#include <stdexcept>
using std::out_of_range;
#include <exception>
using std::bad_exception;
#include <sstream>
#include <string>
#include <iomanip>
// One day exception specifications will be fully supported...
#pragma warning(disable:4290)
// Just for Full Sail.
namespace FS
{
// A character attribute. Color primarily but see the CHAR_INFO Help for more options.
typedef WORD Attr;
// Handy built-in colors for all to use. Specifying BLACKOUT makes both the fore- and back-ground colors black.
extern const Attr DEF, BLACKOUT, DARKRED, DARKGREEN, DARKBLUE, DARKYELLOW, DARKMAGENTA, DARKCYAN,
LIGHTGRAY, DARKGRAY, RED, GREEN, BLUE, YELLOW, MAGENTA, CYAN, WHITE,
BGDARKRED, BGDARKGREEN, BGDARKBLUE, BGDARKYELLOW, BGDARKMAGENTA, BGDARKCYAN,
BGLIGHTGRAY, BGDARKGRAY, BGRED, BGGREEN, BGBLUE, BGYELLOW, BGMAGENTA, BGCYAN, BGWHITE;
// The console window.
typedef std::ostream Output;
typedef Output & (*OutputPF)(Output &); // Function pointers are our friends.
class Console : public Output
{
public:
// A 2D picture, comfortably nested.
class Image
{
// What to display.
CHAR_INFO *chiBuffer;
// How tall and wide it is.
int rows, cols;
// Make us nothingness.
void clear()
{
this->chiBuffer = 0;
this->rows = this->cols = 0;
}
// Give back our RAM.
void free()
{
delete [] this->chiBuffer;
}
// Efficiently procure space for our dynamic memory.
//
// In: cols The number of columns we'll have.
// rows The number of rows we'll have.
void reallocate(int cols, int rows);
// Deep duplicate a CHAR_INFO array.
//
// In: cols The number of columns we'll have.
// rows The number of rows we'll have.
// ci A CHAR_INFO for each cell (cols * rows).
void DeepCopy(int cols, int rows, CHAR_INFO const ci[]);
public:
// Create a 2D picture.
//
// In: img An array of strings containing the characters to display.
// rows The height of "img" (1+). The width is assumed to be the length of the first string and should be the same for all the strings, i.e., the picture
// is rectangular.
// attr Character attributes to use for every cell. Color primarily but see the CHAR_INFO Help for more options.
Image(const wchar_t * const img[] = 0, int rows = 0, Attr attr = 0);
// Create a 2D picture.
//
// In: cols How many columns each row has (1+).
// rows How many rows it has (1+).
// ci A CHAR_INFO for each cell (rows * cols).
Image(int cols, int rows, CHAR_INFO const ci[]);
// Create a 2D picture.
//
// In: fileName Where to get our information from.
Image(std::string const &fileName) throw(ios_base::failure &, bad_exception &);
// Create a 2D picture.
//
// In: cols How many columns each row has (1+).
// rows How many rows it has (1+).
//
// Note: The image's cells are not initialized.
Image(int cols, int rows) : cols(cols < 1 ? 1 : cols),
rows(max(rows, 1))
{
this->chiBuffer = new CHAR_INFO[this->rows * this->cols];
}
// Copy constructor.
//
// In: i An existing one to mimic.
Image(const Image &i);
// Assignment operator.
//
// In: i An existing one to emulate.
Image & operator =(const Image &i);
// Destructor.
virtual ~Image() { this->free(); }
// Return: Our buffer, read-only.
const CHAR_INFO *getChiBuffer() const { return this->chiBuffer; }
// Return: Our height (1+).
int getRows() const { return this->rows; }
// Return: Our width (1+).
int getCols() const { return this->cols; }
// Define a 2D picture.
//
// In: img An array of strings containing the characters to display.
// rows The height of "img" (1+). The width is assumed to be the length of the first string and should be the same for all the strings, i.e., the picture
// is rectangular.
// attr Character attributes to use for every cell. Color primarily but see the CHAR_INFO Help for more options.
void set(const wchar_t * const img[], int rows, Attr attr);
// Create a 2D picture.
//
// In: cols How many columns each row has (1+).
// rows How many rows it has (1+).
// ci A CHAR_INFO for each cell (rows * cols).
void set(int cols, int rows, CHAR_INFO const ci[]);
// Define a 2D picture.
//
// In: i An existing one to clone.
void set(const Image &i);
// Return: true if we can be successfully displayed.
bool good() const
{
return this->chiBuffer && this->rows > 0 && this->cols > 0;
}
// Get access to one of our rows.
//
// In: i The index (0 -=> rows - 1) of the one to retrieve.
//
// Return: The address of the first CHAR_INFO in the row - use another [] to
// access a specific column.
CHAR_INFO * operator [](int i) throw(out_of_range &, bad_exception &);
// Read-only version for constant objects.
CHAR_INFO const * operator [](int i) const throw(out_of_range &, bad_exception &);
// Store an Image to a file.
//
// In: fileName Where to send our information to.
void Save(std::string const &fileName) const throw(ios_base::failure &, bad_exception &);
// Retrieve an Image from a file.
//
// In: fileName Where to get our information from.
void Load(std::string const &fileName) throw(ios_base::failure &, bad_exception &);
};
private:
// Where our output goes.
HANDLE out;
// Where our input comes from.
HANDLE in;
// The visible screen buffer.
HANDLE vis;
// DirectDraw, for WaitForVerticalBlank().
HINSTANCE ddrawLib;
// Secretly an IDirectDraw7 *, for WaitForVerticalBlank().
void *DDobject;
// Do we bother to WaitForVerticalBlank()?
bool w4VB;
// How many columns our console window has (0+).
int cols;
// How many lines our console window has (0+).
int rows;
// Our current background (high nibble) and foreground (low nibble) colors & more.
Attr attrs;
// Our Windows handle.
HWND hwnd;
// The coords of any little scrollable window area within the screen.
int wleft, wtop, wright, wbottom;
// Are we back-buffered?
bool bb;
// Default constructor.
Console();
// Return: true if the standard output screen buffer is currently visible.
bool StdIsVis() const;
// Perform a "safe" newline, i.e., one which preserves our colors.
void nl(void) const;
// Print something to the output.
//
// In: ch The character to show.
template <typename Type>
void put(Type ch) const
{
if ('\n' == ch)
// Color safe!
nl();
else if ('\t' == ch)
{
// Thinner tabs.
const int Width = 4;
int x, y;
CP(x, y);
int pos = (x / Width + 1) * Width;
if (pos >= cols)
// Wrap.
nl();
else
// Jump.
Goto(y, pos);
}
else
{
// Nuthin' special.
wchar_t c = ch;
DWORD dummy;
WriteConsole(out, &c, 1, &dummy, NULL);
}
}
// Make sure a box's dimensions make sense.
//
// I/O: left The left coordinate of the area (l -> r, <= right).
// top The top coordinate of the area (t -> b, <= bottom).
// right The right coordinate of the area (l -> r, >= left).
// bottom The bottom coordinate of the area (t -> b, >= top).
//
// In: l, t, r, b Acceptable left, top, right and bottom coordinates.
void BoxCheck(int &left, int &top, int &right, int &bottom, int l, int t, int r, int b) const;
void BoxCheck(int &left, int &top, int &right, int &bottom) const
{
BoxCheck(left, top, right, bottom, 0, 0, cols - 1, rows - 1);
}
// Format and display something as a string.
//
// In: v The value to use the insertion operator with.
template <typename T>
void T2String(const T &v)
{
std::wostringstream oss;
oss.flags(flags());
oss.width(width());
oss.fill(fill());
oss.precision(precision());
oss << v;
std::wstring const &s = oss.str();
if (std::wstring::npos == s.find_first_of(L"\n\t"))
// It's clear - dump it quick.
putRawString(s);
else
{
// Darn, special formatting.
for (std::wstring::const_iterator i = s.begin(); i != s.end(); i++)
put(*i);
}
width(0); // Reset.
}
// What are the dimensions of our client area, in pixels?
//
// Out: width How wide we are.
// height How tall we are.
//
// Return: true if we're windowed, false if full screen.
bool getClientDim(int &width, int &height) const;
public:
// Singletons are our friends!
//
// Return: The only object.
static Console &GetTheOnlyInstance()
{
// There can be only one.
static Console c;
return c;
}
// Destructor.
virtual ~Console(void);
// Return: How many columns our console window has (0+).
int Cols(void) const { return cols; }
// Return: How many lines our console window has (0+).
int Rows(void) const { return rows; }
// Clear the entire screen or just our limited window. This leaves the cursor in the
// upper-left corner o' whatever area it just wiped.
//
// In: window true to limit ourselves to our box.
void Clear(bool window = true) const;
// Clear a line. The cursor doesn't move.
//
// In: r The row to wipe (0 -> "Rows()" - 1).
// window true to limit ourselves to our box.
void Clear(int r, bool window = true) const;
// Return: true if the cursor is visible right now or false if it's not.
bool CursorIsOn(void) const;
// Turn on/off the blinkin' cursor.
//
// In: visible true to make it show or false to hide it.
void CursorOn(bool visible) const;
// Find where the cursor is now.
//
// Out: x The horizontal location (0 -> "Cols()" - 1).
// y The vertical location (0 -> "Rows()" - 1).
void CP(int &x, int &y) const;
// Find what client pixel the mouse is hovering over right now, with (0, 0) being the upper-left corner.
//
// Out: x The horizontal location.
// y The vertical location.
//
// Return: false if the mouse is outside our window.
bool MPAbs(int &x, int &y) const;
// Find where the mouse is now.
//
// Out: x The column (0 -> "Cols()" - 1, usually).
// y The row (0 -> "Rows()" - 1, usually).
//
// Return: false if the mouse is outside our window ("x" and "y" should be used for relative, not absolute, positioning).
bool MP(int &x, int &y) const;
// Place the cursor at a specific spot on the screen.
//
// In: y The vertical location (0 -> "Rows()" - 1).
// x The horizontal location (0 -> "Cols()" - 1).
void Goto(int y = 0, int x = 0) const;
// Turn End-o'-Line (EOL) wrapping on or off. This can be used to keep the display from
// moving up a line when a character is written in the lower-right hand corner of the
// window.
//
// In: on true to enable scrolling (the default) or false to disable it.
//
// Note: Turn this back ON before getting any input!!
void EOLWrap(bool on = true) const;
// See if a given key is being pressed or not.
//
// In: vKey Specifies one of 256 possible virtual-key codes. For more information, see Virtual-Key Codes in the Help.
//
// Return: true if that key is down right now.
bool keyDown(int vKey) const;
// See if a given key has just been pressed.
//
// In: vKey Specifies one of 256 possible virtual-key codes. For more information, see Virtual-Key Codes in the Help.
//
// Return: true if that key's been pressed since last we checked. This may not always work properly - see the Help on GetAsyncKeyState.
bool keyPressed(int vKey) const;
// Consume everything in our input buffer.
void flushKeys() const;
// Change our window's title.
//
// In: s The text to display.
void Title(const wchar_t *s) const;
// Copy a section of the window somewhere else.
//
// In: sleft The left coordinate of the area to scroll (0 -> "Cols()" - 1).
// stop The top coordinate of the area to scroll (0 -> "Rows()" - 1).
// sright The right coordinate of the area to scroll (0 -> "Cols()" - 1).
// sbottom The bottom coordinate of the area to scroll (0 -> "Rows()" - 1).
// dx The left coordinate of the destination (0 -> "Cols()" - 1).
// dy The top coordinate of the destination (0 -> "Rows()" - 1).
void Scroll(int sleft, int stop, int sright, int sbottom, int dx, int dy) const;
void Scroll(int sleft, int stop) const
{
Scroll(sleft, stop, cols - 1, rows - 1, 0, 0);
}
// Adjust the window dimensions.
//
// In: width The desired number o' columns (1+, 0 for max possible).
// height The desired number o' rows (1+, 0 for max possible).
// pos true to maximize and center the window.
// fs true to go full screen.
//
// Note: You may not get the size you want and this resets any internal window to the full
// screen.
void Size(int width = 80, int height = 25, bool pos = true, bool fs = false);
// Create a "separate" box within our screen.
//
// In: left The left coordinate of the area (0 -> "Cols()" - 1).
// top The top coordinate of the area (0 -> "Rows()" - 1).
// right The right coordinate of the area (0 -> "Cols()" - 1).
// bottom The bottom coordinate of the area (0 -> "Rows()" - 1).
//
// Note: Letting lines wrap without explicit newlines and/or getting input with cin can
// mess the window up!
void Window(int left, int top, int right, int bottom);
void Window()
{
Window(0, 0, cols - 1, rows - 1);
}
// Return: Our current fore/background colors & more.
Attr getAttr() const { return this->attrs; }
// Turn on/off the mouse cursor.
//
// In: visible true to make it show or false to hide it.
//
// Note: This only works in full screen mode and then only to ditch the cursor...
void MouseOn(bool visible) const;
// Return: true if we are currently back-buffering.
bool getBB() const { return this->bb; }
// Change whether or not we're back-buffering the screen.
//
// In: bb true if we should back-buffer to reduce flicker or false to skip it and save memory instead.
void setBB(bool bb);
// Flip the back-buffer, if we've got one, so we see it on the screen.
void Flip();
// Display an Image.
//
// In: i The picture to print.
// x The column for its upper-left cell.
// y The row for its upper-left cell.
//
// Note: No error checking is done on the coordinates!
void show(const Image &i, int x, int y) const;
// Return: Do we bother to WaitForVerticalBlank()?
bool getW4VB() const { return this->w4VB; }
// Alter whether or not we delay for WaitForVerticalBlank().
//
// In: w4VB true to make windowed mode smoother but slower.
void setW4VB(bool w4VB) { this->w4VB = w4VB; }
// Change the output's default character attributes.
//
// In: l The stream to alter.
// a The new values to use.
//
// Return: The stream "l".
Output & setAttr(Output &l, const Attr &a);
// Display a string without looking for any special formatting characters.
//
// In: s The text to show.
void putRawString(const std::wstring &s) const;
// All the carefully-watched forms o' output.
//
// In: ??? What to show.
//
// Return: Us.
#define INSERTION(pt) Console & operator <<(pt);
INSERTION(char)
INSERTION(unsigned char)
INSERTION(signed char)
INSERTION(wchar_t)
INSERTION(const char *);
INSERTION(const unsigned char *)
INSERTION(const signed char *)
INSERTION(const wchar_t *)
INSERTION(short)
INSERTION(unsigned short)
INSERTION(int)
INSERTION(unsigned int)
INSERTION(long)
INSERTION(unsigned long)
INSERTION(long long)
INSERTION(unsigned long long)
INSERTION(float)
INSERTION(double)
INSERTION(long double)
INSERTION(const void *)
INSERTION(const std::string &)
INSERTION(const std::wstring &)
INSERTION(OutputPF const &)
INSERTION(std::ios_base & (* const)(std::ios_base &));
template<class _Elem> inline
Console & operator<<(const std::_Fillobj<_Elem>& _Manip)
{ // set fill character in output stream
this->fill(_Manip._Fill);
return (*this);
}
template<class _Arg> inline
Console & operator <<(const std::_Smanip<_Arg>& _Manip)
{ // insert by calling function with output stream and argument
(*_Manip._Pfun)(*this, _Manip._Manarg);
return (*this);
}
};
// The one-and-only!
extern Console &con;
// Handy "manipulators".
#define COLOR(fn, attr) inline Output & fn(Output &o) { return con.setAttr(o, attr); }
COLOR(Def, DEF)
COLOR(Blackout, BLACKOUT)
COLOR(DarkRed, DARKRED)
COLOR(DarkGreen, DARKGREEN)
COLOR(DarkBlue, DARKBLUE)
COLOR(DarkYellow, DARKYELLOW)
COLOR(DarkMagenta, DARKMAGENTA)
COLOR(DarkCyan, DARKCYAN)
COLOR(LightGray, LIGHTGRAY)
COLOR(DarkGray, DARKGRAY)
COLOR(Red, RED)
COLOR(Green, GREEN)
COLOR(Blue, BLUE)
COLOR(Yellow, YELLOW)
COLOR(Magenta, MAGENTA)
COLOR(Cyan, CYAN)
COLOR(White, WHITE)
COLOR(BGDarkRed, BGDARKRED)
COLOR(BGDarkGreen, BGDARKGREEN)
COLOR(BGDarkBlue, BGDARKBLUE)
COLOR(BGDarkYellow, BGDARKYELLOW)
COLOR(BGDarkMagenta, BGDARKMAGENTA)
COLOR(BGDarkCyan, BGDARKCYAN)
COLOR(BGLightGray, BGLIGHTGRAY)
COLOR(BGDarkGray, BGDARKGRAY)
COLOR(BGRed, BGRED)
COLOR(BGGreen, BGGREEN)
COLOR(BGBlue, BGBLUE)
COLOR(BGYellow, BGYELLOW)
COLOR(BGMagenta, BGMAGENTA)
COLOR(BGCyan, BGCYAN)
COLOR(BGWhite, BGWHITE)
} // namespace FS
#endif
console.cpp
// Console.cpp : Add support for some interesting console tricks.
#include <iostream>
#include <fstream>
#include "Console.h"
// For WaitForVerticalBlank().
#define INITGUID
#include <ddraw.h>
#define DDOBJECT reinterpret_cast<IDirectDraw7 *>(DDobject)
namespace FS
{
// Handy built-in colors for all to use.
const Attr
DEF = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
BLACKOUT = 0,
DARKRED = FOREGROUND_RED,
DARKGREEN = FOREGROUND_GREEN,
DARKBLUE = FOREGROUND_BLUE,
DARKYELLOW = FOREGROUND_RED | FOREGROUND_GREEN,
DARKMAGENTA = FOREGROUND_RED | FOREGROUND_BLUE,
DARKCYAN = FOREGROUND_GREEN | FOREGROUND_BLUE,
LIGHTGRAY = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
DARKGRAY = FOREGROUND_INTENSITY,
RED = FOREGROUND_RED | FOREGROUND_INTENSITY,
GREEN = FOREGROUND_GREEN | FOREGROUND_INTENSITY,
BLUE = FOREGROUND_BLUE | FOREGROUND_INTENSITY,
YELLOW = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY,
MAGENTA = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
BGDARKRED = BACKGROUND_RED,
BGDARKGREEN = BACKGROUND_GREEN,
BGDARKBLUE = BACKGROUND_BLUE,
BGDARKYELLOW = BACKGROUND_RED | BACKGROUND_GREEN,
BGDARKMAGENTA = BACKGROUND_RED | BACKGROUND_BLUE,
BGDARKCYAN = BACKGROUND_GREEN | BACKGROUND_BLUE,
BGLIGHTGRAY = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE,
BGDARKGRAY = BACKGROUND_INTENSITY,
BGRED = BACKGROUND_RED | BACKGROUND_INTENSITY,
BGGREEN = BACKGROUND_GREEN | BACKGROUND_INTENSITY,
BGBLUE = BACKGROUND_BLUE | BACKGROUND_INTENSITY,
BGYELLOW = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_INTENSITY,
BGMAGENTA = BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY,
BGCYAN = BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY,
BGWHITE = BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY;
/***********/
/* Globals */
/***********/
// The one-and-only!
Console &con = Console::GetTheOnlyInstance();
/***********/
/* Private */
/***********/
// Default constructor.
Console::Console() : Output(std::cout.rdbuf(), true)
{
// Where our standard output goes.
out = GetStdHandle(STD_OUTPUT_HANDLE);
vis = out; // You da man.
// Where our standard input comes from.
in = GetStdHandle(STD_INPUT_HANDLE);
// Use the current dimensions.
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(out, &info);
cols = info.dwSize.X;
rows = info.dwSize.Y;
Window();
// Get what the attributes are.
attrs = info.wAttributes;
// Only one screen for now.
bb = false;
// Who are we?
hwnd = GetConsoleWindow();
// Stay smooth even in windowed mode.
WCHAR wszPath[MAX_PATH] = { 0 };
::GetSystemDirectory(wszPath, MAX_PATH);
std::wstring path(wszPath);
path += L"\\ddraw.dll";
ddrawLib = LoadLibraryW(path.c_str());
DDobject = 0; // In case we fail.
if (ddrawLib)
{
// Nasty-lookin' pointer-to-function:
typedef HRESULT (WINAPI *pf)(GUID *, void **, REFIID, IUnknown *);
pf _DirectDrawCreateEx = reinterpret_cast<pf>(GetProcAddress(ddrawLib, "DirectDrawCreateEx"));
if (_DirectDrawCreateEx)
{
if (SUCCEEDED(_DirectDrawCreateEx(NULL, reinterpret_cast<void **>(&DDobject), IID_IDirectDraw7, NULL)))
// Not sure if this is necessary but what the heck.
DDOBJECT->SetCooperativeLevel(NULL, DDSCL_NORMAL);
}
}
w4VB = true;
}
// Return: true if the standard output screen buffer is currently visible.
bool Console::StdIsVis() const
{
return GetStdHandle(STD_OUTPUT_HANDLE) == vis;
}
// Perform a "safe" newline, i.e., one which preserves our colors.
void Console::nl(void) const
{
// Where are we?
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(out, &info);
// Go to the start of the next row.
++info.dwCursorPosition.Y;
info.dwCursorPosition.X = (info.dwCursorPosition.Y >= wtop && info.dwCursorPosition.Y <= wbottom + 1)
? wleft : 0;
if (info.dwCursorPosition.Y == wbottom + 1 || info.dwCursorPosition.Y == rows)
{
// Stay on the last line and shift everything up one.
if (--info.dwCursorPosition.Y == wbottom)
// Just our box.
Scroll(wleft, wtop + 1, wright, wbottom, wleft, wtop);
else
// The entire window.
Scroll(0, 1);
}
SetConsoleCursorPosition(out, info.dwCursorPosition);
}
// Make sure a box's dimensions make sense.
//
// I/O: left The left coordinate of the area (l -> r, <= right).
// top The top coordinate of the area (t -> b, <= bottom).
// right The right coordinate of the area (l -> r, >= left).
// bottom The bottom coordinate of the area (t -> b, >= top).
//
// In: l, t, r, b Acceptable left, top, right and bottom coordinates.
void Console::BoxCheck(int &left, int &top, int &right, int &bottom,
int l, int t, int r, int b) const
{
// Bounds-check...
if (left < l) left = l;
if (top < t) top = t;
if (right > r) right = r;
if (bottom > b) bottom = b;
// Extra paranoia.
if (right < left) right = left;
if (bottom < top) bottom = top;
}
// What are the dimensions of our client area, in pixels?
//
// Out: width How wide we are.
// height How tall we are.
//
// Return: true if we're windowed, false if full screen.
bool Console::getClientDim(int &width, int &height) const
{
RECT c;
GetClientRect(hwnd, &c);
bool fullscreen = 0 == c.bottom && 0 == c.left && 0 == c.right && 0 == c.top; // Probably imperfect check for full screen.
if (fullscreen)
{
// What's the current rez?
c.right = GetSystemMetrics(SM_CXSCREEN);
c.bottom = GetSystemMetrics(SM_CYSCREEN);
}
width = c.right;
height = c.bottom;
return !fullscreen;
}
/**********/
/* Public */
/**********/
// Destructor.
Console::~Console(void)
{
// Free the extra buffer if we've got it.
this->setBB(false);
// DirectX.
if (DDobject)
DDOBJECT->Release();
if (ddrawLib)
FreeLibrary(ddrawLib);
}
// Clear the entire screen or just our limited window. This leaves the cursor in the
// upper-left corner o' whatever area it just wiped.
//
// In: window true to limit ourselves to our box.
void Console::Clear(bool window) const
{
// What's the extent?
int t = (!window) ? 0 : wtop,
b = (!window) ? rows : wbottom + 1;
// Do the selected rows.
for (int r = t; r < b; r++)
Clear(r, window);
// Where do we go from here?
if (window)
Goto(wtop, wleft);
else
Goto();
}
// Clear a line. The cursor doesn't move.
//
// In: r The row to wipe (0 -> "Rows()" - 1).
// window true to limit ourselves to our box.
void Console::Clear(int r, bool window) const
{
if (r < 0 || r >= rows)
// Nope.
return;
// Buh-bye.
DWORD len = (!window) ? cols : wright - wleft + 1, num;
COORD c = { (window && r >= wtop && r <= wbottom) ? wleft : 0, r, };
FillConsoleOutputAttribute(out, attrs, len, c, &num);
FillConsoleOutputCharacter(out, ' ', len, c, &num);
}
// Return: true if the cursor is visible right now or false if it's not.
bool Console::CursorIsOn(void) const
{
CONSOLE_CURSOR_INFO curs;
GetConsoleCursorInfo(out, &curs);
return ((curs.bVisible == TRUE) ? true : false);
}
// Turn on/off the blinkin' cursor.
//
// In: visible true to make it show or false to hide it.
void Console::CursorOn(bool visible) const
{
CONSOLE_CURSOR_INFO curs;
GetConsoleCursorInfo(out, &curs);
if (curs.bVisible == TRUE && visible || curs.bVisible == FALSE && !visible)
// We're already the way they want it.
return;
// Change it.
curs.bVisible = (visible) ? TRUE : FALSE;
SetConsoleCursorInfo(out, &curs);
}
// Find where the cursor is now.
//
// Out: x The horizontal location (0 -> "Cols()" - 1).
// y The vertical location (0 -> "Rows()" - 1).
void Console::CP(int &x, int &y) const
{
// Where are you?
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(out, &info);
x = info.dwCursorPosition.X;
y = info.dwCursorPosition.Y;
}
// Find what client pixel the mouse is hovering over right now, with (0, 0) being the upper-left corner.
//
// Out: x The horizontal location.
// y The vertical location.
//
// Return: false if the mouse is outside our window.
bool Console::MPAbs(int &x, int &y) const
{
// Where is it in screen coords?
POINT p;
GetCursorPos(&p);
// Translate to our window, if needed.
int width, height;
bool fullscreen = !getClientDim(width, height);
if (!fullscreen)
ScreenToClient(hwnd, &p);
// Here ya' go:
x = p.x; y = p.y;
return fullscreen ? true : !(x < 0 || y < 0 || x >= width || y >= height);
}
// Find where the mouse is now.
//
// Out: x The column (0 -> "Cols()" - 1, usually).
// y The row (0 -> "Rows()" - 1, usually).
//
// Return: false if the mouse is outside our window ("x" and "y" should be used for relative, not absolute, positioning).
bool Console::MP(int &x, int &y) const
{
// Where are we in relation to our client area?
bool inside = MPAbs(x, y);
// Map that to a row and column based on our dimensions.
int width, height;
getClientDim(width, height);
x = static_cast<int>(x / static_cast<double>(width) * cols);
y = static_cast<int>(y / static_cast<double>(height) * rows);
// The final summary:
return inside;
}
// Place the cursor at a specific spot on the screen.
//
// In: y The vertical location (0 -> "Rows()" - 1).
// x The horizontal location (0 -> "Cols()" - 1).
void Console::Goto(int y, int x) const
{
// Keep within acceptable bounds.
x = max(x, 0);
x = min(x, cols - 1);
y = max(y, 0);
y = min(y, rows - 1);
// Make it so.
COORD cp = { x, y, };
SetConsoleCursorPosition(out, cp);
}
// Turn End-o'-Line (EOL) wrapping on or off. This can be used to keep the display from moving
// up a line when a character is written in the lower-right hand corner of the window.
//
// In: on true to enable scrolling (the default) or false to disable it.
//
// Note: Turn this back ON before getting any input!!
void Console::EOLWrap(bool on) const
{
// How are we now?
DWORD mode;
GetConsoleMode(out, &mode);
if (on)
// Turn it on.
mode |= ENABLE_WRAP_AT_EOL_OUTPUT;
else
// Turn it off.
mode &= ~ENABLE_WRAP_AT_EOL_OUTPUT;
SetConsoleMode(out, mode);
}
// See if a given key is being pressed or not.
//
// In: vKey Specifies one of 256 possible virtual-key codes. For more information, see Virtual-Key Codes in the Help.
//
// Return: true if that key is down right now.
bool Console::keyDown(int vKey) const
{
bool down = 0 != (GetAsyncKeyState(vKey) & 0x8000); // Test that high bit.
FlushConsoleInputBuffer(in); // Don't let cin see it!
return down;
}
// See if a given key has just been pressed.
//
// In: vKey Specifies one of 256 possible virtual-key codes. For more information, see Virtual-Key Codes in the Help.
//
// Return: true if that key's been pressed since last we checked. This may not always work properly - see the Help on GetAsyncKeyState.
bool Console::keyPressed(int vKey) const
{
bool down = 0 != (GetAsyncKeyState(vKey) & 0x0001); // Test that low bit.
FlushConsoleInputBuffer(in); // Don't let cin see it!
return down;
}
// Consume everything in our input buffer.
void Console::flushKeys() const
{
// Check 'em all.
for (int i = 0; i < 256; i++)
GetAsyncKeyState(i);
FlushConsoleInputBuffer(in); // Don't let cin see 'em!
}
// Change our window's title.
//
// In: s The text to display.
void Console::Title(const wchar_t *s) const
{
SetConsoleTitle(s);
}
// Copy a section of the window somewhere else.
//
// In: sleft The left coordinate of the area to scroll (0 -> "Cols()" - 1).
// stop The top coordinate of the area to scroll (0 -> "Rows()" - 1).
// sright The right coordinate of the area to scroll (0 -> "Cols()" - 1).
// sbottom The bottom coordinate of the area to scroll (0 -> "Rows()" - 1).
// dx The left coordinate of the destination (0 -> "Cols()" - 1).
// dy The top coordinate of the destination (0 -> "Rows()" - 1).
void Console::Scroll(int sleft, int stop, int sright, int sbottom, int dx, int dy) const
{
// Bounds-check...
BoxCheck(sleft, stop, sright, sbottom);
if (dx < 0) dx = 0;
if (dx >= cols) dx = cols - 1;
if (dy < 0) dy = 0;
if (dy >= rows) dy = rows - 1;
// Git ready!
SMALL_RECT src = { sleft, stop, sright, sbottom, };
COORD dest = { dx, dy, };
CHAR_INFO fill = { ' ', attrs, };
// Make it so.
ScrollConsoleScreenBuffer(out, &src, NULL, dest, &fill);
}
// Adjust the window dimensions.
//
// In: width The desired number o' columns (1+, 0 for max possible).
// height The desired number o' rows (1+, 0 for max possible).
// pos true to maximize and center the window.
// fs true to go full screen.
//
// Note: You may not get the size you want and this resets any internal window to the full
// screen.
void Console::Size(int width, int height, bool pos, bool fs)
{
// They don't need to see this...
ShowWindow(hwnd, SW_HIDE);
// How high can we go?
COORD c = GetLargestConsoleWindowSize(out);
if (width < 1 || width > c.X) width = c.X;
if (height < 1 || height > c.Y) height = c.Y;
// Don't want no scroll bars!
SMALL_RECT dim = { 0, 0, width - 1, height - 1 };
SetConsoleWindowInfo(out, TRUE, &dim);
// Now for the buffer.
c.X = width; c.Y = height;
SetConsoleScreenBufferSize(out, c);
// How'd we do?
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(out, &info);
cols = info.dwSize.X;
rows = info.dwSize.Y;
Window();
if (pos)
{
// Time to git big!
SendMessage(hwnd, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
// How large are we and the entire screen?
RECT rect;
GetWindowRect(hwnd, &rect);
int w = rect.right - rect.left,
h = rect.bottom - rect.top,
sw = GetSystemMetrics(SM_CXSCREEN),
sh = GetSystemMetrics(SM_CYSCREEN);
// Center us.
MoveWindow(hwnd, (sw - w) >> 1, (sh - h) >> 1, w, h, TRUE);
}
if (fs)
{
// BEFORE simulating Alt+Enter, be sure
// the Ctrl key is up. Otherwise the
// system sees Ctrl+Alt+Enter and won't
// go full screen. The Ctrl key is
// probably being held down right now
// because we usually start running our
// programs with Ctrl+F5.
//
// fixed by Lari Norri & Darryl Malcomb,(Darryl)
while (GetAsyncKeyState(VK_CONTROL)) { }
// Simulate Alt+Enter.
SendMessage(hwnd, WM_SYSKEYDOWN, VK_RETURN, 0x20000000);
}
// Here we are!
ShowWindow(hwnd, SW_SHOW);
}
// Create a "separate" box within our screen.
//
// In: left The left coordinate of the area (0 -> "Cols()" - 1).
// top The top coordinate of the area (0 -> "Rows()" - 1).
// right The right coordinate of the area (0 -> "Cols()" - 1).
// bottom The bottom coordinate of the area (0 -> "Rows()" - 1).
//
// Note: Letting lines wrap without explicit newlines and/or getting input with cin can mess
// the window up!
void Console::Window(int left, int top, int right, int bottom)
{
// Bounds-check...
BoxCheck(left, top, right, bottom);
// Such a cute little window!
wleft = left;
wtop = top;
wright = right;
wbottom = bottom;
}
// Turn on/off the mouse cursor.
//
// In: visible true to make it show or false to hide it.
//
// Note: This only works in full screen mode and then only to ditch the cursor...
void Console::MouseOn(bool visible) const
{
// How are we now?
DWORD mode;
GetConsoleMode(in, &mode);
if (visible)
// Turn it on.
mode |= ENABLE_MOUSE_INPUT;
else
// Turn it off.
mode &= ~ENABLE_MOUSE_INPUT;
SetConsoleMode(in, mode);
}
// Change whether or not we're back-buffering the screen.
//
// In: bb true if we should back-buffer to reduce flicker or false to skip it and save memory instead.
void Console::setBB(bool bb)
{
if (this->bb == bb)
// It's the same.
return;
if (this->bb)
{
// Destroy the back-buffer we've got.
if (!this->StdIsVis())
{
// Clone what's currently showin' to the default.
// Create a temporary buffer the size of our window.
COORD bs = { this->cols, this->rows };
CHAR_INFO *buff = new CHAR_INFO[bs.Y * bs.X];
// Do the entire window.
SMALL_RECT rect = { 0, 0, bs.X - 1, bs.Y - 1 };
// Where to copy in the buffer.
COORD bc = { 0, 0 };
// Copy...
if (ReadConsoleOutput(vis, buff, bs, bc, &rect))
// ...paste!
WriteConsoleOutput(GetStdHandle(STD_OUTPUT_HANDLE),
buff, bs, bc, &rect);
// Big flush!
delete [] buff;
// Show the default critter.
this->Flip();
}
// Back to normal.
CloseHandle(out);
out = vis;
}
else
{
// Create a clone.
if (INVALID_HANDLE_VALUE == (out = CreateConsoleScreenBuffer(GENERIC_READ | GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL)))
{
// Darn, can't.
out = vis;
return;
}
// Mimic all our existing settings.
CONSOLE_CURSOR_INFO curs;
GetConsoleCursorInfo(vis, &curs);
SetConsoleCursorInfo(out, &curs);
DWORD mode;
GetConsoleMode(vis, &mode);
SetConsoleMode(out, mode);
CONSOLE_SCREEN_BUFFER_INFO info;
GetConsoleScreenBufferInfo(vis, &info);
SetConsoleCursorPosition(out, info.dwCursorPosition);
SetConsoleTextAttribute(out, attrs);
}
// Make it so.
this->bb = bb;
}
// Flip the back-buffer, if we've got one, so we see it on the screen.
void Console::Flip()
{
if (!this->bb)
// Got none.
return;
// Exchange 'em.
std::swap(out, vis);
if (w4VB && DDobject)
{
int width, height;
if (getClientDim(width, height))
// Windowed.
DDOBJECT->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN, 0);
}
SetConsoleActiveScreenBuffer(vis);
}
// Display an Image.
//
// In: i The picture to print.
// x The column for its upper-left cell.
// y The row for its upper-left cell.
//
// Note: No error checking is done on the coordinates!
void Console::show(const Image &i, int x, int y) const
{
if (!i.good())
// Sorry Charlie.
return;
// What are its dimensions?
COORD coordBufSize = { i.getCols(), i.getRows() };
// Assume they want to start displaying beginning at the upper-left cell.
COORD coordBufCoord = { 0, 0 };
// Where are we writing to?
SMALL_RECT srctWriteRect = { x, y, x + coordBufSize.X - 1, y + coordBufSize.Y - 1 };
// Blit it!
WriteConsoleOutput(out, i.getChiBuffer(), coordBufSize, coordBufCoord, &srctWriteRect);
}
// Change the output's default character attributes.
//
// In: l The stream to alter.
// a The new values to use.
//
// Return: The stream "l".
Output & Console::setAttr(Output &l, const Attr &a)
{
// Retrieve what we need.
Attr attr = a;
if (attr & 0xFF)
{
// Bits that determine the foreground and background colors.
const Attr FG = 0xF, BG = 0xF0;
if (attr & FG)
// Replace the current foreground.
this->attrs &= ~FG;
if (attr & BG)
// Replace the current background.
this->attrs &= ~BG;
this->attrs |= attr;
}
else
// Black - turn all the bits off.
this->attrs = attr;
SetConsoleTextAttribute(this->out, this->attrs);
// Here we are:
return l;
}
// Display a string without looking for any special formatting characters.
//
// In: s The text to show.
void Console::putRawString(const std::wstring &s) const
{
DWORD dummy;
WriteConsole(out, s.c_str(), static_cast<DWORD>(s.size()), &dummy, NULL);
}
// All the carefully-watched forms o' output.
//
// In: ??? What to show.
//
// Return: Us.
#define INSERTIONED(pt) \
Console & Console::operator <<(pt p) \
{ \
this->T2String(p); \
return *this; \
}
INSERTIONED(char)
INSERTIONED(unsigned char)
INSERTIONED(signed char)
INSERTIONED(wchar_t)
INSERTIONED(const char *);
INSERTIONED(const unsigned char *)
INSERTIONED(const signed char *)
INSERTIONED(const wchar_t *)
INSERTIONED(short)
INSERTIONED(unsigned short)
INSERTIONED(int)
INSERTIONED(unsigned int)
INSERTIONED(long)
INSERTIONED(unsigned long)
INSERTIONED(long long)
INSERTIONED(unsigned long long)
INSERTIONED(float)
INSERTIONED(double)
INSERTIONED(long double)
INSERTIONED(const void *)
Console & Console::operator <<(const std::string &s)
{
return *this << s.c_str();
}
INSERTIONED(const std::wstring &)
Console & Console::operator <<(OutputPF const &pf)
{
pf(*this);
return *this;
}
Console & Console::operator <<(std::ios_base & (* const pf)(std::ios_base &))
{
pf(*this);
return *this;
}
/***********/
/* Private */
/***********/
// Efficiently procure space for our dynamic memory.
//
// In: cols The number of columns we'll have.
// rows The number of rows we'll have.
void Console::Image::reallocate(int cols, int rows)
{
// If our size is the same don't fragment the heap.
if (cols * rows != this->cols * this->rows)
{
// Out with the old!
this->free();
// In with the new!!
this->cols = cols;
this->rows = rows;
this->chiBuffer = new CHAR_INFO[this->cols * this->rows];
}
}
// Deep duplicate a CHAR_INFO array.
//
// In: cols The number of columns we'll have.
// rows The number of rows we'll have.
// ci A CHAR_INFO for each cell (cols * rows).
void Console::Image::DeepCopy(int cols, int rows, CHAR_INFO const ci[])
{
reallocate(cols, rows);
rsize_t num = this->cols * this->rows * sizeof CHAR_INFO;
memcpy_s(this->chiBuffer, num, ci, num);
}
/**********/
/* Public */
/**********/
// Create a 2D picture.
//
// In: img An array of strings containing the characters to display.
// rows The height of "img" (1+). The width is assumed to be the length of the first string and should be the same for all the strings, i.e., the picture
// is rectangular.
// attr Character attributes to use for every cell. Color primarily but see the CHAR_INFO Help for more options.
Console::Image::Image(const wchar_t * const img[], int rows, Attr attr)
{
this->clear();
this->set(img, rows, attr);
}
// Create a 2D picture.
//
// In: cols How many columns each row has (1+).
// rows How many rows it has (1+).
// ci A CHAR_INFO for each cell (rows * cols).
Console::Image::Image(int cols, int rows, CHAR_INFO const ci[])
{
this->clear();
this->set(cols, rows, ci);
}
// Create a 2D picture.
//
// In: fileName Where to get our information from.
Console::Image::Image(std::string const &fileName) throw(ios_base::failure &, bad_exception &)
{
this->clear();
this->Load(fileName);
}
// Copy constructor.
//
// In: i An existing one to mimic.
Console::Image::Image(const Image &i)
{
this->clear();
this->set(i);
}
// Assignment operator.
//
// In: i An existing one to emulate.
Console::Image & Console::Image::operator =(const Image &i)
{
if (this != &i)
this->set(i);
return *this;
}
// Define a 2D picture.
//
// In: img An array of strings containing the characters to display.
// rows The height of "img" (1+). The width is assumed to be the length of the first string and should be the same for all the strings, i.e., the picture
// is rectangular.
// attr Character attributes to use for every cell. Color primarily but see the CHAR_INFO Help for more options.
void Console::Image::set(const wchar_t * const img[], int rows, Attr attr)
{
if (!img || rows < 1)
// No sir I don't like it.
return;
// What's the width?
int cols = 0;
const wchar_t *c = img[0];
while ('\0' != *c++)
++cols;
if (cols < 1)
// Too thin!
return;
// Deep copy!
reallocate(cols, rows);
for (int r = 0, i = 0; r < this->rows; r++)
{
for (int c = 0; c < this->cols; c++, i++)
{
// Use the correct character type in the union.
this->chiBuffer[i].Char.UnicodeChar = img[r][c];
this->chiBuffer[i].Attributes = attr;
}
}
}
// Create a 2D picture.
//
// In: cols How many columns each row has (1+).
// rows How many rows it has (1+).
// ci A CHAR_INFO for each cell (rows * cols).
void Console::Image::set(int cols, int rows, CHAR_INFO const ci[])
{
if (cols < 1 || rows < 1 || !ci)
// No sir I don't like it.
return;
// I suppose we can trust 'em.
DeepCopy(cols, rows, ci);
}
// Define a 2D picture.
//
// In: i An existing one to clone.
void Console::Image::set(const Image &i)
{
// I suppose we can trust 'em.
DeepCopy(i.cols, i.rows, i.chiBuffer);
}
// Get access to one of our rows.
//
// In: i The index (0 -=> rows - 1) of the one to retrieve.
//
// Return: The address of the first CHAR_INFO in the row - use another [] to
// access a specific column.
#define ARRAY_SUBSCRIPT_OPERATOR \
if (i < 0 || i >= this->rows) \
throw out_of_range("Invalid index!"); \
return this->chiBuffer + i * this->cols;
CHAR_INFO * Console::Image::operator [](int i) throw(out_of_range &, bad_exception &)
{
ARRAY_SUBSCRIPT_OPERATOR
}
// Read-only version for constant objects.
CHAR_INFO const * Console::Image::operator [](int i) const throw(out_of_range &, bad_exception &)
{
ARRAY_SUBSCRIPT_OPERATOR
}
// Store an Image to a file.
//
// In: fileName Where to send our information to.
void Console::Image::Save(std::string const &fileName) const throw(ios_base::failure &, bad_exception &)
{
std::ofstream ofl;
// Throw an exception if anything unexpected happens.
ofl.exceptions(ios_base::badbit | ios_base::eofbit | ios_base::failbit);
// This will create the file if it doesn't already exist and overwrite it
// if it does.
ofl.open(fileName.c_str(), ios_base::out | ios_base::trunc | ios_base::binary);
// Store our dimensions.
#define WRITE(var) ofl.write(reinterpret_cast<char const *>(&var), sizeof var);
WRITE(this->rows)
WRITE(this->cols)
// Now for the real data.
ofl.write(reinterpret_cast<char const *>(this->chiBuffer),
this->rows * this->cols * sizeof(CHAR_INFO));
// Finished.
ofl.close();
}
// Retrieve an Image from a file.
//
// In: fileName Where to get our information from.
void Console::Image::Load(std::string const &fileName) throw(ios_base::failure &, bad_exception &)
{
std::ifstream ifl;
// Throw an exception if anything unexpected happens.
ifl.exceptions(ios_base::badbit | ios_base::eofbit | ios_base::failbit);
try
{
ifl.open(fileName.c_str(), ios_base::in | ios_base::binary);
// Recover our dimensions.
int rows, cols;
#define READ(var) ifl.read(reinterpret_cast<char *>(&var), sizeof var);
READ(rows)
READ(cols)
// Now for the real data.
reallocate(cols, rows);
ifl.read(reinterpret_cast<char *>(this->chiBuffer),
this->cols * this->rows * sizeof CHAR_INFO);
// Finished.
ifl.close();
}
catch (ios_base::failure &)
{
if (ifl.is_open())
{
// We failed somewhere after opening the file so ditch any
// partial successes we've had.
this->free();
this->clear();
ifl.close();
}
// Let someone above us worry about it some more.
throw;
}
}
} // namespace FS
Just be aware that there aren't any problems with the SLList.h, DynArray.h, or Console.h. The first two, while they may not be perfect and possibly might leak a little memory, are from past projects and will work fine for their assigned tasks here. Console.h was created by a past instructor is a re-implementation of parts of iostream and will likely make your head hurt unless your experience is far greater than mine.
Last edited by cable_guy_67 : 18-Aug-2006 at 20:32.
Reason: Please surround your C++ code with [cpp] ... [/cpp]
|