📄 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.cppint 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[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; } } else
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -