龙空技术网

【新书推荐】5.5 插入符号

爱达人编程达人 25

前言:

今天兄弟们对“mac打印pdf报错invalidfont”可能比较关切,小伙伴们都想要分析一些“mac打印pdf报错invalidfont”的相关资讯。那么小编在网摘上网罗了一些对于“mac打印pdf报错invalidfont””的相关内容,希望大家能喜欢,我们快快来学习一下吧!

当你向程序中输入文本时,通常会有下划线、竖线或方框指示你输入的下一个字符将出现在屏幕上的位置。你也许认为这是“光标”,但在编写Windows程序时,你必须避免这种习惯。在Windows中,它被称为“插入符号”(caret)。“光标”(cursor)特指表示鼠标位置的位图图像,即鼠标指针。

本节必须掌握的知识点:

关于插入符号的函数

第34练:文本编辑器插入符号

5.5.1 关于插入符号的函数

■Windows中有五个基本的插入符号函数:

●CreateCaret:创建和窗口关联的插入符号。

●SetCaretPos:设置窗口内的插入符号的位置。

●ShowCaret:显示插入符号。

●HideCaret:险藏插入符号。

●DestroyCaret:销毁插入符号。

■操作插入符的函数

此外,还有用于获得当前插入符号位置的函数(GetCaretPos)与获得和设置插入符号闪烁时间的函数(GetCaretBlinkTime 和 SetCaretBlinkTime)。

●GetCaretPos 函数的原型:

BOOL GetCaretPos(

LPPOINT lpPoint // 接收光标位置的指针

);

返回值:

如果函数调用成功,返回值为非零值(TRUE)。

如果函数调用失败,返回值为零(FALSE)。

使用 GetCaretPos 函数可以获取当前光标的位置,即光标在屏幕上的坐标。通过传递一个指向 POINT 结构的指针,函数将光标的坐标信息存储在该结构中。

示例代码:

#include <Windows.h>

int main() {

POINT caretPos;

// 获取光标位置

if (GetCaretPos(&caretPos)) {

// 输出光标的坐标

printf("Caret position: x = %d, y = %d\n", caretPos.x, caretPos.y);

} else {

// 获取光标位置失败

printf("Failed to get caret position.\n");

}

return 0;

}

●GetCaretBlinkTime函数原型:

UINT GetCaretBlinkTime();

返回值:

返回一个无符号整数,表示光标闪烁的时间间隔(以毫秒为单位)。

使用 GetCaretBlinkTime 函数可以获取当前系统中光标闪烁的时间间隔。光标闪烁是指光标在显示和隐藏之间的交替效果。

示例代码:

#include <Windows.h>

int main() {

UINT blinkTime = GetCaretBlinkTime();

// 输出光标闪烁时间间隔

printf("Caret blink time: %u ms\n", blinkTime);

return 0;

}

●SetCaretBlinkTime函数原型:

BOOL SetCaretBlinkTime(

UINT uMSeconds // 光标闪烁的时间间隔(以毫秒为单位)

);

返回值:

如果函数调用成功,返回值为非零值(TRUE)。

如果函数调用失败,返回值为零(FALSE)。

使用 SetCaretBlinkTime 函数可以设置光标闪烁的时间间隔。你可以将所需的时间间隔(以毫秒为单位)作为参数传递给该函数。

示例代码:

#include <Windows.h>

int main() {

UINT blinkTime = 500; // 设置光标闪烁时间间隔为 500 毫秒

// 设置光标闪烁时间间隔

if (SetCaretBlinkTime(blinkTime)) {

printf("Caret blink time set successfully.\n");

} else {

printf("Failed to set caret blink time.\n");

}

return 0;

}

■处理插入符

在Windows中,插入符号通常是一个字符大小的水平线或方框,或是与字符高度相一 致的竖线。使用变宽字体的时候推荐使用竖线插入符号,比如Windows默认系统字体。因 为变宽字体字符不是固定大小的,不能把水平线或者方框设置为字符的大小。

如果你的程序中需要插入符号,那么不应该简单地在窗口过程的WM_CREATE消息中创建它并在WM_DESTROY消息中销毁它。不建议你这样做的原因是一个消息队列仅能够支持一个插入符号。因此,如果你的程序有多于一个窗口,则多个窗口必须有效地共享同一个插入符号。

