📄 wcon.cpp
字号:
/* C++ I/O using a GUI console window
* UnderC C++ interpreter
* Steve Donovan, 2001
* This is GPL'd software, and the usual disclaimers apply.
* See LICENCE
*
* WCON is rather like Petzold's winio library, as described in his _Undocumented Windows_.
* The C++ interface is using the 'fake' iostreams library (iostrm.cpp) rather than
* pukka iostreams. Currently this feature is only available in the Windows version, altho
* this file does not depend directly on the Win32 API. Rather, all platform access goes
* through YAWL (Yet Another Windows Library) which should be portable.
*
* Ideally, WCON should be made independent of UnderC, but this version depends on being
* able to tell whether it's in a program thread, and to kill that program thread if there's
* a buffer overrun. Also the 'paste into' feature which Greg Perry had on his wishlist
* means we need uc_eval() from the main program.
*/
#include "twl.h"
#include "keys.h"
#include "gdi_stock.h"
#include "twl_misc.h"
//#include <stdio.h>
#include <string.h>
#include <ctype.h>
//#include <stdarg.h>
#include "classlib.h" // iostreams & strings
#include "ex_vfscanf.h"
#include "wcon.h"
#include <list>
// (a fiddle! WCON shd not depend on the rest of the program!!)
#include "program.h"
// for in_main_thread()
#include "main.h"
// for finalization call...
namespace Engine {
void kill(int retcode=0); // in Engine
}
// from main.cpp
int uc_eval(char *expr, bool append_semicolon=true, bool synchronous=false, char *name=NULL, int lineno=0);
static TWin* sConsoleParent = NULL;
const int white = 0xFFFFFF;
const char CTRL_C = 3, CTRL_V = 22, CTRL_Z = 26, CTRL_X = 24;
// where are these buggers usually?
template <class T>
T min(T t1, T t2) { return t1 < t2 ? t1 : t2; }
template <class T>
T max(T t1, T t2) { return t1 > t2 ? t1 : t2; }
struct Position {
int x,y;
void set(int _x, int _y)
{ x = _x; y = _y; }
Position(int _x, int _y)
: x(_x),y(_y) {}
Position()
: x(0),y(0) {}
};
typedef char *pchar;
const int MAX_LINES = 5000,ROWS = 4000, COLUMNS = 100,
CLIP_BUFF_SIZE = 4096;
char gPromptChar;
int gPromptCol;
class Buffer {
protected:
char *m_buff[MAX_LINES];
long m_line_colour[MAX_LINES];
int m_col, m_row;
public:
void clear_line(int r)
{
char *line = m_buff[r];
for(int j = 0; j < m_col; j++)
line[j] = ' '; //'A' + i;
line[m_col] = '\0'; //was line[j] = '\0';
m_line_colour[r] = 0;
}
void alloc(int r1, int r2)
{
for(int i = r1; i <= r2; i++) {
m_buff[i] = new char[m_col+4];
clear_line(i);
}
}
void clear()
{
for(int i = 0; i < m_row; i++)
clear_line(i);
}
Buffer(int rows, int cols)
: m_col(cols), m_row(rows)
{
alloc(0,rows-1);
}
~Buffer()
{
for(int i = 0; i < m_row; i++) delete[] m_buff[i];
}
void reserve(int nrows)
{
if (nrows > m_row) {
alloc(m_row,nrows-1);
m_row = nrows;
}
}
void remove(Position& pos)
{
char *line = m_buff[pos.y];
for(int i = pos.x; i < m_col-1; i++)
line[i] = line[i+1];
line[m_col-1] = ' ';
}
void insert(Position& pos)
{
char *line = m_buff[pos.y];
for(int i = m_col-2; i >= pos.x; i--)
line[i+1] = line[i];
//line[m_col-1] = ' ';
}
int cols() const { return m_col; }
int rows() const { return m_row; }
long colour(int r) { return m_line_colour[r]; }
void colour(int r, long c) { m_line_colour[r] = c; }
char& operator() (int i, int j) // x,y!
{ return m_buff[j][i]; }
void copy_buffer(char *out,int iy, int ix1, int ix2=-1, bool strip_space=false)
{
char *line = m_buff[iy], *p = out;
if(ix2 == -1) ix2 = m_col-1;
if(strip_space) while(ix2 > ix1 && isspace(line[ix2])) ix2--;
for(int i = ix1; i <= ix2; i++) *p++ = line[i];
*p++ = '\0';
}
void copy_out(ostream& os, int iy1, int iy2, bool strip_prompts=false)
{
char tbuff[255];
for(int k = iy1; k <= iy2; k++) {
copy_buffer(tbuff,k,0,-1,true);
char *ps = tbuff;
if (strip_prompts && ps[gPromptCol] == gPromptChar)
do { ps++; } while(!isspace(*ps));
os << ps << endl;
}
}
};
typedef std::list<string> StringList;
const int default_cursor_width = 2, MAX_HISTORY=20;
Point curr_pt;
int curr_ln, end_ln, old_end_ln;
long curr_tm;
bool is_dragging = false;
const int DRAG_WAIT = 150;
int kount = 0;
class ConsoleWin: public TFrameWindow {
protected:
Position m_pos, m_start,m_end, m_char;
Buffer m_buff;
TCaret m_cursor;
long m_colour, m_write_colour;
bool m_insert_mode, m_immediate;
int m_buff_begin, m_buff_end, m_last_end, m_last_line;
int m_nchar;
char m_delim;
char *m_read_buff;
// scroll management
int m_pagesize, m_top;
// history list
StringList::iterator m_hlp;;
StringList m_history;
public:
void reset_pos()
{
m_top = 0;
m_pos.set(0,0);
m_start = m_pos;
m_end.set(m_buff.cols()-1,m_buff.rows()-1);
scroll_bar()->set_range(0,100);
}
ConsoleWin(int rows, int cols)
: m_buff(rows,cols),TFrameWindow("",NULL,VERT_SCROLL,sConsoleParent)
{
//set_background(1.0,0.8,0.8);
m_colour = 0;
m_write_colour = 0;
m_insert_mode = false;
m_immediate = true;
m_buff_begin = 0;
m_buff_end = 0;
m_last_line = 0;
m_read_buff = NULL;
m_nchar = -1;
m_delim = '\r';
TClientDC dc(this);
dc.select_stock(SYSTEM_FIXED_FONT);
m_char.x = metrics(TM_CHAR_WIDTH);
m_char.y = metrics(TM_CHAR_HEIGHT);
m_cursor.set(this,default_cursor_width,m_char.y);
reset_pos();
}
// *fix 1.1.1 Alex's problem w/ closing the UCW window
void destroy()
{
TFrameWindow::destroy();
if (Program::in_main_thread())
putchars("#q",true,true);
}
Buffer& buff() { return m_buff; }
void copy_out(ostream& os, int iy1, int iy2=-1, bool strip_prompts=false)
{
if(iy2==-1) iy2 = m_pos.y-1; //?
m_buff.copy_out(os,iy1,iy2,strip_prompts);
}
void copy_file_to_clip(char *file)
{
// this can properly become a TClipboard static method...
TClipboard clp(this);
char *pstr = clp.buffer(CLIP_BUFF_SIZE); // we'll clean this up!
// pull _all_ of the file in - binary mode gets the \r\n!
ifstream(file,ios::binary).read(pstr,CLIP_BUFF_SIZE);
clp.write(); // and write the buffer to the clipboard
}
void copy_to_clip(int iy1, int iy2)
{
copy_out(ofstream("tmp.tmp"),iy1,iy2,true);
copy_file_to_clip("tmp.tmp");
}
void copy_selection()
{
copy_to_clip(min(curr_ln,end_ln),max(curr_ln,end_ln));
invalidate();
}
void paste_into()
{
// *add 0.9.8 Can directly paste expressions into the console
TClipboard clp(this);
uc_eval(clp.read(),false,false); // *fix 1.2.5 async eval is safer...
putchars("",true,true); // push an extra line...
}
void vscroll(int action, int pos)
{
TScrollBar *sb = scroll_bar();
int old_pos = pos;
switch(action) {
case TScrollBar::LINEUP: pos = max(0,pos-1); break;
case TScrollBar::LINEDOWN: pos = min(m_pos.y,pos+1); break;
case TScrollBar::PAGEUP:
pos = max(0,pos - m_pagesize);
break;
case TScrollBar::PAGEDOWN:
pos = min(m_last_line,pos + m_pagesize);
break;
// case TScrollBar::PAGEUP: pos = max(0,pos - m_pagesize); break;
// case TScrollBar::PAGEDOWN: pos = min(m_pos.y,pos + m_pagesize); break;
case TScrollBar::POS: break;
}
m_top = max(0,m_top + pos - old_pos); // *fix 1.2.8 _must_ be +ve!
scroll_bar()->set_pos(m_top);
invalidate();
}
void set_read_buff(char *buff, int delim='\r', int nchar=-1)
{ m_read_buff = buff; m_delim = delim; m_nchar = nchar; }
void size(int width, int height)
{
m_pagesize = height/m_char.y - 1; //fiddle
m_end.x = min(width/m_char.x - 1,COLUMNS);
}
void immediate_paint(bool yes)
{
if (yes != m_immediate) {
m_immediate = yes;
if (m_immediate) invalidate();
}
}
void insert_mode(bool yes)
{
if (yes != m_insert_mode) {
m_insert_mode = yes;
m_cursor.set(this,
m_insert_mode ? m_char.x : default_cursor_width,m_char.y);
}
}
void set_colour(long c)
{
m_write_colour = c;
m_buff.colour(m_pos.y, m_write_colour);
}
void clear_line()
{
m_buff.clear_line(m_pos.y);
m_buff_begin = 0;
m_pos.x = 0;
m_last_end = 0;
m_buff_end = 0;
draw_buff(TClientDC(this),m_pos,Position(m_end.x,m_pos.y));
}
// *fix 1.1.1 Command history list is now better behaved
// *fix 1.2.8 Fails miserably if there's nothing in the history list.
void history_list(bool up)
{
if (m_history.size()==0) return;
if(!up){
++m_hlp;
if (m_hlp == m_history.end()) { --m_hlp; return; }
}
clear_line();
putchars(m_hlp->c_str(),false,true);
if (up) {
if (m_hlp == m_history.begin()) return;
--m_hlp;
}
}
void add_to_history_list(char *line)
{
m_history.push_back(line);
m_hlp = m_history.end();
--m_hlp;
if (m_history.size() > MAX_HISTORY) m_history.pop_front();
}
int xc2pix(int x) { return x*m_char.x; }
int yc2pix(int y) { return (y-m_top)*m_char.y; }
int pix2xc(int x) { return x/m_char.x; }
int pix2yc(int y) { return y/m_char.y + m_top; }
int xpos() { return xc2pix(m_pos.x); }
int ypos() { return yc2pix(m_pos.y); }
void cursor_pos()
{
if (m_immediate) m_cursor.set_pos(xpos(),ypos());
}
void focus(bool yes)
{
if (yes) {
m_cursor.create();
cursor_pos();
m_cursor.show();
} else {
m_cursor.hide();
m_cursor.destroy();
}
}
void draw_buff(TDC& dc, Position& s1, Position& s2, bool invert=false)
{
m_cursor.hide();
if(invert) {
dc.set_text_colour(white);
dc.set_back_colour(0,0,0);
}
// *fix 1.1.1 limit width of _drawable_ buffer!
int width = min(s2.x - s1.x, COLUMNS);
for(int iy = s1.y; iy <= s2.y; iy++) {
int x = xc2pix(s1.x), y = yc2pix(iy);
char *str = &m_buff(s1.x,iy);
if (!invert && m_buff.colour(iy) != m_colour) {
m_colour = m_buff.colour(iy);
dc.set_text_colour(m_colour);
}
dc.text_out(x,y,str,width);
}
if (invert) { // reset background
dc.set_back_colour(1,1,1); // really we shd choose this...
dc.set_text_colour(m_colour);
}
m_cursor.show();
}
void paint(TDC& dc)
{
// + 1 to account for end!
draw_buff(dc,
Position(0,m_top),
Position(m_end.x,m_top + m_pagesize + 1)
);//m_start,m_end);
}
// mouse management
void mouse_down(Point& pt)
{
curr_pt = pt;
curr_tm = current_time();
curr_ln = pix2yc(pt.y);
old_end_ln = curr_ln;
}
// *add 1.1.1 double-click on error line opens up Notepad!
// *add 1.1.2 uses Metapad's '/g' option! Really shd make editor an option..
void mouse_double_click(Point& pt)
{
char temp[COLUMNS], *file;
int ln;
m_buff.copy_buffer(temp,curr_ln,0);
file = _strdup(strtok(temp," "));
ln = atoi(strtok(NULL,":"));
sprintf(temp,"metapad /g %d:1 %s",ln,file);
exec(temp);
}
void mouse_move(Point& pt)
{
if (is_dragging) {
end_ln = pix2yc(pt.y);
if (end_ln != old_end_ln) {
draw_buff(TClientDC(this),
Position(0, min(old_end_ln,end_ln)),
Position(m_end.x,max(old_end_ln,end_ln)),
end_ln > old_end_ln);
old_end_ln = end_ln;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -