尝试用新方法编写的文本编辑器。基于Windows API,极简。方便交流学习。 不需下载,直接从微博网页上拷贝。只需创建一个空的窗口应用程序项目,添加一个cpp文件,把本页内容拷入编译即可运行。

支持的字符集UCS BMP0(0x0000 - 0xFFFD),即UCS-2。

只支持最基本的编辑功能:文字输入,BackSpace,删除,上下左右移动,鼠标点击,滚动条, 自动换行,文件保存(UTF-16 LE)。CTRL+S: 文件保存, CTRL+N: 新文件。

不支持的基本功能:tab制表符(现在是直接添加4个空格),拷贝,黏贴,选择,文件打开,wordwrap…

希望能为大家带来一些启发,写出更好的文本编辑器,也欢迎提出宝贵意见。下面是完整代码:

/**本软件作者: davidshekcn@sina.com 于2022年10月                *
* 使用方法:VC++创建一个空窗口项目,添加一个cpp源文件,把本页内 *
* 容拷入,编译即可运行。不需要包含其它文件,不需要添加资源文件。*
* 作者不对软件提供任何担保,也不对运行或传播该软件所产生的任何  *
* 后果承担任何责任。                                            *
*****************************************************************/
//#include <SDKDDKVer.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include <fstream>
#include <commdlg.h>
#include <vector>
#include <string>
#pragma comment(lib,"Imm32.lib")#define IDM_EXIT              105     //未使用const int scrnLEFTMARGIN = 2;          //屏幕左右都留一点空
const int scrnRIGHTMARGIN = 3;
const WCHAR *strEOLCRLF = L"\r\n";      //文件行尾    HINSTANCE hInst;                        // 当前实例class TextEditor;
class LineNode {                    //一整行为一个节点,双向链表,屏幕行动态调整,每行起点存入vecLineOffset
public:                             //字符宽度存入vecCharWidths;LineNode();~LineNode();
public:LineNode* AddLineAfterThis();  //增加一行BOOL DeleteLineAfterThis();     //删除一行BOOL ClearContent(LineNode** pFirstLine, LineNode** pThisLine);   //内容清零,保留一行int ReCalCaretPos(LineNode* pFirstLine, int& iLineOffset, int& xCaret, int bufPos);  //pThisLine调用LineNode* GetTheScrnLine(LineNode* pfirstLine, int& iLineOffset, int lineNum);   //获得任意指定位置的屏幕行int GetTotalScnLines(LineNode* pfirstLine);   //计算总的屏幕显示行LineNode* GetPrevScrnLine(LineNode* ptheLine, int& iLineOffset);  //前一行LineNode* GetNextScrnLine(LineNode* ptheLine, int& iLineOffset);  //下一行int ScrnLineLength(int iLineOffset);        //屏幕显示行的长度,字符数VOID ReLayoutLine(LONG scrnWidth);       //重新调整显示位置,LONG LinePaintWidth(int iLineOffset);           //屏幕行的显示宽度,物理宽度  private: friend TextEditor;int                _lineNum;std::wstring       _text;std::vector<LONG>       vecCharWidths;std::vector<int>        vecLineOffset;  //构造函数自动push_back(0)LineNode* _prevLine;LineNode* _nextLine;
};class TextEditor {public:void PaintDC(WPARAM wParam, LPARAM lParam);           //显示void onVKeyDown(WPARAM wParam, LPARAM lParam);         //virual keys,左键,右键,上下键和delete键void onWmChar(WPARAM wParam, LPARAM lParam);        //WM_CHAR, 字符消息,文字输入void onMouseLBDown(LPARAM lParam);             //鼠标左键void onWmVscroll(WPARAM wParam);               //鼠标滚动,滚动条void onIMENotify(WPARAM wParam);               //中文输入框跟随光标联动void onCreate(HWND hWnd);                      //初始化void onSize(LPARAM lParam);                    //窗口大小变化BOOL AbortAnyway();                           //需要保存吗?退出吗?void onSetFoucus();                           //输入光标void onKillFocus();                           bool FileSaveAs();                            //保存文件             bool DoFileSave(LPTSTR file);void FileNew();                              //清空从新开始,问询是否放弃已输入内容TextEditor();~TextEditor() {        if (pFirstLine) {                 //LineNode内存清零, 窗口关闭销毁TextEditor对象pFirstLine->ClearContent(&pFirstLine, &pThisLine);delete pFirstLine;pThisLine = pFirstLine = nullptr;//Beep(2000, 20);}}
private:HWND hwndWin = nullptr;HDC hdc;SCROLLINFO si;          //滚动条信息int scrollYPos = 0;LONG scrnWidth = 0;int scrnHeight = 0;int bufPos = 0;          //光标字符位置,在行中的位置int totalLines = 0;int cyChar = 0;int xCaret = scrnLEFTMARGIN;    //光标宽度位置int yCaret= 0;          //光标的高度位置RECT clientRect;bool isTextAltered = false;LOGFONT lf;         //可以不存在这里HFONT   hFontSet;       //当前FONT句柄LineNode* pFirstLine = nullptr;        //第一行LineNode* pThisLine = nullptr;         //当前行,工作行int iL;     //当前行的屏幕行vector index, 第一行为0
};
static TextEditor* pEditor = nullptr;  //TextEditor的指针, 在WM_CREATE时赋值/=====TextEditor======================================================================================================
TextEditor::TextEditor() {pFirstLine = new LineNode;pThisLine = pFirstLine;   this->lf = { -18, 0, 0, 0, FW_LIGHT, FALSE, FALSE, FALSE, GB2312_CHARSET,OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE };::wcscpy_s(this->lf.lfFaceName, 1 + wcslen(L"微软雅黑"), L"微软雅黑");}void TextEditor::onCreate(HWND hWnd) { this->hwndWin = hWnd;hdc = ::GetDC(hWnd);this->hFontSet = CreateFontIndirect(&this->lf);//this->hFontSet = CreateFont(-18, 0, 0, 0, FW_LIGHT, FALSE, FALSE, FALSE, GB2312_CHARSET,//    OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE, L"微软雅黑"); ::SelectObject(hdc, this->hFontSet);TEXTMETRIC tm;::GetTextMetrics(hdc, &tm);this->cyChar = tm.tmHeight;       ::GetClientRect(hWnd, &clientRect);this->scrnWidth = clientRect.right - scrnLEFTMARGIN - scrnRIGHTMARGIN;this->scrnHeight = clientRect.bottom;       ::ReleaseDC(hWnd, hdc);::SetFocus(hWnd);
}void TextEditor::onSize(LPARAM lParam) {this->clientRect.right = LOWORD(lParam);this->clientRect.bottom = HIWORD(lParam);int newScrnWidth = this->clientRect.right - scrnLEFTMARGIN - scrnRIGHTMARGIN;this->scrnHeight = clientRect.bottom;::HideCaret(this->hwndWin);if (newScrnWidth != scrnWidth) {               //屏幕宽度变化this->scrnWidth = newScrnWidth;               for (LineNode* pTemp = pFirstLine; pTemp != NULL; pTemp = pTemp->_nextLine) {for (int i = 0; i < pTemp->vecLineOffset.size(); i++) {long textWidth = pTemp->LinePaintWidth(i);if (textWidth > scrnWidth) {pTemp->ReLayoutLine(scrnWidth);            //窗口变小了       }else if ((i < (pTemp->vecLineOffset.size() - 1)) && ((textWidth + pTemp->vecCharWidths[pTemp->vecLineOffset[i + 1]]) < scrnWidth)) {pTemp->ReLayoutLine(scrnWidth);       //窗口变大了,wrap行也要重排, 非wrap行不用重排             }}}}int newScrnLineNum = pThisLine->ReCalCaretPos(pFirstLine, this->iL, xCaret, bufPos);  // iL, xCaret引用已更新,bufPos没改动if (cyChar) {this->totalLines = pFirstLine->GetTotalScnLines(pFirstLine);si.cbSize = sizeof(si);si.fMask = SIF_RANGE | SIF_PAGE;si.nMin = 0;si.nMax = totalLines;si.nPage = this->scrnHeight / cyChar;::SetScrollInfo(this->hwndWin, SB_VERT, &si, TRUE);if (((this->scrnHeight - yCaret) / cyChar) < 1) {             //屏幕高度变化::SendMessage(this->hwndWin, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), NULL);}si.cbSize = sizeof(si);si.fMask = SIF_POS;::GetScrollInfo(this->hwndWin, SB_VERT, &si);yCaret = cyChar * (newScrnLineNum - si.nPos);      ::SetCaretPos(xCaret, yCaret);}::ShowCaret(this->hwndWin);::InvalidateRect(this->hwndWin, NULL, false);
}void TextEditor::PaintDC(WPARAM wParam, LPARAM lParam) {::HideCaret(this->hwndWin);PAINTSTRUCT ps;this->hdc = ::BeginPaint(this->hwndWin, &ps);si.cbSize = sizeof(si);si.fMask = SIF_POS;::GetScrollInfo(this->hwndWin, SB_VERT, &si);scrollYPos = si.nPos;RECT rcPaint;::GetClientRect(this->hwndWin, &rcPaint);int FirstRefreshLine = max(0, scrollYPos + rcPaint.top / cyChar);       //屏幕窗口第一行行数int LastRefreshLine = min(this->totalLines, scrollYPos + rcPaint.bottom / cyChar);   //最后一行行数int iLp = 0;LineNode* pLineNodePaint = pFirstLine->GetTheScrnLine(pFirstLine, iLp, FirstRefreshLine);  //找到第一行if (!pLineNodePaint) {::ShowCaret(this->hwndWin);::EndPaint(this->hwndWin, &ps);return;}HDC memDC = ::CreateCompatibleDC(hdc);      //内存DC先绘制::SelectObject(memDC, this->hFontSet);HBITMAP hBitmap = ::CreateCompatibleBitmap(hdc, rcPaint.right - rcPaint.left,rcPaint.bottom - rcPaint.top);HGDIOBJ oldBitmap = ::SelectObject(memDC, hBitmap);HBRUSH hBrush = ::CreateSolidBrush(RGB(0xFA, 0xFA, 0xFA));::FillRect(memDC, &rcPaint, hBrush);::SetBkColor(memDC, RGB(0xFA,0xFA,0xFA));   //背景色RECT rc1 = rcPaint;   int lineLength = 0;int thisLineStart = 0;for (int i = FirstRefreshLine; i <= LastRefreshLine; i++) {    //一行一行绘制thisLineStart = pLineNodePaint->vecLineOffset[iLp];int numSubLines = static_cast<int>(pLineNodePaint->vecLineOffset.size());if ((iLp + 1) < numSubLines) {lineLength = pLineNodePaint->vecLineOffset[iLp + 1] - thisLineStart;}else {lineLength = static_cast<int>(pLineNodePaint->_text.size()) - thisLineStart;}rc1.top = rcPaint.top + (i-scrollYPos) * cyChar;rc1.bottom = rc1.top + cyChar;::ExtTextOut(memDC, scrnLEFTMARGIN, rc1.top, ETO_CLIPPED, &rc1, &pLineNodePaint->_text[thisLineStart], lineLength, NULL);pLineNodePaint = pLineNodePaint->GetNextScrnLine(pLineNodePaint, iLp);if (!pLineNodePaint) {break;}    }::BitBlt(hdc, rcPaint.left, rcPaint.top, rcPaint.right - rcPaint.left,rcPaint.bottom - rcPaint.top, memDC, 0, 0, SRCCOPY);   //显示到窗口::SelectObject(memDC, oldBitmap);   //打扫::DeleteObject(hBitmap);::DeleteObject(oldBitmap);::DeleteObject(hBrush);::DeleteDC(memDC);::EndPaint(this->hwndWin, &ps);::ShowCaret(this->hwndWin);
}void TextEditor::onMouseLBDown(LPARAM lParam){::SetFocus(this->hwndWin);   POINT pt;{short x, y;x = LOWORD(lParam);y = HIWORD(lParam);pt.x = x;pt.y = y;}   si.cbSize = sizeof(si);si.fMask = SIF_POS;::GetScrollInfo(this->hwndWin, SB_VERT, &si);scrollYPos = si.nPos;int mLineNum = (pt.y / this->cyChar)-1;    //mLineNum从0开始if (pt.y > (mLineNum * this->cyChar)) {++mLineNum;}int totalLines = pFirstLine->GetTotalScnLines(pFirstLine);if ((mLineNum + scrollYPos + 1) > totalLines) {   //在空白区点击return;}this->yCaret = mLineNum * this->cyChar;   //新的光标纵坐标mLineNum += scrollYPos;int iLOffset = 0;pThisLine = pFirstLine->GetTheScrnLine(pFirstLine, iLOffset, mLineNum);  //新的工作行,当前行this->iL = iLOffset;         //新工行的Offset索引                       int lineLength = pThisLine->ScrnLineLength(iLOffset);LONG lineWidth = pThisLine->LinePaintWidth(iLOffset);this->xCaret = 0;if (pt.x <= scrnLEFTMARGIN) {           //点击在左边空白区this->xCaret = scrnLEFTMARGIN;      //新的光标横坐标this->bufPos = pThisLine->vecLineOffset[iLOffset];  //新的行内位置,大行,总行}else if (pt.x > pThisLine->LinePaintWidth(iLOffset)) {  //点击在文字的右后面this->xCaret = scrnLEFTMARGIN + lineWidth;this->bufPos = pThisLine->vecLineOffset[iLOffset] + lineLength;}else {          //点击在文字中LONG mPosition = scrnLEFTMARGIN;int lineStart = pThisLine->vecLineOffset[iLOffset];for (int i = 0; i < lineLength; i++) {mPosition += pThisLine->vecCharWidths[lineStart + i];if (mPosition == pt.x) {            //正好点在两个字符之间this->xCaret = mPosition;this->bufPos = lineStart + i+1;break;}else if (mPosition > pt.x) {    //点在文字上,判断哪一边更近int temp = scrnLEFTMARGIN;if (i) {temp = mPosition - pThisLine->vecCharWidths[lineStart + i];}//else {//    temp = scrnLEFTMARGIN;// }if ((pt.x - temp) >= (mPosition - pt.x)) {  //更靠右面或正好在中间,选右面this->xCaret += mPosition;this->bufPos = lineStart + i + 1;break;}else {this->xCaret += temp;   //选左面this->bufPos = lineStart + i;                   break;}}}}::SetCaretPos(xCaret, yCaret);
}
void TextEditor::onVKeyDown(WPARAM wParam, LPARAM lParam) {switch (wParam) {case VK_LEFT:{::HideCaret(this->hwndWin);  //只支持行内移动, 可以根据BackSpace扩展int posInLine = this->bufPos - pThisLine->vecLineOffset[this->iL];if (posInLine > 0) {this->bufPos--;xCaret -= pThisLine->vecCharWidths[this->bufPos];}::ShowCaret(this->hwndWin);break;}case VK_RIGHT:          //只支持行内移动, 可以根据VK_DELETE扩展{::HideCaret(this->hwndWin);int posInLine = this->bufPos - pThisLine->vecLineOffset[this->iL];      //int thisLineLength = pThisLine->ScrnLineLength(this->iL);if (posInLine < thisLineLength) {xCaret += pThisLine->vecCharWidths[this->bufPos];this->bufPos++;}::ShowCaret(this->hwndWin);break;}case VK_UP:{int iLPrevLine = this->iL;LineNode* pTemp = pThisLine->GetPrevScrnLine(pThisLine, iLPrevLine);if (pTemp) {   //有前一行存在,光标上移int prevLineLength = pTemp->ScrnLineLength(iLPrevLine);LONG prevLineWidth = pTemp->LinePaintWidth(iLPrevLine);int curXCaret = this->xCaret;  //保存当前光标位置,上移后位置靠近当前横坐标this->xCaret = 0;this->yCaret -= cyChar;if (curXCaret > prevLineWidth) {     //前面一行短this->xCaret = scrnLEFTMARGIN + prevLineWidth;this->bufPos = pTemp->vecLineOffset[iLPrevLine] + prevLineLength;}else {LONG mPosition = scrnLEFTMARGIN;int lineStart = pTemp->vecLineOffset[iLPrevLine];for (int i = 0; i < prevLineLength; i++) {mPosition += pTemp->vecCharWidths[lineStart + i];if (mPosition == curXCaret) {this->xCaret += mPosition;this->bufPos = lineStart + i + 1;break;}else if (mPosition > curXCaret) {int temp = scrnLEFTMARGIN;if (i) {temp = mPosition - pTemp->vecCharWidths[lineStart + i];}//else {//   temp = scrnLEFTMARGIN;//}if ((curXCaret - temp) >= (mPosition - curXCaret)) {this->xCaret += mPosition;this->bufPos = lineStart + i + 1;break;}else {this->xCaret += temp;this->bufPos = lineStart + i;break;}}}}pThisLine = pTemp;this->iL = iLPrevLine;if (yCaret < cyChar) {::SendMessage(this->hwndWin, WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), NULL);}}}break;case VK_DOWN:{int iLNextLine = this->iL;LineNode* pTemp = pThisLine->GetNextScrnLine(pThisLine, iLNextLine);if (pTemp) {        //有下一行,移动到下一行int nextLineLength = pTemp->ScrnLineLength(iLNextLine);LONG nextLineWidth = pTemp->LinePaintWidth(iLNextLine);int curXCaret = this->xCaret;this->xCaret = 0;this->yCaret += cyChar;if (curXCaret > nextLineWidth) {  //下一行短this->xCaret = scrnLEFTMARGIN + nextLineWidth;this->bufPos = pTemp->vecLineOffset[iLNextLine] + nextLineLength;}else {LONG mPosition = scrnLEFTMARGIN;int lineStart = pTemp->vecLineOffset[iLNextLine];for (int i = 0; i < nextLineLength; i++) {mPosition += pTemp->vecCharWidths[lineStart + i];if (mPosition == curXCaret) {this->xCaret += mPosition;this->bufPos = lineStart + i + 1;break;}else if (mPosition > curXCaret) {int temp = scrnLEFTMARGIN;if (i) {temp = mPosition - pTemp->vecCharWidths[lineStart + i];}//else {//    temp = scrnLEFTMARGIN;//}if ((curXCaret - temp) >= (mPosition - curXCaret)) {this->xCaret += mPosition;this->bufPos = lineStart + i + 1;break;}else {this->xCaret += temp;this->bufPos = lineStart + i;break;}}}}pThisLine = pTemp;this->iL = iLNextLine;if (((this->scrnHeight - yCaret) / cyChar) < 1) {::SendMessage(this->hwndWin, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), NULL);}}}break;case VK_DELETE:{::HideCaret(this->hwndWin);int lineStart = pThisLine->vecLineOffset[this->iL];int posInLine = this->bufPos - lineStart;int lineLength = pThisLine->ScrnLineLength(this->iL);int numOfLinesinthis = static_cast<int>(pThisLine->vecLineOffset.size());if ((posInLine < lineLength) && (this->iL == (numOfLinesinthis - 1))) {  //每行最后一个屏幕行的中间, 直接删除一个字符pThisLine->_text.erase(bufPos, 1);this->isTextAltered = TRUE;for (int i = bufPos; i < (lineLength - 1); i++) {pThisLine->vecCharWidths[i] = pThisLine->vecCharWidths[i + 1];}pThisLine->vecCharWidths.pop_back();                                            //其它保持不变}else if (this->iL < (numOfLinesinthis - 1)) {                       //wrapped行,删除一个字符,重排pThisLine->_text.erase(bufPos, 1);this->isTextAltered = TRUE;for (int i = bufPos; i < pThisLine->_text.size(); i++) {pThisLine->vecCharWidths[i] = pThisLine->vecCharWidths[i + 1];}pThisLine->vecCharWidths.pop_back();pThisLine->ReLayoutLine(this->scrnWidth);if (cyChar) {this->totalLines = pFirstLine->GetTotalScnLines(pFirstLine);si.cbSize = sizeof(si);si.fMask = SIF_RANGE | SIF_PAGE;si.nMin = 0;si.nMax = totalLines;si.nPage = this->scrnHeight / cyChar;::SetScrollInfo(this->hwndWin, SB_VERT, &si, TRUE);}}else if (posInLine == lineLength) { //在行尾int iLNextLine = this->iL;                  //vecLineOffset的下标LineNode* pTemp = pThisLine->GetNextScrnLine(pThisLine, iLNextLine);if (pTemp) {if (pTemp->_text.empty()) {  //下一行为没有字符的空行,直接删除pThisLine->DeleteLineAfterThis();this->isTextAltered = TRUE;}else {pThisLine->_text.append(pTemp->_text);   //合并两行,删除下一行for (int i = 0; i < pTemp->_text.size(); i++) {pThisLine->vecCharWidths.push_back(pTemp->vecCharWidths[i]);}pThisLine->DeleteLineAfterThis();this->isTextAltered = TRUE;pThisLine->ReLayoutLine(this->scrnWidth);}if (cyChar) {this->totalLines = pFirstLine->GetTotalScnLines(pFirstLine);si.cbSize = sizeof(si);si.fMask = SIF_RANGE | SIF_PAGE;si.nMin = 0;si.nMax = totalLines;si.nPage = this->scrnHeight / cyChar;::SetScrollInfo(this->hwndWin, SB_VERT, &si, TRUE);}}        }::InvalidateRect(this->hwndWin, NULL, false);::ShowCaret(this->hwndWin);break;}   }::SetCaretPos(xCaret, yCaret);
}void TextEditor::onWmChar(WPARAM wParam, LPARAM lParam) {for (int i = 0; i < (int)LOWORD(lParam); i++){switch (wParam){case '\x1B':    //ESCAPE{}break;case '\r':{::HideCaret(this->hwndWin);this->isTextAltered = TRUE;LineNode* pTemp = pThisLine->AddLineAfterThis();   //增加一行。int thisLineLength = static_cast<int>(pThisLine->_text.size());int nextLineLength = thisLineLength - bufPos;if (nextLineLength > 0) {              //如果截断一行,内容移到下一行pTemp->_text = pThisLine->_text.substr(bufPos);for (int i = bufPos; i < thisLineLength; i++) {LONG charWidth = pThisLine->vecCharWidths[i];pTemp->vecCharWidths.push_back(charWidth);}for (int i = bufPos; i < thisLineLength; i++) {pThisLine->vecCharWidths.pop_back();}                pThisLine->_text.erase(bufPos);pThisLine->ReLayoutLine(this->scrnWidth);  //在这里有点用猛力, 但考虑未来的wordwrap,先写成这样pTemp->ReLayoutLine(this->scrnWidth);              } if (this->iL != pThisLine->vecLineOffset.size()) {  //在行头换行,只截断,不增加空行,可扩展为增加一空行yCaret += cyChar;}bufPos = 0;xCaret = scrnLEFTMARGIN;            this->iL = 0;pThisLine = pTemp;if (cyChar) {this->totalLines = pFirstLine->GetTotalScnLines(pFirstLine);si.cbSize = sizeof(si);si.fMask = SIF_RANGE | SIF_PAGE; si.nMin = 0;si.nMax = totalLines;si.nPage = this->scrnHeight / cyChar;::SetScrollInfo(this->hwndWin, SB_VERT, &si, TRUE);if (((this->scrnHeight - yCaret) / cyChar) < 1) {::SendMessage(this->hwndWin, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), NULL);}}::ShowCaret(this->hwndWin);::InvalidateRect(this->hwndWin, NULL, false);}break;case '\b':{::HideCaret(this->hwndWin);            int posInLine = this->bufPos - pThisLine->vecLineOffset[this->iL];if ((posInLine > 0) && (this->iL == (pThisLine->vecLineOffset.size()-1))) {     //行内BackSpace                       int totalLineLength = static_cast<int>(pThisLine->_text.size());if (bufPos == totalLineLength) {    //在行尾this->bufPos--;xCaret -= pThisLine->vecCharWidths[this->bufPos];pThisLine->_text.pop_back();}else {this->bufPos--;     //在行内xCaret -= pThisLine->vecCharWidths[this->bufPos];pThisLine->_text.erase(bufPos, 1);                    for (int i = bufPos; i < (totalLineLength-1); i++) {pThisLine->vecCharWidths[i] = pThisLine->vecCharWidths[i+ 1];}}               pThisLine->vecCharWidths.pop_back();this->isTextAltered = TRUE;}else if ((posInLine > 0) && (this->iL < (pThisLine->vecLineOffset.size() - 1))) {    //wrapped行this->bufPos--;xCaret -= pThisLine->vecCharWidths[this->bufPos];pThisLine->_text.erase(bufPos, 1);for (int i = bufPos; i < (pThisLine->_text.size() - 1); i++) {pThisLine->vecCharWidths[i] = pThisLine->vecCharWidths[i + 1];}pThisLine->vecCharWidths.pop_back();this->isTextAltered = TRUE;pThisLine->ReLayoutLine(this->scrnWidth);}else if (0 == posInLine) {               //在行头if (this->iL > 0 ) {                    //前面是同一行, 删除一个字符, ReLayoutLine                   this->bufPos--;this->iL--;xCaret = scrnLEFTMARGIN;int prevLineWidth = pThisLine->LinePaintWidth(this->iL);xCaret += prevLineWidth- pThisLine->vecCharWidths[this->bufPos];  //光标上移,删除一个字符yCaret -= cyChar;                    pThisLine->_text.erase(bufPos, 1);for (int i = bufPos; i < (pThisLine->_text.size() - 1); i++) {pThisLine->vecCharWidths[i] = pThisLine->vecCharWidths[i + 1];}pThisLine->vecCharWidths.pop_back();this->isTextAltered = TRUE;pThisLine->ReLayoutLine(this->scrnWidth);if (cyChar) {this->totalLines = pFirstLine->GetTotalScnLines(pFirstLine);si.cbSize = sizeof(si);si.fMask = SIF_RANGE | SIF_PAGE; si.nMin = 0;si.nMax = totalLines;si.nPage = this->scrnHeight / cyChar;::SetScrollInfo(this->hwndWin, SB_VERT, &si, TRUE);if (yCaret < cyChar) {::SendMessage(this->hwndWin, WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), NULL);}}}else {                //前面是另一行, 两行合并, 移动到前一行删除当前行,不删除字符, ReLayoutLineint prevLineiL = this->iL;LineNode* pTemp = pThisLine->GetPrevScrnLine(pThisLine, prevLineiL);if (pTemp && (pTemp != pThisLine)) {    //有前一行xCaret = scrnLEFTMARGIN;if (pThisLine->_text.empty()) {   //如果这一行为空,移到前一行,删除这行pThisLine = pTemp;this->iL = prevLineiL;bufPos = static_cast<int>(pThisLine->_text.size());xCaret += pThisLine->LinePaintWidth(this->iL);yCaret -= cyChar;if (yCaret < 0) {yCaret = 0;}pThisLine->DeleteLineAfterThis();this->isTextAltered = TRUE;}else {   //两行合并不删除字符bufPos = static_cast<int>(pTemp->_text.size());xCaret += pTemp->LinePaintWidth(prevLineiL);yCaret -= cyChar;if (yCaret < 0) {yCaret = 0;}pTemp->_text.append(pThisLine->_text);for (int i = 0; i < pThisLine->_text.size(); i++) {pTemp->vecCharWidths.push_back(pThisLine->vecCharWidths[i]);}this->iL = prevLineiL;pThisLine = pTemp;pThisLine->DeleteLineAfterThis();this->isTextAltered = TRUE;pThisLine->ReLayoutLine(this->scrnWidth);                            }}if (cyChar) {this->totalLines = pFirstLine->GetTotalScnLines(pFirstLine);si.cbSize = sizeof(si);si.fMask = SIF_RANGE | SIF_PAGE; // | SIF_DISABLENOSCROLL;si.nMin = 0;si.nMax = totalLines;si.nPage = this->scrnHeight / cyChar;::SetScrollInfo(this->hwndWin, SB_VERT, &si, TRUE);if (yCaret < cyChar) {::SendMessage(this->hwndWin, WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), NULL);}}}}::InvalidateRect(this->hwndWin, NULL, false);::ShowCaret(this->hwndWin);}break;case '\t':::SendMessage(this->hwndWin, WM_CHAR, L' ', 4);     //tab键加4个空格, 临时方案break;default:{if ((wParam != 9) && (wParam < 0x20)) {break;}               //文字输入pThisLine->_text.insert(bufPos, 1, (TCHAR)wParam);   //插入字符this->isTextAltered = TRUE;            ::HideCaret(this->hwndWin);hdc = ::GetDC(this->hwndWin);         ::SelectObject(hdc, hFontSet);SIZE sz;::GetTextExtentPoint32(hdc, &pThisLine->_text[bufPos], 1, &sz);  //字符的屏幕宽度sz.cx,  存入vecCharWidthspThisLine->vecCharWidths.insert(pThisLine->vecCharWidths.begin() + bufPos, sz.cx);int textLength = static_cast<int>(pThisLine->_text.size());if (textLength == (bufPos + 1)) {                   //在行尾if ((this->xCaret + sz.cx) > scrnWidth) {       //超宽,到下一行pThisLine->vecLineOffset.push_back(bufPos);++iL;xCaret = scrnLEFTMARGIN + sz.cx;yCaret += cyChar;}else {this->xCaret += sz.cx;}}else {int textWidth = pThisLine->LinePaintWidth(iL);if ((textWidth > scrnWidth) || ((pThisLine->vecLineOffset.size() - 1) > this->iL)) {  //wrapline也要ReLayoutpThisLine->ReLayoutLine(scrnWidth);int scrnLineNum = pThisLine->ReCalCaretPos(pFirstLine, this->iL, xCaret, bufPos + 1);si.cbSize = sizeof(si);si.fMask = SIF_POS;::GetScrollInfo(this->hwndWin, SB_VERT, &si);scrollYPos = si.nPos;scrnLineNum -= scrollYPos;                   yCaret = scrnLineNum * cyChar;}            else {this->xCaret += sz.cx;}}++bufPos;::ReleaseDC(this->hwndWin, hdc);if (cyChar) {this->totalLines = pFirstLine->GetTotalScnLines(pFirstLine);si.cbSize = sizeof(si);si.fMask = SIF_RANGE | SIF_PAGE; // | SIF_DISABLENOSCROLL;si.nMin = 0;si.nMax = totalLines;si.nPage = this->scrnHeight / cyChar;::SetScrollInfo(this->hwndWin, SB_VERT, &si, TRUE);if (((this->scrnHeight - yCaret) / cyChar) < 1) {::SendMessage(this->hwndWin, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), NULL);}} ::ShowCaret(this->hwndWin);::InvalidateRect(this->hwndWin, NULL, false);} //end of default}//end of for loop}::SetCaretPos(xCaret, yCaret);
}void TextEditor::FileNew(){if (this->AbortAnyway()) {pFirstLine->ClearContent(&pFirstLine, &pThisLine);this->isTextAltered = false;this->bufPos = 0;this->iL = 0;this->xCaret = scrnLEFTMARGIN;this->yCaret = 0;::SetCaretPos(xCaret, yCaret);::InvalidateRect(this->hwndWin, NULL, false);}
}bool TextEditor::FileSaveAs(){TCHAR openName[MAX_PATH] = TEXT("\0");   TCHAR ext[] = TEXT(".txt\0") TEXT("\0\0");  TCHAR szFilter[] = TEXT("文本文件 (*.txt)\0*.txt\0")    TEXT("所有文件 (*.*)\0*.*\0\0");OPENFILENAME ofn = { sizeof(ofn) };ofn.hwndOwner = this->hwndWin;ofn.hInstance = hInst;ofn.lpstrFilter = szFilter;ofn.lpstrDefExt = ext;ofn.nFilterIndex = 1;ofn.lpstrFile = openName;ofn.nMaxFile = sizeof(openName);ofn.lpstrTitle = TEXT("保存文件");ofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;if (::GetSaveFileName(&ofn)) {      this->DoFileSave(openName);       }return TRUE;
}bool TextEditor::DoFileSave(LPTSTR file) {const BYTE UnicodeHead[2] = { 0xFF,0xFE };   //BOM: UTF-16 LEstd::ofstream ofp;ofp.open(file, std::ios::out | std::ios::binary);if (!ofp.is_open()) {MessageBox(this->hwndWin, TEXT("创建文件失败! 文件未保存!"), TEXT("提示"),MB_OK | MB_ICONEXCLAMATION);return FALSE;}ofp.write((char*)UnicodeHead, 2);LineNode* pTemp = pFirstLine;size_t j = 0;while (NULL != pTemp) {j = pTemp->_text.size();ofp.write((char*)pTemp->_text.data(), (int)(j * 2));ofp.write((char*)strEOLCRLF, int(wcslen(strEOLCRLF) * 2));pTemp = pTemp->_nextLine;}ofp.close();this->isTextAltered = FALSE;return TRUE;
}void TextEditor::onWmVscroll(WPARAM wParam) {// Get all the vertial scroll bar information.si.cbSize = sizeof(si);si.fMask = SIF_ALL;GetScrollInfo(this->hwndWin, SB_VERT, &si);// Save the position for comparison later on.this->scrollYPos = si.nPos;switch (LOWORD(wParam)){// User clicked the HOME keyboard key.case SB_TOP:si.nPos = si.nMin;break;// User clicked the END keyboard key.case SB_BOTTOM:si.nPos = si.nMax;break;// User clicked the top arrow.case SB_LINEUP:si.nPos -= 1;break;// User clicked the bottom arrow.case SB_LINEDOWN:si.nPos += 1;break;// User clicked the scroll bar shaft above the scroll box.case SB_PAGEUP:si.nPos -= si.nPage;break;// User clicked the scroll bar shaft below the scroll box.case SB_PAGEDOWN:si.nPos += si.nPage;break;// User dragged the scroll box.case SB_THUMBTRACK:si.nPos = si.nTrackPos;break;default:break;}// Set the position and then retrieve it.  Due to adjustments// by Windows it may not be the same as the value set.si.fMask = SIF_POS;SetScrollInfo(this->hwndWin, SB_VERT, &si, TRUE);GetScrollInfo(this->hwndWin, SB_VERT, &si);if (si.nPos != scrollYPos){yCaret += cyChar * (scrollYPos - si.nPos); ScrollWindow(this->hwndWin, 0, cyChar * (scrollYPos - si.nPos), NULL, NULL);UpdateWindow(this->hwndWin);}
}
BOOL TextEditor::AbortAnyway()
{if (this->isTextAltered) {if (IDYES != MessageBox(this->hwndWin, TEXT("文本已修改! 您要放弃修改吗?"),TEXT("提示"), MB_YESNO | MB_ICONQUESTION)) {return FALSE;}}return TRUE;}
void TextEditor::onIMENotify(WPARAM wParam) {HIMC hIMC = ::ImmGetContext(this->hwndWin);if (hIMC) { COMPOSITIONFORM CompForm;CompForm.dwStyle = CFS_POINT;CompForm.ptCurrentPos.x = xCaret;CompForm.ptCurrentPos.y = yCaret;::ImmSetCompositionWindow(hIMC, &CompForm);        }::ImmReleaseContext(this->hwndWin, hIMC);
}void TextEditor::onSetFoucus() {CreateCaret(this->hwndWin, NULL, 1, cyChar);SetCaretPos(xCaret, yCaret);ShowCaret(this->hwndWin);
}
void TextEditor::onKillFocus() {HideCaret(this->hwndWin);DestroyCaret();
}
/=======Windows====================================================================================================
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {switch (message){case WM_CREATE:{pEditor = new TextEditor;pEditor->onCreate(hWnd); }break;case WM_SIZE:pEditor->onSize(lParam);break;case WM_PAINT:pEditor->PaintDC(wParam, lParam);break;case WM_KEYDOWN:switch (wParam) {case 'S':{          if ((GetKeyState(VK_CONTROL) & 0x80) != 0) {pEditor->FileSaveAs();}}break;case 'N':{if ((GetKeyState(VK_CONTROL) & 0x80) != 0) {pEditor->FileNew();}}break;case VK_RIGHT:case VK_LEFT:case VK_UP:case VK_DOWN:case VK_DELETE:pEditor->onVKeyDown(wParam, lParam);}break;case WM_CHAR:pEditor->onWmChar(wParam, lParam);break;case WM_MOUSEMOVE:break;case WM_LBUTTONDOWN:pEditor->onMouseLBDown(lParam);break;case WM_VSCROLL:pEditor->onWmVscroll(wParam);break;case WM_MOUSEWHEEL:{        UINT userSetting;BOOL success = SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &userSetting, 0);if (success == FALSE)userSetting = 1;int zDelta = GET_WHEEL_DELTA_WPARAM(wParam);int yScroll = (zDelta / WHEEL_DELTA) * userSetting;int xScroll = 0;if (yScroll > 0) {for (int i = 1; i <= yScroll; i++) {SendMessage(hWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), 0);}}else if (yScroll < 0) {yScroll = 0 - yScroll;for (int i = 1; i <= yScroll; i++) {SendMessage(hWnd, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), 0);}}        }break;case WM_IME_NOTIFY:if (IMN_SETCOMPOSITIONWINDOW == wParam) {break;}pEditor->onIMENotify(wParam);break; case WM_SETFOCUS:pEditor->onSetFoucus();break;case WM_KILLFOCUS:pEditor->onKillFocus();break;case WM_COMMAND:{int wmId = LOWORD(wParam);switch (wmId){case IDM_EXIT:   //未使用if (pEditor) {delete pEditor;pEditor = nullptr;               }DestroyWindow(hWnd);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}}break;case WM_SYSCOMMAND: {if (SC_CLOSE == wParam) {if (pEditor->AbortAnyway()) {if (pEditor) {delete pEditor;pEditor = nullptr;                   }DestroyWindow(hWnd);}break;}return DefWindowProc(hWnd, message, wParam, lParam);}break;case WM_ERASEBKGND:return TRUE;break;   case WM_DESTROY:PostQuitMessage(0);break;default:return DefWindowProc(hWnd, message, wParam, lParam);}return 0;
}/Windows===========================================================================================================
ATOM MyRegisterClass(HINSTANCE hInstance){WNDCLASSEXW wcex;wcex.cbSize = sizeof(WNDCLASSEX);wcex.style = CS_HREDRAW | CS_VREDRAW;wcex.lpfnWndProc = WndProc;wcex.cbClsExtra = 0;wcex.cbWndExtra = 0;wcex.hInstance = hInstance;wcex.hIcon = NULL; wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);wcex.lpszMenuName = NULL;wcex.lpszClassName = L"EditorWindow";wcex.hIconSm = NULL;return RegisterClassExW(&wcex);
}BOOL InitInstance(HINSTANCE hInstance, int nCmdShow){hInst = hInstance;HWND hwndDesktop = ::GetDesktopWindow();HDC hScrDC = ::GetWindowDC(hwndDesktop);   int xScrn = ::GetDeviceCaps(hScrDC, HORZRES);int yScrn = ::GetDeviceCaps(hScrDC, VERTRES);HWND hWnd = CreateWindowExW(0, L"EditorWindow", L"Editor", WS_OVERLAPPEDWINDOW,xScrn / 2 - 50, yScrn / 2 - 80, xScrn/2, yScrn/2, nullptr, nullptr, hInstance, nullptr);if (!hWnd){return FALSE;}::ShowWindow(hWnd, nCmdShow);::UpdateWindow(hWnd);return TRUE;
}int APIENTRY wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow){  if (! MyRegisterClass(hInstance) || !InitInstance(hInstance, nCmdShow)) {return FALSE;}MSG msg;while (GetMessage(&msg, nullptr, 0, 0)) {TranslateMessage(&msg);DispatchMessage(&msg);}return (int)msg.wParam;
}/LineNode==========================================================================================================LineNode::LineNode()
{this->_lineNum = 0;   this->_prevLine = NULL;this->_nextLine = NULL;this->vecCharWidths.clear();this->vecLineOffset.clear();this->vecLineOffset.push_back(0);
}LineNode::~LineNode()
{}LineNode* LineNode::AddLineAfterThis() {LineNode* pNewLine = new LineNode;if (!pNewLine) {return NULL;}pNewLine->_lineNum = this->_lineNum + 1;pNewLine->_nextLine = this->_nextLine;this->_nextLine = pNewLine;pNewLine->_prevLine = this;if (NULL != (pNewLine->_nextLine)) {LineNode* ptemp = pNewLine->_nextLine;ptemp->_prevLine = pNewLine;do{ptemp->_lineNum++;ptemp = ptemp->_nextLine;} while (NULL != ptemp);}return pNewLine;
}BOOL LineNode::DeleteLineAfterThis() {LineNode* pTemp = this->_nextLine;if (NULL == pTemp) {return FALSE;}this->_nextLine = pTemp->_nextLine;if (NULL != pTemp->_nextLine) {pTemp->_nextLine->_prevLine = this;}delete pTemp;if (NULL != this->_nextLine) {pTemp = this->_nextLine;do {pTemp->_lineNum--;pTemp = pTemp->_nextLine;} while (NULL != pTemp);}return TRUE;
}BOOL LineNode::ClearContent(LineNode** pFirstLine, LineNode** pThisLine) {LineNode* pTemp = (*pFirstLine)->_nextLine;LineNode* pTemp1 = NULL;(*pFirstLine)->_nextLine = NULL;*pThisLine = *pFirstLine;  (*pFirstLine)->_text.erase(0);(*pFirstLine)->vecCharWidths.clear();(*pFirstLine)->vecLineOffset.clear();(*pFirstLine)->vecLineOffset.push_back(0);while (NULL != pTemp) {pTemp1 = pTemp->_nextLine;delete pTemp;pTemp = pTemp1;}return TRUE;
}int LineNode::GetTotalScnLines(LineNode* pfirstLine) {LineNode* pTemp = pfirstLine;int totalLines = static_cast<int>(pTemp->vecLineOffset.size());while (NULL != pTemp->_nextLine) {pTemp = pTemp->_nextLine;totalLines += static_cast<int>(pTemp->vecLineOffset.size());}return totalLines;
}LineNode* LineNode::GetTheScrnLine(LineNode* pfirstLine, int& iLineOffset, int lineNum) {++lineNum;if (lineNum <= pfirstLine->vecLineOffset.size()) {iLineOffset = lineNum-1;return pfirstLine;}else {LineNode* pTemp = pfirstLine;int totalLines = static_cast<int>(pfirstLine->vecLineOffset.size());while (lineNum > totalLines) {if (NULL == pTemp->_nextLine) {iLineOffset = 0;return NULL;break;}pTemp = pTemp->_nextLine;totalLines += static_cast<int>(pTemp->vecLineOffset.size());}iLineOffset = static_cast<int>(pTemp->vecLineOffset.size()) - (totalLines - (lineNum-1));return pTemp;}
}LineNode* LineNode::GetPrevScrnLine(LineNode* ptheLine, int& iLineOffset) {if (iLineOffset > 0) {iLineOffset--;return ptheLine;}else {if (ptheLine->_prevLine) {iLineOffset = static_cast<int>(ptheLine->_prevLine->vecLineOffset.size()) - 1;return ptheLine->_prevLine;}else {iLineOffset = 0;return NULL;}}
}LineNode* LineNode::GetNextScrnLine(LineNode* ptheLine, int& iLineOffset) {if (iLineOffset < (ptheLine->vecLineOffset.size() - 1)) {iLineOffset++;return ptheLine;}else {iLineOffset = 0;if (!ptheLine->_nextLine) {return NULL;}else {return ptheLine->_nextLine;}}
}LONG LineNode::LinePaintWidth(int iLineOffset) {if (this->vecCharWidths.empty()) {          //空行return 0;}if (iLineOffset >= this->vecLineOffset.size()) {return -1;}LONG paintWidth = 0;if ((0 == iLineOffset) && (1 == this->vecLineOffset.size())) {  //只有一行for (LONG value : this->vecCharWidths) {paintWidth += value;}return paintWidth;}if (iLineOffset == (this->vecLineOffset.size() - 1)) {//最后一行int iLineStart = this->vecLineOffset[iLineOffset];int lineLength = static_cast<int>(this->_text.size()) - iLineStart;for (int i = 0; i < lineLength; i++) {paintWidth += this->vecCharWidths[iLineStart + i];}return paintWidth;}int iLineStart = this->vecLineOffset[iLineOffset];    //非最后一行int lineLength = this->vecLineOffset[iLineOffset+1] - iLineStart;for (int i = 0; i < lineLength; i++) {paintWidth += this->vecCharWidths[iLineStart + i];}return paintWidth;
}VOID LineNode::ReLayoutLine(LONG scrnWidth) {this->vecLineOffset.clear();this->vecLineOffset.push_back(0);LONG charWidths = 0;for (int i = 0; i < this->_text.size(); i++) {if ((i>0) && (charWidths + this->vecCharWidths[i]) > scrnWidth) {this->vecLineOffset.push_back(i);charWidths = this->vecCharWidths[i];continue;} charWidths += this->vecCharWidths[i];}
}int LineNode::ReCalCaretPos(LineNode* pFirstLine,int& iLineOffset, int& xCaret, int bufPos) { //pThisLine调用LineNode* pTemp = pFirstLine;int scrnLineNum = 0;while (pTemp != this) {scrnLineNum += static_cast<int>(pTemp->vecLineOffset.size());pTemp = pTemp->_nextLine;}if (1 == this->vecLineOffset.size()) {xCaret = scrnLEFTMARGIN;iLineOffset = 0;   //可以删除for (int i = 0; i < bufPos; i++) {xCaret += this->vecCharWidths[i];}return scrnLineNum;}int numberOfLinesInThis = static_cast<int>(this->vecLineOffset.size());if (bufPos >= this->vecLineOffset[numberOfLinesInThis - 1]) {scrnLineNum += numberOfLinesInThis-1;iLineOffset = numberOfLinesInThis - 1;bufPos = bufPos - this->vecLineOffset[numberOfLinesInThis - 1];int iLineStart = this->vecLineOffset[numberOfLinesInThis - 1];xCaret = scrnLEFTMARGIN;for (int i = 0; i < bufPos; i++) {xCaret += this->vecCharWidths[iLineStart + i];}return scrnLineNum;}else {for (int i = 0; i < this->vecLineOffset.size(); i++) {if (this->vecLineOffset[i] > bufPos) {bufPos = bufPos - this->vecLineOffset[i - 1];int iLineStart = this->vecLineOffset[i - 1];iLineOffset = i - 1;xCaret = scrnLEFTMARGIN;for (int i = 0; i < bufPos; i++) {xCaret += this->vecCharWidths[iLineStart + i];}scrnLineNum += (i - 1);if (scrnLineNum < 0) {scrnLineNum = 0;}return scrnLineNum;}}}
}int LineNode::ScrnLineLength(int iLineOffset) {if (this->vecCharWidths.empty()) {return 0;}if (1 == this->vecLineOffset.size()) {return static_cast<int>(this->_text.size());}int numberOfLinesInThis = static_cast<int>(this->vecLineOffset.size());if (iLineOffset == (numberOfLinesInThis - 1)) {return (static_cast<int>(this->_text.size()) - this->vecLineOffset[iLineOffset]);}else {return (this->vecLineOffset[iLineOffset + 1] - this->vecLineOffset[iLineOffset]);}
}

VC++用新方法编写的最基本的用于交流学习的文本编辑软件相关推荐

  1. ASP.NET 2.0 本地化功能:本地化 Web 应用程序的新方法

    适用于: Microsoft ASP.NET 2.0 Microsoft Visual Studio .NET Microsoft Visual Studio 2005 本地化 摘要:随着越来越多的公 ...

  2. 一种通过scout ESI和CNN解码EEG运动想象四分类任务的新方法

    目录 导读 系统框架 实验 特征提取 结论 导读 东北电力大学和长春理工大学研究团队开发并实现一种结合脑电图源成像(ESI)技术和卷积神经网络(CNN)的新方法,以对运动想象(MI)任务进行分类.ES ...

  3. VC/MFC 进程间通信方法总结

    VC/MFC 进程间通信方法总结 摘   要   随着人们对应用程序的要求越来越高,单进程应用在许多场合已不能满足人们的要求.编写多进程 / 多线程程序成为现代程序设计的一个重要特点,在多进程程序设计 ...

  4. VC控件MSComm编写串口通信程序

    转载:http://blog.csdn.net/liangzhao_jay/article/details/45647229 在众多网友的支持下,串口调试助手从2001年5月21日发布至今,短短一个月 ...

  5. Yammer Metrics,一种监视应用程序的新方法

    当您运行诸如Web应用程序之类的长期应用程序时,最好了解一些关于它们的统计信息,例如,服务的请求数,请求持续时间或活动请求数. 但是还有一些更通用的信息,例如内部集合的状态,代码的某些部分被执行了多少 ...

  6. orcale可视化建立用户_建立动态可视化的新方法

    orcale可视化建立用户 by Sushrut Shivaswamy 通过Sushrut Shivaswamy 建立动态可视化的新方法 (A new way of building dynamic ...

  7. Java 11:String类中的新方法

    Java 11:String类中的新方法 Java 11没有很多特定于语言的功能.因此,令人惊讶的是,在Java String Class中引入了6种新方法. Java 11 String类新方法 让 ...

  8. Java 11:字符串类中的新方法

    Java 11 doesn't have a lot of language specific features. So, it was surprising to see 6 new methods ...

  9. 数学模型天气预测方法_预测即将到来的天气的新方法

    数学模型天气预测方法 By: Teja Balasubramanian 创建人:Teja Balasubramanian A new wave arises. Computer programming ...

最新文章

  1. Neurala与CSDN宣布战略合作,将一站式AI平台BrainBuilder带给中国开发者
  2. 聊天机器人有了长期记忆,遇到不懂的还能上网搜索,网友:像极了不懂装懂时偷偷百度的我...
  3. 应力循环次数60ant_中国航发:金属粉末循环使用导致的成分及打印件性能变化...
  4. python语言程序设计书-清华大学出版社-图书详情-《Python语言程序设计》
  5. 莱特准则 matlab,初学MATLAB,遇到一简单的题目,一点头绪也没有啊.99
  6. Hadoop学习之MapReduce(三)
  7. 东华之旅vs第一次坐飞机经历2018-06-10
  8. POJ 3087 Shuffle'm Up DFS
  9. 化工原理期中考,流体
  10. 约束布局constraint-layout导入失败的解决方案 - 转
  11. 哪里有mysql认证_国内哪个城市可以考mysql认证
  12. centos7下docker启动失败解决
  13. 平面设计师okr_设计团队如何推进OKR实现设计赋能
  14. 动态规划经典题:给出两个字符串s1和s2,返回其中最大的公共子串
  15. php怎么写确认密码,如何在Laravel 5中验证当前密码,新密码和新密码的确认? - php...
  16. 约瑟夫问题的数学方法
  17. Elasticsearch的javaAPI之percolator
  18. 蓝鸽集团云计算机,‎App Store 上的“蓝鸽教育云”
  19. 多元异方差检验 怀特检验 white检验python实现
  20. 项目管理知识体系指南学习(三)项目整合管理

热门文章

  1. 冻结表格(tablefix)
  2. JAVA使用POI对Excel中的条件格式处理
  3. win10如何提高电脑画质_教你win10系统提高图片分辨率的修复教程
  4. 原型工具之团队协作: Axure VS Mockplus
  5. 我的网站是用阿里云的独立云虚拟主机,也能配置https加密吗?
  6. 使用node编译sol文件报错
  7. 02《软件需求分析教程》
  8. 65W多功能氮化镓扩展坞一体式快充电源CD0205如何实现的
  9. 那些年,我与IE的爱恨别离(一)
  10. 2022.1版本idea 安装教程