这并不像听起来那样有限制性。你想一下,仅当窗口具有输入焦点时,窗口中插入符号的显示才有意义。的确,闪烁的插入符号的存在是一种视觉提示:它让用户意识到他可以向程序中输入文本。因为任何时候仅有一个窗口具有输入焦点,所有多个窗口同时使插入符号闪烁是没有意义的。

程序能通过处理WM_SETFOCUS消息和WM_KILLFOCUS消息来决定它是否具有输入焦点。正如名称所暗示的,当窗口过程接收输入焦点时,它接收到一个WM_SETFOCUS 消息:当它失去输入焦点时,收到一个WM_KILLFOCUS消息。这些消息成对出现:窗口过程在接收到一条WM_KILLFOCUS消息前,总是会接收到一条WM_SETFOCUS消息。 并且在窗口的生命期窗口过程总是接收到相同数目的WM_SETFOCUS消息和 WM_KILLFOCUS 消息。

使用插入符号的主要规则很简单:在窗口过程处理WM_SETFOCUS消息时调用 CreateCaret 函数,处理 WM_KILLFOCUS 消息时调用 DestroyCaret 函数。

还有一些其他规则:创建的插入符号是隐藏的。在调用CreateCaret之后,窗口过程必 须调用ShowCaret使之可见。另外,如果窗口过程处理的是一个非WM_PAINT消息,但要在窗口内绘制某些东西时,它必须调用HideCaret隐藏插入符号。当它结束在窗口内的绘制之后,再调用ShowCaret来显示插入符号。HideCaret的效果是叠加的:如果你调用了HideCaret很多次,但没调用过ShowCaret,那么你必须再调用同样次数的ShowCaret才能使插入符号可见。

5.5.2 第34练:文本编辑器插入符号

/*---------------------------------------------

034 WIN32 API 每日一练

第34个例子TYPER.C:文本编辑器---插入符号

GetFocus函数

CreateCaret函数

SetCaretPos函数

ShowCaret函数

HideCaret函数

DestroyCaret函数

GetCaretPos函数

WM_SETFOCUS消息

WM_KILLFOCUS消息

注:TYPER程序 不允许使用 双字节宽度的字符

无搜索,替换,保存文件,拼写检查,滚动,帮助等扩展功能。

(c) , 2020

-------------------------------------------*/

#include <windows.h>

#define BUFFER(x,y) *(pBuffer + y * cxBuffer + x)

TCHAR * pBuffer = NULL;

LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,

PSTR szCmdLine, int iCmdShow)

{

static TCHAR szAppName[] = TEXT("Typer");

…(略)

return msg.wParam;

}

LRESULT CALLBACK WndProc ( HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam)

{

static DWORD dwCharSet = DEFAULT_CHARSET ;//#define DEFAULT_CHARSET 1

static int cxChar, cyChar, cxClient, cyClient,

cxBuffer, cyBuffer,xCaret, yCaret ;

//static TCHAR * pBuffer = NULL ;

HDC hdc ;

int x, y, i ;

PAINTSTRUCT ps ;

TEXTMETRIC tm ;

switch (message)

{

case WM_INPUTLANGCHANGE:

dwCharSet = wParam ;

//继续执行下去

case WM_CREATE:

hdc = GetDC (hwnd) ;

//创建逻辑字体

SelectObject ( hdc, CreateFont (0, 0, 0, 0, 0, 0, 0, 0,

dwCharSet, 0, 0, 0, FIXED_PITCH, NULL)) ;

//获取字体信息

GetTextMetrics (hdc, &tm) ;

cxChar = tm.tmAveCharWidth ;

cyChar = tm.tmHeight ;

DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT))) ;

ReleaseDC (hwnd, hdc) ;

//继续执行下去

case WM_SIZE:

// 获取窗口大小,以像素为单位

if (message == WM_SIZE)

{

cxClient = LOWORD (lParam) ;

cyClient = HIWORD (lParam) ;

}

// 计算窗口大小(字符)

cxBuffer = max (1, cxClient / cxChar) ; //cxBuffer窗口字符宽度

cyBuffer = max (1, cyClient / cyChar) ; //cyBuffer窗口字符高度

// 为缓冲区分配内存并清除它

if (pBuffer != NULL)

free (pBuffer) ;

//创建保存窗口所有字符的缓冲区

pBuffer = (TCHAR *) malloc (cxBuffer * cyBuffer * sizeof (TCHAR)) ;

//初始化pBuffer为一个‘ ’空字符

