TView.cpp
上传用户:hmc_gdtv
上传日期:2013-08-04
资源大小:798k
文件大小:92k
源码类别:

Windows Mobile

开发平台:

Visual C++

  1. /*
  2.  * Copyright (c) 2001,2002,2003 Mike Matsnev.  All Rights Reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice immediately at the beginning of the file, without modification,
  10.  *    this list of conditions, and the following disclaimer.
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in the
  13.  *    documentation and/or other materials provided with the distribution.
  14.  * 3. Absolutely no warranty of function or purpose is made by the author
  15.  *    Mike Matsnev.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  18.  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  19.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  20.  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  21.  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26.  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  * 
  28.  * $Id: TView.cpp,v 1.164.2.65 2005/09/28 15:44:21 mike Exp $
  29.  * 
  30.  */
  31. #include <afxcmn.h>
  32. #include <afxtempl.h>
  33. #include <afxext.h>
  34. #include <shlobj.h>
  35. #include <shellapi.h>
  36. #include <mmsystem.h>
  37. #include "config.h"
  38. #include "resource.h"
  39. #include "ptr.h"
  40. #include "FilePos.h"
  41. #include "Colors.h"
  42. #include "OptionsDialog.h"
  43. #include "FileFormatDialog.h"
  44. #include "InputBox.h"
  45. #include "ColorSelector.h"
  46. #include "XMLParser.h"
  47. #include "StylesDlg.h"
  48. #include "MiscOptDlg.h"
  49. #include "DictSetupDlg.h"
  50. #include "Keys.h"
  51. #include "Unicode.h"
  52. #include "TextFile.h"
  53. #include "TextFormatter.h"
  54. #include "TextViewNG.h"
  55. #include "TVFrame.h"
  56. #include "Rotate.h"
  57. #include "TView.h"
  58. #include "Bookmarks.h"
  59. #include "ContentsDlg.h"
  60. #include "Dictionary.h"
  61. #ifdef _DEBUG
  62. #define new DEBUG_NEW
  63. #undef THIS_FILE
  64. static char THIS_FILE[] = __FILE__;
  65. #endif
  66. #ifndef CSIDL_STARTMENU
  67. #define CSIDL_STARTMENU 0x000b
  68. #endif
  69. #define DO_CLIP   0
  70. #define PROGRESS_M   3
  71. #define PROGRESS_A   11
  72. #define PROGRESS_C   10
  73. #define PROGRESS_F   6
  74. #define FRAME_SIZE   3
  75. #ifdef _WIN32_WCE
  76. // can overflow, so take care
  77. static inline int MulDivS(int x,int y,int z) {
  78.   __int64 m=(__int64)x*y;
  79.   return (int)(m/z);
  80. }
  81. #endif
  82. // colors
  83. ColorItem    g_colors[]={
  84.   { _T("Text"), RGB(0,0,0) },
  85.   { _T("Highlight 1") , RGB(0,0,192) },
  86.   { _T("Highlight 2"), RGB(129,0,0) },
  87.   { _T("Highlight 3"), RGB(192,0,192) },
  88.   { _T("Highlight 4"), RGB(0,112,0) },
  89.   { _T("Highlight 5"), RGB(0,112,112) },
  90.   { _T("Highlight 6"), RGB(0,192,0) },
  91.   { _T("Highlight 7"), RGB(128,128,128) },
  92.   { _T("Background"), RGB(255,255,255) },
  93.   { _T("Highlighted Background"), RGB(16,192,255) },
  94.   { _T("Gauge"), RGB(150,150,150) },
  95.   { _T("Section Tick"), RGB(0,0,0) },
  96.   { _T("Bookmark Tick"), RGB(255,0,0) },
  97.   { _T("User input color"), RGB(0,128,0) },
  98.   { _T("User input background"), RGB(203,224,199) },
  99.   { _T("Autoscroll underline"), RGB(0,0,0) },
  100.   { NULL }
  101. };
  102. int     g_color_profile;
  103. enum {
  104.   TM_SAVEINFO=1,
  105.   TM_USERTEXT,
  106.   TM_AS,
  107.   TM_PD,
  108. };
  109. enum {
  110.   MAXUSERINPUTWIDTH=400,
  111.   USERINPUTFLAGS=Attr::BOLD,
  112.   USERINPUTSIZE=3
  113. };
  114. COLORREF  CTView::v_C_BG() { return m_textfile->IsImage() ? RGB(0,0,0) : C_BG; }
  115. /////////////////////////////////////////////////////////////////////////////
  116. // CTView
  117. CTView::CTView()
  118. {
  119.   LoadColors();
  120.   // read other params
  121.   CFDC::SetCacheSize(CTVApp::GetInt(_T("FontCacheSize"),6));
  122.   SetRotAngle(m_TextDisp.angle);
  123. }
  124. // colors support
  125. void  SaveColors() {
  126.   AfxGetApp()->WriteProfileInt(_T("Colors"),_T("Profile"),g_color_profile);
  127.   if (g_color_profile != 0) {
  128.     CString   name;
  129.     for (int i=0;g_colors[i].name;++i) {
  130.       name.Format(_T("%s_%d"),g_colors[i].name,g_color_profile);
  131.       AfxGetApp()->WriteProfileInt(_T("Colors"),name,g_colors[i].value);
  132.     }
  133.   } else
  134.     for (int i=0;g_colors[i].name;++i)
  135.       AfxGetApp()->WriteProfileInt(_T("Colors"),g_colors[i].name,
  136. g_colors[i].value);
  137. }
  138. void  LoadColors() {
  139.   // read color profile id
  140.   g_color_profile = AfxGetApp()->GetProfileInt(_T("Colors"),_T("Profile"),0);
  141.   // read colors
  142.   if (g_color_profile != 0) {
  143.     CString name;
  144.     for (int i=0;g_colors[i].name;++i) {
  145.       name.Format(_T("%s_%d"),g_colors[i].name,g_color_profile);
  146.       g_colors[i].value=AfxGetApp()->GetProfileInt(_T("Colors"),
  147. name,g_colors[i].value);
  148.     }
  149.   } else
  150.     for (int i=0;g_colors[i].name;++i)
  151.       g_colors[i].value=AfxGetApp()->GetProfileInt(_T("Colors"),
  152. g_colors[i].name,g_colors[i].value);
  153. }
  154. bool  NextColorProfile() {
  155.   HKEY hKey = AfxGetApp()->GetSectionKey(_T("Colors"));
  156.   if (hKey == NULL)
  157.     return false;
  158.   int   next_id = 0;
  159.   bool   found = false;
  160.   TCHAR name[16];
  161.   for (DWORD idx = 0; ; ++idx) {
  162.     DWORD   namesize = sizeof(name)/sizeof(name[0]);
  163.     DWORD   type;
  164.     if (RegEnumValue(hKey,idx,name,&namesize,0,&type,NULL,NULL) != ERROR_SUCCESS)
  165.       break;
  166.     int     id;
  167.     if (_stscanf(name,_T("%d"),&id) != 1)
  168.       continue;
  169.     if (found) {
  170.       next_id = id;
  171.       break;
  172.     }
  173.     if (g_color_profile == id)
  174.       found = true;
  175.     if (g_color_profile == 0) {
  176.       next_id = id;
  177.       break;
  178.     }
  179.   }
  180.   RegCloseKey(hKey);
  181.   if (next_id == g_color_profile)
  182.     return false;
  183.   AfxGetApp()->WriteProfileInt(_T("Colors"),_T("Profile"),next_id);
  184.   LoadColors();
  185.   return true;
  186. }
  187. void  AddColorProfileNames(void *vmenu,int startpos) {
  188.   CMenu *menu = (CMenu *)vmenu;
  189.   menu->InsertMenu(startpos,MF_BYPOSITION|MF_STRING,COLORS_BASE,_T("Default"));
  190.   if (0 == g_color_profile)
  191.     menu->CheckMenuRadioItem(startpos,startpos,startpos,MF_BYPOSITION);
  192.   ++startpos;
  193.   HKEY hKey = AfxGetApp()->GetSectionKey(_T("Colors"));
  194.   if (hKey == NULL)
  195.     return;
  196.   TCHAR name[16];
  197.   for (DWORD idx = 0; ; ++idx) {
  198.     DWORD   namesize = sizeof(name)/sizeof(name[0]);
  199.     DWORD   type;
  200.     if (RegEnumValue(hKey,idx,name,&namesize,0,&type,NULL,NULL) != ERROR_SUCCESS)
  201.       break;
  202.     int     id;
  203.     if (_stscanf(name,_T("%d"),&id) != 1)
  204.       continue;
  205.     TCHAR   data[64];
  206.     DWORD   dsize = sizeof(data);
  207.     if (RegQueryValueEx(hKey,name,0,&type,(BYTE*)data,&dsize) == ERROR_SUCCESS && type == REG_SZ) {
  208.       menu->InsertMenu(startpos,MF_BYPOSITION|MF_STRING,COLORS_BASE + id,data);
  209.       if (id == g_color_profile)
  210. menu->CheckMenuRadioItem(startpos,startpos,startpos,MF_BYPOSITION);
  211.       ++startpos;
  212.     }
  213.   }
  214.   RegCloseKey(hKey);
  215. }
  216. void CTView::xx_Window::LoadSettings() {
  217.   columns=CTVApp::GetInt(_T("Columns"),DEF_COLUMNS);
  218.   rotbuttons=CTVApp::GetInt(_T("RotateButtons"),DEF_ROTB)!=0;
  219.   autorepeatlimit=CTVApp::GetInt(_T("AutoRepeatLimit"),DEF_AUTOREPEATLIMIT)!=0;
  220.   showprog=CTVApp::GetInt(_T("ShowProgress"),DEF_SHOWPROGRESS)!=0;
  221.   pb.flags=CTVApp::GetInt(_T("ProgressBar"),DEF_PROGRESSBAR);
  222.   if (columns<1 || columns>4)
  223.     columns=1;
  224. }
  225. void  CTView::xx_Window::SaveSettings() {
  226.   CTVApp::SetInt(_T("ShowProgress"),showprog);
  227.   CTVApp::SetInt(_T("ProgressBar"),pb.flags);
  228.   CTVApp::SetInt(_T("Columns"),columns);
  229.   CTVApp::SetInt(_T("RotateButtons"),rotbuttons);
  230.   CTVApp::SetInt(_T("AutoRepeatLimit"),autorepeatlimit);
  231. }
  232. void  CTView::xx_TextDisp::LoadSettings() {
  233.   angle=CTVApp::GetInt(_T("Orientation"),DEF_ORIENTATION);
  234.   margin_width=CTVApp::GetInt(_T("Margins"),DEF_MARGINS);
  235.   justify=CTVApp::GetInt(_T("Justify"),DEF_JUSTIFY)!=0;
  236.   hyphenate=CTVApp::GetInt(_T("Hyphenate"),DEF_HYPHENATE)!=0;
  237.   SetFont(CTVApp::GetStr(_T("FontFace"),DEF_FACE),
  238.     CTVApp::GetInt(_T("FontBold"),DEF_BOLD)!=0,
  239.     CTVApp::GetInt(_T("FontSize"),DEF_SIZE),
  240.     CTVApp::GetInt(_T("FontCleartype"),DEF_CLEARTYPE));
  241. }
  242. void CTView::xx_TextDisp::SaveSettings()
  243. {
  244.   CTVApp::SetStr(_T("FontFace"),fontface);
  245.   CTVApp::SetInt(_T("FontSize"),fontsize);
  246.   CTVApp::SetInt(_T("FontBold"),bold);
  247.   CTVApp::SetInt(_T("FontCleartype"),cleartype);
  248.   CTVApp::SetInt(_T("Orientation"),angle);
  249.   CTVApp::SetInt(_T("Margins"),margin_width);
  250.   CTVApp::SetInt(_T("Justify"),justify);
  251.   CTVApp::SetInt(_T("Hyphenate"),hyphenate);
  252. }
  253. void CTView::xx_TextDisp::SetFont(const CString &face, bool pbold,
  254.   int size, int pct)
  255. {
  256.   bold=pbold;
  257.   cleartype=pct;
  258.   fontface=face;
  259.   fontsize=size;
  260.   SetDefFont();
  261. }
  262. void CTView::xx_TextDisp::SetDefFont()
  263. {
  264.   CFDC::SetDefaultFont(fontface,fontsize,bold,cleartype,angle);
  265. }
  266. void  CTView::xx_AS::LoadSettings() {
  267.   delay=CTVApp::GetInt(_T("AutoScrollDelay"),DEF_AS_DELAY);
  268. }
  269. void  CTView::xx_AS::SaveSettings() {
  270.   CTVApp::SetInt(_T("AutoScrollDelay"),delay);
  271. }
  272. CTView::~CTView() {
  273.   SaveInfo();
  274.   Bookmarks::CleanupRegistry(CTVApp::GetInt(_T("NumBookmarks"),DEF_BOOKMARKS));
  275. }
  276. void  CTView::SaveInfo() {
  277.   if (m_textfile.get() && m_formatter.get())
  278.     m_textfile->SaveBookmarks(CurFilePos());
  279. }
  280. void  CTView::Init() {
  281.   GetClientRect(&m_Window.cli);
  282.   CalcSizes();
  283.   Keys::SetWindow(m_hWnd);
  284.   m_timer=SetTimer(TM_SAVEINFO,DEF_SAVEINTERVAL,0);
  285.   StartWindowPDTimer();
  286.   // initialize progress bar height
  287.   CFDC dc(m_hWnd);
  288.   dc.SelectFontAbs(MulDivS(PROGRESS_F, GetDeviceCaps(dc.DC(), LOGPIXELSY), 72),CFDC::FORCENORMALWEIGHT|CFDC::FORCETAHOMA,true);
  289.   int dummy;
  290.   dc.GetFontSize(m_Window.progress_height,dummy);
  291.   m_Window.progress_height-=2; // XXX no inter-line spacing
  292. }
  293. void  CTView::SetFile(kilo::auto_ptr<TextFile> tfile) {
  294.   if (!tfile.get())
  295.     return;
  296.   SaveInfo();
  297.   m_textfile=tfile;
  298.   m_Window.progoverride=!m_textfile->IsImage();
  299.   m_formatter=new TextFormatter(m_textfile.get());
  300.   m_formatter->SetJustified(m_TextDisp.justify);
  301.   m_formatter->SetHyphenate(m_TextDisp.hyphenate);
  302.   m_formatter->SetTop(m_textfile->bmk().StartPos());
  303.   m_Search.matchpos=m_formatter->Eof();
  304.   m_History.Clear();
  305.   SaveInfo();
  306.   // and format the page
  307.   CalcSizes();
  308.   QueueRepaint();
  309.   StartWindowPDTimer();
  310. }
  311. BEGIN_MESSAGE_MAP(CTView,CWnd )
  312. //{{AFX_MSG_MAP(CTView)
  313. ON_WM_PAINT()
  314. ON_WM_SIZE()
  315. ON_WM_KEYDOWN()
  316. ON_WM_ERASEBKGND()
  317. ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
  318. ON_UPDATE_COMMAND_UI(ID_DISP_OPTIONS, OnUpdateOptions)
  319. ON_COMMAND(ID_DISP_OPTIONS, OnOptions)
  320. ON_UPDATE_COMMAND_UI(ID_FILEFORMAT, OnUpdateFileformat)
  321. ON_COMMAND(ID_FILEFORMAT, OnFileformat)
  322. ON_UPDATE_COMMAND_UI(ID_BACK, OnUpdateBack)
  323. ON_COMMAND(ID_BACK, OnBack)
  324. ON_WM_LBUTTONDOWN()
  325. ON_COMMAND(ID_DICT_SETUP, OnDictSetup)
  326. ON_UPDATE_COMMAND_UI(ID_DICT_SETUP, OnUpdateDictSetup)
  327. ON_UPDATE_COMMAND_UI(ID_FIND, OnUpdateFind)
  328. ON_COMMAND(ID_FIND, OnFind)
  329. ON_COMMAND(ID_FINDNEXT, OnFindnext)
  330. ON_UPDATE_COMMAND_UI(ID_FINDNEXT, OnUpdateFindnext)
  331. ON_UPDATE_COMMAND_UI(ID_COLORS, OnUpdateColors)
  332. ON_COMMAND(ID_COLORS, OnColors)
  333. ON_WM_LBUTTONDBLCLK()
  334. ON_COMMAND(ID_ADD_BMK, OnAddBmk)
  335. ON_UPDATE_COMMAND_UI(ID_ADD_BMK, OnUpdateAddBmk)
  336. ON_COMMAND(ID_BOOKMARKS, OnBookmarks)
  337. ON_UPDATE_COMMAND_UI(ID_BOOKMARKS, OnUpdateBookmarks)
  338. ON_COMMAND(ID_LINE_UP, OnLineUp)
  339. ON_COMMAND(ID_LINE_DOWN, OnLineDown)
  340. ON_COMMAND(ID_PAGE_UP, OnPageUp)
  341. ON_COMMAND(ID_PAGE_DOWN, OnPageDown)
  342. ON_COMMAND(ID_START_OF_FILE, OnStartFile)
  343. ON_COMMAND(ID_END_OF_FILE, OnEndFile)
  344. ON_COMMAND(ID_KEYS, OnKeys)
  345. ON_UPDATE_COMMAND_UI(ID_KEYS, OnUpdateKeys)
  346. ON_WM_DESTROY()
  347. ON_COMMAND(ID_STYLES, OnStyles)
  348. ON_UPDATE_COMMAND_UI(ID_STYLES, OnUpdateStyles)
  349. ON_WM_LBUTTONUP()
  350. ON_WM_MOUSEMOVE()
  351. ON_COMMAND(ID_MISCOPT, OnMiscopt)
  352. ON_UPDATE_COMMAND_UI(ID_MISCOPT, OnUpdateMiscopt)
  353. ON_COMMAND(ID_EDIT_COPY, OnEditCopy)
  354. ON_UPDATE_COMMAND_UI(ID_EDIT_COPY, OnUpdateEditCopy)
  355. ON_COMMAND(ID_ROTATE,OnRotate)
  356. ON_WM_TIMER()
  357. ON_COMMAND(ID_NEXTCH,OnNextSection)
  358. ON_COMMAND(ID_PREVCH,OnPrevSection)
  359. ON_COMMAND(ID_NEXTBM,OnNextBm)
  360. ON_COMMAND(ID_PREVBM,OnPrevBm)
  361. ON_WM_CHAR()
  362. ON_COMMAND(ID_FORWARD, OnForward)
  363. ON_UPDATE_COMMAND_UI(ID_FORWARD, OnUpdateForward)
  364. ON_COMMAND(ID_LOOKUP_SEL,OnLookupSel)
  365. ON_COMMAND(ID_DO_FIND, DoFind)
  366. ON_COMMAND(IDOK,OnOK)
  367. ON_WM_KILLFOCUS()
  368. ON_WM_SETFOCUS()
  369. ON_COMMAND(ID_AS_STOP,StopAS)
  370. ON_COMMAND(ID_AS_START,StartAS)
  371. ON_COMMAND(ID_AS_SLOWER,SlowerAS)
  372. ON_COMMAND(ID_AS_FASTER,FasterAS)
  373. ON_COMMAND(ID_AS_SLOWER_FINE,SlowerASFine)
  374. ON_COMMAND(ID_AS_FASTER_FINE,FasterASFine)
  375. ON_COMMAND(ID_AS_TOGGLE,ToggleAS)
  376. ON_UPDATE_COMMAND_UI(ID_PB_TOGGLE, OnUpdateTogglePB)
  377. ON_COMMAND(ID_PB_TOGGLE, OnTogglePB)
  378. ON_UPDATE_COMMAND_UI(ID_PB_POS, OnUpdateTogglePBPos)
  379. ON_COMMAND(ID_PB_POS, OnTogglePBPos)
  380. ON_UPDATE_COMMAND_UI(ID_PB_CHAPTER, OnUpdateTogglePBChapter)
  381. ON_COMMAND(ID_PB_CHAPTER, OnTogglePBChapter)
  382. ON_UPDATE_COMMAND_UI(ID_PB_TOP, OnUpdateTogglePBTop)
  383. ON_COMMAND(ID_PB_TOP, OnTogglePBTop)
  384. ON_UPDATE_COMMAND_UI(ID_PB_TIME, OnUpdateTogglePBTime)
  385. ON_COMMAND(ID_PB_TIME, OnTogglePBTime)
  386. ON_UPDATE_COMMAND_UI(ID_PB_AS, OnUpdateTogglePBAS)
  387. ON_COMMAND(ID_PB_AS, OnTogglePBAS)
  388. ON_UPDATE_COMMAND_UI(ID_PB_BATTERY, OnUpdateTogglePBBat)
  389. ON_COMMAND(ID_PB_BATTERY, OnTogglePBBat)
  390. ON_UPDATE_COMMAND_UI(ID_FILE_EXPORTBMK, OnUpdateExportBmk)
  391. ON_COMMAND(ID_FILE_EXPORTBMK, OnExportBmk)
  392. ON_UPDATE_COMMAND_UI(ID_COLORS_ADDP, OnUpdateNewColorProfile)
  393. ON_COMMAND(ID_COLORS_ADDP, OnNewColorProfile)
  394. ON_UPDATE_COMMAND_UI(ID_COLORS_DELP, OnUpdateDelColorProfile)
  395. ON_COMMAND(ID_COLORS_DELP, OnDelColorProfile)
  396. ON_UPDATE_COMMAND_UI(ID_NEXT_PROFILE, OnUpdateNextColorProfile)
  397. ON_COMMAND(ID_NEXT_PROFILE, OnNextColorProfile)
  398. //}}AFX_MSG_MAP
  399. #ifndef _WIN32_WCE
  400. ON_WM_MOUSEWHEEL()
  401. #endif
  402. ON_MESSAGE(WM_HOTKEY, OnHotkey)
  403. ON_COMMAND_RANGE(DICT_BASE,DICT_BASE+DICT_FILES,OnDict)
  404. ON_UPDATE_COMMAND_UI_RANGE(DICT_BASE,DICT_BASE+DICT_FILES,OnUpdateDict)
  405. ON_COMMAND_RANGE(COLORS_BASE,COLORS_BASE+COLORS_MAX,OnSelColor)
  406. ON_UPDATE_COMMAND_UI_RANGE(COLORS_BASE,COLORS_BASE+COLORS_MAX,OnUpdateSelColor)
  407. #ifdef WM_POWERBROADCAST
  408. ON_MESSAGE(WM_POWERBROADCAST, OnPower)
  409. #endif
  410. END_MESSAGE_MAP()
  411. /////////////////////////////////////////////////////////////////////////////
  412. // CTView message handlers
  413. BOOL CTView::PreCreateWindow(CREATESTRUCT& cs)
  414. {
  415.   if (!CWnd::PreCreateWindow(cs))
  416.     return FALSE;
  417.   cs.style &= ~WS_BORDER;
  418.   cs.lpszClass = AfxRegisterWndClass(CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS,
  419.     NULL, NULL, NULL);
  420.   return TRUE;
  421. }
  422. struct FormatterGetLine: public CTView::IGetLine {
  423.   TextFormatter   *m_tf;
  424.   int   m_page;
  425.   int   m_off;
  426.   int   m_len;
  427.   FormatterGetLine(TextFormatter *tf,int page) : m_tf(tf), m_page(page), m_off(0) {
  428.     for (int i=0;i<page;++i)
  429.       m_off+=tf->PageLength(i);
  430.     m_len=tf->PageLength(page);
  431.   }
  432.   int   Length() { return m_len; }
  433.   const Line&   At(int i) { return m_tf->GetLine(m_off+i); }
  434. };
  435. static inline bool Overlap(const RECT& r1,const RECT& r2) {
  436.   return !(
  437.       r1.right<=r2.left ||
  438.       r1.left>=r2.right ||
  439.       r1.bottom<=r2.top ||
  440.       r1.top>=r2.bottom
  441.   );
  442. }
  443. void CTView::OnPaint()
  444. {
  445.   RECT     update_rect;
  446.   if (!GetUpdateRect(&update_rect))
  447.     return;
  448.   System2Window(update_rect,m_Window.cli);
  449.   PAINTSTRUCT ps;
  450.   {
  451.     // do a quick check for bookmarks
  452.     bool    checkbmk=false;
  453.     if (m_Window.showprogress() && m_textfile->bmk().BookmarksInRange(
  454.   m_formatter->Top(),m_formatter->Bottom()))
  455.       checkbmk=true;
  456.     // draw stuff
  457.     CFDC     fdc(m_hWnd,&ps);
  458.     RECT     col;
  459.     col.left=col.top=0;
  460.     col.right=m_Window.width;
  461.     col.bottom=m_Window.rheight;
  462.     if (m_Window.showprogress())
  463.       col.bottom-=m_Window.progress_height;
  464.     for (int column=0;column<m_Window.columns;++column) {
  465.       if (Overlap(col,update_rect)) {
  466. FormatterGetLine   gl(m_formatter.get(),column);
  467. PaintColumn(fdc,update_rect,col,m_Window.cli,
  468.   &gl,m_TextDisp.margin_width,checkbmk);
  469.       }
  470.       col.left+=m_Window.width;
  471.       col.right+=m_Window.width;
  472.     }
  473.     if (m_Window.showprogress()) {
  474.       col.left=0; col.top=m_Window.rheight-m_Window.progress_height;
  475.       col.right=m_Window.rwidth; col.bottom=m_Window.rheight;
  476.       if (Overlap(col,update_rect)) {
  477. UpdateWindowPD();
  478. PaintProgressBar(fdc,col,m_Window.cli);
  479.       }
  480.     }
  481.     if (m_UI.visible && Overlap(m_UI.rc,update_rect))
  482.       PaintUserInput(fdc,m_UI.rc,m_Window.cli,Unicode::ToWCbuf(m_UI.inp));
  483.     if (m_BP.visible && Overlap(m_BP.rc,update_rect))
  484.       PaintBookmarkPopup(fdc,m_BP.rc,m_Window.cli);
  485.   }
  486. }
  487. void  CTView::RedrawProgressBar() {
  488.   if (!m_Window.showprogress())
  489.     return;
  490.   CFDC fdc(m_hWnd);
  491.   RECT col;
  492.   col.left=0; col.top=m_Window.rheight-m_Window.progress_height;
  493.   col.right=m_Window.rwidth; col.bottom=m_Window.rheight;
  494.   UpdateWindowPD();
  495.   PaintProgressBar(fdc,col,m_Window.cli);
  496. }
  497. void CTView::PaintSingleLine(int column,int line,COLORREF underline) {
  498.   if (column<0 || column>=m_Window.columns)
  499.     return;
  500.   if (line<0 || line>=m_formatter->PageLength(column))
  501.     return;
  502.   FormatterGetLine   gl(m_formatter.get(),column);
  503.   RECT   line_rect;
  504.   line_rect.left=m_Window.width*column;
  505.   line_rect.right=line_rect.left+m_Window.width;
  506.   line_rect.top=0;
  507.   for (int i=0;i<line;++i)
  508.     line_rect.top+=gl.At(i).height;
  509.   const Line& line_data=gl.At(line);
  510.   line_rect.bottom=line_rect.top+line_data.height;
  511.   RECT   cli;
  512.   GetClientRect(&cli);
  513.   CFDC   dc(m_hWnd);
  514.   dc.SetTextColor(C_NORM);
  515.   dc.SetBkColor(v_C_BG());
  516.   RECT   tmp=line_rect;
  517.   PaintLine(dc,cli,tmp,m_TextDisp.margin_width,line_data);
  518.   dc.SetColor(C_TOCBM);
  519.   HighlightBookmarks(dc,cli,line_rect.left,m_TextDisp.margin_width,
  520.     line_rect.top,line_data,line_data.pos,gl.At(line+1).pos);
  521.   if (underline!=CLR_DEFAULT) {
  522.     tmp=line_rect;
  523.     tmp.top=tmp.bottom=tmp.bottom-1;
  524.     dc.SetColor(underline);
  525.     TDrawLine(dc.DC(),cli,tmp);
  526.   }
  527.   // if this is the last line in column, then paint till column bottom
  528.   if (line==gl.Length()-1) {
  529.     line_rect.top=line_rect.bottom;
  530.     line_rect.bottom=m_Window.rheight;
  531.     TDrawText(dc.DC(),line_rect.left,line_rect.top,cli,line_rect,NULL,0,NULL);
  532.     if (m_Window.showprogress()) {
  533.       tmp.left=0; tmp.top=m_Window.rheight-m_Window.progress_height;
  534.       tmp.right=m_Window.rwidth; tmp.bottom=m_Window.rheight;
  535.       UpdateWindowPD();
  536.       PaintProgressBar(dc,tmp,m_Window.cli);
  537.     }
  538.   }
  539. }
  540. void CTView::PaintSbItem(CFDC& dc,const wchar_t *text,int len,const RECT& rc,
  541. const RECT& cli,int& pb_width)
  542. {
  543.   if (len<0)
  544.     len=wcslen(text);
  545.   RECT       ii;
  546.   SIZE       sz;
  547.   int       nch=0;
  548.   dc.GetTextExtent(text,len,rc.right-rc.left,nch,NULL,sz);
  549.   if (pb_width>0)
  550.     sz.cx+=3; // XXX padding
  551.   ii.top=rc.top-2;
  552.   ii.bottom=rc.bottom;
  553.   ii.left=rc.right-sz.cx-pb_width-1;
  554.   ii.right=ii.left+sz.cx;
  555.   TDrawText(dc.DC(),ii.left,ii.top,cli,ii,text,len,NULL,ETO_OPAQUE);
  556.   pb_width+=sz.cx;
  557. }
  558. bool CTView::UpdateWindowPD() {
  559.   bool   upd=false;
  560.   // calculate current position
  561.   int   top=m_textfile->GetTotalLength(m_formatter->DocId());
  562.   int   cur=m_formatter->DocId()<0 ? m_textfile->GetPStart(m_formatter->DocId(),m_formatter->Top().para) :
  563.        m_textfile->AbsPos(m_formatter->Bottom());
  564.   m_Window.pd.cc=cur;
  565.   if (m_formatter->DocId()>=0)
  566.     cur=(cur+1)>>11;
  567.   else
  568.     cur=-(top ? MulDivS(100,cur,top) : 100) - 1;
  569.   if (cur!=m_Window.pd.pos) {
  570.     if (m_Window.pb.position)
  571.       upd=true;
  572.     m_Window.pd.pos=cur;
  573.   }
  574.   if (top!=m_Window.pd.top) {
  575.     if (m_Window.pb.top && m_formatter->DocId()>=0)
  576.       upd=true;
  577.     m_Window.pd.top=top;
  578.   }
  579.   // find current chapter title
  580.   if (m_Window.pb.chapter) {
  581.     int idx=m_textfile->bmk().BFind(m_formatter->Top(),Bookmarks::SPREVCH);
  582.     if (idx<m_textfile->bmk().GetSize() &&
  583. m_textfile->bmk().Ref(idx).docid==m_formatter->Top().docid)
  584.     {
  585.       CString cur=m_textfile->bmk().Text(idx,m_textfile.get());
  586.       if (cur!=m_Window.pd.title) {
  587. upd=true;
  588. m_Window.pd.title=cur;
  589.       }
  590.     } else if (!m_Window.pd.title.IsEmpty()) {
  591.       m_Window.pd.title.Empty();
  592.       upd=true;
  593.     }
  594.   }
  595.   cur=m_AS.delay/1000;
  596.   if (cur!=m_Window.pd.as) {
  597.     if (m_Window.pb.as_delay)
  598.       upd=true;
  599.     m_Window.pd.as=cur;
  600.   }
  601.   SYSTEMTIME stm;
  602.   ::GetLocalTime(&stm);
  603.   cur=(stm.wHour<<8) | stm.wMinute;
  604.   if (cur!=m_Window.pd.tm) {
  605.     if (m_Window.pb.time)
  606.       upd=true;
  607.     m_Window.pd.tm=cur;
  608.   }
  609. #ifdef _WIN32_WCE
  610.   SYSTEM_POWER_STATUS_EX  pws;
  611.   BOOL     ok=GetSystemPowerStatusEx(&pws,FALSE);
  612. #else
  613.   SYSTEM_POWER_STATUS     pws;
  614.   BOOL     ok=GetSystemPowerStatus(&pws);
  615. #endif
  616.   if (ok && pws.BatteryLifePercent>=0 && pws.BatteryLifePercent<=100) {
  617.     if (pws.ACLineStatus==1)
  618.       cur=101;
  619.     else
  620.       cur=pws.BatteryLifePercent;
  621.   } else
  622.     cur=-1;
  623.   if (cur!=m_Window.pd.bat) {
  624.     if (m_Window.pb.battery)
  625.       upd=true;
  626.     m_Window.pd.bat=cur;
  627.   }
  628.   return upd;
  629. }
  630. void CTView::StartWindowPDTimer() {
  631.   if (m_Window.showprogress() && (m_Window.pb.battery || m_Window.pb.time))
  632.     m_Window.pd_timer=SetTimer(TM_PD,2000,NULL);
  633.   else {
  634.     KillTimer(m_Window.pd_timer);
  635.     m_Window.pd_timer=0;
  636.   }
  637. }
  638. void CTView::UpdateProgressBar() {
  639.   if (UpdateWindowPD()) {
  640.     CFDC fdc(m_hWnd);
  641.     RECT col;
  642.     col.left=0; col.top=m_Window.rheight-m_Window.progress_height;
  643.     col.right=m_Window.rwidth; col.bottom=m_Window.rheight;
  644.     PaintProgressBar(fdc,col,m_Window.cli);
  645.   }
  646. }
  647. void CTView::PaintProgressBar(CFDC& dc,const RECT& rc,const RECT& cli) {
  648.   RECT       col;
  649.   wchar_t     buf[128];
  650.   int       fonthdpi = MulDivS(PROGRESS_F, GetDeviceCaps(dc.DC(), LOGPIXELSY), 72);
  651.   dc.SelectFontAbs(fonthdpi,CFDC::FORCENORMALWEIGHT|CFDC::FORCETAHOMA);
  652.   ::SetBkMode(dc.DC(),OPAQUE);
  653.   // clear area
  654.   dc.SetBkColor(v_C_BG());
  655.   TDrawText(dc.DC(),rc.left,rc.top,m_Window.cli,rc,NULL,0,NULL);
  656.   m_Window.pb_width=0;
  657.   if (m_Window.pb.battery && m_Window.pd.bat>=0) {
  658.     if (m_Window.pd.bat>100)
  659.       wcscpy(buf,L"AC");
  660.     else
  661.       swprintf(buf,L"%d%%",m_Window.pd.bat);
  662.     PaintSbItem(dc,buf,-1,rc,m_Window.cli,m_Window.pb_width);
  663.   }
  664.   if (m_Window.pb.time) {
  665.     swprintf(buf,L"%02d:%02d",m_Window.pd.tm>>8,m_Window.pd.tm&0xff);
  666.     PaintSbItem(dc,buf,-1,rc,m_Window.cli,m_Window.pb_width);
  667.   }
  668.   if (m_Window.pb.as_delay && m_AS.timer) {
  669.     swprintf(buf,L"%d",m_Window.pd.as);
  670.     PaintSbItem(dc,buf,-1,rc,m_Window.cli,m_Window.pb_width);
  671.   }
  672.   // draw vpage number for normal docs, or a back button for dictionary
  673.   if (m_Window.pb.position || (m_Window.pb.top && m_Window.pd.pos>=0)) {
  674.     if (m_Window.pd.pos>=0) {
  675.       if (m_Window.pb.top)
  676. swprintf(buf,L"%d/%d",m_Window.pd.pos,m_Window.pd.top>>11);
  677.       else
  678. swprintf(buf,L"%d",m_Window.pd.pos);
  679.     } else
  680.       swprintf(buf,L"%d%%",-m_Window.pd.pos-1);
  681.     PaintSbItem(dc,buf,-1,rc,m_Window.cli,m_Window.pb_width);
  682.   }
  683.   
  684.   if (m_Window.pb.chapter && !m_Window.pd.title.IsEmpty()) {
  685.     // draw chapter title
  686.     RECT  rc2=rc;
  687.     rc2.top-=2;
  688.     rc2.left+=3; // XXX padding
  689.     rc2.right-=m_Window.pb_width+2; // XXX padding
  690.     SIZE    sz;
  691.     int     nch=0;
  692.     CString tmp(m_Window.pd.title);
  693.     dc.SelectFontAbs(fonthdpi,CFDC::FORCENORMALWEIGHT|CFDC::FORCETAHOMA,true);
  694.     dc.GetTextExtent(tmp,tmp.GetLength(),rc2.right-rc2.left,nch,NULL,sz);
  695.     if (nch<tmp.GetLength()) { // doesnt fit
  696.       int     nch2;
  697.       wchar_t ch=0x2026;
  698.       dc.GetTextExtent(&ch,1,8192,nch2,NULL,sz);
  699.       dc.GetTextExtent(tmp,tmp.GetLength(),rc2.right-rc2.left-sz.cx,nch,NULL,sz);
  700.       if (nch>0) {
  701. tmp.Delete(nch,tmp.GetLength()-nch);
  702. tmp+=ch;
  703.       } else
  704. tmp.Empty();
  705.     }
  706.     dc.SelectFontAbs(fonthdpi,CFDC::FORCENORMALWEIGHT|CFDC::FORCETAHOMA);
  707.     TDrawText(dc.DC(),rc2.left,rc2.top,cli,rc2,tmp,tmp.GetLength(),NULL,ETO_OPAQUE|ETO_CLIPPED);
  708.   } else {
  709.     // draw position bar
  710.     RECT  rc2=rc;
  711.     rc2.top += 1 + (m_Window.progress_height - 5) / 2;
  712.     int   bw=rc2.right-rc2.left-2*PROGRESS_M-2*PROGRESS_A-m_Window.pb_width;
  713.     int   pos=m_Window.pd.top ? MulDivS(bw,m_Window.pd.cc,m_Window.pd.top) : bw;
  714.     col.left=rc2.left+PROGRESS_M+PROGRESS_A; col.top=rc2.top+1;
  715.     col.right=col.left+pos; col.bottom=col.top;
  716.     dc.SetColor(C_GAUGE);
  717.     TDrawLine(dc.DC(),m_Window.cli,col);
  718.     col.top+=2;
  719.     col.bottom=col.top;
  720.     TDrawLine(dc.DC(),m_Window.cli,col);
  721.     // draw a thin black line
  722.     col.left=rc2.left+PROGRESS_M; col.top=col.bottom=rc2.top+2;
  723.     col.right=rc2.right-PROGRESS_M-m_Window.pb_width;
  724.     dc.SetColor(C_TOCL0); // XXX
  725.     TDrawLine(dc.DC(),m_Window.cli,col);
  726.     // draw arrows
  727.     col.top=rc2.top+1;
  728.     col.bottom=col.top+3;
  729.     col.left=col.right=rc2.left+PROGRESS_M+1;
  730.     TDrawLine(dc.DC(),m_Window.cli,col);
  731.     col.left=col.right=rc2.left+PROGRESS_M+4;
  732.     TDrawLine(dc.DC(),m_Window.cli,col);
  733.     col.left=col.right=rc2.right-PROGRESS_M-m_Window.pb_width-5;
  734.     TDrawLine(dc.DC(),m_Window.cli,col);
  735.     col.left=col.right=rc2.right-PROGRESS_M-m_Window.pb_width-2;
  736.     TDrawLine(dc.DC(),m_Window.cli,col);
  737.     col.top=rc2.top;
  738.     col.bottom=col.top+5;
  739.     col.left=col.right=rc2.left+PROGRESS_M+2;
  740.     TDrawLine(dc.DC(),m_Window.cli,col);
  741.     col.left=col.right=rc2.left+PROGRESS_M+5;
  742.     TDrawLine(dc.DC(),m_Window.cli,col);
  743.     col.left=col.right=rc2.right-PROGRESS_M-m_Window.pb_width-6;
  744.     TDrawLine(dc.DC(),m_Window.cli,col);
  745.     col.left=col.right=rc2.right-PROGRESS_M-m_Window.pb_width-3;
  746.     TDrawLine(dc.DC(),m_Window.cli,col);
  747.     // draw chapter ticks
  748.     if (m_Window.pd.top) {
  749.       Bookmarks& bm=m_textfile->bmk();
  750.       bool  doall=bm.GetSize()<(rc2.right-rc2.left)/3;
  751.       bool  doch=bm.NumTopMarks()<(rc2.right-rc2.left)/3;
  752.       bool  dobm=bm.NumBookmarks()<(rc2.right-rc2.left)/3;
  753.       if (doch||dobm||doall) {
  754. for (int ii=0;ii<bm.GetSize();++ii) {
  755.   if (bm.Ref(ii).docid!=m_formatter->DocId())
  756.     continue;
  757.   COLORREF   color=C_TOCL0;
  758.   col.top=rc2.top;
  759.   col.bottom=col.top+5;
  760.   if (bm.Flags(ii)&Bookmarks::BMK) {
  761.     if (!dobm)
  762.       continue;
  763.     color=C_TOCBM;
  764.   } else if (bm.Level(ii)>0) {
  765.     if (!doall)
  766.       continue;
  767.          col.top=rc2.top+1;
  768.     col.bottom=col.top+3;
  769.   } else if (!doch)
  770.       continue;
  771.   col.left=col.right=rc2.left+PROGRESS_M+PROGRESS_A+MulDivS(bw,
  772.     m_textfile->AbsPos(bm.Ref(ii)),m_Window.pd.top);
  773.   dc.SetColor(color);
  774.   TDrawLine(dc.DC(),m_Window.cli,col);
  775. }
  776.       }
  777.     }
  778.   }
  779. }
  780. void CTView::PaintBookmarkPopup(CFDC& dc,const RECT& rc,const RECT& cli) {
  781.   // bascially it's a rectangle+paintcolumn
  782.   // draw frame first
  783.   POINT   pt[5];
  784.   pt[0].x=rc.left; pt[0].y=rc.top;
  785.   pt[1].x=rc.right-1; pt[1].y=rc.top;
  786.   pt[2].x=rc.right-1; pt[2].y=rc.bottom-1;
  787.   pt[3].x=rc.left; pt[3].y=rc.bottom-1;
  788.   pt[4]=pt[0];
  789.   dc.SetColor(C_NORM);
  790.   TDrawPolyLine(dc.DC(),cli,pt,5);
  791.   // shrink rc a bit to avoid overwriting frame
  792.   RECT   nrc=rc;
  793.   nrc.left++; nrc.right--;
  794.   nrc.top++; nrc.bottom--;
  795.   PaintColumn(dc,nrc,nrc,cli,&m_BP,FRAME_SIZE-1,false);
  796. }
  797. void CTView::OnSize(UINT nType, int cx, int cy) {
  798.   CWnd::OnSize(nType, cx, cy);
  799.   if (cx==0 || cy==0) // don't bother with invalid sizes
  800.     return;
  801.   GetClientRect(&m_Window.cli);
  802.   CalcSizes();
  803. }
  804. void CTView::PaintLine(CFDC& dc,const RECT& cli,RECT& line,
  805.        int margin,const Line& l)
  806. {
  807.   int   x=margin+l.ispace;
  808.   if (l.flags&Line::image) {
  809.     Image img;
  810.     if (m_textfile->GetImage(l.href,dc.DC(),l.base,l.imageheight,
  811. m_TextDisp.angle,img))
  812.     {
  813.       // clean left and right margins
  814.       RECT  rma=line;
  815.       if (x) {
  816. rma.right=rma.left+x;
  817. TDrawText(dc.DC(),rma.left,rma.top,cli,rma,NULL,0,NULL);
  818.       }
  819.       rma.left=line.left+x+img.width;
  820.       rma.right=line.right;
  821.       if (rma.right!=rma.left)
  822. TDrawText(dc.DC(),rma.left,rma.top,cli,rma,NULL,0,NULL);
  823.       TDrawBitmap(dc.DC(),img.hBmp,line.left+x,line.top,
  824.   l.height,cli,img.width,img.height,l.yoffset);
  825.     } else
  826.       TDrawText(dc.DC(),line.left,line.top,cli,line,NULL,0,NULL);
  827.   } else if (l.flags&Line::defstyle) {
  828.     int   fh,fa; // fontheight,fontascent
  829.     dc.SelectFont(0,0);
  830.     dc.GetFontSize(fh,fa);
  831.     TDrawText(dc.DC(),x+line.left,line.top+l.base-fa,cli,line,
  832.       l.str,l.str.size(),l.dx);
  833.   } else {
  834.     ComplexLine(dc,cli,line,x,l);
  835.   }
  836. }
  837. void  CTView::HighlightBookmarks(CFDC& dc,const RECT& cli,int left,
  838.  int margin,int y,const Line& l,
  839.  FilePos bmkstart,FilePos bmkend)
  840. {
  841.   RECT     r;
  842.   int     j,nc;
  843.   while (m_textfile->bmk().BookmarkFind(bmkstart,bmkend)) {
  844.     r.top=y;
  845.     r.left=left+margin+l.ispace;
  846.     nc=bmkstart.off-l.pos.off;
  847.     for (j=0;j<nc;++j)
  848.       r.left+=l.dx[j];
  849.     r.right=r.left;
  850.     r.bottom=y+l.height;
  851.     TDrawLine(dc.DC(),cli,r);
  852.     r.right=r.left+3;
  853.     r.bottom=r.top;
  854.     TDrawLine(dc.DC(),cli,r);
  855.     bmkstart.off++;
  856.   }
  857. }
  858. void CTView::PaintColumn(CFDC &dc,const RECT& update,
  859.  const RECT& col,const RECT& cli,IGetLine *gl,
  860.  int margin,bool chkbmk)
  861. {
  862.   RECT   line;
  863.   dc.SetTextColor(C_NORM);
  864.   dc.SetBkColor(v_C_BG());
  865.   line.top=col.top;
  866.   int i;
  867.   int pl=gl->Length();
  868.   for (i=0;i<pl;++i,line.top=line.bottom) {
  869.     line.left=col.left;
  870.     line.right=col.right;
  871.     const Line& l=gl->At(i);
  872.     line.bottom=line.top+l.height;
  873.     if (!Overlap(line,update))
  874.       continue;
  875.     PaintLine(dc,cli,line,margin,l);
  876.   }
  877.   line.left=col.left;
  878.   line.right=col.right;
  879.   line.bottom=col.bottom;
  880.   TDrawText(dc.DC(),line.left,line.top,cli,line,NULL,0,NULL);
  881.   if (chkbmk && pl) { // now paint bookmarks if any
  882.     FilePos bmkstart,bmkend;
  883.     bmkend=gl->At(0).pos;
  884.     dc.SetColor(C_TOCBM);
  885.     int     y=col.top;
  886.     for (i=0;i<pl;++i) {
  887.       const Line& l=gl->At(i);
  888.       bmkstart=bmkend;
  889.       bmkend=gl->At(i+1).pos;
  890.       HighlightBookmarks(dc,cli,col.left,margin,y,l,bmkstart,bmkend);
  891.       y+=l.height;
  892.     }
  893.   }
  894. }
  895. /*
  896.   Generic line structure:
  897.   +-----------------+------+----------------------------+----+------------+
  898.   |                 +  LM  +       text                 + RM +            +
  899.   +-----------------+------+----------------------------+----+------------+
  900.     |                                        +---
  901.     |       this is the
  902.     |                                         currently
  903.     |                                         painted coulmn
  904.     +--------------------------------------------
  905.   LM is the left margin an can be 0, same for RM
  906.   normally we iterate over text extracting segments with the same
  907.   formatting and painting them, but initial LM is handled separately.
  908.   if LM is not zero, and the first text segment has a different
  909.   backgound, then we reset the segment and paint only LM, otherwise
  910.   LM is attached to the first text segment. As a shortcut, when the
  911.   entire line has the same default formatting (as is the case for
  912.   99% of all text), the painting is done with the single
  913.   TDrawText call in the PaintColumn main loop.
  914. */
  915. void  CTView::ComplexLine(CFDC& dc,const RECT& cli,RECT& line,
  916.   int x,const Line& l)
  917. {
  918.   int   flags=ETO_OPAQUE;
  919.   int   len=l.str.size();
  920.   int   off=0;
  921.   int   fh,fa;
  922.   int   x0=line.left;
  923.   int   width=line.right-line.left;
  924.   // check if the backgound is the same
  925.   for (int i=0;i<len;++i)
  926.     if (l.attr[i].hibg)
  927.       goto normal_draw;
  928.   // all bg is normal, so we clear the entire line and use transparent mode
  929.   dc.SetBkColor(v_C_BG());
  930.   TDrawText(dc.DC(),line.left,line.top,cli,line,NULL,0,NULL);
  931.   flags=0;
  932. normal_draw:
  933.   while (len>0) {
  934.     line.right=x0+x;
  935.     COLORREF  bg=v_C_BG();
  936.     Attr      attr=l.attr[off];
  937.     int       run=0;
  938.     if (attr.img) {
  939.       line.right+=l.dx[off];
  940.       run=1;
  941.     } else
  942.       while (run<len && attr.wa==l.attr[off+run].wa) {
  943. line.right+=l.dx[off+run];
  944. ++run;
  945.       }
  946.     if (run==len && !attr.hibg) // fill till the end
  947.       line.right=x0+width;
  948.     dc.SelectFont(attr.fsize,attr.fontattr());
  949.     dc.GetFontSize(fh,fa);
  950.     // non-default backgound, left margin present, and starting at the
  951.     // very left edge of the column, then discard fisrt segment
  952.     if (attr.hibg && x && line.left==x0) {
  953.       line.right=x0+x;
  954.       run=0;
  955.       attr.wa=0;
  956.     } else
  957.       dc.SetTextColor(C_TCOLOR(attr.color));
  958.     dc.SetBkColor(attr.hibg ? C_HBG : v_C_BG());
  959.     const wchar_t   *txt=attr.img ? L" " : l.str+off;
  960.     TDrawText(dc.DC(),x0+x,line.top+l.base-fa,cli,line,txt,run,l.dx+off,flags);
  961.     if (attr.img) {
  962.       // slow, but we were adding the feature later, and
  963.       // the code wasn't designed for this in the first
  964.       // place
  965.       Paragraph p(m_textfile->GetParagraph(l.pos.docid,l.pos.para));
  966.       int idx=l.str[off];
  967.       Image img;
  968.       if (idx>=0 && idx<p.links.size() &&
  969.   m_textfile->GetImage(p.links[idx].target,dc.DC(),
  970.   l.dx[off],
  971.   l.height,m_TextDisp.angle,img))
  972. TDrawBitmap(dc.DC(),img.hBmp,x0+x,line.top+l.base-img.height,
  973.   l.height,cli,img.width,img.height,0);
  974.     }
  975.     len-=run;
  976.     off+=run;
  977.     line.left=line.right;
  978.     x=line.left-x0;
  979.   }
  980.   if (x<width && flags) { // fill right part
  981.     line.right=x0+width;
  982.     dc.SetBkColor(v_C_BG());
  983.     TDrawText(dc.DC(),line.left,line.top,cli,line,NULL,0,NULL);
  984.   }
  985.   dc.SetTextColor(C_NORM);
  986.   dc.SetBkColor(v_C_BG());
  987. }
  988. void  CTView::PaintUserInput(CFDC& dc,const RECT& rc,const RECT& cli,
  989.      const Buffer<wchar_t>& text)
  990. {
  991.   dc.SelectFont(USERINPUTSIZE,USERINPUTFLAGS);
  992.   dc.SetBkColor(C_UINPBG);
  993.   dc.SetTextColor(C_UINP);
  994.   TDrawText(dc.DC(),rc.left+2,rc.top+2,cli,rc,text,text.size(),NULL);
  995. }
  996. void CTView::CalcSizes()
  997. {
  998.   if (m_TextDisp.angle==900 || m_TextDisp.angle==2700) {
  999.     m_Window.rheight=m_Window.cli.right-m_Window.cli.left;
  1000.     m_Window.rwidth=m_Window.cli.bottom-m_Window.cli.top;
  1001.   } else {
  1002.     m_Window.rheight=m_Window.cli.bottom-m_Window.cli.top;
  1003.     m_Window.rwidth=m_Window.cli.right-m_Window.cli.left;
  1004.   }
  1005.   m_Window.width=m_Window.rwidth;
  1006.   m_Window.height=m_Window.rheight;
  1007.   if (m_Window.showprogress())
  1008.     m_Window.height-=m_Window.progress_height;
  1009.   m_Window.width/=m_Window.columns;
  1010.   if (m_Window.width==0)
  1011.     m_Window.width=1;
  1012.   if (m_Window.height==0)
  1013.     m_Window.height=1;
  1014.   CFDC fdc(m_hWnd);
  1015.   // calculate userinput window position
  1016.   fdc.SelectFont(3,0);
  1017.   int   height,ascent;
  1018.   fdc.GetFontSize(height,ascent);
  1019.   int   uwidth=m_Window.rwidth;
  1020.   if (uwidth>MAXUSERINPUTWIDTH)
  1021.     uwidth=MAXUSERINPUTWIDTH;
  1022.   m_UI.rc.left=(m_Window.rwidth-uwidth)/2;
  1023.   m_UI.rc.right=m_UI.rc.left+uwidth;
  1024.   m_UI.rc.top=20; // XXX
  1025.   m_UI.rc.bottom=m_UI.rc.top+height+4;
  1026.   if (m_formatter.get()) {
  1027.     m_formatter->SetSize(m_Window.width,m_TextDisp.margin_width,
  1028.       m_Window.height,m_Window.columns,m_TextDisp.angle);
  1029.     m_formatter->Reformat(fdc);
  1030.   }
  1031.   m_BP.visible=false;
  1032. }
  1033. void CTView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
  1034. {
  1035.   UINT cmd;
  1036.   // Check for key chatter and ignore event
  1037.   DWORD now=::GetTickCount();
  1038.   if (m_Window.autorepeatlimit && now-m_Window.lastkeypress<REPEAT_THRESHOLD)
  1039.     return;
  1040.   m_Window.lastkeypress=now;
  1041.   // spaces are handled separately in OnChar
  1042.   if (nChar!=VK_SPACE && Keys::TranslateKey(nChar,cmd,
  1043.   m_Window.rotbuttons ? m_TextDisp.angle : 0))
  1044.   {
  1045.     // repeated key
  1046.     if (nFlags&0x4000 && CTVApp::TopQueuedCmd()==cmd)
  1047.       return;
  1048.     CTVApp::QueueCmd(cmd);
  1049.     return;
  1050.   }
  1051.   CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
  1052. }
  1053. void CTView::OnLineUp() { Move(mBack,mLine); }
  1054. void CTView::OnLineDown() { Move(mFwd,mLine); }
  1055. void CTView::OnPageUp() { Move(mBack,mPage); }
  1056. void CTView::OnPageDown() { Move(mFwd,mPage); }
  1057. void CTView::OnStartFile() { Move(mBack,mFile); }
  1058. void CTView::OnEndFile() { Move(mFwd,mFile); }
  1059. void CTView::Move(int dir, int amount)
  1060. {
  1061.   if (dir==mBack) {
  1062.     if (m_formatter->AtTop())
  1063.       return;
  1064.   } else {
  1065.     if (m_formatter->AtEof())
  1066.       return;
  1067.   }
  1068.   Line     l;
  1069.   CFDC     fdc(m_hWnd);
  1070.   if (dir==mBack)
  1071.     switch (amount) {
  1072.     case mLine:
  1073.       m_formatter->FormatBack(fdc,m_formatter->GetLine(
  1074. m_formatter->Length()-1).pos,m_formatter->Top());
  1075.       break;
  1076.     case mFile:
  1077.       PushPos();
  1078.       m_formatter->FormatFwd(fdc,m_formatter->Sof());
  1079.       break;
  1080.     default:
  1081.     case mPage:
  1082.       m_formatter->FormatBack(fdc,m_formatter->Top(),FilePos());
  1083.       break;
  1084.     }
  1085.   else
  1086.     switch (amount) {
  1087.     case mLine:
  1088.       m_formatter->FormatFwd(fdc,m_formatter->GetLine(1).pos);
  1089.       break;
  1090.     case mFile:
  1091.       PushPos();
  1092.       m_formatter->FormatBack(fdc,m_formatter->Eof(),FilePos());
  1093.       break;
  1094.     default:
  1095.     case mPage:
  1096.       m_formatter->FormatFwdAdj(fdc);
  1097.       break;
  1098.     }
  1099.   QueueRepaint();
  1100.   m_BP.bmkidx=-1;
  1101. }
  1102. BOOL CTView::OnEraseBkgnd(CDC* pDC)
  1103. {
  1104.   return FALSE; // we'll erase it in OnPaint
  1105. }
  1106. class CAboutDialog: public CDialog {
  1107. public:
  1108.   CAboutDialog(UINT id,CWnd *parent=NULL) : CDialog(id,parent) { }
  1109.   virtual BOOL OnInitDialog();
  1110.   virtual BOOL OnCommand(WPARAM,LPARAM);
  1111.   CString   m_info;
  1112.   bool     m_finfo;
  1113. };
  1114. #include "buildnum.h"
  1115. #define FILE_INFO_FORMAT _T("File: %srnSize: %d byte(s), %d paragraph(s)rnrnFormat: %srnEncoding: %srnCompression: %srnrnPosition: %d:%d")
  1116. #define ABOUT_FORMAT1
  1117.   _T("expat 1.95.5 Copyright (C) 1998, 1999, 2000 Thai Open Source Software Center Ltd and Clark Cooper.rnrn") 
  1118.   _T("jpeg6b Copyright (C) 1991-1998, Thomas G. Lane.rnrn") 
  1119.   _T("libpng version 1.2.8 Copyright (c) 1998-2002 Glenn Randers-Pehrson, Copyright (c) 1996-1997 Andreas Dilger, Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.rnrn") 
  1120.   _T("zlib 1.2.2 Copyright (C) 1995-2002 Mark Adler.")
  1121. #if PSPC
  1122. #define ABOUT_FORMAT2 _T("Haali Reader v2.0b%dnCopyright (C) 2001-2005nMike Matsnevnhttp://haali.cs.msu.ru/pocketpc/")
  1123. #else
  1124. #define ABOUT_FORMAT2 _T("Haali Reader v2.0b%dnCopyright (C) 2001-2005 Mike Matsnevnhttp://haali.cs.msu.ru/pocketpc/")
  1125. #endif
  1126. BOOL CAboutDialog::OnInitDialog() {
  1127.   CDialog::OnInitDialog();
  1128.   CString ver;
  1129.   ver.Format(ABOUT_FORMAT2,BUILD_NUM,BUILD_DATE);
  1130.   SetDlgItemText(IDC_ABOUTVER,ver);
  1131.   SetDlgItemText(IDC_ABOUTINFO,m_info);
  1132.   m_finfo=true;
  1133.   return TRUE;
  1134. }
  1135. BOOL CAboutDialog::OnCommand(WPARAM wParam,LPARAM lParam) {
  1136.   if (LOWORD(wParam)==IDC_ABOUT_TOGGLE) {
  1137.     if (m_finfo) {
  1138.       SetDlgItemText(IDC_ABOUTINFO,ABOUT_FORMAT1);
  1139.       SetDlgItemText(IDC_ABOUT_TOGGLE,_T("File Info"));
  1140.     } else {
  1141.       SetDlgItemText(IDC_ABOUTINFO,m_info);
  1142.       SetDlgItemText(IDC_ABOUT_TOGGLE,_T("Copyrights"));
  1143.     }
  1144.     m_finfo=!m_finfo;
  1145.   }
  1146.   return CDialog::OnCommand(wParam,lParam);
  1147. }
  1148. void CTView::OnAppAbout()
  1149. {
  1150.   CAboutDialog   dlg(IDD_ABOUTBOX,this);
  1151.   CString   fmt,enc;
  1152.   FilePos   p=CurFilePos();
  1153.   if (m_textfile->GetFormat()<0) { // auto
  1154.     fmt.Format(_T("Auto, detected: %s"),
  1155.       m_textfile->GetFormatName(m_textfile->GetRealFormat()));
  1156.   } else { // set by user
  1157.     fmt=m_textfile->GetFormatName(m_textfile->GetFormat());
  1158.   }
  1159.   if (m_textfile->GetEncoding()<0) { // auto
  1160.     enc.Format(_T("Auto, detected: %s"),
  1161.       m_textfile->GetEncodingName(m_textfile->GetRealEncoding()));
  1162.   } else {
  1163.     enc=m_textfile->GetEncodingName(m_textfile->GetEncoding());
  1164.   }
  1165.   dlg.m_info.Format(FILE_INFO_FORMAT,
  1166.     (LPCTSTR)m_textfile->Name(),m_textfile->ByteLength(),
  1167.     m_textfile->Length(CurFilePos().docid),(LPCTSTR)fmt,(LPCTSTR)enc,
  1168.     (LPCTSTR)m_textfile->CompressionInfo(),
  1169.     p.para,p.off);
  1170.   dlg.DoModal();
  1171. }
  1172. void CTView::OnUpdateOptions(CCmdUI* pCmdUI) {
  1173.   pCmdUI->Enable();
  1174. }
  1175. void CTView::OnOptions()
  1176. {
  1177.   COptionsDialog    opt;
  1178.   opt.m_bold=m_TextDisp.bold;
  1179.   opt.m_cleartype=m_TextDisp.cleartype;
  1180.   opt.m_justify=m_TextDisp.justify;
  1181.   opt.m_margins=m_TextDisp.margin_width;
  1182.   opt.m_size=m_TextDisp.fontsize;
  1183.   opt.m_face=m_TextDisp.fontface;
  1184.   opt.m_hyphenate=m_TextDisp.hyphenate;
  1185.   opt.m_angle=m_TextDisp.angle/900;
  1186.   opt.m_columns=m_Window.columns-1;
  1187.   if (opt.DoModal()==IDOK) { // try to apply settings
  1188.     bool    update=false;
  1189.     if (m_TextDisp.fontface!=opt.m_face || m_TextDisp.fontsize!=opt.m_size ||
  1190. m_TextDisp.bold!=(opt.m_bold!=0) || m_TextDisp.cleartype!=opt.m_cleartype ||
  1191. m_TextDisp.angle!=opt.m_angle*900 || m_TextDisp.margin_width!=opt.m_margins ||
  1192. m_TextDisp.justify!=(opt.m_justify!=0) ||
  1193. m_TextDisp.hyphenate!=(opt.m_hyphenate!=0))
  1194.     {
  1195.       m_TextDisp.hyphenate=(opt.m_hyphenate!=0);
  1196.       m_formatter->SetHyphenate(m_TextDisp.hyphenate);
  1197.       m_TextDisp.justify=(opt.m_justify!=0);
  1198.       m_formatter->SetJustified(m_TextDisp.justify);
  1199.       m_TextDisp.angle=opt.m_angle*900;
  1200.       m_TextDisp.margin_width=opt.m_margins;
  1201.       m_TextDisp.SetFont(opt.m_face,opt.m_bold!=0,opt.m_size,opt.m_cleartype);
  1202.       m_TextDisp.SaveSettings();
  1203.       SetRotAngle(m_TextDisp.angle);
  1204.       update=true;
  1205.     }
  1206.     if (m_Window.columns!=opt.m_columns+1)
  1207.     {
  1208.       m_Window.columns=opt.m_columns+1;
  1209.       m_Window.SaveSettings();
  1210.       update=true;
  1211.     }
  1212.     if (update) {
  1213.       CalcSizes();
  1214.       QueueRepaint();
  1215.     }
  1216.   }
  1217. }
  1218. void CTView::OnUpdateFileformat(CCmdUI* pCmdUI) {
  1219.   pCmdUI->Enable(m_formatter->DocId()>=0);
  1220. }
  1221. void CTView::OnFileformat()
  1222. {
  1223.   if (m_formatter->DocId()<0)
  1224.     return;
  1225.   CFileFormatDialog fmt;
  1226.   fmt.m_format=m_textfile->GetFormat()+1;
  1227.   fmt.m_encoding=m_textfile->GetEncoding()+1;
  1228.   fmt.m_defencoding=CTVApp::GetInt(_T("DefEncoding"),-1)+1;
  1229.   if (fmt.DoModal()==IDOK) {
  1230.     if (fmt.m_defencoding!=CTVApp::GetInt(_T("DefEncoding"),-1)+1)
  1231.       CTVApp::SetInt(_T("DefEncoding"),fmt.m_defencoding-1);
  1232.     m_textfile->SetFormatEncoding(fmt.m_format-1,fmt.m_encoding-1);
  1233.     m_formatter->SetTop(FilePos());
  1234.     CFDC   fdc(m_hWnd);
  1235.     m_formatter->Reformat(fdc);
  1236.     QueueRepaint();
  1237.     m_Search.matchpos=m_formatter->Eof();
  1238.   }
  1239. }
  1240. void  CTView::PushPos() {
  1241.   while (m_History.stacktop) {
  1242.     POSITION  tmp=m_History.stacktop;
  1243.     m_History.pstack.GetNext(m_History.stacktop);
  1244.     m_History.pstack.RemoveAt(tmp);
  1245.   }
  1246.   m_History.pstack.AddTail(m_formatter->Top());
  1247.   if (m_History.pstack.GetCount()>100)
  1248.     m_History.pstack.RemoveHead();
  1249. }
  1250. static CString GetNextFile(const CString& filename,bool fNext) {
  1251.   CString     path(filename);
  1252.   int     p1=path.ReverseFind(_T('\'));
  1253.   int     p2=path.ReverseFind(_T('/'));
  1254.   p1=max(p1,p2)+1;
  1255.   path.Delete(p1,path.GetLength()-p1);
  1256.   path+=_T("*");
  1257.   CString     file(filename);
  1258.   file.Delete(0,p1);
  1259.   CString     prev,first;
  1260.   WIN32_FIND_DATA   fd;
  1261.   HANDLE     hFind=::FindFirstFile(path,&fd);
  1262.   BOOL     fFind=hFind!=INVALID_HANDLE_VALUE;
  1263.   for (;fFind;fFind=::FindNextFile(hFind,&fd)) {
  1264.     if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  1265.       continue;
  1266.     const TCHAR   *ext=fd.cFileName+_tcslen(fd.cFileName);
  1267.     while (ext>fd.cFileName && ext[-1]!=_T('.'))
  1268.       --ext;
  1269.     if (_tcsicmp(ext,_T("jpg")) && _tcsicmp(ext,_T("png")) &&
  1270. _tcsicmp(ext,_T("jpeg")))
  1271.       continue;
  1272.     if (first.IsEmpty())
  1273.       first=fd.cFileName;
  1274.     if (fNext) {
  1275.       if (file.CompareNoCase(prev)==0) {
  1276. prev=fd.cFileName;
  1277. break;
  1278.       }
  1279.     } else {
  1280.       if (file.CompareNoCase(fd.cFileName)==0)
  1281. break;
  1282.     }
  1283.     prev=fd.cFileName;
  1284.   }
  1285.   if (hFind!=INVALID_HANDLE_VALUE)
  1286.     ::FindClose(hFind);
  1287.   if (!fFind) { // looped over all files and didnt find a match
  1288.     if (fNext && file.CompareNoCase(prev)==0)
  1289.       prev=first;
  1290.   }
  1291.   path.Delete(path.GetLength()-1); // remove last '*'
  1292.   return prev.IsEmpty() ? prev : path+prev;
  1293. }
  1294. static TextFile  *OpenNextImage(const CString& cur,bool fNext) {
  1295.   for (CString next(GetNextFile(cur,fNext));!next.IsEmpty() && next!=cur;next=GetNextFile(next,fNext)) {
  1296.     TextFile  *tf=TextFile::Open(next);
  1297.     if (tf)
  1298.       return tf;
  1299.   }
  1300.   return NULL;
  1301. }
  1302. void CTView::OnUpdateBack(CCmdUI* pCmdUI) {
  1303.   if (m_textfile->IsImage()) {
  1304.     pCmdUI->Enable();
  1305.     return;
  1306.   }
  1307.   pCmdUI->Enable(!m_History.pstack.IsEmpty() &&
  1308.     m_History.stacktop!=m_History.pstack.GetHeadPosition());
  1309. #if POCKETPC
  1310.   // tweak ok button
  1311.   // when current view is a dict, and there are !dict items below
  1312.   if (m_formatter->DocId()<0)
  1313.     for (POSITION q=m_History.pstack.GetHeadPosition();q && q!=m_History.stacktop;)
  1314.       if (m_History.pstack.GetNext(q).docid>=0) { // ok, enable
  1315. if (!m_Dict.okstate) {
  1316.   ShowDoneButton(TRUE);
  1317.   m_Dict.okstate=true;
  1318. }
  1319. return;
  1320.       }
  1321.   if (m_Dict.okstate) {
  1322.     ShowDoneButton(FALSE);
  1323.     m_Dict.okstate=false;
  1324.   }
  1325. #endif
  1326. }
  1327. void CTView::OnBack() {
  1328.   if (m_textfile->IsImage()) {
  1329.     TextFile  *tf=OpenNextImage(m_textfile->Name(),false);
  1330.     if (tf) {
  1331.       AfxGetMainWnd()->SetWindowText(FileName(tf->Name()));
  1332.       SetFile(kilo::auto_ptr<TextFile>(tf));
  1333.     }
  1334.     return;
  1335.   }
  1336.   if (!m_History.pstack.IsEmpty() &&
  1337. m_History.stacktop!=m_History.pstack.GetHeadPosition())
  1338.   {
  1339.     FilePos   cur=m_formatter->Top();
  1340.     if (m_History.stacktop==NULL)
  1341.       m_History.stacktop=m_History.pstack.GetTailPosition();
  1342.     else
  1343.       m_History.pstack.GetPrev(m_History.stacktop);
  1344.     MoveAbs(m_History.pstack.GetAt(m_History.stacktop));
  1345.     m_History.pstack.SetAt(m_History.stacktop,cur);
  1346.   }
  1347. }
  1348. void  CTView::OnNextSection() {
  1349.   Bookmarks& bm=m_textfile->bmk();
  1350.   FilePos cur=m_formatter->Bottom();
  1351.   if (cur<m_formatter->Eof()) {
  1352.     int   index=bm.BFind(cur,Bookmarks::SNEXTCH);
  1353.     if (index<bm.GetSize()) {
  1354.       if (cur==bm.Ref(index) && index<bm.GetSize())
  1355. ++index;
  1356.       if (cur<bm.Ref(index)) {
  1357. PushPos();
  1358. EnsureVisible(bm.Ref(index));
  1359.       }
  1360.     }
  1361.   }
  1362. }
  1363. void  CTView::OnPrevSection() {
  1364.   Bookmarks& bm=m_textfile->bmk();
  1365.   FilePos cur=m_formatter->Top();
  1366.   if (cur>FilePos()) {
  1367.     int   index=bm.BFind(cur,Bookmarks::SPREVCH);
  1368.     if (index<bm.GetSize()) {
  1369.       if (cur==bm.Ref(index) && index>0)
  1370. do --index; while (index>=0 && bm.Flags(index)&Bookmarks::BMK);
  1371.       if (index<0)
  1372. OnStartFile();
  1373.       else
  1374. if (bm.Ref(index)<cur) {
  1375.   PushPos();
  1376.   EnsureVisible(bm.Ref(index));
  1377. }
  1378.     }
  1379.   }
  1380. }
  1381. void  CTView::DisplayBookmarkPopup(int index) {
  1382.   Bookmarks& bm=m_textfile->bmk();
  1383.   if (bm.Flags(index) & Bookmarks::BMK) {
  1384.     POINT pt;
  1385.     if (LookupPoint(bm.Ref(index),pt))
  1386.       DisplayBookmarkPopup(pt,Unicode::ToWCbuf(bm.Text(index,m_textfile.get())));
  1387.   }
  1388.   m_BP.bmkidx=index;
  1389. }
  1390. void  CTView::OnNextBm() {
  1391.   Bookmarks& bm=m_textfile->bmk();
  1392.   int index;
  1393.   if (m_BP.bmkidx>=0 && m_BP.bmkidx<bm.GetSize()-1) {
  1394.     index=m_BP.bmkidx+1;
  1395.   } else {
  1396.     if (m_formatter->AtEof())
  1397.       return;
  1398.     FilePos cur=m_formatter->Bottom();
  1399.     index=bm.BFind(cur,Bookmarks::SNEXTANY);
  1400.     if (index>=bm.GetSize())
  1401.       return;
  1402.     if (cur==bm.Ref(index) && index<bm.GetSize()-1)
  1403.       ++index;
  1404.     if (cur>=bm.Ref(index))
  1405.       return;
  1406.   }
  1407.   PushPos();
  1408.   EnsureVisible(bm.Ref(index));
  1409.   DisplayBookmarkPopup(index);
  1410. }
  1411. void  CTView::OnPrevBm() {
  1412.   Bookmarks& bm=m_textfile->bmk();
  1413.   int index;
  1414.   if (m_BP.bmkidx>0 && m_BP.bmkidx<bm.GetSize()) {
  1415.     index=m_BP.bmkidx-1;
  1416.   } else {
  1417.     if (m_formatter->AtTop())
  1418.       return;
  1419.     FilePos cur=m_formatter->Top();
  1420.     index=bm.BFind(cur,Bookmarks::SPREVANY);
  1421.     if (index>=bm.GetSize())
  1422.       return;
  1423.     if (cur==bm.Ref(index) && index>0)
  1424.       --index;
  1425.  
  1426.     if (bm.Ref(index)>=cur)
  1427.       return;
  1428.   }
  1429.   PushPos();
  1430.   EnsureVisible(bm.Ref(index));
  1431.   DisplayBookmarkPopup(index);
  1432. }
  1433. #if POCKETPC
  1434. static bool TryRunLingvo(const TCHAR *path,HWND hWnd) {
  1435.   // construct shortcut name
  1436.   CString   pp(path);
  1437.   if (pp.IsEmpty())
  1438.     return false;
  1439.   if (pp[pp.GetLength()-1]!=_T('\'))
  1440.     pp+=_T('\');
  1441.   pp+=_T("ABBYY Lingvo.lnk");
  1442.   // extract filename from shortcut
  1443.   CString   exe;
  1444.   TCHAR     *cp=exe.GetBuffer(MAX_PATH);
  1445.   if (!SHGetShortcutTarget(pp,cp,MAX_PATH))
  1446.     return false;
  1447.   exe.ReleaseBuffer();
  1448.   // replace the executable with LingvoCE.exe
  1449.   int p=exe.ReverseFind(_T('\'));
  1450.   int q=exe.ReverseFind(_T('/'));
  1451.   if (p<q)
  1452.     p=q;
  1453.   exe.Delete(p+1,exe.GetLength()-p-1);
  1454.   exe+=_T("LingvoCE.exe");
  1455.   // strip leading quote if there is one
  1456.   if (exe.GetLength()>0 && exe[0]==_T('"'))
  1457.     exe.Delete(0);
  1458.   // try to run it
  1459.   PROCESS_INFORMATION pi;
  1460.   if (!CreateProcess(exe,_T("-c"),NULL,NULL,FALSE,0,NULL,NULL,NULL,&pi))
  1461.     return false;
  1462.   CloseHandle(pi.hProcess);
  1463.   CloseHandle(pi.hThread);
  1464.   return true;
  1465. }
  1466. #endif
  1467. void CTView::LookupText(Buffer<wchar_t> text) {
  1468.   InitDictName();
  1469. #ifdef _WIN32_WCE
  1470.   if (m_Dict.curdict==_T(":SlovoEd")) {
  1471.     // do a double copy to invoke slovoed resident module
  1472.     CopyToClipboard(text,text.size(),m_hWnd);
  1473.     Sleep(200);
  1474.     CopyToClipboard(text,text.size(),m_hWnd);
  1475.     m_Dict.lastdictlookup=text;
  1476.   } else
  1477. #endif
  1478.   if (m_Dict.curdict==_T(":Lingvo")) {
  1479. #if POCKETPC
  1480.     CopyToClipboard(text,text.size(),m_hWnd);
  1481.     // check if lingvo is already running
  1482.     HANDLE  hMutex = ::CreateMutex(0, 0, _T("Lingvo CE"));
  1483.     DWORD   dwError = ::GetLastError();
  1484.     ::CloseHandle(hMutex);
  1485.     if(dwError != ERROR_ALREADY_EXISTS) {
  1486.       TCHAR   buffer[MAX_PATH];
  1487.       // try to run it
  1488.       if (!::SHGetSpecialFolderPath(m_hWnd,buffer,CSIDL_PROGRAMS,FALSE) ||
  1489.   !TryRunLingvo(buffer,m_hWnd))
  1490. if (::SHGetSpecialFolderPath(m_hWnd,buffer,CSIDL_STARTMENU,FALSE))
  1491.   TryRunLingvo(buffer,m_hWnd);
  1492.     } else {
  1493.       // send command to an existing window
  1494.       HWND wnd = ::FindWindow(L"Afx:WLV_MainWindow100", 0);
  1495.       if(wnd)
  1496. ::PostMessage(wnd, WM_COMMAND, WM_USER + 400, 0);
  1497.     }
  1498. #else
  1499.     // use automation interface
  1500. #endif
  1501.     m_Dict.lastdictlookup=text;
  1502.   } else {
  1503.     if (!OpenDict())
  1504.       return;
  1505.     FilePos     wp;
  1506.     // dict interface wants 0 terminated strings, so we create a copy here
  1507.     Buffer<wchar_t> ztext(text.size()+1);
  1508.     memcpy(ztext,text,text.size()*sizeof(wchar_t));
  1509.     ztext[text.size()]=L'';
  1510.     if (m_textfile->LookupDict(ztext,wp)) {
  1511.       PushPos();
  1512.       MoveAbs(wp);
  1513.       m_Dict.lastdictlookup=text;
  1514.     }
  1515.   }
  1516. }
  1517. void CTView::HandleMouseDown(CPoint point) {
  1518.   if (m_BP.visible) {
  1519.     HideBookmarkPopup();
  1520.     return;
  1521.   }
  1522.   POINT     pt;
  1523.   FilePos   pos;
  1524.   pt.x=point.x;
  1525.   pt.y=point.y;
  1526.   // transform point into virtual coords
  1527.   System2Window(pt,m_Window.cli);
  1528.   // check if we are in a progress bar
  1529.   if (pt.y>=m_Window.rheight-PROGRESS_C && pt.y<m_Window.rheight) {
  1530.     if (pt.x<PROGRESS_M+PROGRESS_A) { // prev section
  1531.       OnPrevSection();
  1532.     } else if (pt.x>=m_Window.rwidth-PROGRESS_M-PROGRESS_A-m_Window.pb_width &&
  1533.        pt.x<m_Window.rwidth-m_Window.pb_width)
  1534.     { // next section
  1535.       OnNextSection();
  1536.     } else if (pt.x<m_Window.rwidth-PROGRESS_M-PROGRESS_A-m_Window.pb_width) { // move absolute
  1537.       PushPos();
  1538.       int   charpos=MulDivS(
  1539.     m_textfile->GetTotalLength(m_formatter->Top().docid),
  1540.     pt.x-PROGRESS_M-PROGRESS_A,
  1541.     m_Window.rwidth-2*PROGRESS_M-2*PROGRESS_A-m_Window.pb_width
  1542.       );
  1543.       int   para=m_textfile->LookupParagraph(m_formatter->DocId(),charpos);
  1544.       EnsureVisible(
  1545. FilePos(
  1546.   para,
  1547.   charpos-m_textfile->GetPStart(m_formatter->DocId(),para),
  1548.   m_formatter->DocId()
  1549. )
  1550.       );
  1551.     } else { // right part, back in dict mode
  1552.       if (m_formatter->DocId()<0)
  1553. OnBack();
  1554.     }
  1555.     return;
  1556.   }
  1557.   
  1558.   if (LookupAddr(pt,pos)) {
  1559.     // check if there is a bookmark nearby
  1560.     int     bmk=m_textfile->bmk().BFind(pos,Bookmarks::SPREVBMK);
  1561.     if (bmk>=0) {
  1562.       FilePos bp(m_textfile->bmk().Ref(bmk));
  1563.       if (bp.docid==pos.docid && bp.para==pos.para && bp.off>=pos.off-2) {
  1564. DisplayBookmarkPopup(pt,Unicode::ToWCbuf(m_textfile->bmk().Text(bmk,m_textfile.get())));
  1565. return;
  1566.       }
  1567.     }
  1568.     // check if it is a link
  1569.     Paragraph p(m_textfile->GetParagraph(pos.docid,pos.para));
  1570.     for (int link=0;link<p.links.size();++link)
  1571.       if (p.links[link].off<=(DWORD)pos.off && p.links[link].off+p.links[link].len>(DWORD)pos.off)
  1572.       {
  1573. // found a link
  1574. // for now only local links are supported
  1575. if (p.links[link].target[0]==_T('#')) {
  1576.   FilePos   dest;
  1577.   if (m_textfile->LookupReference(p.links[link].target+1,dest)) {
  1578.     PushPos();
  1579.     MoveAbs(dest);
  1580.   } else
  1581.     goto beep;
  1582. } else
  1583. beep:
  1584.   MessageBeep(MB_OK);
  1585. return;
  1586.       }
  1587.     // lookup a word in the dictionary then
  1588.     InitDictName();
  1589.     if (m_Dict.curdict!=_T(":Disable")) {
  1590.       int start,end;
  1591.       for (start=pos.off+1;start>0 && (iswalpha(p.str[start-1]) ||
  1592.    p.str[start-1]==L''' ||
  1593.    p.str[start-1]==L'-');--start) ;
  1594.       for (end=pos.off+1;end<p.str.size() && (iswalpha(p.str[end]) ||
  1595.   p.str[end]==L''' || p.str[end]==L'-');++end) ;
  1596.       if (start!=end) {
  1597. LookupText(Buffer<wchar_t>(p.str+start,end-start));
  1598. return;
  1599.       }
  1600.     }
  1601.   }
  1602.   // didn't have an action yet, interpret as movement
  1603.   if (m_Dict.curdict==_T(":Disable")) {
  1604.     if (pt.y<(m_Window.rheight-PROGRESS_C)/2)
  1605.       Move(mBack,mPage);
  1606.     else
  1607.       Move(mFwd,mPage);
  1608.   }
  1609. }
  1610. void CTView::OnLButtonDblClk(UINT nFlags, CPoint point) {
  1611.   FilePos   pos;
  1612.   System2Window(point,m_Window.cli);
  1613.   if (point.y<m_Window.rheight-m_Window.progress_height)
  1614.     CTVApp::QueueCmd(ID_FULLSCREEN);
  1615. }
  1616. void CTView::MoveAbs(FilePos pos) {
  1617.   CFDC fdc(m_hWnd);
  1618.   m_formatter->FormatFwd(fdc,pos);
  1619.   QueueRepaint();
  1620. }
  1621. void CTView::EnsureVisible(FilePos pos) {
  1622.   CFDC fdc(m_hWnd);
  1623.   if (m_formatter->EnsureVisible(fdc,pos))
  1624.     QueueRepaint();
  1625. }
  1626. void CTView::OnLookupSel() {
  1627.   InitDictName();
  1628.   if (m_Dict.curdict==_T(":Disable"))
  1629.     return;
  1630.   Buffer<wchar_t> sel;
  1631.   if (!GetSelText(sel))
  1632.     return;
  1633.   LookupText(sel);
  1634. }
  1635. void CTView::OnDictSetup() {
  1636.   CDictSetupDlg   dlg;
  1637.   dlg.DoModal();
  1638. }
  1639. void CTView::OnUpdateDictSetup(CCmdUI* pCmdUI) {
  1640.   pCmdUI->Enable();
  1641. }
  1642. void CTView::OnUpdateFind(CCmdUI* pCmdUI) {
  1643.   pCmdUI->Enable();
  1644. }
  1645. void CTView::OnFind() {
  1646.   CFindDlg    dlg(this);
  1647.   Buffer<wchar_t> sel;
  1648.   if (GetSelText(sel))
  1649.     dlg.m_text=Unicode::ToCS(sel);
  1650.   else
  1651.     dlg.m_text=Unicode::ToCS(m_Search.searchstr);
  1652.   dlg.m_matchcase=m_Search.matchcase;
  1653.   if (dlg.DoModal()==IDOK && dlg.m_text.GetLength()>0) {
  1654.     m_Search.matchpos=m_formatter->Sof();
  1655.     m_Search.matchcase=dlg.m_matchcase!=0;
  1656.     m_Search.searchstr=Unicode::ToWCbuf(dlg.m_text);
  1657.     if (!m_Search.matchcase)
  1658.       m_Search.searchstr=Unicode::Lower(m_Search.searchstr);
  1659.     CTVApp::QueueCmd(ID_DO_FIND);
  1660.   }
  1661. }
  1662. void CTView::OnFindnext() {
  1663.   if (m_Search.searchstr.size()>0 &&
  1664.       m_Search.matchpos+m_Search.searchstr.size()<m_formatter->Eof()) {
  1665.     m_Search.matchpos.off+=m_Search.searchstr.size();
  1666.     DoFind();
  1667.   }
  1668. }
  1669. void CTView::OnUpdateFindnext(CCmdUI* pCmdUI) {
  1670.   pCmdUI->Enable(m_Search.searchstr.size()>0 &&
  1671.     m_Search.matchpos+m_Search.searchstr.size()<m_formatter->Eof());
  1672. }
  1673. static Buffer<int>  kmptable(const Buffer<wchar_t>& s) {
  1674.   Buffer<int> b(s.size()+1);
  1675.   if (s.size()>0) {
  1676.     int i,j;
  1677.     i=0;
  1678.     j=b[0]=-1;
  1679.     while (i<s.size()) {
  1680.       while (j>-1 && s[i]!=s[j])
  1681. j=b[j];
  1682.       ++i;
  1683.       ++j;
  1684.       if (i<s.size() && j<s.size() && s[i]==s[j])
  1685. b[i]=b[j];
  1686.       else
  1687. b[i]=j;
  1688.     }
  1689.   }
  1690.   return b;
  1691. }
  1692. static int   kmpfind(const wchar_t *s,int len,int off,const wchar_t *pat,
  1693.   int patlen,int *tab)
  1694. {
  1695.   int   i=0,j=off;
  1696.   while (j<len) {
  1697.     while (i>-1 && pat[i]!=s[j])
  1698.       i=tab[i];
  1699.     ++i;
  1700.     ++j;
  1701.     if (i>=patlen)
  1702.       return j-i;
  1703.   }
  1704.   return -1;
  1705. }
  1706. void CTView::DoFind() {
  1707.   if (m_Search.matchpos.para<m_textfile->Length(m_Search.matchpos.docid)) {
  1708.     CWaitCursor wait;
  1709.     Buffer<int> tab(kmptable(m_Search.matchcase ? m_Search.searchstr :
  1710. Unicode::Lower(m_Search.searchstr)));
  1711.     while (m_Search.matchpos.para<m_textfile->Length(m_Search.matchpos.docid)) {
  1712.       Paragraph       para(m_textfile->GetParagraph(m_Search.matchpos.docid,
  1713.     m_Search.matchpos.para));
  1714.       Buffer<wchar_t> text(m_Search.matchcase ? para.str : Unicode::Lower(para.str));
  1715.       int       pp=kmpfind(text,text.size(),
  1716. m_Search.matchpos.off,m_Search.searchstr,m_Search.searchstr.size(),tab);
  1717.       if (pp>=0) {
  1718. m_Search.matchpos.off=pp;
  1719. if (m_Search.matchpos<m_formatter->Top() ||
  1720.     m_Search.matchpos>=m_formatter->Bottom())
  1721. {
  1722.   PushPos();
  1723.   EnsureVisible(m_Search.matchpos);
  1724. }
  1725. SetSelection(m_Search.matchpos,m_Search.searchstr.size());
  1726. QueueRepaint();
  1727. return;
  1728.       }
  1729.       ++m_Search.matchpos.para;
  1730.       m_Search.matchpos.off=0;
  1731.     }
  1732.   }
  1733.   // didnt find anything
  1734.   SetSelection(FilePos(),0);
  1735.   QueueRepaint();
  1736.   MessageBeep(MB_OK);
  1737. }
  1738. void CTView::OnUpdateColors(CCmdUI* pCmdUI) {
  1739.   pCmdUI->Enable();
  1740. }
  1741. void CTView::OnColors() {
  1742.   int gamma=CTVApp::GetInt(_T("Gamma"),DEF_GAMMA);
  1743.   if (myChooseColors(g_colors,&gamma,this)) {
  1744.     SaveColors();
  1745.     CTVApp::SetInt(_T("Gamma"),gamma);
  1746.     m_textfile->InvalidateImageCache();
  1747.     QueueRepaint();
  1748.   }
  1749. }
  1750. void CTView::OnAddBmk() {
  1751.   CAddBmDialog   dlg(this);
  1752.   FilePos   pos(CurFilePos());
  1753.   Buffer<wchar_t> sel;
  1754.   if (m_Sel.start.docid==pos.docid && GetSelText(sel))
  1755.     pos=m_Sel.start;
  1756.   dlg.m_text=Unicode::ToCS(sel);
  1757.   if (dlg.DoModal()==IDOK) {
  1758.     int nidx=m_textfile->bmk().Add(dlg.m_text,pos);
  1759.     if (nidx<=m_BP.bmkidx)
  1760.       ++m_BP.bmkidx;
  1761.     QueueRepaint();
  1762.   }
  1763. }
  1764. void CTView::OnUpdateAddBmk(CCmdUI* pCmdUI) {
  1765.   pCmdUI->Enable(m_formatter->DocId()>=0);
  1766. }
  1767. void CTView::OnBookmarks() {
  1768.   if (m_textfile->bmk().GetSize()<=0 && m_textfile->GetSubDocCount()<=1)
  1769.     return;
  1770.   CContentsDlg    dlg(m_textfile->bmk(),m_textfile.get(),CurFilePos(),this);
  1771.   if (dlg.DoModal()!=IDOK)
  1772.     return;
  1773.   dlg.m_index=m_textfile->bmk().Commit(dlg.m_index);
  1774.   if (dlg.m_index>=0) {
  1775.     PushPos();
  1776.     EnsureVisible(m_textfile->bmk().Ref(dlg.m_index));
  1777.     DisplayBookmarkPopup(dlg.m_index);
  1778.   } else if (-dlg.m_index<=m_textfile->GetSubDocCount()) {
  1779.     PushPos();
  1780.     EnsureVisible(FilePos(0,0,-dlg.m_index-1));
  1781.   }
  1782. }
  1783. void CTView::OnUpdateBookmarks(CCmdUI* pCmdUI) {
  1784.   pCmdUI->Enable(m_textfile->bmk().GetSize()>0);
  1785. }
  1786. void CTView::OnKeys() {
  1787.   Keys::SetupKeys(this);
  1788. }
  1789. void CTView::OnUpdateKeys(CCmdUI* pCmdUI) {
  1790.   pCmdUI->Enable();
  1791. }
  1792. void CTView::OnDestroy() 
  1793. {
  1794.   Keys::SetWindow(0);
  1795.   KillTimer(m_timer);
  1796.   KillTimer(m_UI.timer);
  1797.   KillTimer(m_AS.timer);
  1798.   KillTimer(m_Window.pd_timer);
  1799.   CWnd::OnDestroy();
  1800. }
  1801. LRESULT CTView::OnHotkey(WPARAM wp,LPARAM lp) {
  1802.   UINT cmd;
  1803.   if (Keys::TranslateKey(wp,cmd))
  1804.     CTVApp::QueueCmd(cmd);
  1805.   return 0;
  1806. }
  1807. void CTView::OnStyles() {
  1808.   CStylesDlg dlg(this);
  1809.   if (dlg.DoModal()==IDOK) {
  1810.     if (dlg.SaveChanges()) {
  1811.       XMLParser::SaveStyles();
  1812.       m_textfile->Reparse();
  1813.       CFDC   fdc(m_hWnd);
  1814.       m_formatter->Reformat(fdc);
  1815.       QueueRepaint();
  1816.     }
  1817.   }
  1818. }
  1819. void CTView::OnUpdateStyles(CCmdUI* pCmdUI) {
  1820.   pCmdUI->Enable();
  1821. }
  1822. bool CTView::LookupAddr(const POINT& vp,FilePos& p) {
  1823.   int   column=vp.x/m_Window.width;
  1824.   if (column<0 || column>=m_Window.columns) {
  1825.     if (column<0)
  1826.       p=m_formatter->Top();
  1827.     else
  1828.       p=m_formatter->Bottom();
  1829.     return false;
  1830.   }
  1831.   int   off=0;
  1832.   for (int col=0;col<column;++col)
  1833.     off+=m_formatter->PageLength(col);
  1834.   int   line=0;
  1835.   int   cury=0;
  1836.   if (vp.y<0) {
  1837.     if (m_formatter->PageLength(column)>0)
  1838.       p=m_formatter->GetLine(off).pos;
  1839.     else
  1840.       p=m_formatter->Bottom();
  1841.     return false;
  1842.   }
  1843.   for (;line<m_formatter->PageLength(column);++line) {
  1844.     const Line& l=m_formatter->GetLine(off+line);
  1845.     if (vp.y>=cury && vp.y<cury+l.height) { // found line
  1846.       if (l.flags&Line::image) { // don't bother selecting this
  1847. p=l.pos;
  1848. return false;
  1849.       }
  1850.       int x=vp.x-m_Window.width*column-l.ispace-m_TextDisp.margin_width;
  1851.       int curx=0;
  1852.       int sym=0;
  1853.       if (x<0) {
  1854. p=l.pos;
  1855. return false;
  1856.       }
  1857.       for (;sym<l.real_len;++sym) {
  1858. if (x>=curx && x<curx+l.dx[sym]) { // found character
  1859.   p=l.pos;
  1860.   p.off+=sym;
  1861.   return true;
  1862. }
  1863. curx+=l.dx[sym];
  1864.       }
  1865.       p=l.pos;
  1866.       p.off+=sym;
  1867.       return false;
  1868.     }
  1869.     cury+=l.height;
  1870.   }
  1871.   if (m_formatter->PageLength(column)>0) {
  1872.     const Line& l=m_formatter->GetLine(off+m_formatter->PageLength(column)-1);
  1873.     p=l.pos;
  1874.     p.off+=l.real_len;
  1875.   } else
  1876.     p=m_formatter->Bottom();
  1877.   return false;
  1878. }
  1879. bool  CTView::LookupPoint(FilePos p,POINT& pt) {
  1880.   if (p<m_formatter->Top() || p>=m_formatter->Bottom())
  1881.     return false;
  1882.   for (int column=0,max=0,i=0,x=0;column<m_Window.columns;++column,x+=m_Window.width) {
  1883.     max+=m_formatter->PageLength(column);
  1884.     for (int y=0;i<max;++i) {
  1885.       const Line& l1=m_formatter->GetLine(i);
  1886.       const Line& l2=m_formatter->GetLine(i+1);
  1887.       if (p>=l1.pos && p<l2.pos) {
  1888. x+=l1.ispace+m_TextDisp.margin_width;
  1889. int dist=p.off-l1.pos.off;
  1890. for (int i=0;i<dist;++i)
  1891.   x+=l1.dx[i];
  1892. pt.x=x;
  1893. pt.y=y;
  1894. return true;
  1895.       }
  1896.       y+=l1.height;
  1897.     }
  1898.   }
  1899.   return false;
  1900. }
  1901. void CTView::OnLButtonDown(UINT nFlags, CPoint point)
  1902. {
  1903.   StopAS();
  1904.   m_Mouse.start=m_Mouse.last=m_Mouse.end=point;
  1905.   m_Mouse.trackmouse=true;
  1906.   m_Mouse.dmove=m_Sel.len>0;
  1907.   SetCapture();
  1908.   TrackMouse();
  1909. }
  1910. void CTView::OnLButtonUp(UINT nFlags, CPoint point) {
  1911.   StopAS();
  1912.   if (m_Mouse.trackmouse) {
  1913.     m_Mouse.end=point;
  1914.     TrackMouse();
  1915.     m_Mouse.trackmouse=false;
  1916.     ReleaseCapture();
  1917.     int len;
  1918.     FilePos start;
  1919.     CalcSelection(start,len);
  1920.     if (!m_Mouse.dmove && !len) // position didnt change
  1921.       HandleMouseDown(point);
  1922.     else if (len)
  1923.       DisplaySelectionMenu(point);
  1924.   }
  1925. }
  1926. class CCustomMenu : public CWnd, public CRotate
  1927. {
  1928. public:
  1929.   CCustomMenu() { }
  1930.   void   AppendMenu(const CString& title,UINT id) {
  1931.     m_title.Add(title);
  1932.     m_id.Add(id);
  1933.   }
  1934.   UINT   TrackPopupMenu(CWnd *parent,int reqx,int reqy,int angle);
  1935. protected:
  1936.   enum { FSZ=13, PAD=2, BORDER=1 };
  1937.   CStringArray   m_title;
  1938.   CUIntArray   m_id;
  1939.   int   m_selection,m_fh,m_fa,m_w,m_h;
  1940.   void MenuSelect() {
  1941. #if 0
  1942.     PlaySound(_T("hwandsw"),NULL,SND_ASYNC|SND_NODEFAULT); // XXX hack
  1943. #endif
  1944.     EndModalLoop(m_selection<0 ? 0 : m_id[m_selection]);
  1945.   }
  1946.   DECLARE_MESSAGE_MAP()
  1947.   afx_msg void OnPaint();
  1948.   afx_msg BOOL OnEraseBkgnd(CDC* pDC) { return FALSE; }
  1949.   afx_msg void OnMouseMove(UINT nFlags,CPoint point);
  1950.   afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
  1951.   afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
  1952.   afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
  1953.   afx_msg void OnCaptureChanged(CWnd *pWnd) { EndModalLoop(0); }
  1954.   afx_msg void OnKillFocus(CWnd* pNewWnd) { EndModalLoop(0); }
  1955. };
  1956. BEGIN_MESSAGE_MAP(CCustomMenu,CWnd )
  1957. ON_WM_PAINT()
  1958. ON_WM_ERASEBKGND()
  1959. ON_WM_KEYDOWN()
  1960. ON_WM_MOUSEMOVE()
  1961. ON_WM_LBUTTONDOWN()
  1962. ON_WM_LBUTTONUP()
  1963. ON_WM_CAPTURECHANGED()
  1964. ON_WM_KILLFOCUS()
  1965. END_MESSAGE_MAP()
  1966. void  CCustomMenu::OnMouseMove(UINT nFlags,CPoint point) {
  1967.   RECT cli;
  1968.   GetClientRect(&cli);
  1969.   System2Window(point,cli);
  1970.   int sel=-1;
  1971.   if (point.x>=BORDER && point.x<m_w-BORDER && point.y>=BORDER && point.y<m_h-BORDER)
  1972.     sel=(point.y-BORDER)/m_fh;
  1973.   if (sel!=m_selection) {
  1974.     m_selection=sel;
  1975.     InvalidateRect(NULL);
  1976.   }
  1977. }
  1978. void  CCustomMenu::OnLButtonDown(UINT nFlags,CPoint point) {
  1979.   RECT cli;
  1980.   GetClientRect(&cli);
  1981.   System2Window(point,cli);
  1982.   if (point.x<BORDER || point.x>=m_w-BORDER || point.y<BORDER || point.y>=m_h-BORDER)
  1983.     EndModalLoop(0);
  1984.   else {
  1985.     m_selection=(point.y-BORDER)/m_fh;
  1986.     InvalidateRect(NULL);
  1987.   }
  1988. }
  1989. void  CCustomMenu::OnLButtonUp(UINT nFlags,CPoint point) {
  1990.   RECT cli;
  1991.   GetClientRect(&cli);
  1992.   System2Window(point,cli);
  1993.   if (point.x>=BORDER && point.x<m_w-BORDER && point.y>=BORDER && point.y<m_h-BORDER) {
  1994.     m_selection=(point.y-BORDER)/m_fh;
  1995.     MenuSelect();
  1996.   }
  1997. }
  1998. void CCustomMenu::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) {
  1999.   switch (nChar) {
  2000.     case VK_RETURN:
  2001.       if (m_selection>=0)
  2002. MenuSelect();
  2003.       break;
  2004.     case VK_ESCAPE:
  2005.       EndModalLoop(0);
  2006.       break;
  2007.     case VK_UP:
  2008.       if (--m_selection<0)
  2009. m_selection=m_id.GetUpperBound();
  2010.       InvalidateRect(NULL);
  2011.       break;
  2012.     case VK_DOWN:
  2013.       if (++m_selection>=m_id.GetSize())
  2014. m_selection=0;
  2015.       InvalidateRect(NULL);
  2016.       break;
  2017.     case VK_HOME:
  2018.       if (m_selection!=0) {
  2019. m_selection=0;
  2020. InvalidateRect(NULL);
  2021.       }
  2022.       break;
  2023.     case VK_END:
  2024.       if (m_selection!=m_id.GetUpperBound()) {
  2025. m_selection=m_id.GetUpperBound();
  2026. InvalidateRect(NULL);
  2027.       }
  2028.       break;
  2029.   }
  2030. }
  2031. void  CCustomMenu::OnPaint() {
  2032.   COLORREF
  2033.     cMenuText   = ::GetSysColor(COLOR_MENUTEXT),
  2034.     cMenu   = ::GetSysColor(COLOR_MENU),
  2035.     cHlBg   = ::GetSysColor(COLOR_HIGHLIGHT),
  2036.     cHlText   = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
  2037.   PAINTSTRUCT ps;
  2038.   {
  2039.     CFDC     fdc(m_hWnd,&ps);
  2040.     RECT     rc,cli;
  2041.     GetClientRect(&cli);
  2042.     rc.left=BORDER; rc.right=m_w-BORDER;
  2043.     fdc.SelectFontAbs((FSZ*GetDeviceCaps(fdc.DC(),LOGPIXELSY))/96,CFDC::FORCENORMALWEIGHT|CFDC::FORCETAHOMA);
  2044.     for (int i=0;i<m_title.GetSize();++i) {
  2045.       if (i==m_selection) {
  2046. fdc.SetTextColor(cHlText);
  2047. fdc.SetBkColor(cHlBg);
  2048.       } else {
  2049. fdc.SetTextColor(cMenuText);
  2050. fdc.SetBkColor(cMenu);
  2051.       }
  2052.       rc.top=m_fh*i+BORDER; rc.bottom=rc.top+m_fh;
  2053.       const CString& s=m_title[i];
  2054.       TDrawText(fdc.DC(),rc.left+PAD,rc.top+PAD,cli,rc,s,s.GetLength(),NULL);
  2055.     }
  2056.     // paint border now
  2057.     fdc.SetColor(RGB(32,48,160)); // XXX hardcoded
  2058.     POINT   pt[5];
  2059.     for (int j=0;j<BORDER;++j) {
  2060.       pt[0].x=pt[3].x=pt[4].x=cli.left+j;
  2061.       pt[0].y=pt[1].y=pt[4].y=cli.top+j;
  2062.       pt[1].x=pt[2].x=cli.right-j-1;
  2063.       pt[2].y=pt[3].y=cli.bottom-j-1;
  2064.       Polyline(fdc.DC(),pt,5);
  2065.     }
  2066.   }
  2067. }
  2068. UINT  CCustomMenu::TrackPopupMenu(CWnd *parent,int reqx,int reqy,int angle) {
  2069.   SetRotAngle(angle);
  2070.   // calculate window size
  2071.   m_w=0;
  2072.   {
  2073.     // borrow parent's DC
  2074.     CFDC   dc(parent->m_hWnd);
  2075.     dc.SelectFontAbs((FSZ*GetDeviceCaps(dc.DC(),LOGPIXELSY))/96,CFDC::FORCENORMALWEIGHT|CFDC::FORCETAHOMA,true);
  2076.     dc.GetFontSize(m_fh,m_fa);
  2077.     m_fh+=PAD*2;
  2078.     m_h=m_fh*m_title.GetSize();
  2079.     for (int i=0;i<m_title.GetSize();++i) {
  2080.       SIZE    sz;
  2081.       int     nch=m_title[i].GetLength();
  2082.       dc.GetTextExtent(m_title[i],nch,8192,nch,NULL,sz);
  2083.       if (sz.cx>m_w)
  2084. m_w=sz.cx;
  2085.     }
  2086.   }
  2087.   m_w+=PAD*2+BORDER*2;
  2088.   m_h+=BORDER*2;
  2089.   int sw=m_w,sh=m_h;
  2090.   if (angle==900 || angle==2700)
  2091.     sw=m_h,sh=m_w;
  2092.   RECT rc;
  2093.   parent->GetClientRect(&rc);
  2094.   int mw=((rc.right-rc.left)*7)>>3;
  2095.   int mh=((rc.bottom-rc.top)*7)>>3;
  2096.   if (sw>mw)
  2097.     sw=mw;
  2098.   if (sh>mh)
  2099.     sh=mh;
  2100.   if (angle==900 || angle==2700)
  2101.     m_w=sh,m_h=sw;
  2102.   else
  2103.     m_w=sw,m_h=sh;
  2104.   // align window's top left corner with requested point
  2105.   int tlx,tly;
  2106.   switch (angle) {
  2107.   case 900: tlx=reqx; tly=reqy-sh; break;
  2108.   case 1800: tlx=reqx-sw; tly=reqy-sh; break;
  2109.   case 2700: tlx=reqx-sw; tly=reqy; break;
  2110.   default: tlx=reqx; tly=reqy; break;
  2111.   }
  2112.   if (tlx<rc.left)
  2113.     tlx=rc.left;
  2114.   if (tly<rc.top)
  2115.     tly=rc.top;
  2116.   if (tlx+sw>rc.right)
  2117.     tlx=rc.right-sw;
  2118.   if (tly+sh>rc.bottom)
  2119.     tly=rc.bottom-sh;
  2120.   rc.left=tlx; rc.top=tly;
  2121.   rc.right=tlx+sw; rc.bottom=tly+sh;
  2122.   // initialize
  2123. #ifdef _WIN32_WCE
  2124.   m_selection=0;
  2125. #else
  2126.   m_selection=-1;
  2127. #endif
  2128.   HWND hFocus=::GetFocus();
  2129.   // create&show window
  2130.   Create(NULL,NULL,WS_CHILD,rc,parent,0);
  2131.   ShowWindow(SW_SHOW);
  2132.   SetCapture();
  2133.   SetFocus();
  2134. #if 0
  2135.   PlaySound(_T("MenuPop"),NULL,SND_ASYNC|SND_NODEFAULT);
  2136. #endif
  2137.   int code=RunModalLoop(); // run a nested message loop
  2138.   ShowWindow(SW_HIDE);
  2139.   ReleaseCapture();
  2140.   ::SetFocus(hFocus);
  2141.   // cleanup
  2142.   DestroyWindow();
  2143.   // return command
  2144.   return code;
  2145. }
  2146. void CTView::InitDictName() {
  2147.   if (m_Dict.curdict.IsEmpty()) {
  2148.     CStringArray list;
  2149.     int cur;
  2150.     CDictSetupDlg::GetDictList(list,cur);
  2151.     if (cur>=0 && cur<list.GetSize())
  2152.       m_Dict.curdict=list[cur];
  2153.     else
  2154.       m_Dict.curdict=_T(":Disable");
  2155.   }
  2156. }
  2157. void CTView::DisplaySelectionMenu(CPoint point) {
  2158.   CCustomMenu menu;
  2159.   menu.AppendMenu(_T("Copy"),ID_EDIT_COPY);
  2160.   menu.AppendMenu(_T("Add bookmark..."),ID_ADD_BMK);
  2161.   InitDictName();
  2162.   if (m_Dict.curdict!=_T(":Disable"))
  2163.     menu.AppendMenu(_T("Lookup in dictionary..."),ID_LOOKUP_SEL);
  2164.   UINT code=menu.TrackPopupMenu(this,point.x,point.y,m_TextDisp.angle);
  2165.   if (code)
  2166.     CTVApp::QueueCmd(code);
  2167. }
  2168. void CTView::OnMouseMove(UINT nFlags, CPoint point) {
  2169.   if (m_Mouse.trackmouse) {
  2170.     m_Mouse.end=point;
  2171.     TrackMouse();
  2172.     m_Mouse.last=point;
  2173.   }
  2174. }
  2175. void CTView::TrackMouse() {
  2176.   FilePos start;
  2177.   int len;
  2178.   CalcSelection(start,len);
  2179.   SetSelection(start,len);
  2180.   if (abs(m_Mouse.end.x-m_Mouse.start.x)>4 ||
  2181.       abs(m_Mouse.end.y-m_Mouse.start.y)>4)
  2182.     m_Mouse.dmove=true;
  2183. }
  2184. void CTView::OnMiscopt() {
  2185.   CMiscOptDlg dlg;
  2186.   dlg.m_allowmulti=CTVApp::GetInt(_T("AllowOnlyOneInstance"),1)==0;
  2187.   dlg.m_rotb=m_Window.rotbuttons;
  2188.   dlg.m_autorepeatlimit=m_Window.autorepeatlimit;
  2189.   dlg.m_fcsize=CTVApp::GetInt(_T("FontCacheSize"),DEF_FONTCACHE);
  2190.   dlg.m_fbsize=CTVApp::GetInt(_T("FileBufSize"),DEF_FBUF)>>10;
  2191.   dlg.m_savetofiles=CTVApp::GetInt(_T("SaveToFiles"),DEF_SAVETOFILES)!=0;
  2192.   dlg.m_lastfiles=CTVApp::GetInt(_T("NumBookmarks"),DEF_BOOKMARKS);
  2193.   if (dlg.DoModal()==IDOK) {
  2194.     if (dlg.m_allowmulti!=(CTVApp::GetInt(_T("AllowOnlyOneInstance"),1)==0))
  2195.       CTVApp::SetInt(_T("AllowOnlyOneInstance"),!dlg.m_allowmulti);
  2196.     if ((dlg.m_rotb!=0)!=m_Window.rotbuttons ||
  2197. (dlg.m_autorepeatlimit!=0)!=m_Window.autorepeatlimit)
  2198.     {
  2199.       m_Window.rotbuttons=dlg.m_rotb!=0;
  2200.       m_Window.autorepeatlimit=dlg.m_autorepeatlimit!=0;
  2201.       m_Window.SaveSettings();
  2202.     }
  2203.     if (dlg.m_fcsize<2)
  2204.       dlg.m_fcsize=2;
  2205.     if (dlg.m_fcsize>32)
  2206.       dlg.m_fcsize=32;
  2207.     if (dlg.m_fcsize!=CTVApp::GetInt(_T("FontCacheSize"),DEF_FONTCACHE)) {
  2208.       CTVApp::SetInt(_T("FontCacheSize"),dlg.m_fcsize);
  2209.       CFDC::SetCacheSize(dlg.m_fcsize);
  2210.     }
  2211.     if (dlg.m_fbsize<8)
  2212.       dlg.m_fbsize=8;
  2213.     if (dlg.m_fbsize>1024)
  2214.       dlg.m_fbsize=1024;
  2215.     dlg.m_fbsize<<=10;
  2216.     int   fbs=8192;
  2217.     while ((fbs<<1)<=dlg.m_fbsize)
  2218.       fbs<<=1;
  2219.     if (fbs!=CTVApp::GetInt(_T("FileBufSize"),DEF_FBUF))
  2220.       CTVApp::SetInt(_T("FileBufSize"),fbs);
  2221.     if ((dlg.m_savetofiles!=0)!=(CTVApp::GetInt(_T("SaveToFiles"),DEF_SAVETOFILES)!=0))
  2222.       CTVApp::SetInt(_T("SaveToFiles"),dlg.m_savetofiles!=0);
  2223.     if (dlg.m_lastfiles!=CTVApp::GetInt(_T("NumBookmarks"),DEF_BOOKMARKS))
  2224.       CTVApp::SetInt(_T("NumBookmarks"),dlg.m_lastfiles);
  2225.   }
  2226. }
  2227. void CTView::OnUpdateMiscopt(CCmdUI* pCmdUI) {
  2228.   pCmdUI->Enable();
  2229. }
  2230. void  CTView::CopyToClipboard(const wchar_t *text,int length,HWND hWnd) {
  2231. #ifdef _WIN32_WCE
  2232.   wchar_t   *result=(wchar_t*)LocalAlloc(0,sizeof(wchar_t)*(length+1));
  2233.   if (!result)
  2234.     return;
  2235. #define hMem  result
  2236. #else
  2237.   HGLOBAL   hMem=GlobalAlloc(GMEM_MOVEABLE,sizeof(wchar_t)*(length+1));
  2238.   if (!hMem)
  2239.     return;
  2240.   wchar_t   *result=(wchar_t*)GlobalLock(hMem);
  2241.   if (!result) {
  2242.     GlobalFree(hMem);
  2243.     return;
  2244.   }
  2245. #endif
  2246.   memcpy(result,text,sizeof(wchar_t)*length);
  2247.   result[length]=L'';
  2248. #ifndef _WIN32_WCE
  2249.   GlobalUnlock(hMem);
  2250. #endif
  2251.   if (::OpenClipboard(hWnd)) {
  2252.     if (EmptyClipboard())
  2253.       if (::SetClipboardData(CF_UNICODETEXT,hMem)) {
  2254. CloseClipboard();
  2255. return;
  2256.       }
  2257.     CloseClipboard();
  2258.   }
  2259. #ifdef _WIN32_WCE
  2260.   LocalFree(result);
  2261. #else
  2262.   GlobalFree(hMem);
  2263. #endif
  2264. }
  2265. #undef hMem
  2266. void CTView::OnEditCopy() {
  2267.   // wastes memory, but easy to implement
  2268.   Buffer<wchar_t>   str;
  2269.   if (GetSelText(str))
  2270.     CopyToClipboard(str,str.size(),m_hWnd);
  2271. }
  2272. void CTView::OnUpdateEditCopy(CCmdUI* pCmdUI) {
  2273.   pCmdUI->Enable(m_Sel.len>0);
  2274. }
  2275. void CTView::CalcSelection(FilePos& p,int& len) {
  2276.   FilePos   start,end;
  2277.   POINT     a,b;
  2278.   a=m_Mouse.start; b=m_Mouse.end;
  2279.   System2Window(a,m_Window.cli);
  2280.   System2Window(b,m_Window.cli);
  2281.   LookupAddr(a,start);
  2282.   LookupAddr(b,end);
  2283.   int     dist=m_formatter->Distance(start,end);
  2284.   if (dist<0) {
  2285.     p=end;
  2286.     len=-dist;
  2287.   } else {
  2288.     p=start;
  2289.     len=dist;
  2290.   }
  2291. }
  2292. void CTView::OnRotate() {
  2293.   m_TextDisp.angle+=900;
  2294.   if (m_TextDisp.angle>2700)
  2295.     m_TextDisp.angle=0;
  2296.   SetRotAngle(m_TextDisp.angle);
  2297.   CTVApp::SetInt(_T("Orientation"),m_TextDisp.angle);
  2298.   m_TextDisp.SetDefFont();
  2299.   CalcSizes();
  2300.   QueueRepaint();
  2301. }
  2302. int   CTView::GetSelParagraphCount() {
  2303.   if (m_Sel.len==0)
  2304.     return 0;
  2305.   int   len=m_Sel.len;
  2306.   int   pcount=0;
  2307.   FilePos p(m_Sel.start);
  2308.   FilePos eof(p);
  2309.   eof.para=m_textfile->Length(p.docid);
  2310.   eof.off=0;
  2311.   while (len>0 && p<eof) {
  2312.     int   plen=m_textfile->GetPLength(p.docid,p.para)-p.off;
  2313.     if (plen<0)
  2314.       plen=0;
  2315.     if (plen>len)
  2316.       plen=len;
  2317.     len-=plen;
  2318.     p.off=0;
  2319.     p.para++;
  2320.     pcount++;
  2321.   }
  2322.   return pcount;
  2323. }
  2324. bool  CTView::GetSelText(Buffer<wchar_t>& str) {
  2325.   if (m_Sel.len<=0)
  2326.     return false;
  2327.   FilePos p(m_Sel.start);
  2328.   FilePos eof(p);
  2329.   eof.para=m_textfile->Length(p.docid);
  2330.   eof.off=0;
  2331.   int   pl=GetSelParagraphCount();
  2332.   int   len=m_Sel.len;
  2333.   Buffer<wchar_t>   tmp(len+2*pl);
  2334.   wchar_t       *bp=tmp;
  2335.   Paragraph pp(m_textfile->GetParagraph(p.docid,p.para));
  2336.   int   ll=pp.str.size()-p.off;
  2337.   if (ll<0)
  2338.     ll=0;
  2339.   if (ll>len)
  2340.     ll=len;
  2341.   memcpy(bp,pp.str+p.off,ll*sizeof(wchar_t));
  2342.   bp+=ll;
  2343.   len-=ll;
  2344.   p.off=0;
  2345.   ++p.para;
  2346.   while (len>0 && p<eof) {
  2347.     *bp++=L'r'; *bp++=L'n';
  2348.     pp=m_textfile->GetParagraph(p.docid,p.para);
  2349.     ll=len;
  2350.     if (ll>pp.str.size())
  2351.       ll=pp.str.size();
  2352.     memcpy(bp,pp.str,ll*sizeof(wchar_t));
  2353.     bp+=ll;
  2354.     ++p.para;
  2355.     len-=ll;
  2356.   }
  2357.   tmp.setsize(bp-tmp);
  2358.   str=tmp;
  2359.   return true;
  2360. }
  2361. void CTView::OnTimer(UINT nIDEvent) {
  2362.   switch (nIDEvent) {
  2363.   case TM_SAVEINFO:
  2364.     SaveInfo();
  2365.     break;
  2366.   case TM_USERTEXT:
  2367.     HideText();
  2368.     break;
  2369.   case TM_AS:
  2370.     StepAS();
  2371.     break;
  2372.   case TM_PD:
  2373.     UpdateProgressBar();
  2374.     break;
  2375.   }
  2376.   CWnd ::OnTimer(nIDEvent);
  2377. }
  2378. void  CTView::MovePercent(int p) {
  2379.   if (p>=0 && p<=100) {
  2380.     FilePos   pp;
  2381.     pp.para=p*m_formatter->Eof().para/100;
  2382.     PushPos();
  2383.     MoveAbs(pp);
  2384.   }
  2385. }
  2386. static int ParsePosSpec(const CString& s,int& v1,int& v2) {
  2387.   int i;
  2388.   v1=v2=0;
  2389.   for (i=0;i<s.GetLength();++i) {
  2390.     if (s[i]<_T('0')||s[i]>_T('9'))
  2391.       break;
  2392.     v1=v1*10+s[i]-_T('0');
  2393.   }
  2394.   if (i==0)
  2395.     return 0;
  2396.   if (i>=s.GetLength())
  2397.     return 1;
  2398.   if (s[i]!=_T(':') && s[i]!=_T(';'))
  2399.     goto end;
  2400.   ++i;
  2401.   for (;i<s.GetLength();++i) {
  2402.     if (s[i]<_T('0')||s[i]>_T('9'))
  2403.       break;
  2404.     v2=v2*10+s[i]-_T('0');
  2405.   }
  2406.   if (i>=s.GetLength())
  2407.     return 1;
  2408. end:
  2409.   if (i!=s.GetLength()-1)
  2410.     return 0;
  2411.   if (s[i]==_T('l') || s[i]==_T('L'))
  2412.     return 4;
  2413.   if (s[i]==_T('p') || s[i]==_T('P') || s[i]==_T('%'))
  2414.     return 3;
  2415.   if (s[i]==_T('=') || s[i]==_T('g') || s[i]==_T('G'))
  2416.     return 2;
  2417.   return 0;
  2418. }
  2419. bool CTView::CanAddUIChar(TCHAR ch) {
  2420.   CFDC dc(m_hWnd);
  2421.   dc.SelectFont(USERINPUTSIZE,USERINPUTFLAGS);
  2422.   m_UI.inp+=ch;
  2423.   int nch=m_UI.inp.GetLength();
  2424.   SIZE sz;
  2425.   dc.GetTextExtent(m_UI.inp,nch,m_UI.rc.right-m_UI.rc.left,nch,NULL,sz);
  2426.   m_UI.inp.Delete(m_UI.inp.GetLength()-1);
  2427.   return nch>m_UI.inp.GetLength() && sz.cx < m_UI.rc.right-m_UI.rc.left;
  2428. }
  2429. void CTView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) {
  2430.   if (nChar==VK_BACK) {
  2431.     if (m_UI.inp.GetLength()>0)
  2432.       m_UI.inp.Delete(m_UI.inp.GetLength()-1);
  2433.     else { // act as back
  2434.       CTVApp::QueueCmd(ID_BACK);
  2435.       return;
  2436.     }
  2437.   } else if (nChar==VK_TAB) {
  2438.     m_UI.inp.Empty();
  2439.   } else if (nChar==VK_SPACE) {
  2440.     UINT  cmd;
  2441.     if (m_UI.inp.GetLength()>0) {
  2442.       if (CanAddUIChar(' '))
  2443. m_UI.inp+=_T(' ');
  2444.     } else if (Keys::TranslateKey(nChar,cmd,m_Window.rotbuttons ? m_TextDisp.angle : 0)) {
  2445.       // repeated key
  2446.       if (nFlags&0x4000 && CTVApp::TopQueuedCmd()==cmd)
  2447. return;
  2448.       CTVApp::QueueCmd(cmd);
  2449.       return;
  2450.     }
  2451.   } else {
  2452.     if (nChar=='/' && m_UI.inp.GetLength()==0) {
  2453.       CTVApp::QueueCmd(ID_FIND);
  2454.       return;
  2455.     }
  2456.     if (nChar>=32 && CanAddUIChar((TCHAR)nChar))
  2457.       m_UI.inp+=(TCHAR)nChar;
  2458.     else
  2459.       return;
  2460.   }
  2461.   StopAS();
  2462.   if (m_UI.inp.GetLength()>0) {
  2463.     // check if this is a valid pos spec
  2464.     FilePos   p(m_formatter->Top());
  2465.     switch (ParsePosSpec(m_UI.inp,p.para,p.off)) {
  2466.     case 1: // partial spec
  2467.       break;
  2468.     case 2: // complete spec, abs pos
  2469.       PushPos();
  2470.       m_formatter->AdjustPos(p);
  2471.       MoveAbs(p);
  2472.       m_UI.inp.Empty();
  2473.       HideText();
  2474.       return;
  2475.     case 3: // complete spec, percentage
  2476.       MovePercent(p.para);
  2477.       m_UI.inp.Empty();
  2478.       HideText();
  2479.       return;
  2480.     case 4: // complete spec, virt. page
  2481.       MoveAbs(FilePos(m_textfile->LookupParagraph(m_formatter->DocId(),p.para<<11),0,m_formatter->DocId()));
  2482.       m_UI.inp.Empty();
  2483.       HideText();
  2484.       return;
  2485.     case 0: // not a valid spec at all, try lookup
  2486.       if (m_formatter->DocId()<0) { // inside dictionary
  2487. Buffer<wchar_t>   tmp(Unicode::ToWCbufZ(m_UI.inp));
  2488. if (m_textfile->LookupDict(tmp,p)) {
  2489.   MoveAbs(p);
  2490.   m_Dict.lastdictlookup=tmp;
  2491. }
  2492.       }
  2493.       break;
  2494.     }
  2495.     ShowText();
  2496.   } else
  2497.     HideText();
  2498. }
  2499. bool  CTView::OpenDict(bool *didchange) {
  2500.   if (didchange)
  2501.     *didchange=false;
  2502.   CStringArray list;
  2503.   int cd;
  2504.   CDictSetupDlg::GetDictList(list,cd);
  2505.   if (list.GetSize()==0) {
  2506.     // can't really happen
  2507.     return false;
  2508.   }
  2509.   if (!list[cd].IsEmpty() && list[cd][0]==_T(':')) {
  2510.     // special pseudodictionary
  2511.     m_Dict.curdict=list[cd];
  2512.     return true;
  2513.   }
  2514.   if (m_Dict.curdict==list[cd] && m_Dict.curdictparser.get()) { // ok, nothing changed
  2515.     m_textfile->SetDict(m_Dict.curdictparser.get());
  2516.     return true;
  2517.   }
  2518.   m_Dict.curdict=list[cd];
  2519.   CString em;
  2520.   DictParser *dp=DictParser::OpenDict(list[cd],&em);
  2521.   if (!dp) {
  2522.     MessageBox(em,_T("Error"),MB_OK|MB_ICONERROR);
  2523.     return false;
  2524.   }
  2525.   m_Dict.curdictparser=dp;
  2526.   m_textfile->SetDict(dp);
  2527.   if (didchange)
  2528.     *didchange=true;
  2529.   return true;
  2530. }
  2531. FilePos   CTView::CurFilePos() {
  2532.   FilePos   p=m_formatter->Top();
  2533.   if (p.docid==-1) { // skip dictionary positions
  2534.     POSITION sp=m_History.stacktop;
  2535.     if (sp)
  2536.       m_History.pstack.GetPrev(sp);
  2537.     else
  2538.       sp=m_History.pstack.GetTailPosition();
  2539.     while (sp && p.docid==-1)
  2540.       p=m_History.pstack.GetPrev(sp);
  2541.   }
  2542.   if (p.docid==-1)
  2543.     return FilePos();
  2544.   return p;
  2545. }
  2546. void CTView::OnForward() {
  2547.   if (m_textfile->IsImage()) {
  2548.     TextFile  *tf=OpenNextImage(m_textfile->Name(),true);
  2549.     if (tf) {
  2550.       AfxGetMainWnd()->SetWindowText(FileName(tf->Name()));
  2551.       SetFile(kilo::auto_ptr<TextFile>(tf));
  2552.     }
  2553.     return;
  2554.   }
  2555.   if (m_History.stacktop!=NULL) {
  2556.     FilePos   next=m_History.pstack.GetAt(m_History.stacktop);
  2557.     m_History.pstack.SetAt(m_History.stacktop,m_formatter->Top());
  2558.     m_History.pstack.GetNext(m_History.stacktop);
  2559.     MoveAbs(next);
  2560.   }
  2561. }
  2562. void CTView::OnUpdateForward(CCmdUI* pCmdUI) {
  2563.   pCmdUI->Enable(m_textfile->IsImage() || m_History.stacktop!=NULL);
  2564. }
  2565. void  CTView::ShowText() {
  2566.   if (m_UI.timer)
  2567.     KillTimer(m_UI.timer);
  2568.   m_UI.timer=SetTimer(TM_USERTEXT,2000,NULL);
  2569.   m_UI.visible=true;
  2570.   // check for popup
  2571.   if (m_BP.visible)
  2572.     QueueRepaint(m_UI.rc);
  2573.   else { // otherwise repaint directly
  2574.     CFDC fdc(m_hWnd);
  2575.     PaintUserInput(fdc,m_UI.rc,m_Window.cli,Unicode::ToWCbuf(m_UI.inp));
  2576.   }
  2577. }
  2578. void  CTView::HideText() {
  2579.   if (!m_UI.visible)
  2580.     return;
  2581.   if (m_UI.timer) {
  2582.     KillTimer(m_UI.timer);
  2583.     m_UI.timer=0;
  2584.   }
  2585.   m_UI.visible=false;
  2586.   QueueRepaint(m_UI.rc);
  2587. }
  2588. void  CTView::OnUpdateDict(CCmdUI *pCmdUI) {
  2589.   pCmdUI->Enable();
  2590. }
  2591. void  CTView::OnDict(UINT cmd) {
  2592.   CDictSetupDlg::SetActiveDict(cmd-DICT_BASE);
  2593.   // update display here
  2594.   bool chg;
  2595.   if (OpenDict(&chg) && chg) {
  2596.     // delete all dict references from pstack
  2597.     POSITION  p,q;
  2598.     for (p=m_History.pstack.GetHeadPosition();p;) {
  2599.       q=p;
  2600.       m_History.pstack.GetNext(p);
  2601.       if (m_History.pstack.GetAt(q).docid<0) {
  2602. if (m_History.stacktop==q)
  2603.   m_History.stacktop=p;
  2604. m_History.pstack.RemoveAt(q);
  2605.       }
  2606.     }
  2607.     // if we are currently in a dictionary, move back to start
  2608.     if (m_formatter->DocId()<0) {
  2609.       FilePos   p;
  2610.       if (m_Dict.lastdictlookup.size()>1 &&
  2611.   m_textfile->LookupDict(m_Dict.lastdictlookup,p))
  2612. MoveAbs(p);
  2613.       else
  2614. MoveAbs(m_formatter->Sof());
  2615.     }
  2616.   }
  2617. }
  2618. bool CTView::SwitchToDict() {
  2619.   if (!OpenDict())
  2620.     return false;
  2621.   MoveAbs(FilePos(0,0,-1));
  2622.   return true;
  2623. }
  2624. void  CTView::OnOK() {
  2625.   // rollback to the most recent !dict view
  2626.   if (m_formatter->DocId()<0 && !m_History.pstack.IsEmpty()) { // inside dict
  2627.     POSITION  p=m_History.stacktop;
  2628.     FilePos   cur(m_formatter->Top());
  2629.     while (cur.docid<0 && p!=m_History.pstack.GetHeadPosition()) {
  2630.       if (p==NULL)
  2631. p=m_History.pstack.GetTailPosition();
  2632.       else
  2633. m_History.pstack.GetPrev(p);
  2634.       FilePos np(m_History.pstack.GetAt(p));
  2635.       m_History.pstack.SetAt(p,cur);
  2636.       cur=np;
  2637.     }
  2638.     m_History.stacktop=p;
  2639.     MoveAbs(cur);
  2640.   }
  2641. }
  2642. void  CTView::FormatBookmarkPopup(CFDC& dc,const POINT& spot) {
  2643.   // format the text using maximum width
  2644.   int width=m_Window.width-21; // XXX
  2645.   // don't make it too wide
  2646.   if (width>400) // XXX
  2647.     width=400;
  2648.   int height=m_Window.height-21;
  2649.   m_formatter->FormatPlainText(dc,width,height,-2,
  2650.     m_BP.text,m_BP.text.size(),m_BP.lines);
  2651.   // now width and height contain real require w&h, and lines hold formatted text
  2652.   // however sizes do not include margins
  2653.   // add a couple of extra pixels for frame
  2654.   width+=FRAME_SIZE*2;
  2655.   height+=1+FRAME_SIZE;
  2656.   // we need to center the popup around the spot now
  2657.   int left=spot.x-(width>>1);
  2658.   int top=spot.y-(height>>1);
  2659.   // and check that it doesnt appear out of bounds
  2660.   if (left<10)
  2661.     left=10;
  2662.   if (top<10)
  2663.     top=10;
  2664.   if (left+width>m_Window.rwidth-10)
  2665.     left=m_Window.rwidth-10-width;
  2666.   if (top+height>m_Window.rheight-10)
  2667.     top=m_Window.rheight-10-height;
  2668.   // and store the thing
  2669.   m_BP.rc.left=left;
  2670.   m_BP.rc.top=top;
  2671.   m_BP.rc.right=left+width;
  2672.   m_BP.rc.bottom=top+height;
  2673. }
  2674. void  CTView::DisplayBookmarkPopup(const POINT& spot,const Buffer<wchar_t>& text) {
  2675.   if (m_BP.visible) // repaint if we already have a bookmark
  2676.     InvalidateRect(m_BP.rc);
  2677.   m_BP.text=text;
  2678.   CFDC dc(m_hWnd);
  2679.   FormatBookmarkPopup(dc,spot);
  2680.   m_BP.visible=true;
  2681.   PaintBookmarkPopup(dc,m_BP.rc,m_Window.cli);
  2682. }
  2683. void  CTView::Advance(FilePos& p,int len) {
  2684.   while (len>0 && p.para<m_textfile->Length(p.docid)) {
  2685.     int pl=m_textfile->GetPLength(p.docid,p.para);
  2686.     if (p.off+len>=pl) {
  2687.       len-=pl-p.off;
  2688.       p.off=0;
  2689.       p.para++;
  2690.     } else {
  2691.       p.off+=len;
  2692.       len=0;
  2693.     }
  2694.   }
  2695. }
  2696. void  CTView::InvalidateRange(const FilePos& x,const FilePos& y) {
  2697.   if (y<=x || y<=m_formatter->GetLine(0).pos) // completely out of range
  2698.     return;
  2699.   int nlines=m_formatter->Length();
  2700.   int column=0;
  2701.   int line=0;
  2702.   while (line<nlines) {
  2703.     int   col_last=line+m_formatter->PageLength(column);
  2704.     int   ystart=-1,yend;
  2705.     int   cury=0;
  2706.     while (line<col_last) {
  2707.       const Line&   ll=m_formatter->GetLine(line);
  2708.       FilePos     le(ll.pos);
  2709.       le.off+=ll.real_len;
  2710.       if (x<le && ll.pos<y) { // in range
  2711. if (ystart<0)
  2712.   ystart=cury;
  2713. yend=cury+ll.height;
  2714.       }
  2715.       cury+=ll.height;
  2716.       ++line;
  2717.     }
  2718.     if (ystart>=0) {
  2719.       RECT    rc;
  2720.       rc.left=column*m_Window.width;
  2721.       rc.right=rc.left+m_Window.width;
  2722.       rc.top=ystart;
  2723.       rc.bottom=yend;
  2724.       InvalidateRect(rc);
  2725.     }
  2726.     ++column;
  2727.   }
  2728. }
  2729. void  CTView::SetSelection(const FilePos& p,int len) {
  2730.   if (p!=m_Sel.start || len!=m_Sel.len) {
  2731.     if (m_formatter->SetHighlight(p,len)) {
  2732.       FilePos e(p);
  2733.       Advance(e,len);
  2734.       FilePos ce(m_Sel.start);
  2735.       Advance(ce,m_Sel.len);
  2736.       if (e<=m_Sel.start || ce<=p) { // do not intersect, invalidate both
  2737. InvalidateRange(p,e);
  2738. InvalidateRange(m_Sel.start,ce);
  2739.       } else { // avoid updating intersection
  2740. if (p<m_Sel.start)
  2741.   InvalidateRange(p,m_Sel.start);
  2742. else
  2743.   InvalidateRange(m_Sel.start,p);
  2744. if (ce<e)
  2745.   InvalidateRange(ce,e);
  2746. else
  2747.   InvalidateRange(e,ce);
  2748.       }
  2749.     }
  2750.     m_Sel.start=p;
  2751.     m_Sel.len=len;
  2752.   }
  2753. }
  2754. void  CTView::InvalidateRect(const RECT& rc) {
  2755.   RECT tmp=rc;
  2756.   Window2System(tmp,m_Window.cli);
  2757.   CWnd::InvalidateRect(&tmp,FALSE);
  2758. }
  2759. void  CTView::QueueRepaint(const RECT& rc) {
  2760.   HideBookmarkPopup();
  2761.   InvalidateRect(rc);
  2762. }
  2763. void  CTView::HideBookmarkPopup() {
  2764.   if (m_BP.visible) {
  2765.     InvalidateRect(m_BP.rc);
  2766.     m_BP.visible=false;
  2767.   }
  2768. }
  2769. #ifndef _WIN32_WCE
  2770. BOOL CTView::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) {
  2771.   // just pretend it's up/down
  2772.   zDelta*=5;
  2773.   if (zDelta>0) {
  2774.     while (zDelta>0) {
  2775.       OnLineUp();
  2776.       zDelta-=WHEEL_DELTA;
  2777.     }
  2778.   } else if (zDelta<0) {
  2779.     while (zDelta<0) {
  2780.       OnLineDown();
  2781.       zDelta+=WHEEL_DELTA;
  2782.     }
  2783.   }
  2784.   return TRUE;
  2785. }
  2786. #endif
  2787. void  CTView::StartAS() {
  2788.   if (m_AS.timer)
  2789.     return; // already running
  2790.   if (m_BP.visible || m_UI.visible) {
  2791.     HideBookmarkPopup();
  2792.     HideText();
  2793.     CTVApp::QueueCmd(ID_AS_START);
  2794.     return;
  2795.   }
  2796.   if (m_AS.column<0 || m_AS.line<0 || m_AS.top_pos!=m_formatter->Top()) {
  2797.     // try to start near the end of page
  2798.     int   tc=m_formatter->Length()-5;
  2799.     if (tc<0)
  2800.       tc=0;
  2801.     m_AS.column=0;
  2802.     while (tc>m_formatter->PageLength(m_AS.column))
  2803.       tc-=m_formatter->PageLength(m_AS.column++);
  2804.     m_AS.line=tc;
  2805.   }
  2806.   m_AS.top_pos=m_formatter->Top();
  2807.   m_AS.timer=SetTimer(TM_AS,m_AS.delay/1000,NULL);
  2808.   if (m_Window.showprogress() && m_AS.timer && m_Window.pb.as_delay)
  2809.     RedrawProgressBar();
  2810. }
  2811. void  CTView::StopAS() {
  2812.   if (!m_AS.timer)
  2813.     return;
  2814.   KillTimer(m_AS.timer);
  2815.   // repaint everything
  2816.   QueueRepaint();
  2817.   m_AS.timer=0;
  2818.   m_AS.column=m_AS.line=-1;
  2819. }
  2820. void  CTView::RestartASTimer() {
  2821.   if (m_AS.timer) {
  2822.     KillTimer(m_AS.timer);
  2823.     m_AS.timer=SetTimer(TM_AS,m_AS.delay/1000,NULL);
  2824.   }
  2825. }
  2826. void  CTView::StepAS() {
  2827.   // only perform steps if the view has focus
  2828.   if (::GetFocus()!=m_hWnd) {
  2829.     KillTimer(m_AS.timer);
  2830.     return;
  2831.   }
  2832.   
  2833.   // cancel AS if user paged through the file on his own
  2834.   if (m_AS.top_pos!=m_formatter->Top()) {
  2835.     StopAS();
  2836.     return;
  2837.   }
  2838.   // avoid suspend
  2839. #ifdef _WIN32_WCE
  2840.   SystemIdleTimerReset();
  2841. #endif
  2842.   // currently highlighted line is in m_AS
  2843.   // here we a) repaint it, b) advance it, and c) underline the next line
  2844.   // repaint
  2845.   if (m_AS.column>=0 && m_AS.column<m_Window.columns &&
  2846.       m_AS.line>=0 && m_AS.line<m_formatter->PageLength(m_AS.column))
  2847.   {
  2848.     PaintSingleLine(m_AS.column,m_AS.line,CLR_DEFAULT);
  2849.     // advance
  2850.     if (++m_AS.line>=m_formatter->PageLength(m_AS.column)) {
  2851.       m_AS.line=0;
  2852.       if (++m_AS.column>=m_Window.columns) {
  2853. CFDC  dc(m_hWnd);
  2854. if (!m_formatter->FormatFwdAdj(dc)) {
  2855.   StopAS();
  2856.   return;
  2857. }
  2858. m_AS.top_pos=m_formatter->Top();
  2859. m_AS.column=0;
  2860.       }
  2861.     }
  2862.   } else
  2863.     // somehow the position was completely invalid, start from top
  2864.     m_AS.line=m_AS.column=0;
  2865.   // underline
  2866.   PaintSingleLine(m_AS.column,m_AS.line,C_AS);
  2867. }
  2868. void  CTView::FasterAS() {
  2869.   if (m_AS.delay<50000)
  2870.     return;
  2871.   m_AS.delay-=25000; // 25ms
  2872.   RestartASTimer();
  2873.   m_AS.SaveSettings();
  2874.   UpdateProgressBar();
  2875. }
  2876. void  CTView::SlowerAS() {
  2877.   m_AS.delay+=25000; // 25ms
  2878.   RestartASTimer();
  2879.   m_AS.SaveSettings();
  2880.   UpdateProgressBar();
  2881. }
  2882. void  CTView::FasterASFine() {
  2883.   if (m_AS.delay<50000)
  2884.     return;
  2885.   m_AS.delay-=5000; // 5ms
  2886.   RestartASTimer();
  2887.   m_AS.SaveSettings();
  2888.   UpdateProgressBar();
  2889. }
  2890. void  CTView::SlowerASFine() {
  2891.   m_AS.delay+=5000; // 5ms
  2892.   RestartASTimer();
  2893.   m_AS.SaveSettings();
  2894.   UpdateProgressBar();
  2895. }
  2896. BOOL CTView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) 
  2897. {
  2898.   if (nCode==CN_COMMAND || nCode==CN_EVENT) {
  2899.     if (nID!=ID_AS_START && nID!=ID_AS_STOP && nID!=ID_AS_FASTER &&
  2900. nID!=ID_AS_SLOWER && nID!=ID_AS_FASTER_FINE &&
  2901. nID!=ID_AS_SLOWER_FINE && nID!=ID_AS_TOGGLE)
  2902.       StopAS();
  2903.   }
  2904.   
  2905.   return CWnd ::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
  2906. }
  2907. void CTView::OnKillFocus(CWnd* pNewWnd) 
  2908. {
  2909.   CWnd ::OnKillFocus(pNewWnd);
  2910.   KillTimer(m_AS.timer); // stop timer, but don't clear values
  2911. }
  2912. void CTView::OnSetFocus(CWnd* pOldWnd) 
  2913. {
  2914.   CWnd ::OnSetFocus(pOldWnd);
  2915.   RestartASTimer();
  2916. }
  2917. void CTView::ToggleAS() {
  2918.   if (m_AS.timer)
  2919.     StopAS();
  2920.   else
  2921.     StartAS();
  2922. }
  2923. void CTView::OnTogglePB() {
  2924.   m_Window.showprog=!m_Window.showprog;
  2925.   m_Window.SaveSettings();
  2926.   QueueRepaint();
  2927.   StartWindowPDTimer();
  2928. }
  2929. void CTView::OnTogglePBChapter() {
  2930.   m_Window.pb.chapter=!m_Window.pb.chapter;
  2931.   m_Window.SaveSettings();
  2932.   RedrawProgressBar();
  2933.   StartWindowPDTimer();
  2934. }
  2935. void CTView::OnTogglePBPos() {
  2936.   m_Window.pb.position=!m_Window.pb.position;
  2937.   m_Window.SaveSettings();
  2938.   RedrawProgressBar();
  2939.   StartWindowPDTimer();
  2940. }
  2941. void CTView::OnTogglePBTop() {
  2942.   m_Window.pb.top=!m_Window.pb.top;
  2943.   m_Window.SaveSettings();
  2944.   RedrawProgressBar();
  2945.   StartWindowPDTimer();
  2946. }
  2947. void CTView::OnTogglePBAS() {
  2948.   m_Window.pb.as_delay=!m_Window.pb.as_delay;
  2949.   m_Window.SaveSettings();
  2950.   RedrawProgressBar();
  2951.   StartWindowPDTimer();
  2952. }
  2953. void CTView::OnTogglePBTime() {
  2954.   m_Window.pb.time=!m_Window.pb.time;
  2955.   m_Window.SaveSettings();
  2956.   RedrawProgressBar();
  2957.   StartWindowPDTimer();
  2958. }
  2959. void CTView::OnTogglePBBat() {
  2960.   m_Window.pb.battery=!m_Window.pb.battery;
  2961.   m_Window.SaveSettings();
  2962.   RedrawProgressBar();
  2963.   StartWindowPDTimer();
  2964. }
  2965. void CTView::OnUpdateTogglePB(CCmdUI* pCmdUI) {
  2966.   pCmdUI->SetCheck(m_Window.showprog);
  2967.   pCmdUI->Enable();
  2968. }
  2969. void CTView::OnUpdateTogglePBPos(CCmdUI* pCmdUI) {
  2970.   pCmdUI->SetCheck(m_Window.pb.position);
  2971.   pCmdUI->Enable();
  2972. }
  2973. void CTView::OnUpdateTogglePBChapter(CCmdUI* pCmdUI) {
  2974.   pCmdUI->SetCheck(m_Window.pb.chapter);
  2975.   pCmdUI->Enable();
  2976. }
  2977. void CTView::OnUpdateTogglePBTop(CCmdUI* pCmdUI) {
  2978.   pCmdUI->SetCheck(m_Window.pb.top);
  2979.   pCmdUI->Enable();
  2980. }
  2981. void CTView::OnUpdateTogglePBAS(CCmdUI* pCmdUI) {
  2982.   pCmdUI->SetCheck(m_Window.pb.as_delay);
  2983.   pCmdUI->Enable();
  2984. }
  2985. void CTView::OnUpdateTogglePBTime(CCmdUI* pCmdUI) {
  2986.   pCmdUI->SetCheck(m_Window.pb.time);
  2987.   pCmdUI->Enable();
  2988. }
  2989. void CTView::OnUpdateTogglePBBat(CCmdUI* pCmdUI) {
  2990.   pCmdUI->SetCheck(m_Window.pb.battery);
  2991.   pCmdUI->Enable();
  2992. }
  2993. void CTView::OnUpdateExportBmk(CCmdUI* pCmdUI) {
  2994.   pCmdUI->Enable();
  2995. }
  2996. void CTView::OnExportBmk() {
  2997.   CString filename;
  2998.   OPENFILENAME ofn;
  2999.   memset(&ofn,0,sizeof(ofn));
  3000.   ofn.lStructSize=sizeof(ofn);
  3001.   ofn.hwndOwner=m_hWnd;
  3002.   ofn.lpstrFilter=_T("Text Files (*.txt)*.txtAll Files (*.*)*.*");
  3003.   ofn.lpstrDefExt=_T("txt");
  3004.   ofn.nMaxFile=1024;
  3005.   ofn.lpstrFile=filename.GetBuffer(ofn.nMaxFile);
  3006.   ofn.Flags=OFN_OVERWRITEPROMPT|OFN_PATHMUSTEXIST;
  3007.   if (!GetSaveFileName(&ofn))
  3008.     return;
  3009.   filename.ReleaseBuffer();
  3010.   SaveInfo();
  3011.   Bookmarks::ExportAllBookmarks(filename);
  3012. }
  3013. void CTView::OnNewColorProfile() {
  3014.   CString pn;
  3015.   if (GetUserInput(_T("Profile name:"),_T("New color profile"),pn,this)) {
  3016.     SaveColors();
  3017.     // allocate new profile id and switch to it
  3018.     int     id;
  3019.     CString name;
  3020.     HKEY    hKey = AfxGetApp()->GetSectionKey(_T("Colors"));
  3021.     for (id = 1;; ++id) {
  3022.       name.Format(_T("%d"),id);
  3023.       if (RegQueryValueEx(hKey,name,0,0,0,0) != ERROR_SUCCESS)
  3024. break;
  3025.     }
  3026.     RegCloseKey(hKey);
  3027.     AfxGetApp()->WriteProfileString(_T("Colors"),name,pn);
  3028.     AfxGetApp()->WriteProfileInt(_T("Colors"),_T("Profile"),id);
  3029.     LoadColors();
  3030.     SaveColors();
  3031.     QueueRepaint();
  3032.   }
  3033. }
  3034. void CTView::OnUpdateNewColorProfile(CCmdUI *pCmdUI) {
  3035.   pCmdUI->Enable();
  3036. }
  3037. void CTView::OnDelColorProfile() {
  3038.   int id = g_color_profile;
  3039.   if (id == 0 || !NextColorProfile())
  3040.     return;
  3041.   HKEY hKey = AfxGetApp()->GetSectionKey(_T("Colors"));
  3042.   if (hKey == NULL)
  3043.     return;
  3044.   CString name;
  3045.   name.Format(_T("%d"),id);
  3046.   RegDeleteValue(hKey,name);
  3047.   for (int i=0;g_colors[i].name;++i) {
  3048.     name.Format(_T("%s_%d"),g_colors[i].name,id);
  3049.     RegDeleteValue(hKey,name);
  3050.   }
  3051.   RegCloseKey(hKey);
  3052.   QueueRepaint();
  3053. }
  3054. void CTView::OnUpdateDelColorProfile(CCmdUI *pCmdUI) {
  3055.   pCmdUI->Enable(g_color_profile != 0);
  3056. }
  3057. void CTView::OnSelColor(UINT cmd) {
  3058.   AfxGetApp()->WriteProfileInt(_T("Colors"),_T("Profile"),cmd - COLORS_BASE);
  3059.   LoadColors();
  3060.   QueueRepaint();
  3061. }
  3062. void CTView::OnUpdateSelColor(CCmdUI *pCmdUI) {
  3063.   pCmdUI->Enable();
  3064. }
  3065. void CTView::OnUpdateNextColorProfile(CCmdUI *pCmdUI) {
  3066.   pCmdUI->Enable();
  3067. }
  3068. void CTView::OnNextColorProfile() {
  3069.   if (NextColorProfile())
  3070.     QueueRepaint();
  3071. }