69,370
社区成员
发帖
与我相关
我的任务
分享
#include "stdio.h"
#include "dirent.h"
#include "string.h"
#include "windows.h"
char* TEXT=NULL; //存储文本文件的字符数组
int LEN=0; //文本的长度(TEXT数组的长度)
int* SITE=NULL; //记录每行输出点的数组
int LEN2=0; //SITE数组的长度
const char* DEFFONT = "楷体"; //默认字体
char* FONTNAME="楷体"; //字体名称的指针
//----------------------查看str后num个字符是不是完整的字符-----------------------------
int WholeWordAfter( char* str , int num )
{
int i=0;
for( i=0 ; i<num ; i++ )
if( str[i] < 0 ) //如果小于0,说明是一个中文符,+1跳过此中文符
i++;
return i==num? 1 : 0 ; //相等则说明完整,不等则说明包含了半个中文符
}
//----------------------------str字符串中,适应宽度wide应有多少个字符--------------------------------------
int GetTextNum( HDC hdc , int wide , char* str )
{
static int num=1; //输出的字节数,用static可使下次参考本次的数据,提高效率
if( str<TEXT || str >=TEXT+LEN ) //遇到不正确的地址应退出
return 0;
int len=LEN-(str-TEXT); //到文尾距离
char* str2 = strstr( str , "\n" ); //到换行的距离(输出长度不能超过这两个)
if( str2 != NULL ) //没有换行则考虑最远到文尾
len = str2+1-str; //+1是将\n考虑在内
if( num > len ) //输出最远不超过换行
num=len;
SIZE s;
GetTextExtentPoint32( hdc , str , num , &s ); //计算num个字节的字符的宽度
if( s.cx < wide ) //如果宽度比wide小
while( num<len && s.cx<wide )
GetTextExtentPoint32( hdc , str , ++num , &s ); //逐个字符累加试探到极限
else if( s.cx > wide ) //如果宽度本就比wide大
while( s.cx>wide )
GetTextExtentPoint32( hdc , str , --num , &s ); //逐个字符缩减至刚好的程度
if( s.cx > wide ) //如果超过wide,是第一个while试探超出,缩减1
num--;
if( WholeWordAfter( str , num ) == 0 ) //如果最后输出的是半个字符,缩减1,避免此情况发生
num--;
return num; //得出结果,对于宽度wide,应输出num个字节
}
//----------------------------计算在指定的宽度下,重新划分每一行文字--------------------------------------
void CalSite( HDC hdc , int wide )
{
int i=0;
SITE[0]=0;
for( i=0 ; i<LEN && SITE[i]<LEN ; i++ ) //SITE[i]记录的是第i行字符串第一个字符相对TEXT的位置
SITE[i+1]=SITE[i]+GetTextNum( hdc , wide , TEXT+SITE[i] );
LEN2 = i;
}
//--------------------------------------------------------------------------------------
LRESULT MyProc( HWND h , UINT msg , WPARAM wParam , LPARAM lParam )
{
static int WordHeight=0, LineSpace=0, LSpace=0, RSpace=0, USpace=0, DSpace=0;
static int ctrl=0, shift=0; //Ctrl键和Shift键按下的标志
static int LineMove=0,PageMove=0, lines=0; //移动一行、移动一页的标志,以及可显示的行数
static int ScrollMove=0, ScrollRange=0, ScrollPos=0 ; //滚动条滚动标志、滚动条范围、滑块位置
static HFONT font; //字体句柄
static int S=0,site=0, SiteFlag=0 ; //显示第一行字符串在SITE的位置、临时变量、SITE重新计算标志
switch( msg )
{
case WM_CREATE:
{
WordHeight = 30; //设置字体高度
LineSpace = 20; //设置行间距
LSpace =10; //设置到左边框的距离
RSpace =10; //设置到右边框的距离
USpace =20; //设置到上边框的距离
DSpace =20; //设置到下边框的距离
ScrollRange=1000; //设置滚动条范围(最大范围)
SetScrollRange( h , 1 , 0, ScrollRange, 1 ); //设置滚动条范围
font = CreateFont( WordHeight,0,0,0,0,0,0,0,0,0,0,0,0,FONTNAME ); //创建字体
if( font==NULL )
{
FONTNAME = (char*)DEFFONT; //创建失败则用默认字体
font = CreateFont( WordHeight,0,0,0,0,0,0,0,0,0,0,0,0,FONTNAME );//创建字体
}
}
return 0;
case WM_SIZE:
SiteFlag = 1; //尺寸变化,修改Site的标志置1
InvalidateRect( h , NULL , 1 ); //重绘
return 0;
case WM_PAINT:
{
RECT r;
GetClientRect( h , &r ); //计算客户区尺寸
int wide=r.right-r.left; //客户区宽
int height=r.bottom-r.top; //客户区高
HDC hdc = GetDC( h );
SelectObject( hdc , font );
if( SiteFlag == 1 ) //SITE数组需要重新计算(边框大小发生了变化)
{
lines = ( height-USpace-DSpace )/( WordHeight+LineSpace ); //计算文字显示行数
lines += ((height-USpace-DSpace)%(WordHeight+LineSpace))>WordHeight ? 1:0;
site = SITE[S]; //备份一下原本的输出位置SITE[S]
CalSite( hdc , wide-LSpace-RSpace ); //计算SITE数组
for( S=0 ; SITE[S]<=site && S<=LEN2 ; S++ ); //找出刚好比site大的SITE[S]
S--; //减1则是刚好比site小的SITE[S]
}
if( LineMove > 0 && S<LEN2 ) //往下翻动一行
S++;
else if( LineMove < 0 && S>0 ) //往上翻动一行
S--;
if( PageMove > 0 && S<LEN2 ) //往下翻一页
S = S+lines>LEN2 ? LEN2 : S+lines ;
else if( PageMove < 0 && S>0 ) //往上翻一页
S = S-lines<0 ? 0 : S-lines;
if( ScrollMove != 0 ) //如果拖动了滚动条滑块
{
if( (int)((double)ScrollPos/ScrollRange*LEN2 ) == S ) //如果S没变
return 0; //就没必要更新
S = (int)((double)ScrollPos/ScrollRange*LEN2 ); //S变化,更新S
}
SiteFlag=LineMove=PageMove=ScrollMove=0; //各个标志置0
SetScrollPos( h , 1 , (double)S/LEN2*ScrollRange , 1 ); //设置滑块位置
SendMessage( h , WM_ERASEBKGND , (WPARAM)hdc , 0 ); //重绘背景
for( int i=0 ; i<lines && S+i<LEN2 ; i++ ) //绘制
TextOut( hdc , LSpace , USpace+i*(WordHeight+LineSpace) ,
TEXT+SITE[S+i] , SITE[S+i+1]-SITE[S+i] );
ReleaseDC( h , hdc );
ValidateRect( h , NULL );
}
return 0;
case WM_MOUSEWHEEL: //滑动鼠标滚轮
if( ctrl == 1 ) //按住了Ctrl键时改变字体大小
{
WordHeight += (short)HIWORD(wParam)/120 ; //修改字体高度
if( WordHeight < 0 )
WordHeight = 0;
DeleteObject( font ); //删除以前的字体,避免程序冗余
font = CreateFont( WordHeight,0,0,0,0,0,0,0,0,0,0,0,0,FONTNAME );
SiteFlag=1; //SITE数组需要重新计算
}
else if( shift == 1 ) //按住了Shift键时
{
LineSpace += (short)HIWORD(wParam)/120; //修改行间距
if( LineSpace <= 0 )
LineSpace = 1;
SiteFlag=1; // SITE数组需要重新计算
}
else //没按住Ctrl键时滑动浏览
LineMove = (short)HIWORD(wParam)*-1; //仅滑动滚轮则是翻行
InvalidateRect( h , NULL, 1 );
return 0;
case WM_KEYDOWN: //按键按下事件
if( wParam == VK_CONTROL ) //Ctrl键按下
ctrl=1; //ctrl标志置1表示该键正按下
else if( wParam == VK_SHIFT ) //Shift键按下
shift=1; //shift标志置1表示该键正按下
else
{
switch( wParam ) //上下左右键翻行翻页
{
case VK_UP : LineMove = -1; break;
case VK_DOWN: LineMove = 1; break;
case VK_LEFT : PageMove = -1; break;
case VK_RIGHT : PageMove = 1; break;
}
InvalidateRect( h , NULL,0 ); //更新
}
return 0;
case WM_KEYUP: //按键弹起事件
if( wParam == VK_CONTROL ) //Ctrl键弹起
ctrl=0; //ctrl标志清0表示该键此时没有按下
else if( wParam == VK_SHIFT ) //Shift键弹起
shift=0; //shift标志清0表示该键此时没有按下
return 0;
case WM_VSCROLL: //滚动条事件
switch( LOWORD(wParam) )
{
case SB_LINEUP: LineMove=-1; break; //以下四条等同按键,翻行翻页
case SB_LINEDOWN: LineMove=1; break;
case SB_PAGEUP: PageMove=-1; break;
case SB_PAGEDOWN: PageMove=1; break;
case SB_THUMBPOSITION:
case SB_THUMBTRACK: ScrollMove=1; //拖动滑块则要修改相关标志
ScrollPos=HIWORD(wParam);break; //记录滑块位置
}
InvalidateRect( h , NULL, 0 );
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc( h , msg , wParam , lParam );
}
void SetWNDCLASS( WNDCLASS* a, WNDPROC proc, LPCTSTR ClassName )
{
a->style =CS_HREDRAW | CS_VREDRAW;
a->lpfnWndProc =proc;
a->cbClsExtra =0;
a->cbWndExtra =0;
a->hInstance =GetModuleHandle(NULL);
a->hIcon =NULL;
a->hCursor =NULL;
a->hbrBackground =(HBRUSH)GetStockObject(WHITE_BRUSH);
a->lpszMenuName =NULL;
a->lpszClassName =ClassName;
}
ATOM RegMyClass( const char* name , WNDPROC proc)
{
WNDCLASS MyClass;
SetWNDCLASS ( &MyClass, proc, name ); //处理函数为MyProc,类名为"MyClass"
return RegisterClass( &MyClass );
}
int main( int argc , char* argv[] )
{
if( argc <=1 || argc >=4 )
return 1;
FILE *fp = fopen( argv[1] , "rb" );
if( fp == NULL )
return 2;
if( argc == 3 )
FONTNAME = argv[2]; //获取指定的字体名
LEN = filelength( fileno(fp) );
TEXT = malloc( LEN+1 );
fread( TEXT , 1 , LEN , fp );
TEXT[LEN]='\0';
fclose( fp );
SITE = malloc( LEN+1 );
memset( SITE , 0 , LEN+1 );
RegMyClass( "MyClass" , MyProc );
CreateWindow( "MyClass" , "文本阅读器" , WS_OVERLAPPEDWINDOW|WS_VISIBLE|WS_VSCROLL,
500, 400, 700, 500, NULL, NULL,NULL,NULL );
MSG msg;
while( GetMessage( &msg , NULL , 0 , 0 ) )
{
printf("-%d-", msg.message );
TranslateMessage(&msg);
DispatchMessage(&msg);
}
free( TEXT );
free( SITE );
return 0;
}