for (y = 0 ; y < cyBuffer ; y++)

for (x = 0 ; x < cxBuffer ; x++)

BUFFER(x,y) = ' ' ;

// 插入符号定位左上角

xCaret = 0 ;

yCaret = 0 ;

if (hwnd == GetFocus ()) //插入符号为当前窗口---焦点窗口

//设置插入符号位置

SetCaretPos (xCaret * cxChar, yCaret * cyChar) ;

InvalidateRect (hwnd, NULL, TRUE) ; //重绘窗口

return 0 ;

//获得键盘焦点后发送到窗口

case WM_SETFOCUS:

// 创建和显示插入符号,指定大小,NULL表示实心

CreateCaret (hwnd, NULL, cxChar, cyChar) ;

SetCaretPos (xCaret * cxChar, yCaret * cyChar) ; //设置符号位置

ShowCaret (hwnd) ; //显示符号

return 0 ;

//在失去键盘焦点之前立即发送到窗口

case WM_KILLFOCUS:

// 隐藏和删除插入符号

HideCaret (hwnd) ; //隐藏符号

DestroyCaret () ; //删除符号

return 0 ;

//击键消息---光标移动的处理

case WM_KEYDOWN:

switch (wParam)

{

case VK_HOME:

xCaret = 0 ;

break ;

case VK_END:

xCaret = cxBuffer - 1 ;

break ;

case VK_PRIOR: //Page Up

yCaret = 0 ;

break ;

case VK_NEXT: //PageDown

yCaret = cyBuffer - 1 ;

break ;

case VK_LEFT:

xCaret = max (xCaret - 1, 0) ;

break ;

case VK_RIGHT:

xCaret = min (xCaret + 1, cxBuffer - 1) ;

break ;

case VK_UP:

yCaret = max (yCaret - 1, 0) ;

break ;

case VK_DOWN:

yCaret = min (yCaret + 1, cyBuffer - 1) ;

break ;

case VK_DELETE: //注意VK_BACK键当字符消息去处理,这里只处理VK_DELETE

//将该行从当前位置后面字符依次前移一格

for (x = xCaret ; x < cxBuffer - 1 ; x++)

BUFFER(x, yCaret) = BUFFER(x + 1, yCaret);

BUFFER(cxBuffer - 1, yCaret) = ' ';//该行最后一个设为空字符。

//绘图前必须先隐藏插入符

HideCaret(hwnd);

hdc = GetDC(hwnd);

SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0,

dwCharSet, 0, 0, 0, FIXED_PITCH, NULL));

TextOut(hdc, xCaret * cxChar, yCaret * cyChar,

&BUFFER(xCaret, yCaret),

cxBuffer - xCaret); //重新输出该行xCaret后面的文字

DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));

ReleaseDC(hwnd, hdc);

ShowCaret(hwnd);

break;

}

SetCaretPos(xCaret * cxChar, yCaret * cyChar);

return 0;

//字符消息

case WM_CHAR:

for (i = 0 ; i < (int) LOWORD (lParam) ; i++)

{

//转义字符处理

switch (wParam)

{

case '\b': // 退格

if (xCaret > 0)

{

xCaret--;

//1为repeat字段的值

SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1);

}

break;

case '\t': //Tab键,8个字符长度

do

{

SendMessage (hwnd, WM_CHAR, ' ', 1) ;

} while (xCaret % 8 != 0) ;

break ;

case '\n': //换行符,改变y到下一行,x坐标没变。

if (++yCaret == cyBuffer)

yCaret = 0 ;

break ;

case '\r': // 回车

xCaret = 0;

if (++yCaret == cyBuffer)

yCaret = 0;

break;

//ESC键,清空屏幕 \xhh表示1到2位十六进制所代表的任意字符

case '\x1B':

for (y = 0; y < cyBuffer; y++)

for (x = 0; x < cxBuffer; x++)

BUFFER(x, y) = ' ';

xCaret = 0;

yCaret = 0;

InvalidateRect(hwnd, NULL, FALSE);

break;

default: // 字符编码

BUFFER(xCaret, yCaret) = (TCHAR)wParam;

HideCaret(hwnd);

hdc = GetDC(hwnd);

SelectObject(hdc, CreateFont(0, 0, 0, 0, 0, 0, 0, 0,

dwCharSet, 0, 0, 0, FIXED_PITCH, NULL));

TextOut(hdc, xCaret * cxChar, yCaret * cyChar,

&BUFFER(xCaret, yCaret), 1);

DeleteObject(

SelectObject(hdc, GetStockObject(SYSTEM_FONT)));

ReleaseDC(hwnd, hdc);

ShowCaret(hwnd);

if (++xCaret == cxBuffer)

{

xCaret = 0;

if (++yCaret == cyBuffer)

yCaret = 0;

}

break;

}

}

SetCaretPos (xCaret * cxChar, yCaret * cyChar) ;

return 0 ;

case WM_PAINT:

hdc = BeginPaint (hwnd, &ps) ;

SelectObject (hdc, CreateFont (0, 0, 0, 0, 0, 0, 0, 0,

dwCharSet, 0, 0, 0, FIXED_PITCH, NULL)) ;

for (y = 0 ; y < cyBuffer ; y++)

TextOut (hdc, 0, y * cyChar, & BUFFER(0,y), cxBuffer) ;

DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT))) ;

EndPaint (hwnd, &ps) ;

return 0 ;

case WM_DESTROY:

if (pBuffer != NULL) free(pBuffer);

PostQuitMessage(0);

return 0;

}

return DefWindowProc (hwnd, message, wParam, lParam) ;

}

/**************************************

CreateCaret:创建和窗口关联的插入符号

SetCaretPos:设置窗口内的插入符号的位置

ShowCaret :显示插入符号

HideCaret :隐藏插入符号

DestroyCaret:销毁插入符号

GetCaretPos :当前插入符号的位置

GetFocus函数:检索具有键盘焦点的窗口的句柄

*****************************************

WM_SETFOCUS消息:获得键盘焦点后发送到窗口

#define WM_SETFOCUS 0x0007

参数:wParam

失去键盘焦点的窗口句柄。此参数可以为NULL。

lParam:不使用此参数。

****************************************

WM_KILLFOCUS消息:在失去键盘焦点之前立即发送到窗口

#define WM_SETFOCUS 0x0008

参数:wParam

失去键盘焦点的窗口句柄。此参数可以为NULL。

lParam:不使用此参数。

*/

运行结果:

图5-7 文本编辑器插入符

总结

实例TYPER.C创建了一个简单的文本编辑器。

●窗口过程首先通过M_INPUTLANGCHANGE消息获取当前字符集,然后在WM_CREATE消息中选入新创建的逻辑字体,并获取新逻辑字体的字符宽和高。

●在WM_SIZE消息中,当窗口客户区大小发生变化时,使用空格字符清空窗口客户区,然后将插入符置于窗口客户区的左上角,然后重绘窗口。【注意】前提时当前窗口为焦点窗口。

●当窗口获得焦点时,窗口过程处理M_SETFOCUS消息,创建、设置并显示插入符。

●当窗口失去焦点时,窗口过程处理WM_KILLFOCUS消息,隐藏并删除插入符。

●在按键消息WM_KEYDOWN中,处理HOME、END、PageUp、PageDown和上下左右箭头键,计算插入符的新位置。处理Delete键稍微复杂一些,先将当前位置后面的字符依次前移一个位置,并在该行字符的结尾处添加一个空格。【注意】移动字符时,先隐藏插入符,再选入新创建的逻辑字体绘制字符,绘制完成后重新显示插入符。所有字符重新绘制完成后,重新将插入符置于该行字符的结尾处。

●窗口过程在WM_CHAR消息代码块绘制字符。我们把字符分为两类:

转义字符处理:

'\b'将字符位置减一,然后转为Delete按键消息处理。

'\t':转换为空格字符消息处理。

'\n':将字符yCaret加一(行数加一)。

'\r':将字符xCaret清零(列数清零)。

'\x1B':ESC键清空屏幕(窗口客户区输出空格),并将插入符坐标清零。

正常可见字符的处理:与Delete键的处理过程类似,先隐藏插入符,然后绘制字符后再重新显示插入符,再将插入符置于下一个位置。

●WM_PAINT消息选入新的逻辑字体绘制所有字符。

【注意】

实例创建的简单文本编辑器并不完善,并不支持复制、粘贴、剪切、选择等功能,也没有对应的菜单选项。我们将在后续的章节中逐步完善此文本编辑器。

此外,当我们输入中文字符时,需要收到将插入符后移一个位置,等待输入下一个字符。

本文摘自编程达人系列教材《Windows API每日一练》Windows程序设计基础篇。

标签: #mac打印pdf报错invalidfont