source: branches/kmk/Lucide/gui/docViewer.cpp @ 344

Last change on this file since 344 was 344, checked in by dmik, 12 years ago

branches/kmk: Use Helvetica Bold for text input fields.

File size: 80.1 KB
Line 
1/* ***** BEGIN LICENSE BLOCK *****
2 * Version: CDDL 1.0/LGPL 2.1
3 *
4 * The contents of this file are subject to the COMMON DEVELOPMENT AND
5 * DISTRIBUTION LICENSE (CDDL) Version 1.0 (the "License"); you may not use
6 * this file except in compliance with the License. You may obtain a copy of
7 * the License at http://www.sun.com/cddl/
8 *
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
13 *
14 * The Initial Developer of the Original Code is
15 * Eugene Romanenko, netlabs.org.
16 * Portions created by the Initial Developer are Copyright (C) 2006
17 * the Initial Developer. All Rights Reserved.
18 *
19 * Contributor(s):
20 *
21 * Alternatively, the contents of this file may be used under the terms of
22 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
23 * in which case the provisions of the LGPL are applicable instead of those
24 * above. If you wish to allow use of your version of this file only under the
25 * terms of the LGPL, and not to allow others to use your version of this file
26 * under the terms of the CDDL, indicate your decision by deleting the
27 * provisions above and replace them with the notice and other provisions
28 * required by the LGPL. If you do not delete the provisions above, a recipient
29 * may use your version of this file under the terms of any one of the CDDL
30 * or the LGPL.
31 *
32 * ***** END LICENSE BLOCK ***** */
33
34
35#include "os2all.h"
36
37#include <string>
38#include <algorithm>
39#include <process.h>
40#include <stdio.h>
41#include <ctype.h>
42
43#include <ludoc.xh>
44#include <luibutton.xh>
45#include <luitext.xh>
46#include "lucide.h"
47#include "docViewer.h"
48#include "progressDlg.h"
49#include "pluginman.h"
50#include "luutils.h"
51#include "lucide_res.h"
52#include "messages.h"
53#include "cpconv.h"
54
55
56// ASYNCH_RENDER_ENABLE, normally must be defined
57#define ASYNCH_RENDER_ENABLE
58
59typedef LuDocument_LuRectSequence    *PLuRectSequence;
60typedef LuDocument_LuLinkMapSequence *PLuLinkMapSequence;
61
62#define LINE_HEIGHT     16
63#define BORDER_COLOR    0x909090L
64#define PAGEBACK_COLOR  0xFFFFFFL
65#define VERT_SPACE      2
66#define NO_MOUSE_TIMER  1
67#define SB_PAGEDRAG     100
68
69// DocumentViewer constructor
70DocumentViewer::DocumentViewer( HWND hWndFrame )
71{
72    hMainFrame  = hWndFrame;
73    sHscrollMax = 0;
74    sVscrollMax = 0;
75    sHscrollPos = 0;
76    sVscrollPos = 0;
77    sVscrollInc = 0;
78    sHscrollInc = 0;
79    cxClient    = 0;
80    cyClient    = 0;
81    hWndDoc     = NULLHANDLE;
82    hWndText    = NULLHANDLE;
83    textField   = NULL;
84    textFieldPage = 0;
85    doc         = NULL;
86    totalpages  = 0;
87    currentpage = 0;
88    hpsBuffer   = NULLHANDLE;
89    hdcBuffer   = NULLHANDLE;
90    width       = 0;
91    height      = 0;
92    fullwidth   = 0;
93    fullheight  = 0;
94    bpp         = 0;
95    zoom        = 1.0;
96    realzoom    = 1.0;
97    zoomMode    = false;
98    rotation    = 0;
99    pixbuf      = NULL;
100    spos_x      = 0;
101    spos_y      = 0;
102    progressDlg = new ProgressDlg( hWndFrame );
103    drawareas   = NULL;
104    drawareaIndex = 0;
105    closed        = true;
106    layout        = SinglePage;
107    // continuous view
108    pagesizes   = NULL;
109    realVscrollMax = 0;
110    VScrollStep = 1;
111    WinSetRectEmpty( hab, &savedRcl );
112    drawPS = false;
113    // mouse drag using right button
114    docDraggingStarted = false;
115    docDraggingStart.x = 0;  docDraggingStart.y = 0;
116    docDraggingEnd.x = 0;  docDraggingEnd.y = 0;
117    // fullscreen
118    fullscreen     = false;
119    secondsNoMouse = 0;
120    mouseHidden    = false;
121    xLastPos       = 0;
122    yLastPos       = 0;
123    // asynch draw
124    abortAsynch = false;
125    termdraw    = false;
126    enableAsynchDraw = false;
127    DosCreateMutexSem( NULL, &todrawAccess, 0, FALSE );
128    DosCreateEventSem( NULL, &haveDraw, 0, FALSE );
129    // selection
130    mousePressed = false;
131    selectionStart.x = 0;  selectionStart.y = 0;
132    selectionEnd.x = 0;  selectionEnd.y = 0;
133    selection = NULL;
134    selrects = NULL;
135    // links
136    links = NULL;
137    handPtr = WinLoadPointer( HWND_DESKTOP, _hmod, IDP_HAND );
138    handClosedPtr = WinLoadPointer( HWND_DESKTOP, _hmod, IDP_HAND_CLOSED );
139    zoomInPtr = WinLoadPointer( HWND_DESKTOP, _hmod, IDP_ZOOM_IN );
140    zoomOutPtr = WinLoadPointer( HWND_DESKTOP, _hmod, IDP_ZOOM_OUT );
141    textPtr = WinQuerySysPointer( HWND_DESKTOP, SPTR_TEXT, FALSE );
142    // input fields
143    inputFields = NULL;
144    // search
145    foundrects = NULL;
146    searchString = NULL;
147    abortSearch = false;
148
149    // create windows
150    ULONG dfFlags = FCF_VERTSCROLL | FCF_HORZSCROLL | FCF_NOBYTEALIGN;
151    hWndDocFrame = WinCreateStdWindow( hWndFrame, WS_VISIBLE, &dfFlags, NULL, NULL,
152                                       WS_VISIBLE, _hmod, 0, NULL );
153    WinSetWindowULong( hWndDocFrame, QWL_USER, (ULONG)this );
154    oldFrameProc = WinSubclassWindow( hWndDocFrame, docFrameProc );
155
156    hWndDoc = WinCreateWindow( hWndDocFrame, "er.docview", NULL,
157                               WS_VISIBLE | WS_TABSTOP | WS_CLIPCHILDREN,
158                               0, 0, 0, 0, hWndDocFrame,
159                               HWND_TOP, FID_CLIENT, this, NULL );
160
161    hWndText = WinCreateWindow( hWndDoc, WC_ENTRYFIELD, NULL,
162                                 ES_AUTOSCROLL | ES_MARGIN,
163                                 0, 0, 0, 0, hWndDoc,
164                                 HWND_TOP, 0, NULL, NULL );
165
166    hWndHscroll = WinWindowFromID( hWndDocFrame, FID_HORZSCROLL );
167    hWndVscroll = WinWindowFromID( hWndDocFrame, FID_VERTSCROLL );
168
169    drawThreadId = _beginthread( drawthread, NULL, 262144, this );
170    WinStartTimer( hab, hWndDoc, NO_MOUSE_TIMER, 1000 );
171}
172
173// DocumentViewer destructor
174DocumentViewer::~DocumentViewer()
175{
176    termdraw    = true;
177    abortAsynch = true;
178    DosPostEventSem( haveDraw );
179    DosWaitThread( &drawThreadId, DCWW_WAIT );
180    DosCloseMutexSem( todrawAccess );
181    DosCloseEventSem( haveDraw );
182
183    if ( doc != NULL ) {
184        freeRects( selrects );
185        freeRects( foundrects );
186        freeLinks();
187        freeInputFields();
188    }
189
190    WinDestroyPointer( handPtr );
191    WinDestroyPointer( handClosedPtr );
192    WinDestroyPointer( zoomInPtr );
193    WinDestroyPointer( zoomOutPtr );
194
195    if ( ( hpsBuffer != NULLHANDLE ) && ( hdcBuffer != NULLHANDLE ) ) {
196        DestroyGraphicsBuffer( hpsBuffer, hdcBuffer );
197        hpsBuffer = hdcBuffer = NULLHANDLE;
198    }
199    delete pixbuf;
200    delete progressDlg;
201    delete searchString;
202    delete pagesizes;
203    delete selection;
204}
205
206
207// static, registration of a window class
208void DocumentViewer::registerClass()
209{
210    WinRegisterClass( hab, "er.docview", docViewProc, CS_SIZEREDRAW, sizeof( ULONG ) * 2 );
211}
212
213// sets the document for viewing
214void DocumentViewer::setDocument( LuDocument *_doc )
215{
216    close();
217    doc = _doc;
218
219    if ( doc != NULL )
220    {
221        closed = false;
222
223        totalpages = doc->getPageCount( ev );
224        bpp = doc->getBpp( ev );
225        if ( !doc->isScalable( ev ) ) {
226            zoom = 1;
227        }
228
229        pagesizes = new LuSize[ totalpages ];
230        countPagesizes();
231        adjustSize();
232
233        selrects = new PLuRectSequence[ totalpages ];
234        memset( selrects, 0, sizeof( PLuRectSequence ) * totalpages );
235
236        foundrects = new PLuRectSequence[ totalpages ];
237        memset( foundrects, 0, sizeof( PLuRectSequence ) * totalpages );
238
239        if ( doc->isHaveLinks( ev ) ) {
240            links = new PLuLinkMapSequence[ totalpages ];
241            memset( links, 0, sizeof( PLuLinkMapSequence ) * totalpages );
242        }
243
244        if ( doc->isHaveInputFields( ev ) ) {
245            inputFields = new PageInputFields[ totalpages ];
246            memset( inputFields, 0, sizeof( PageInputFields ) * totalpages );
247        }
248
249        selection = new LuRectangle[ totalpages ];
250        memset( selection, 0, sizeof( LuRectangle ) * totalpages );
251
252        drawPS = doc->isRenderIntoPS( ev );
253        if ( drawPS ) {
254            enableAsynchDraw = false;
255        }
256        else {
257#ifdef ASYNCH_RENDER_ENABLE
258            enableAsynchDraw = doc->isAsynchRenderingSupported( ev );
259#else
260            enableAsynchDraw = false;
261#endif
262        }
263        goToPage( 0 );
264    }
265}
266
267void DocumentViewer::countPagesizes()
268{
269    for ( long i = 0; i < totalpages; i++ )
270    {
271        doc->getPageSize( ev, i, &pagesizes[i].x, &pagesizes[i].y );
272        if ( isRotated() ) {
273            double tmp = pagesizes[i].x;
274            pagesizes[i].x = pagesizes[i].y;
275            pagesizes[i].y = tmp;
276        }
277        fullwidth = std::max( fullwidth, pagesizes[i].x );
278        fullheight += ( pagesizes[i].y + VERT_SPACE );
279    }
280}
281
282// closes the document
283void DocumentViewer::close()
284{
285    if ( closed ) {
286        return;
287    }
288
289    closed = true;
290    abortAsynch = true;
291    DosRequestMutexSem( todrawAccess, SEM_INDEFINITE_WAIT );
292
293    delete drawareas;
294    drawareas = NULL;
295
296    delete pagesizes;
297    pagesizes   = NULL;
298
299    delete selection;
300    selection   = NULL;
301
302    freeRects( foundrects );
303    delete foundrects;
304    foundrects  = NULL;
305
306    freeRects( selrects );
307    delete selrects;
308    selrects    = NULL;
309
310    freeLinks();
311
312    freeInputFields();
313
314    doc             = NULL;
315    totalpages      = 0;
316    currentpage     = 0;
317    fullwidth       = 0;
318    fullheight      = 0;
319
320    DosReleaseMutexSem( todrawAccess );
321
322    WinInvalidateRect( hWndDocFrame, NULL, TRUE );
323}
324
325// sets the page layout
326void DocumentViewer::setPageLayout( PgLayout _layout )
327{
328    layout = _layout;
329    if ( doc != NULL ) {
330        long pg = currentpage;
331        drawPage();
332        if ( isContinuous() ) {
333            goToPage( pg );
334        }
335    }
336}
337
338void DocumentViewer::freeRects( LuDocument_LuRectSequence **rects )
339{
340    if ( rects != NULL )
341    {
342        for ( long i = 0; i < totalpages; i++ ) {
343            if ( rects[ i ] != NULL ) {
344                LuDocument::freeRectangles( ev, rects[ i ] );
345                rects[ i ] = NULL;
346            }
347        }
348    }
349}
350
351void DocumentViewer::freeLinks()
352{
353    if ( links != NULL )
354    {
355        for ( long i = 0; i < totalpages; i++ ) {
356            if ( links[ i ] != NULL ) {
357                LuDocument::freeLinkMapping( ev, links[ i ] );
358                links[ i ] = NULL;
359            }
360        }
361
362        delete links;
363        links = NULL;
364    }
365}
366
367void DocumentViewer::freeInputFields()
368{
369    if ( inputFields != NULL )
370    {
371        for ( long i = 0; i < totalpages; i++ ) {
372            if ( inputFields[ i ].fields != NULL ) {
373                LuDocument::freeInputFields( ev, inputFields[ i ].fields );
374                inputFields[ i ].fields = NULL;
375            }
376            if ( inputFields[ i ].cache != NULL ) {
377                delete[] inputFields[ i ].cache;
378                inputFields[ i ].cache = NULL;
379            }
380        }
381
382        delete inputFields;
383        inputFields = NULL;
384    }
385}
386
387// switch view to specified page
388void DocumentViewer::goToPage( long page )
389{
390    if ( ( page < 0 ) || ( page >= totalpages ) ) {
391        return;
392    }
393
394    if ( isContinuous() && ( doc != NULL ) )
395    {
396        bool needRedraw = ( page == currentpage );
397        double pgpos = pagenumToPos( page ) / VScrollStep;
398        vertScroll( hWndDoc, MPFROM2SHORT( pgpos, SB_SLIDERPOSITION ) );
399        if ( needRedraw ) {
400            drawPage();
401        }
402    }
403    else
404    {
405        currentpage = page;
406        sVscrollPos = 0;
407        if ( doc != NULL ) {
408            drawPage();
409            Lucide::checkNavigationMenus();
410        }
411    }
412}
413
414// Sets the zoom level
415// _zoom - actual zoom level or:
416//         -1 - fit width
417//         -2 - fit page
418void DocumentViewer::setZoom( double _zoom )
419{
420    if ( ( _zoom == 0 ) || ( _zoom < -2 ) || ( ( _zoom > 0 ) && ( _zoom < 0.05 ) ) ) {
421        return;
422    }
423
424    if ( doc != NULL ) {
425        if ( doc->isScalable( ev ) ) {
426            zoom = _zoom;
427            drawPage();
428        }
429    }
430    else {
431        zoom = _zoom;
432    }
433}
434
435// Sets the rotation
436// rotation may be 0, 90, 180 or 270 degrees
437// -90 will be changed to 270, 360 to 0
438void DocumentViewer::setRotation( long _rotation )
439{
440    if ( _rotation == -90 ) {
441        _rotation = 270;
442    }
443    if ( _rotation == 360 ) {
444        _rotation = 0;
445    }
446
447    if ( doc != NULL )
448    {
449        if ( doc->isRotable( ev ) )
450        {
451            rotation = _rotation;
452            countPagesizes();
453            drawPage();
454        }
455    }
456    else {
457        rotation = _rotation;
458    }
459}
460
461void DocumentViewer::setFullscreen( bool _fullscreen )
462{
463    fullscreen = _fullscreen;
464    ULONG ulFrameStyle = WinQueryWindowULong( hWndDocFrame, QWL_STYLE );
465
466    if ( fullscreen )
467    {
468        pglSave = getPageLayout();
469        zoomSave = getZoom();
470        setPageLayout( SinglePage );
471        setZoom( -2 );
472        WinSetParent( hWndHscroll, HWND_OBJECT, FALSE );
473        WinSetParent( hWndVscroll, HWND_OBJECT, FALSE );
474        ulFrameStyle &= ~FS_SIZEBORDER;
475    }
476    else
477    {
478        setPageLayout( pglSave );
479        setZoom( zoomSave );
480        WinSetParent( hWndHscroll, hWndDocFrame, FALSE );
481        WinSetParent( hWndVscroll, hWndDocFrame, FALSE );
482        ulFrameStyle &= ~FS_SIZEBORDER;
483    }
484
485    WinSetWindowULong( hWndDocFrame, QWL_STYLE, ulFrameStyle );
486    WinSendMsg( hWndDocFrame, WM_UPDATEFRAME,
487                MPFROMLONG( FCF_VERTSCROLL | FCF_HORZSCROLL | FCF_SIZEBORDER ), MPVOID );
488
489    if ( mouseHidden ) {
490        WinShowPointer( HWND_DESKTOP, TRUE );
491        mouseHidden = false;
492    }
493}
494
495// copy selected text to clipboard
496void DocumentViewer::copyToClipbrd()
497{
498    if ( isContinuous() )
499    {
500        std::string txt = "";
501        for ( long i = 0; i < totalpages; i++ ) {
502            if ( selrects[ i ] != NULL ) {
503                txt += doc->getText( ev, i, &(selection[ i ]) );
504            }
505        }
506        textToClipbrd( hab, txt.c_str() );
507    }
508    else {
509        char *t = doc->getText( ev, currentpage, &(selection[ currentpage ]) );
510        textToClipbrd( hab, t );
511    }
512}
513
514// select all text (continuous view) or current page (single page view)
515void DocumentViewer::selectAll()
516{
517    if ( isContinuous() )
518    {
519        for ( long i = 0; i < totalpages; i++ )
520        {
521            selection[ i ].x1 = 0;
522            selection[ i ].y1 = 0;
523            selection[ i ].x2 = pagesizes[ i ].x;
524            selection[ i ].y2 = pagesizes[ i ].y;
525            LuDocument::freeRectangles( ev, selrects[ i ] );
526            selrects[ i ] = doc->getSelectionRectangles( ev, i, &(selection[i]) );
527        }
528    }
529    else
530    {
531        selection[ currentpage ].x1 = 0;
532        selection[ currentpage ].y1 = 0;
533        selection[ currentpage ].x2 = pagesizes[ currentpage ].x;
534        selection[ currentpage ].y2 = pagesizes[ currentpage ].y;
535        LuDocument::freeRectangles( ev, selrects[ currentpage ] );
536        selrects[ currentpage ] = doc->getSelectionRectangles( ev, currentpage, &(selection[currentpage]) );
537    }
538
539    Lucide::enableCopy( true );
540    WinInvalidateRect( hWndDoc, NULL, FALSE );
541}
542
543// perform search in document
544void DocumentViewer::searchDocument( const char *_searchString, bool _caseSensitive,
545                                     bool _continueSearch )
546{
547    abortSearch = false;
548    if ( !continueSearch ) {
549        freeRects( foundrects );
550    }
551
552    delete searchString;
553    searchString = newstrdup( _searchString );
554    caseSensitive = _caseSensitive;
555    continueSearch = _continueSearch;
556
557    progressDlg->setBreakFunc( searchabort, this );
558    progressDlg->setText( "" );
559    progressDlg->show( searchthread, this );
560}
561
562// static method, cancels asynch rendering if abortAsynch is true
563void DocumentViewer::searchabort( void *data )
564{
565    ((DocumentViewer *)data)->abortSearch = true;
566}
567
568// static method, thread for asynchronous searching
569void DocumentViewer::searchthread( void *p )
570{
571    DosSetPriority( PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MINIMUM, 0 );
572    DocumentViewer *_this = (DocumentViewer *)p;
573
574    HAB thab = WinInitialize( 0 );
575    HMQ thmq = WinCreateMsgQueue( thab, 0 );
576
577    long i = _this->currentpage;
578    if ( _this->continueSearch && ( _this->currentpage < ( _this->totalpages - 1 ) ) ) {
579        i = _this->currentpage + 1;
580    }
581
582    bool found = false;
583    for ( ; i < _this->totalpages; i++ )
584    {
585        char *fmt = newstrdupL( FIND_SEARCH_PAGE_OF );
586        char *buf = new char[ 255 ];
587        snprintf( buf, 255, fmt, i + 1, _this->totalpages );
588        _this->progressDlg->setText( buf );
589        delete fmt;
590        delete buf;
591
592        _this->foundrects[ i ] = _this->doc->searchText( ev, i,
593                                        (char *)_this->searchString, _this->caseSensitive );
594        if ( _this->foundrects[ i ] != NULL )
595        {
596            found = true;
597            _this->progressDlg->hide();
598            _this->goToPage( i );
599            if ( _this->foundrects[i]->_length > 0 ) {
600                RECTL r;
601                _this->docPosToWinPos( i, &(_this->foundrects[i]->_buffer[0]), &r );
602                _this->scrollToPos( _this->hWndDoc, r.xLeft, r.yBottom, false );
603            }
604            break;
605        }
606
607        if ( _this->abortSearch ) {
608            break;
609        }
610    }
611    _this->progressDlg->hide();
612
613    if ( !found && !_this->abortSearch )
614    {
615        char *notfound = newstrdupL( FIND_NOT_FOUND );
616        WinMessageBox( HWND_DESKTOP, _this->hMainFrame, notfound, NULL,
617                       1, MB_OK | MB_ICONEXCLAMATION | MB_MOVEABLE );
618        delete notfound;
619    }
620
621    WinDestroyMsgQueue( thmq );
622    WinTerminate( thab );
623    _endthread();
624}
625
626// count real zoom level based on specified
627void DocumentViewer::adjustSize()
628{
629    if ( doc != NULL )
630    {
631        width  = pagesizes[ currentpage ].x;
632        height = pagesizes[ currentpage ].y;
633
634        fullwidth = 0;
635        fullheight = 0;
636        for ( long i = 0; i < totalpages; i++ ) {
637            fullwidth = std::max( fullwidth, pagesizes[i].x );
638            fullheight += pagesizes[i].y;
639        }
640
641        if ( zoom == -1 ) { // fit width
642            realzoom = (double)cxClient / ( isContinuous() ? fullwidth : width );
643        }
644        else if ( zoom == -2 ) { // fit page
645            realzoom = std::min( (double)cxClient / width, (double)cyClient / height );
646        }
647        else {
648            realzoom = zoom;
649        }
650        width *= realzoom;
651        height *= realzoom;
652        fullwidth *= realzoom;
653        fullheight *= realzoom;
654        fullheight += ( VERT_SPACE * totalpages );
655    }
656}
657
658// page redraw
659void DocumentViewer::drawPage()
660{
661    if ( !isContinuous() )
662    {
663        LuDocument::freeRectangles( ev, selrects[ currentpage ] );
664        selrects[ currentpage ] = NULL;
665
666        if ( links != NULL ) {
667            if ( links[ currentpage ] == NULL ) {
668                links[ currentpage ] = doc->getLinkMapping( ev, currentpage );
669            }
670        }
671
672        if ( inputFields != NULL ) {
673            if ( inputFields[ currentpage ].fields == NULL ) {
674                inputFields[ currentpage ].fields = doc->getInputFields( ev, currentpage );
675                unsigned long len = inputFields[ currentpage ].fields->_length;
676                inputFields[ currentpage ].cache = new PageInputFields::Cache[ len ];
677                memset( inputFields[ currentpage ].cache, 0, sizeof( PageInputFields::Cache ) * len );
678            }
679        }
680
681        Lucide::enableCopy( false );
682    }
683    WinSendMsg( hWndDoc, WM_SIZE, MPFROM2SHORT( cxClient, cyClient ),
684                MPFROM2SHORT( cxClient, cyClient ) );
685    WinInvalidateRect( hWndDoc, NULL, FALSE );
686}
687
688
689// handles vertical scrolling
690MRESULT DocumentViewer::vertScroll( HWND hwnd, MPARAM mp2 )
691{
692    if ( fullscreen ) {
693        return ( MRFROMLONG( 0 ) );
694    }
695
696    sVscrollInc = 0;
697
698    switch ( SHORT2FROMMP( mp2 ) )
699    {
700        case SB_LINEUP:
701            sVscrollInc = -(std::max( LINE_HEIGHT, (int)VScrollStep ));
702            break ;
703        case SB_LINEDOWN:
704            sVscrollInc = std::max( LINE_HEIGHT, (int)VScrollStep );
705            break;
706        case SB_PAGEUP:
707            sVscrollInc = std::min( -1, -( (int)cyClient - LINE_HEIGHT ) );
708            break;
709        case SB_PAGEDOWN:
710            sVscrollInc = std::max( 1, (int)cyClient - LINE_HEIGHT );
711            break;
712        case SB_SLIDERTRACK:
713        case SB_SLIDERPOSITION:
714            sVscrollInc = ( SHORT1FROMMP( mp2 ) - sVscrollPos ) * VScrollStep;
715            break;
716        case SB_PAGEDRAG:
717            sVscrollInc = (SHORT)SHORT1FROMMP( mp2 );
718            break;
719    }
720
721    if ( SHORT2FROMMP( mp2 ) != SB_PAGEDRAG ) {
722        sVscrollInc = std::max( -sVscrollPos * (LONG)VScrollStep, std::min( sVscrollInc,
723                              ( sVscrollMax - sVscrollPos ) * (LONG)VScrollStep ) );
724        sVscrollInc = ( sVscrollInc / VScrollStep ) * VScrollStep;
725    }
726
727    if ( sVscrollInc != 0 )
728    {
729        sVscrollPos += (SHORT)( sVscrollInc / VScrollStep );
730        WinScrollWindow( hwnd, 0, sVscrollInc, NULL, NULL, NULLHANDLE, NULL,
731                         SW_INVALIDATERGN | SW_SCROLLCHILDREN );
732        WinSendMsg( hWndVscroll, SBM_SETPOS, MPFROMSHORT( sVscrollPos ), MPVOID );
733        WinUpdateWindow( hwnd );
734        sVscrollInc = 0;
735    }
736    return ( MRFROMLONG( 0 ) );
737}
738
739// handles horizontal scrolling
740MRESULT DocumentViewer::horizScroll( HWND hwnd, MPARAM mp2 )
741{
742    if ( fullscreen ) {
743        return ( MRFROMLONG( 0 ) );
744    }
745
746    sHscrollInc = 0;
747
748    switch ( SHORT2FROMMP( mp2 ) )
749    {
750        case SB_LINELEFT:
751            sHscrollInc = -LINE_HEIGHT;
752            break;
753        case SB_LINERIGHT:
754            sHscrollInc = LINE_HEIGHT;
755            break;
756        case SB_PAGELEFT:
757            sHscrollInc = std::min( -1, -( (int)cxClient - LINE_HEIGHT ) );
758            break;
759        case SB_PAGERIGHT:
760            sHscrollInc = std::max( 1, (int)cxClient - LINE_HEIGHT );
761            break;
762        case SB_SLIDERTRACK:
763        case SB_SLIDERPOSITION:
764            sHscrollInc = SHORT1FROMMP( mp2 ) - sHscrollPos;
765            break;
766        case SB_PAGEDRAG:
767            sHscrollInc = (SHORT)SHORT1FROMMP( mp2 );
768            break;
769    }
770
771    sHscrollInc = std::max( -(LONG)sHscrollPos, std::min( sHscrollInc, (LONG)(sHscrollMax - sHscrollPos) ) );
772
773    if ( sHscrollInc != 0 )
774    {
775        sHscrollPos += (SHORT)sHscrollInc;
776        WinScrollWindow( hwnd, -sHscrollInc, 0, NULL, NULL, NULLHANDLE, NULL,
777                         SW_INVALIDATERGN | SW_SCROLLCHILDREN );
778        WinSendMsg( hWndHscroll, SBM_SETPOS, MPFROMSHORT( sHscrollPos ), MPVOID );
779        WinUpdateWindow( hwnd );
780        sHscrollInc = 0;
781    }
782    return ( MRFROMLONG( 0 ) );
783}
784
785
786// handles WM_SIZE message
787// creates appropriate hps buffer, sets scrollbars limits
788void DocumentViewer::wmSize( HWND hwnd, MPARAM mp2 )
789{
790    if ( !WinIsWindowShowing( hwnd ) ) {
791        return;
792    }
793
794    cxClient = SHORT1FROMMP( mp2 );
795    cyClient = SHORT2FROMMP( mp2 );
796
797    double relativeScrollPos = ( sVscrollMax == 0 ) ? 0 :
798                                    (double)sVscrollPos / (double)sVscrollMax;
799
800    adjustSize();
801
802    if ( ( hpsBuffer != NULLHANDLE ) && ( hdcBuffer != NULLHANDLE ) ) {
803        DestroyGraphicsBuffer( hpsBuffer, hdcBuffer );
804        hpsBuffer = hdcBuffer = NULLHANDLE;
805    }
806
807    HPS hps = WinGetPS( hwnd );
808    RECTL rectl = { 0, 0, cxClient, cyClient };
809    CreateGraphicsBuffer( hab, &rectl, hps, &hpsBuffer, &hdcBuffer );
810    WinReleasePS( hps );
811
812    if ( fullscreen )
813    {
814        sHscrollMax = 0;
815        sHscrollPos = 0;
816        realVscrollMax = 0;
817        VScrollStep = 1;
818        sVscrollPos = 0;
819    }
820    else
821    {
822        sHscrollMax = (SHORT)std::max( 0., ( isContinuous() ? fullwidth : width ) - cxClient );
823        sHscrollPos = std::min( sHscrollPos, sHscrollMax );
824
825        WinSendMsg( hWndHscroll, SBM_SETSCROLLBAR,
826                    MPFROMSHORT(sHscrollPos), MPFROM2SHORT(0, sHscrollMax) );
827        WinSendMsg( hWndHscroll, SBM_SETTHUMBSIZE,
828                    MPFROM2SHORT( cxClient, width ), MPVOID );
829        WinEnableWindow( hWndHscroll, (BOOL)( sHscrollMax != 0 ) );
830
831        VScrollStep = 1;
832        if ( isContinuous() )
833        {
834            realVscrollMax = std::max( 0., fullheight - cyClient );
835            ULONG ssize = realVscrollMax / VScrollStep;
836            while ( ssize > 32000 ) {
837                VScrollStep += LINE_HEIGHT;
838                ssize = realVscrollMax / VScrollStep;
839            }
840
841            sVscrollMax = (SHORT)ssize;
842            if ( realVscrollMax > ( sVscrollMax * VScrollStep ) ) {
843                sVscrollMax += 1;
844            }
845        }
846        else {
847            realVscrollMax = sVscrollMax = (SHORT)std::max( 0., height - cyClient );
848        }
849        sVscrollPos = std::min( sVscrollPos, sVscrollMax );
850
851        WinSendMsg( hWndVscroll, SBM_SETSCROLLBAR,
852                    MPFROMSHORT(sVscrollPos), MPFROM2SHORT(0, sVscrollMax) );
853        if ( isContinuous() ) {
854            WinSendMsg( hWndVscroll, SBM_SETTHUMBSIZE,
855                        MPFROM2SHORT( cyClient/VScrollStep, fullheight/VScrollStep ), MPVOID );
856        }
857        else {
858            WinSendMsg( hWndVscroll, SBM_SETTHUMBSIZE,
859                        MPFROM2SHORT( cyClient, height ), MPVOID );
860        }
861        WinEnableWindow( hWndVscroll, (BOOL)( sVscrollMax != 0 ) );
862
863        SHORT realScrollPos = (SHORT)(sVscrollMax * relativeScrollPos);
864        vertScroll( hWndDoc, MPFROM2SHORT( realScrollPos, SB_SLIDERPOSITION ) );
865    }
866
867    positionTextField();
868}
869
870// returns true if subrect inside rect
871inline bool isSubrect( PRECTL rect, PRECTL subrect )
872{
873    return ( ( subrect->xLeft >= rect->xLeft ) &&
874             ( subrect->yBottom >= rect->yBottom ) &&
875             ( subrect->xRight <= rect->xRight ) &&
876             ( subrect->yTop <= rect->yTop ) );
877}
878
879// static method, cancels asynch rendering if abortAsynch is true
880long _System DocumentViewer::asynchCallbackFnAbort( void *data )
881{
882    return (long)(((DocumentViewer *)data)->abortAsynch);
883}
884
885// static method, draws area during asynch rendering
886long _System DocumentViewer::asynchCallbackFnDraw( void *data )
887{
888    DocumentViewer *_this = (DocumentViewer *)data;
889    HPS hps = WinGetPS( _this->hWndDoc );
890    if ( hps != NULLHANDLE )
891    {
892        PRECTL drawRect = &((*_this->drawareas)[_this->drawareaIndex].drawrect);
893        LONG rclx = drawRect->xRight - drawRect->xLeft;
894        LONG rcly = drawRect->yTop - drawRect->yBottom;
895
896        POINTL aptlPoints[4]={ drawRect->xLeft, drawRect->yBottom,
897                               drawRect->xRight-1, drawRect->yTop-1,
898                               0, 0, rclx, rcly };
899
900        LONG lRop = ROP_SRCCOPY;
901        BITMAPINFO2 pbmi;
902        pbmi.cbFix = 16L;
903        pbmi.cx = rclx;
904        pbmi.cy = rcly;
905        pbmi.cPlanes = 1;
906        pbmi.cBitCount = _this->bpp * 8;
907        GpiDrawBits( hps, _this->pixbuf->getDataPtr( ev ), &pbmi, 4L,
908                     aptlPoints, lRop, BBO_IGNORE );
909
910        WinReleasePS( hps );
911    }
912    return 0;
913}
914
915// static method, thread for asynchronous rendering
916void DocumentViewer::drawthread( void *p )
917{
918    DosSetPriority( PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MINIMUM, 0 );
919    DocumentViewer *_this = (DocumentViewer *)p;
920
921    HAB thab = WinInitialize( 0 );
922    HMQ thmq = WinCreateMsgQueue( thab, 0 );
923
924    ULONG postCnt;
925    while ( !_this->termdraw )
926    {
927        DosWaitEventSem( _this->haveDraw, SEM_INDEFINITE_WAIT );
928        DosResetEventSem( _this->haveDraw, &postCnt );
929        _this->abortAsynch = false;
930
931        if ( ( _this->drawareas != NULL ) && ( _this->doc != NULL ) )
932        {
933            DosRequestMutexSem( _this->todrawAccess, SEM_INDEFINITE_WAIT );
934
935            for ( _this->drawareaIndex = 0;
936                  _this->drawareaIndex < _this->drawareas->size();
937                  _this->drawareaIndex++ )
938            {
939                long renderErrorCode = LU_RERR_NO_ERROR;
940                char *renderError = NULL;
941
942                PageDrawArea *pda = &(*_this->drawareas)[ _this->drawareaIndex ];
943
944                LONG rclx = pda->drawrect.xRight - pda->drawrect.xLeft;
945                LONG rcly = pda->drawrect.yTop - pda->drawrect.yBottom;
946                _this->pixbuf = new LuPixbuf( ev, rclx, rcly, _this->bpp );
947                _this->doc->renderPageToPixbufAsynch( ev, pda->pagenum,
948                       pda->startpos.x, pda->startpos.y, rclx, rcly, _this->realzoom,
949                       _this->rotation, _this->pixbuf,
950                       (LuDocument_asynchCallbackFn)asynchCallbackFnDraw,
951                       (LuDocument_asynchCallbackFn)asynchCallbackFnAbort, p,
952                       &renderErrorCode, &renderError );
953                delete _this->pixbuf;
954                _this->pixbuf = NULL;
955
956                if ( renderErrorCode != LU_RERR_NO_ERROR )
957                {
958                    // TODO: display error/warning (renderErrorCode, renderError)
959
960                    // ...
961
962                    if ( renderError != NULL ) {
963                        SOMFree( renderError );
964                    }
965                }
966
967                if ( _this->abortAsynch ) {
968                    break;  // TODO: remove completed areas from drawareas (?)
969                }
970            }
971
972            if ( !_this->abortAsynch )
973            {
974                HPS hps = WinGetPS( _this->hWndDoc );
975                if ( hps != NULLHANDLE ) {
976                    for ( int i = 0; i < _this->drawareas->size(); i++ )
977                    {
978                        PageDrawArea *pda = &(*_this->drawareas)[ i ];
979
980                        _this->drawSelection( pda->pagenum, hps, &pda->drawrect );
981                        _this->drawFound( pda->pagenum, hps, &pda->drawrect );
982                    }
983                    WinReleasePS( hps );
984                }
985                WinSetRectEmpty( thab, &_this->savedRcl );
986                delete _this->drawareas;
987                _this->drawareas = NULL;
988            }
989
990            DosReleaseMutexSem( _this->todrawAccess );
991        }
992    }
993    WinDestroyMsgQueue( thmq );
994    WinTerminate( thab );
995    _endthread();
996}
997
998// handles WM_PAINT if single-page asynchronous rendering used
999// posts events to drawthread
1000void DocumentViewer::wmPaintAsynch( HWND hwnd )
1001{
1002    LONG xPos = 0, yPos = 0;
1003    RECTL rclPage = { 0 };
1004    RECTL rcl;
1005    HPS hps = WinBeginPaint( hwnd, 0L, &rcl );
1006    GpiCreateLogColorTable( hpsBuffer, 0, LCOLF_RGB, 0, 0, NULL );
1007    WinFillRect( hpsBuffer, &rcl, BORDER_COLOR );
1008    if ( doc != NULL )
1009    {
1010        if ( width < cxClient ) {
1011            xPos = ( cxClient - width ) / 2;
1012        }
1013        if ( height < cyClient ) {
1014            yPos = ( cyClient - height ) / 2;
1015        }
1016
1017        rclPage.xLeft   = xPos;
1018        rclPage.yBottom = yPos;
1019        rclPage.xRight  = width + xPos;
1020        rclPage.yTop    = height + yPos;
1021        WinFillRect( hpsBuffer, &rclPage, PAGEBACK_COLOR );
1022    }
1023    BlitGraphicsBuffer( hps, hpsBuffer, &rcl );
1024    WinEndPaint( hps );
1025
1026    if ( doc != NULL )
1027    {
1028        RECTL rclDraw = { 0 };
1029        if ( WinIntersectRect( hab, &rclDraw, &rcl, &rclPage ) )
1030        {
1031            if ( ( drawareas != NULL ) && ( drawareas->size() > 0 ) ) {
1032                if ( isSubrect( &((*drawareas)[0].drawrect), &rclDraw ) &&
1033                     ( sVscrollInc == 0 ) && ( sHscrollInc == 0 ) ) {
1034                    return;
1035                }
1036            }
1037
1038            abortAsynch = true;
1039            DosRequestMutexSem( todrawAccess, SEM_INDEFINITE_WAIT );
1040
1041            if ( drawareas == NULL ) {
1042                drawareas = new DrawAreas;
1043            }
1044            if ( drawareas->size() == 0 ) {
1045                PageDrawArea pda;
1046                memset( &pda, 0, sizeof( pda ) );
1047                pda.pagenum = currentpage;
1048                drawareas->push_back( pda );
1049            }
1050
1051            PageDrawArea *ppda = &((*drawareas)[0]);
1052
1053            if ( !WinIsRectEmpty( hab, &ppda->drawrect ) )
1054            {
1055                if ( sVscrollInc > 0 ) {
1056                    ppda->drawrect.yTop    += sVscrollInc;
1057                } else if ( sVscrollInc < 0 ) {
1058                    ppda->drawrect.yBottom += sVscrollInc;
1059                }
1060                if ( sHscrollInc > 0 ) {
1061                    ppda->drawrect.xLeft  -= sHscrollInc;
1062                } else if ( sHscrollInc < 0 ) {
1063                    ppda->drawrect.xRight -= sHscrollInc;
1064                }
1065            }
1066            WinUnionRect( hab, &ppda->drawrect, &ppda->drawrect, &rclDraw );
1067            ppda->startpos.x = sHscrollPos + ppda->drawrect.xLeft - xPos;
1068            ppda->startpos.y = ( yPos > 0 ) ? rclPage.yTop - ppda->drawrect.yTop :
1069                    ( cyClient - ppda->drawrect.yTop ) + sVscrollPos;
1070
1071            DosReleaseMutexSem( todrawAccess );
1072            DosPostEventSem( haveDraw );
1073        }
1074    }
1075}
1076
1077
1078// handles WM_PAINT if continuous asynchronous rendering used
1079void DocumentViewer::wmPaintContAsynch( HWND hwnd )
1080{
1081    RECTL rcl, rclWin, rclDraw = { 0 };
1082    HPS hps = WinBeginPaint( hwnd, 0L, &rcl );
1083    GpiCreateLogColorTable( hpsBuffer, 0, LCOLF_RGB, 0, 0, NULL );
1084    WinFillRect( hpsBuffer, &rcl, BORDER_COLOR );
1085
1086    if ( doc != NULL )
1087    {
1088        long foundpage = -1;
1089        double pageRest;
1090        for ( LONG i = rcl.yTop; i >= rcl.yBottom; i-- )
1091        {
1092            pageRest = 0;
1093            long pg = posToPagenum( i, &pageRest );
1094            if ( ( pg != foundpage ) && ( pg != -1 ) )
1095            {
1096                RECTL rclPage = { 0 };
1097                LuRectangle lr = { 0, 0,
1098                    isRotated() ? (pagesizes[ pg ].y - 1) : (pagesizes[ pg ].x - 1),
1099                    isRotated() ? (pagesizes[ pg ].x - 1) : (pagesizes[ pg ].y - 1) };
1100                docPosToWinPos( pg, &lr, &rclPage );
1101                WinFillRect( hpsBuffer, &rclPage, PAGEBACK_COLOR );
1102                foundpage = pg;
1103                i -= pageRest;
1104            }
1105        }
1106    }
1107
1108    BlitGraphicsBuffer( hps, hpsBuffer, &rcl );
1109    WinEndPaint( hps );
1110
1111    if ( doc != NULL )
1112    {
1113        if ( isSubrect( &savedRcl, &rcl ) && ( sVscrollInc == 0 ) && ( sHscrollInc == 0 ) ) {
1114            return;
1115        }
1116
1117        abortAsynch = true;
1118        DosRequestMutexSem( todrawAccess, SEM_INDEFINITE_WAIT );
1119
1120        WinQueryWindowRect( hwnd, &rclWin );
1121        WinUnionRect( hab, &rcl, &rcl, &savedRcl );
1122
1123        if ( sVscrollInc > 0 ) {
1124            rcl.yTop    += sVscrollInc;
1125        } else if ( sVscrollInc < 0 ) {
1126            rcl.yBottom += sVscrollInc;
1127        }
1128        if ( sHscrollInc > 0 ) {
1129            rcl.xLeft  -= sHscrollInc;
1130        } else if ( sHscrollInc < 0 ) {
1131            rcl.xRight -= sHscrollInc;
1132        }
1133
1134        WinIntersectRect( hab, &rclDraw, &rcl, &rclWin );
1135        WinCopyRect( hab, &rcl, &rclDraw );
1136        WinCopyRect( hab, &savedRcl, &rcl );
1137
1138        delete drawareas;
1139        drawareas = findDrawAreas( &rcl );
1140
1141        for ( int i = 0; i < drawareas->size(); i++ )
1142        {
1143            PageDrawArea *pda = &(*drawareas)[ i ];
1144
1145            // load links for page if not loaded before
1146            if ( links[ pda->pagenum ] == NULL ) {
1147                links[ pda->pagenum ] = doc->getLinkMapping( ev, pda->pagenum );
1148            }
1149
1150            // load input fields for page if not loaded before
1151            if ( inputFields[ pda->pagenum ].fields == NULL ) {
1152                inputFields[ pda->pagenum ].fields = doc->getInputFields( ev, pda->pagenum );
1153                unsigned long len = inputFields[ pda->pagenum ].fields->_length;
1154                inputFields[ pda->pagenum ].cache = new PageInputFields::Cache[ len ];
1155                memset( inputFields[ pda->pagenum ].cache, 0, sizeof( PageInputFields::Cache ) * len );
1156            }
1157        }
1158        DosReleaseMutexSem( todrawAccess );
1159        DosPostEventSem( haveDraw );
1160
1161        determineCurrentPage();
1162    }
1163}
1164
1165
1166// handles WM_PAINT if single-page synchronous rendering used
1167void DocumentViewer::wmPaint( HWND hwnd )
1168{
1169    RECTL rcl;
1170    HPS hps = WinBeginPaint( hwnd, 0L, &rcl );
1171    GpiCreateLogColorTable( hpsBuffer, 0, LCOLF_RGB, 0, 0, NULL );
1172    WinFillRect( hpsBuffer, &rcl, BORDER_COLOR );
1173
1174    if ( doc != NULL )
1175    {
1176        LONG xPos = 0, yPos = 0;
1177        if ( width < cxClient ) {
1178            xPos = ( cxClient - width ) / 2;
1179        }
1180        if ( height < cyClient ) {
1181            yPos = ( cyClient - height ) / 2;
1182        }
1183
1184        RECTL rclPage = { xPos, yPos, width + xPos, height + yPos };
1185        RECTL rclDraw = { 0 };
1186        if ( WinIntersectRect( hab, &rclDraw, &rcl, &rclPage ) )
1187        {
1188            spos_x = sHscrollPos + rclDraw.xLeft - xPos;
1189            spos_y = ( height < cyClient ) ? rclPage.yTop - rclDraw.yTop : (cyClient - rclDraw.yTop) + sVscrollPos;
1190            LONG rclx = rclDraw.xRight - rclDraw.xLeft;
1191            LONG rcly = rclDraw.yTop - rclDraw.yBottom;
1192
1193            long renderErrorCode = LU_RERR_NO_ERROR;
1194            char *renderError = NULL;
1195
1196            if ( drawPS )
1197            {
1198                doc->renderPageToPS( ev, currentpage, spos_x, spos_y, rclx, rcly,
1199                                     realzoom, rotation, hpsBuffer, &rclDraw,
1200                                     &renderErrorCode, &renderError );
1201            }
1202            else
1203            {
1204                pixbuf = new LuPixbuf( ev, rclx, rcly, bpp );
1205                POINTL aptlPoints[4]={ rclDraw.xLeft, rclDraw.yBottom,
1206                                       rclDraw.xRight-1, rclDraw.yTop-1,
1207                                       0, 0, rclx, rcly };
1208
1209                doc->renderPageToPixbuf( ev, currentpage, spos_x, spos_y,
1210                                         rclx, rcly, realzoom, rotation, pixbuf,
1211                                         &renderErrorCode, &renderError );
1212                LONG lRop = ROP_SRCCOPY;
1213                BITMAPINFO2 pbmi;
1214                pbmi.cbFix = 16L;
1215                pbmi.cx = rclx;
1216                pbmi.cy = rcly;
1217                pbmi.cPlanes = 1;
1218                pbmi.cBitCount = bpp * 8;
1219                GpiDrawBits( hpsBuffer, pixbuf->getDataPtr( ev ), &pbmi, 4L,
1220                             aptlPoints, lRop, BBO_IGNORE );
1221                delete pixbuf;
1222                pixbuf = NULL;
1223            }
1224
1225            if ( renderErrorCode != LU_RERR_NO_ERROR )
1226            {
1227                // TODO: display error/warning (renderErrorCode, renderError)
1228
1229                // ...
1230
1231                if ( renderError != NULL ) {
1232                    SOMFree( renderError );
1233                }
1234            }
1235
1236            drawSelection( currentpage, hpsBuffer, &rclDraw );
1237            drawFound( currentpage, hpsBuffer, &rclDraw );
1238        }
1239    }
1240    BlitGraphicsBuffer( hps, hpsBuffer, &rcl );
1241    WinEndPaint( hps );
1242}
1243
1244
1245// founds number of page at specified vertical position
1246// for continuous view only
1247long DocumentViewer::posToPagenum( LONG yPosWin, double *pageRest )
1248{
1249    double yPos = ( cyClient - yPosWin ) + ( sVscrollPos * VScrollStep );
1250    double pgstart = 0;
1251    double pgend = 0;
1252    for ( long i = 0; i < totalpages; i++ )
1253    {
1254        pgend = pgstart + ( pagesizes[ i ].y * realzoom );
1255        if ( ( yPos >= pgstart ) && ( yPos < pgend ) ) {
1256            *pageRest = pgend - yPos;
1257            return i;
1258        }
1259        pgstart = ( pgend + VERT_SPACE );
1260    }
1261    return -1;
1262}
1263
1264// founds vertical position of specified
1265// for continuous view only
1266double DocumentViewer::pagenumToPos( long pagenum )
1267{
1268    double ypos = 0;
1269    for ( long i = 0; i < pagenum; i++ ) {
1270        ypos += pagesizes[ i ].y;
1271    }
1272    return ( ( ypos * realzoom ) + ( pagenum * VERT_SPACE ) );
1273}
1274
1275void DocumentViewer::showTextField( LuInputText *text, long page, PRECTL r )
1276{
1277    // save the previous changes if any
1278    if ( textField != NULL )
1279        hideTextField();
1280
1281    textField = text;
1282    textFieldPage = page;
1283
1284    positionTextField( r );
1285
1286    // @todo uncomment this once it returns anything useful
1287    // int maxLen = textField->getMaximumLength( ev );
1288    WinSendMsg( hWndText, EM_SETTEXTLIMIT, MPFROMLONG( 65520 ), NULL );
1289
1290    const char *contents = textField->getContents( ev );
1291    char *str = uniUtf8ToSys( contents, NULL, NULL );
1292    WinSetWindowText( hWndText, str );
1293    delete[] str;
1294
1295    WinShowWindow( hWndText, TRUE );
1296    WinSetFocus( HWND_DESKTOP, hWndText );
1297}
1298
1299void DocumentViewer::positionTextField( PRECTL r )
1300{
1301    if ( textField == NULL )
1302        return;
1303
1304    RECTL rtmp = {0};
1305    if ( r == NULL ) {
1306        LuRectangle *rect = textField->getRectangle( ev );
1307        docPosToWinPos( textFieldPage, rect, &rtmp );
1308        r = &rtmp;
1309    }
1310
1311    WinSetWindowPos( hWndText, HWND_TOP,
1312                     r->xLeft + 3, r->yBottom + 3,
1313                     r->xRight - r->xLeft - 6,
1314                     r->yTop - r->yBottom - 6,
1315                     SWP_MOVE | SWP_SIZE | SWP_ZORDER );
1316
1317    static LONG ulDpi = 0;
1318    if ( ulDpi == 0 ) {
1319        // DPI is constant beteen reboots
1320        HPS hps = WinGetScreenPS( HWND_DESKTOP );
1321        DevQueryCaps( GpiQueryDevice( hps ), CAPS_HORIZONTAL_FONT_RES,
1322                      1, &ulDpi );
1323        WinReleasePS( hps );
1324    }
1325
1326    LONG points = ( r->yTop - r->yBottom - 6 ) * 72 / 120 - 2;
1327    if ( points < 2 )
1328        points = 2;
1329
1330    char font[ 32 ];
1331    sprintf( font, "%d.Helvetica Bold", points );
1332    WinSetPresParam( hWndText, PP_FONTNAMESIZE, strlen( font ) + 1, font );
1333}
1334
1335void DocumentViewer::hideTextField( bool apply )
1336{
1337    if ( textField != NULL ) {
1338        if ( apply ) {
1339            LONG len = WinQueryWindowTextLength( hWndText );
1340            char *str = new char [ len + 1 ];
1341            *str = 0;
1342            WinQueryWindowText( hWndText, len + 1, str );
1343            str[ len ] = 0;
1344            char *contents = uniSysToUtf8( str, NULL, NULL );
1345            textField->setContents( ev, contents );
1346            delete[] contents;
1347            delete[] str;
1348        }
1349        textField = NULL;
1350        SWP swp;
1351        WinQueryWindowPos( hWndText, &swp );
1352        WinShowWindow( hWndText, FALSE );
1353        // repaint little bit more (rounding errors)
1354        RECTL r = { swp.x - 1, swp.y - 1, swp.x + swp.cx + 2, swp.y + swp.cy + 2 };
1355        WinInvalidateRect( hWndDoc, &r, TRUE );
1356    }
1357}
1358
1359// founds pages and it's areas to draw
1360// for continuous view only
1361DrawAreas *DocumentViewer::findDrawAreas( PRECTL r )
1362{
1363    DrawAreas *areas = new DrawAreas;
1364    if ( doc != NULL )
1365    {
1366        long foundpage = -1;
1367        double pageRest;
1368        for ( LONG i = r->yTop; i >= r->yBottom; i-- )
1369        {
1370            pageRest = 0;
1371            long pg = posToPagenum( i, &pageRest );
1372            if ( ( pg != foundpage ) && ( pg != -1 ) )
1373            {
1374                double w = pagesizes[ pg ].x * realzoom;
1375
1376                PageDrawArea pda = {0};
1377                pda.pagenum = pg;
1378
1379                LONG xPos = 0;
1380                if ( w < cxClient ) {
1381                    xPos = ( cxClient - w ) / 2;
1382                }
1383                RECTL rclPage = { 0 };
1384                LuRectangle lr = { 0, 0,
1385                    isRotated() ? (pagesizes[ pg ].y - 1) : (pagesizes[ pg ].x - 1),
1386                    isRotated() ? (pagesizes[ pg ].x - 1) : (pagesizes[ pg ].y - 1) };
1387                docPosToWinPos( pg, &lr, &rclPage );
1388                if ( WinIntersectRect( hab, &pda.drawrect, r, &rclPage ) )
1389                {
1390                    pda.startpos.x = sHscrollPos + pda.drawrect.xLeft - xPos;
1391                    pda.startpos.y = ( pagesizes[ pg ].y * realzoom ) - pageRest;
1392                    areas->push_back( pda );
1393                }
1394                foundpage = pg;
1395                i -= pageRest;
1396            }
1397        }
1398    }
1399
1400    return areas;
1401}
1402
1403
1404// found current page in continuous view mode.
1405// it's a page which occupes a most larger area in the window.
1406void DocumentViewer::determineCurrentPage()
1407{
1408    RECTL rcl = { 0 };
1409    WinQueryWindowRect( hWndDoc, &rcl );
1410    DrawAreas *areas = findDrawAreas( &rcl );
1411    long pg = 0;
1412    long sz = 0;
1413    for ( int i = 0; i < areas->size(); i++ )
1414    {
1415        PageDrawArea *pda = &(*areas)[ i ];
1416        long pgsz = pda->drawrect.yTop - pda->drawrect.yBottom;
1417        if ( pgsz > sz ) {
1418            pg = pda->pagenum;
1419            sz = pgsz;
1420        }
1421    }
1422    delete areas;
1423
1424    if ( pg != currentpage ) {
1425        currentpage = pg;
1426        Lucide::checkNavigationMenus();
1427    }
1428}
1429
1430
1431// handles WM_PAINT if continuous synchronous rendering used
1432void DocumentViewer::wmPaintCont( HWND hwnd )
1433{
1434    RECTL rcl;
1435    HPS hps = WinBeginPaint( hwnd, 0L, &rcl );
1436    GpiCreateLogColorTable( hpsBuffer, 0, LCOLF_RGB, 0, 0, NULL );
1437    WinFillRect( hpsBuffer, &rcl, BORDER_COLOR );
1438
1439    if ( doc != NULL )
1440    {
1441        delete drawareas;
1442        drawareas = findDrawAreas( &rcl );
1443
1444        for ( int i = 0; i < drawareas->size(); i++ )
1445        {
1446            PageDrawArea *pda = &(*drawareas)[ i ];
1447
1448            // load links for page if not loaded before
1449            if ( links[ pda->pagenum ] == NULL ) {
1450                links[ pda->pagenum ] = doc->getLinkMapping( ev, pda->pagenum );
1451            }
1452
1453            // load input fields for page if not loaded before
1454            if ( inputFields[ pda->pagenum ].fields == NULL ) {
1455                inputFields[ pda->pagenum ].fields = doc->getInputFields( ev, pda->pagenum );
1456                unsigned long len = inputFields[ pda->pagenum ].fields->_length;
1457                inputFields[ pda->pagenum ].cache = new PageInputFields::Cache[ len ];
1458                memset( inputFields[ pda->pagenum ].cache, 0, sizeof( PageInputFields::Cache ) * len );
1459            }
1460
1461            spos_x = pda->startpos.x;
1462            spos_y = pda->startpos.y;
1463            LONG rclx = pda->drawrect.xRight - pda->drawrect.xLeft;
1464            LONG rcly = pda->drawrect.yTop - pda->drawrect.yBottom;
1465
1466            long renderErrorCode = LU_RERR_NO_ERROR;
1467            char *renderError = NULL;
1468
1469            if ( drawPS )
1470            {
1471                doc->renderPageToPS( ev, pda->pagenum, spos_x, spos_y, rclx, rcly,
1472                                     realzoom, rotation, hpsBuffer, &(pda->drawrect),
1473                                     &renderErrorCode, &renderError );
1474            }
1475            else
1476            {
1477                pixbuf = new LuPixbuf( ev, rclx, rcly, bpp );
1478                POINTL aptlPoints[4]={ pda->drawrect.xLeft, pda->drawrect.yBottom,
1479                                       pda->drawrect.xRight-1, pda->drawrect.yTop-1,
1480                                       0, 0, rclx, rcly };
1481                doc->renderPageToPixbuf( ev, pda->pagenum, spos_x, spos_y,
1482                                         rclx, rcly, realzoom, rotation, pixbuf,
1483                                         &renderErrorCode, &renderError );
1484                LONG lRop = ROP_SRCCOPY;
1485                BITMAPINFO2 pbmi;
1486                pbmi.cbFix = 16L;
1487                pbmi.cx = rclx;
1488                pbmi.cy = rcly;
1489                pbmi.cPlanes = 1;
1490                pbmi.cBitCount = bpp * 8;
1491                GpiDrawBits( hpsBuffer, pixbuf->getDataPtr( ev ), &pbmi, 4L,
1492                             aptlPoints, lRop, BBO_IGNORE );
1493                delete pixbuf;
1494                pixbuf = NULL;
1495            }
1496
1497            if ( renderErrorCode != LU_RERR_NO_ERROR )
1498            {
1499                // TODO: display error/warning (renderErrorCode, renderError)
1500
1501                // ...
1502
1503                if ( renderError != NULL ) {
1504                    SOMFree( renderError );
1505                }
1506            }
1507
1508            drawSelection( pda->pagenum, hpsBuffer, &pda->drawrect );
1509            drawFound( pda->pagenum, hpsBuffer, &pda->drawrect );
1510        }
1511        delete drawareas;
1512        drawareas = NULL;
1513    }
1514    BlitGraphicsBuffer( hps, hpsBuffer, &rcl );
1515    WinEndPaint( hps );
1516
1517    if ( doc != NULL ) {
1518        determineCurrentPage();
1519    }
1520}
1521
1522
1523// Rotates document rectangle
1524void DocumentViewer::rotateRectangle( long pagenum, LuRectangle *r )
1525{
1526    double tmp_x1 = r->x1;
1527    double tmp_y1 = r->y1;
1528    double tmp_x2 = r->x2;
1529    double tmp_y2 = r->y2;
1530
1531    double w = pagesizes[ pagenum ].x;
1532    double h = pagesizes[ pagenum ].y;
1533
1534    if ( rotation == 90 ) {
1535        r->x1 = tmp_y1;
1536        r->y1 = w - tmp_x1;
1537        r->x2 = tmp_y2;
1538        r->y2 = w - tmp_x2;
1539    }
1540    else if ( rotation == 180 )
1541    {
1542        r->x1 = w - tmp_x2;
1543        r->y1 = h - tmp_y2;
1544        r->x2 = w - tmp_x1;
1545        r->y2 = h - tmp_y1;
1546    }
1547    else if ( rotation == 270 )
1548    {
1549        r->x1 = h - tmp_y1;
1550        r->y1 = tmp_x1;
1551        r->x2 = h - tmp_y2;
1552        r->y2 = tmp_x2;
1553    }
1554
1555    if ( r->x1 > r->x2 ) {
1556        double tmp = r->x1;
1557        r->x1 = r->x2;
1558        r->x2 = tmp;
1559    }
1560
1561    if ( r->y1 > r->y2 ) {
1562        double tmp = r->y1;
1563        r->y1 = r->y2;
1564        r->y2 = tmp;
1565    }
1566}
1567
1568// converts window position to document position
1569// single page mode only
1570void DocumentViewer::winPosToDocPos( PPOINTL startpoint, PPOINTL endpoint, LuRectangle *r )
1571{
1572    LONG sx = startpoint->x;
1573    LONG sy = startpoint->y;
1574    LONG ex = endpoint->x;
1575    LONG ey = endpoint->y;
1576    if ( width < cxClient ) {
1577        LONG xPos = ( cxClient - width ) / 2;
1578        sx -= xPos;
1579        ex -= xPos;
1580    }
1581    if ( height < cyClient ) {
1582        LONG yPos = ( cyClient - height ) / 2;
1583        sy += yPos;
1584        ey += yPos;
1585    }
1586
1587    r->x1 = ( sx + sHscrollPos ) / realzoom;
1588    r->y1 = ( ( cyClient - sy ) + sVscrollPos ) / realzoom;
1589    r->x2 = ( ex + sHscrollPos ) / realzoom;
1590    r->y2 = ( ( cyClient - ey ) + sVscrollPos ) / realzoom;
1591
1592    rotateRectangle( currentpage, r );
1593}
1594
1595// converts window position to document position
1596// continuous view mode only
1597void DocumentViewer::winPosToDocPos( PageDrawArea *pda, LuRectangle *r )
1598{
1599    LONG sx = pda->drawrect.xLeft;
1600    LONG ex = pda->drawrect.xRight;
1601    double w = pagesizes[ pda->pagenum ].x * realzoom;
1602    if ( w < cxClient ) {
1603        LONG xPos = ( cxClient - w ) / 2;
1604        sx -= xPos;
1605        ex -= xPos;
1606    }
1607
1608    r->x1 = ( sHscrollPos + sx ) / realzoom;;
1609    r->y1 = pda->startpos.y / realzoom;
1610    r->x2 = ( ( ex - sx ) / realzoom ) + r->x1;
1611    r->y2 = ( ( pda->drawrect.yTop - pda->drawrect.yBottom ) / realzoom ) + r->y1;
1612
1613    rotateRectangle( pda->pagenum, r );
1614}
1615
1616// converts document position to window position
1617void DocumentViewer::docPosToWinPos( long pagenum, LuRectangle *r, PRECTL rcl )
1618{
1619    double yplus = isContinuous() ? pagenumToPos( pagenum ) : 0;
1620    double w = pagesizes[ pagenum ].x;
1621    double h = pagesizes[ pagenum ].y;
1622
1623    double tmp_x1 = r->x1;
1624    double tmp_y1 = r->y1;
1625    double tmp_x2 = r->x2;
1626    double tmp_y2 = r->y2;
1627
1628    if ( rotation == 90 )
1629    {
1630        tmp_x1 = w - r->y2;
1631        tmp_y1 = r->x1;
1632        tmp_x2 = w - r->y1;
1633        tmp_y2 = r->x2;
1634    }
1635    else if ( rotation == 180 )
1636    {
1637        tmp_x1 = w - r->x2;
1638        tmp_y1 = h - r->y2;
1639        tmp_x2 = w - r->x1;
1640        tmp_y2 = h - r->y1;
1641    }
1642    else if ( rotation == 270 )
1643    {
1644        tmp_x1 = r->y1;
1645        tmp_y1 = h - r->x2;
1646        tmp_x2 = r->y2;
1647        tmp_y2 = h - r->x1;
1648    }
1649
1650    rcl->xLeft   = ( tmp_x1 * realzoom ) - sHscrollPos;
1651    rcl->yBottom = cyClient - ( yplus + ( tmp_y2 * realzoom ) ) + ( sVscrollPos * VScrollStep );
1652    rcl->xRight  = ( tmp_x2 * realzoom ) - sHscrollPos;
1653    rcl->yTop    = cyClient - ( yplus + ( tmp_y1 * realzoom ) ) + ( sVscrollPos * VScrollStep );
1654
1655    LONG pw = w * realzoom;
1656    if ( pw < cxClient ) {
1657        LONG xPos = ( cxClient - pw ) / 2;
1658        rcl->xLeft  += xPos;
1659        rcl->xRight += xPos;
1660    }
1661    if ( !isContinuous() )
1662    {
1663        LONG ph = h * realzoom;
1664        if ( ph < cyClient ) {
1665            LONG yPos = ( cyClient - ph ) / 2;
1666            rcl->yBottom -= yPos;
1667            rcl->yTop    -= yPos;
1668        }
1669    }
1670}
1671
1672// creates region from sequence of rectangles
1673HRGN DocumentViewer::rectsToRegion( long pagenum, HPS hps, LuDocument_LuRectSequence *rects )
1674{
1675    HRGN hrgn = GpiCreateRegion( hps, 0, NULL );
1676    if ( rects != NULL )
1677    {
1678        RECTL r = {0};
1679        for ( int i = 0; i < rects->_length; i++ )
1680        {
1681            docPosToWinPos( pagenum, &(rects->_buffer[i]), &r );
1682            HRGN tmprgn = GpiCreateRegion( hps, 1, &r );
1683            GpiCombineRegion( hps, hrgn, hrgn, tmprgn, CRGN_OR );
1684            GpiDestroyRegion( hps, tmprgn );
1685        }
1686    }
1687    return hrgn;
1688}
1689
1690// draws selected area in window, using XOR mix
1691// drawing area may be restricted by r rectangle
1692void DocumentViewer::drawSelection( long pagenum, HPS hps, PRECTL r )
1693{
1694    GpiSetMix( hps, FM_XOR );
1695    GpiSetColor( hps, CLR_YELLOW );
1696    HRGN selectRegion = rectsToRegion( pagenum, hps, selrects[ pagenum ] );
1697    if ( r != NULL )
1698    {
1699        HRGN tmprgn = GpiCreateRegion( hps, 1, r );
1700        GpiCombineRegion( hps, selectRegion, selectRegion, tmprgn, CRGN_AND );
1701        GpiDestroyRegion( hps, tmprgn );
1702    }
1703    GpiPaintRegion( hps, selectRegion );
1704    GpiDestroyRegion( hps, selectRegion );
1705}
1706
1707void DocumentViewer::drawFound( long pagenum, HPS hps, PRECTL r )
1708{
1709    GpiSetMix( hps, FM_XOR );
1710    GpiSetColor( hps, CLR_CYAN );
1711    HRGN selectRegion = rectsToRegion( pagenum, hps, foundrects[ pagenum ] );
1712    if ( r != NULL )
1713    {
1714        HRGN tmprgn = GpiCreateRegion( hps, 1, r );
1715        GpiCombineRegion( hps, selectRegion, selectRegion, tmprgn, CRGN_AND );
1716        GpiDestroyRegion( hps, tmprgn );
1717    }
1718    GpiPaintRegion( hps, selectRegion );
1719    GpiDestroyRegion( hps, selectRegion );
1720}
1721
1722// scrolls window to specified pos (optionally with text selection)
1723void DocumentViewer::scrollToPos( HWND hwnd, LONG xpos, LONG ypos,
1724                                  bool withSelection )
1725{
1726    SHORT xinc = 0;
1727    SHORT yinc = 0;
1728
1729    if ( ( xpos < 0 ) && ( sHscrollPos > 0 ) ) {
1730        xinc = std::max( (SHORT)( -sHscrollPos ), (SHORT)xpos );
1731    } else if ( ( xpos > cxClient ) && ( sHscrollPos < sHscrollMax ) ) {
1732        xinc = std::min( (SHORT)( sHscrollMax - sHscrollPos ), (SHORT)( xpos - cxClient ) );
1733    }
1734    if ( ( ypos < 0 ) && ( sVscrollPos < sVscrollMax ) ) {
1735        yinc = std::min( (SHORT)( ( sVscrollMax - sVscrollPos ) * VScrollStep ), (SHORT)( -ypos ) );
1736    }
1737    else if ( ( ypos > cyClient ) && ( sVscrollPos > 0 ) ) {
1738        yinc = std::max( (SHORT)( ( -sVscrollPos ) * VScrollStep ), (SHORT)( cyClient - ypos ) );
1739    }
1740
1741    if ( xinc != 0 ) {
1742        horizScroll( hwnd, MPFROM2SHORT( sHscrollPos + xinc, SB_SLIDERPOSITION ) );
1743        if ( withSelection ) {
1744            selectionStart.x -= xinc;
1745        }
1746    }
1747
1748    if ( yinc != 0 )
1749    {
1750        SHORT remainder = yinc % VScrollStep;
1751        if ( remainder != 0 ) {
1752            SHORT add = VScrollStep - remainder;
1753            yinc += ( ( yinc > 0 ) ? add : -add );
1754        }
1755
1756        vertScroll( hwnd, MPFROM2SHORT( ( ( sVscrollPos * VScrollStep ) + yinc ) / VScrollStep,
1757                                        SB_SLIDERPOSITION ) );
1758        if ( withSelection ) {
1759            selectionStart.y += yinc;
1760        }
1761    }
1762}
1763
1764// handles WM_MOUSEMOVE
1765// performs text selection if mouse button pressed
1766// changes mouse ptr to 'hand' if it moves over link area
1767BOOL DocumentViewer::wmMouseMove( HWND hwnd, SHORT xpos, SHORT ypos )
1768{
1769    if ( ( xpos != xLastPos ) || ( ypos != yLastPos ) ) // only if mouse really moved
1770    {
1771        secondsNoMouse = 0;
1772        if ( fullscreen && mouseHidden )
1773        {
1774            WinShowPointer( HWND_DESKTOP, TRUE );
1775            mouseHidden = false;
1776        }
1777    }
1778    xLastPos = xpos;
1779    yLastPos = ypos;
1780
1781    if ( zoomMode )
1782    {
1783        HPOINTER ptr = zoomInPtr;
1784        if ( WinGetPhysKeyState( HWND_DESKTOP, 0x1d ) & 0x8000 ) {
1785            ptr = zoomOutPtr;
1786        }
1787        WinSetPointer( HWND_DESKTOP, ptr );
1788        return TRUE;
1789    }
1790    else
1791    {
1792        if ( mousePressed && ( doc != NULL ) )
1793        {
1794            selectionEnd.x = xpos;
1795            selectionEnd.y = ypos;
1796
1797            if ( isContinuous() )
1798            {
1799                scrollToPos( hwnd, xpos, ypos, true );
1800
1801                RECTL selRect = {
1802                    selectionStart.x < selectionEnd.x ? selectionStart.x : selectionEnd.x,
1803                    selectionStart.y < selectionEnd.y ? selectionStart.y : selectionEnd.y,
1804                    selectionStart.x < selectionEnd.x ? selectionEnd.x : selectionStart.x,
1805                    selectionStart.y < selectionEnd.y ? selectionEnd.y : selectionStart.y
1806                };
1807
1808                DrawAreas *areas = findDrawAreas( &selRect );
1809
1810                HPS hps = WinGetPS( hwnd );
1811                GpiSetMix( hps, FM_XOR );
1812                GpiSetColor( hps, CLR_YELLOW );
1813
1814                for ( int i = 0; i < areas->size(); i++ )
1815                {
1816                    PageDrawArea *pda = &(*areas)[ i ];
1817
1818                    winPosToDocPos( pda, &(selection[pda->pagenum]) );
1819
1820                    HRGN clearRegion = rectsToRegion( pda->pagenum, hps, selrects[ pda->pagenum ] );
1821                    LuDocument::freeRectangles( ev, selrects[ pda->pagenum ] );
1822                    selrects[ pda->pagenum ] = doc->getSelectionRectangles( ev, pda->pagenum, &(selection[pda->pagenum]) );
1823                    HRGN selectRegion = rectsToRegion( pda->pagenum, hps, selrects[ pda->pagenum ] );
1824                    GpiCombineRegion( hps, selectRegion, selectRegion, clearRegion, CRGN_XOR );
1825                    GpiPaintRegion( hps, selectRegion );
1826                    GpiDestroyRegion( hps, clearRegion );
1827                    GpiDestroyRegion( hps, selectRegion );
1828                }
1829
1830                WinReleasePS( hps );
1831                delete areas;
1832            }
1833            else
1834            {
1835                winPosToDocPos( &selectionStart, &selectionEnd, &(selection[currentpage]) );
1836
1837                HPS hps = WinGetPS( hwnd );
1838
1839                scrollToPos( hwnd, xpos, ypos, true );
1840
1841                // 127/191/255
1842                //LONG lclr = ( 127 << 16 ) | ( 191 << 8 ) | 255;
1843                //LONG lclr = ( 128 << 16 ) | ( 64 << 8 );
1844                //LONG ltabl[ 1 ] = { lclr };
1845                //GpiCreateLogColorTable( hps, 0, LCOLF_CONSECRGB, 100, 1, ltabl );
1846
1847                GpiSetMix( hps, FM_XOR );
1848                GpiSetColor( hps, CLR_YELLOW );
1849                //GpiSetColor( hps, 100 );
1850
1851                HRGN clearRegion = rectsToRegion( currentpage, hps, selrects[ currentpage ] );
1852                LuDocument::freeRectangles( ev, selrects[ currentpage ] );
1853                if ( ( selectionStart.x == selectionEnd.x ) &&
1854                     ( selectionStart.y == selectionEnd.y ) ) {
1855                    selrects[ currentpage ] = NULL;
1856                    memset( &(selection[ currentpage ]), 0, sizeof( LuRectangle ) );
1857                }
1858                else {
1859                    selrects[ currentpage ] = doc->getSelectionRectangles( ev, currentpage, &(selection[currentpage]) );
1860                }
1861                HRGN selectRegion = rectsToRegion( currentpage, hps, selrects[ currentpage ] );
1862                GpiCombineRegion( hps, selectRegion, selectRegion, clearRegion, CRGN_XOR );
1863                GpiPaintRegion( hps, selectRegion );
1864                GpiDestroyRegion( hps, clearRegion );
1865                GpiDestroyRegion( hps, selectRegion );
1866
1867                WinReleasePS( hps );
1868            }
1869        }
1870        else if ( docDraggingStarted && ( doc != NULL ) )
1871        {
1872            WinSetPointer( HWND_DESKTOP, handClosedPtr );
1873            docDraggingEnd.x = xpos;
1874            docDraggingEnd.y = ypos;
1875
1876            SHORT hMove = docDraggingEnd.x - docDraggingStart.x;
1877            if ( abs( hMove ) > 5 )
1878            {
1879                horizScroll( hwnd, MPFROM2SHORT( hMove, SB_PAGEDRAG ) );
1880                docDraggingStart.x = xpos;
1881            }
1882
1883            SHORT vMove = docDraggingEnd.y - docDraggingStart.y;
1884            if ( abs( vMove ) > 5 )
1885            {
1886                vertScroll( hwnd, MPFROM2SHORT( vMove, SB_PAGEDRAG ) );
1887                docDraggingStart.y = ypos;
1888            }
1889            return TRUE;
1890        }
1891        else if ( links != NULL || inputFields != NULL )
1892        {
1893            long pg = currentpage;
1894            if ( isContinuous() ) {
1895                double tmp;
1896                pg = posToPagenum( ypos, &tmp );
1897            }
1898
1899            if ( links != NULL )
1900            {
1901                if ( ( pg != -1 ) && ( links[ pg ] != NULL ) )
1902                {
1903                    for ( int i = 0; i < links[ pg ]->_length; i++ )
1904                    {
1905                        RECTL r = {0};
1906                        docPosToWinPos( pg, &(links[ pg ]->_buffer[i].area), &r );
1907
1908                        POINTL ptl = { xpos, ypos };
1909                        if ( WinPtInRect( hab, &r, &ptl ) ) {
1910                            WinSetPointer( HWND_DESKTOP, handPtr );
1911                            return TRUE;
1912                        }
1913                    }
1914                }
1915            }
1916
1917            if ( inputFields != NULL )
1918            {
1919                if ( ( pg != -1 ) && ( inputFields[ pg ].fields != NULL ) )
1920                {
1921                    for ( int i = 0; i < inputFields[ pg ].fields->_length; i++ )
1922                    {
1923                        LuRectangle *&rect = inputFields[ pg ].cache[ i ].rect;
1924                        LuInputField_Type &type = inputFields[ pg ].cache[ i ].type;
1925                        if ( rect == NULL ) {
1926                            rect = inputFields[ pg ].fields->_buffer[ i ]->getRectangle( ev );
1927                            type = inputFields[ pg ].fields->_buffer[ i ]->getType( ev );
1928                        }
1929
1930                        RECTL r = {0};
1931                        docPosToWinPos( pg, rect, &r );
1932
1933                        POINTL ptl = { xpos, ypos };
1934                        if ( WinPtInRect( hab, &r, &ptl ) ) {
1935                            WinSetPointer( HWND_DESKTOP,
1936                                           type == LuInputField_Text ? textPtr : handPtr );
1937                            return TRUE;
1938                        }
1939                    }
1940                }
1941            }
1942        }
1943    }
1944    return FALSE;
1945}
1946
1947void DocumentViewer::zoomInOut( bool zoomIn )
1948{
1949    if ( ( doc != NULL ) && doc->isScalable( ev ) )
1950    {
1951        double z = getRealZoom() / 4;
1952        double zval = 0;
1953        if ( zoomIn ) {
1954            zval = getRealZoom() + z;
1955        } else {
1956            zval = getRealZoom() - z;
1957        }
1958        zval = (long)( zval * 20.0 ) / 20.0;   // Round to 0.05 (5%)
1959        if ( zval == getRealZoom() ) {
1960            zval += ( zoomIn ? 0.01 : -0.01 );
1961        }
1962        if ( zval > 0.1 ) {
1963            Lucide::setZoom( zval );
1964        }
1965    }
1966}
1967
1968// handles WM_BUTTON1CLICK
1969BOOL DocumentViewer::wmClick( HWND hwnd, SHORT xpos, SHORT ypos )
1970{
1971    if ( zoomMode )
1972    {
1973        zoomInOut( ( WinGetPhysKeyState( HWND_DESKTOP, 0x1d ) & 0x8000 ) == 0 );
1974        return TRUE;
1975    }
1976    else
1977    {
1978        if ( links == NULL && inputFields == NULL ) {
1979            return FALSE;
1980        }
1981
1982        long pg = currentpage;
1983        if ( isContinuous() ) {
1984            double tmp;
1985            pg = posToPagenum( ypos, &tmp );
1986        }
1987
1988        if ( links != NULL )
1989        {
1990            if ( ( pg != -1 ) && ( links[ pg ] != NULL ) )
1991            {
1992                for ( int i = 0; i < links[ pg ]->_length; i++ )
1993                {
1994                    RECTL r = {0};
1995                    docPosToWinPos( pg, &(links[ pg ]->_buffer[i].area), &r );
1996
1997                    POINTL ptl = { xpos, ypos };
1998                    if ( WinPtInRect( hab, &r, &ptl ) )
1999                    {
2000                        if ( links[ pg ]->_buffer[i].link.type == LU_LINK_TYPE_EXTERNAL_URI )
2001                        {
2002                            if ( !startBrowser( links[ pg ]->_buffer[i].link.uri ) )
2003                            {
2004                                char *m = newstrdupL( MSGS_ERROR_STARTING_BROWSER );
2005                                WinMessageBox( HWND_DESKTOP, hMainFrame, m,
2006                                           NULL, 0, MB_OK | MB_ICONEXCLAMATION | MB_MOVEABLE );
2007                                delete m;
2008                            }
2009                        }
2010                        else if ( links[ pg ]->_buffer[i].link.type == LU_LINK_TYPE_EXTERNAL_FILE )
2011                        {
2012                            char *uri = links[ pg ]->_buffer[i].link.uri;
2013                            if ( uri != NULL ) {
2014                                Lucide::newWindow( uri, true );
2015                            }
2016                        }
2017                        else if ( links[ pg ]->_buffer[i].link.type == LU_LINK_TYPE_TITLE )
2018                        {
2019                            char *title = links[ pg ]->_buffer[i].link.title;
2020                            if ( title == NULL ) {
2021                                title = "???";
2022                            }
2023                            WinMessageBox( HWND_DESKTOP, hMainFrame,
2024                                title, "?", 1, MB_OK | MB_INFORMATION | MB_MOVEABLE );
2025                        }
2026                        else if ( links[ pg ]->_buffer[i].link.type == LU_LINK_TYPE_PAGE )
2027                        {
2028                            goToPage( links[ pg ]->_buffer[i].link.page );
2029                        }
2030
2031                        return TRUE;
2032                    }
2033                }
2034            }
2035        }
2036
2037        if ( inputFields != NULL )
2038        {
2039            if ( ( pg != -1 ) && ( inputFields[ pg ].fields != NULL ) )
2040            {
2041                for ( int i = 0; i < inputFields[ pg ].fields->_length; i++ )
2042                {
2043                    LuRectangle *&rect = inputFields[ pg ].cache[ i ].rect;
2044                    LuInputField_Type &type = inputFields[ pg ].cache[ i ].type;
2045                    if ( rect == NULL ) {
2046                        rect = inputFields[ pg ].fields->_buffer[ i ]->getRectangle( ev );
2047                        type = inputFields[ pg ].fields->_buffer[ i ]->getType( ev );
2048                    }
2049
2050                    RECTL r = {0};
2051                    docPosToWinPos( pg, rect, &r );
2052
2053                    POINTL ptl = { xpos, ypos };
2054                    if ( WinPtInRect( hab, &r, &ptl ) )
2055                    {
2056                        LuInputField *field = inputFields[ pg ].fields->_buffer[ i ];
2057
2058                        switch ( type )
2059                        {
2060                            case LuInputField_Button:
2061                            {
2062                                LuInputButton *button = static_cast<LuInputButton *>( field );
2063                                LuInputButton_ButtonType type = button->getButtonType( ev );
2064                                if ( type == LuInputButton_Check || type == LuInputButton_Radio ) {
2065                                    boolean state = button->getState( ev );
2066                                    button->setState( ev, !state );
2067                                    WinInvalidateRect( hwnd, &r, FALSE );
2068                                }
2069                                break;
2070                            }
2071                            case LuInputField_Text:
2072                            {
2073                                LuInputText *text = static_cast<LuInputText *>( field );
2074                                showTextField( text, pg, &r );
2075                                break;
2076                            }
2077                            default:
2078                                break;
2079                        }
2080
2081                        return TRUE;
2082                    }
2083                }
2084            }
2085        }
2086    }
2087    return FALSE;
2088}
2089
2090// handles WM_BUTTON2CLICK
2091BOOL DocumentViewer::wmRightClick( HWND hwnd, SHORT xpos, SHORT ypos )
2092{
2093    if ( zoomMode )
2094    {
2095        zoomInOut( false );
2096        return TRUE;
2097    }
2098    return FALSE;
2099}
2100
2101BOOL DocumentViewer::wmChar( HWND hwnd, MPARAM mp1, MPARAM mp2 )
2102{
2103    USHORT fsflags = SHORT1FROMMP( mp1 );
2104    USHORT usch = SHORT1FROMMP( mp2 );
2105    USHORT usvk = SHORT2FROMMP( mp2 );
2106
2107    if ( ( fsflags & KC_VIRTUALKEY ) && !( fsflags & KC_KEYUP ) )
2108    {
2109        switch ( usvk )
2110        {
2111            case VK_UP:
2112                WinSendMsg( hwnd, WM_VSCROLL, MPVOID, MPFROM2SHORT( 0, SB_LINEUP ) );
2113                return TRUE;
2114
2115            case VK_DOWN:
2116                WinSendMsg( hwnd, WM_VSCROLL, MPVOID, MPFROM2SHORT( 0, SB_LINEDOWN ) );
2117                return TRUE;
2118
2119            case VK_PAGEUP:
2120                if ( fsflags & KC_CTRL )
2121                {
2122                    if ( fullscreen ) {
2123                        goToPage( 0 );
2124                    } else {
2125                        vertScroll( hwnd, MPFROM2SHORT( 0, SB_SLIDERPOSITION ) );
2126                    }
2127                }
2128                else
2129                {
2130                    bool dojump = ( !isContinuous() && ( sVscrollPos == 0 )
2131                                        && ( currentpage > 0 ) );
2132
2133                    if ( fullscreen || dojump ) {
2134                        goToPage( currentpage - 1 );
2135                        if ( dojump ) {
2136                            vertScroll( hwnd, MPFROM2SHORT( sVscrollMax, SB_SLIDERPOSITION ) );
2137                        }
2138                    } else {
2139                        WinSendMsg( hwnd, WM_VSCROLL, MPVOID, MPFROM2SHORT( 0, SB_PAGEUP ) );
2140                    }
2141                }
2142                return TRUE;
2143
2144            case VK_PAGEDOWN:
2145                if ( fsflags & KC_CTRL )
2146                {
2147                    if ( fullscreen ) {
2148                        goToPage( totalpages - 1 );
2149                    } else {
2150                        vertScroll( hwnd, MPFROM2SHORT( sVscrollMax, SB_SLIDERPOSITION ) );
2151                    }
2152                }
2153                else
2154                {
2155                    bool dojump = ( !isContinuous() && ( sVscrollPos == sVscrollMax ) );
2156
2157                    if ( fullscreen || dojump ) {
2158                        goToPage( currentpage + 1 );
2159                    } else {
2160                        WinSendMsg( hwnd, WM_VSCROLL, MPVOID, MPFROM2SHORT( 0, SB_PAGEDOWN ) );
2161                    }
2162                }
2163                return TRUE;
2164
2165            case VK_LEFT:
2166                WinSendMsg( hwnd, WM_HSCROLL, MPVOID, MPFROM2SHORT( 0, SB_LINELEFT ) );
2167                return TRUE;
2168
2169            case VK_RIGHT:
2170                WinSendMsg( hwnd, WM_HSCROLL, MPVOID, MPFROM2SHORT( 0, SB_LINERIGHT ) );
2171                return TRUE;
2172
2173            case VK_HOME:
2174                horizScroll( hwnd, MPFROM2SHORT( 0, SB_SLIDERPOSITION ) );
2175                return TRUE;
2176
2177            case VK_END:
2178                horizScroll( hwnd, MPFROM2SHORT( sHscrollMax, SB_SLIDERPOSITION ) );
2179                return TRUE;
2180
2181            case VK_ESC:
2182                if ( textField != NULL ) {
2183                    hideTextField( false );
2184                    return TRUE;
2185                }
2186                break;
2187
2188            case VK_ENTER:
2189            case VK_NEWLINE:
2190                if ( textField != NULL ) {
2191                    hideTextField();
2192                    return TRUE;
2193                }
2194                break;
2195        }
2196    }
2197
2198    //
2199    // In fullscreen mode accelerators not work, so we process these keys here.
2200    //
2201
2202    // Ctrl+L
2203    if ( ( fsflags & KC_CTRL ) && !( fsflags & KC_KEYUP ) && ( toupper( usch ) == 'L' ) )
2204    {
2205        Lucide::toggleFullscreen();
2206        return TRUE;
2207    }
2208
2209    // Esc && fullscreen
2210    if ( fullscreen && ( fsflags & KC_VIRTUALKEY ) && ( usvk == VK_ESC ) ) {
2211        Lucide::toggleFullscreen();
2212        return TRUE;
2213    }
2214
2215    // +
2216    if ( ( fsflags & KC_CHAR ) && !( fsflags & KC_KEYUP ) && ( usch == '+' ) ) {
2217        goToPage( currentpage + 1 );
2218        return TRUE;
2219    }
2220    // -
2221    if ( ( fsflags & KC_CHAR ) && !( fsflags & KC_KEYUP ) && ( usch == '-' ) ) {
2222        goToPage( currentpage - 1 );
2223        return TRUE;
2224    }
2225
2226    // Ctrl && zoomMode
2227    if ( ( fsflags & KC_VIRTUALKEY ) && ( usvk == VK_CTRL ) && zoomMode ) {
2228        wmMouseMove( hwnd, 0, 0 ); // to switch mouse pointer if in zoomMode
2229    }
2230
2231    return FALSE;
2232}
2233
2234// handles WM_BUTTON1DOWN
2235void DocumentViewer::wmButton1Down( HWND hwnd, SHORT xpos, SHORT ypos )
2236{
2237    if ( isContinuous() && ( doc != NULL ) )
2238    {
2239        // clear selection
2240        RECTL rcl = { 0 };
2241        WinQueryWindowRect( hwnd, &rcl );
2242        DrawAreas *areas = findDrawAreas( &rcl );
2243
2244        HPS hps = WinGetPS( hwnd );
2245        GpiSetMix( hps, FM_XOR );
2246        GpiSetColor( hps, CLR_YELLOW );
2247
2248        for ( int i = 0; i < areas->size(); i++ )
2249        {
2250            PageDrawArea *pda = &(*areas)[ i ];
2251
2252            HRGN clearRegion = rectsToRegion( pda->pagenum, hps, selrects[ pda->pagenum ] );
2253            GpiPaintRegion( hps, clearRegion );
2254            GpiDestroyRegion( hps, clearRegion );
2255        }
2256        WinReleasePS( hps );
2257        delete areas;
2258
2259        freeRects( selrects );
2260
2261        memset( selection, 0, sizeof( LuRectangle ) * totalpages );
2262    }
2263
2264    WinSetCapture( HWND_DESKTOP, hwnd );
2265    mousePressed = true;
2266    selectionStart.x = xpos;
2267    selectionStart.y = ypos;
2268}
2269
2270
2271// handles WM_BUTTON1UP
2272void DocumentViewer::wmButton1Up()
2273{
2274    WinSetCapture( HWND_DESKTOP, NULLHANDLE );
2275    mousePressed = false;
2276
2277    bool haveSelection = false;
2278    for ( long i = 0; i < totalpages; i++ ) {
2279        if ( selrects[ i ] != NULL ) {
2280            haveSelection = true;
2281            break;
2282        }
2283    }
2284
2285    Lucide::enableCopy( haveSelection );
2286}
2287
2288
2289// handles WM_BUTTON2DOWN
2290void DocumentViewer::wmButton2Down( HWND hwnd, SHORT xpos, SHORT ypos )
2291{
2292    if ( doc != NULL )
2293    {
2294        WinSetCapture( HWND_DESKTOP, hwnd );
2295        docDraggingStarted = true;
2296        docDraggingStart.x = xpos;
2297        docDraggingStart.y = ypos;
2298    }
2299}
2300
2301
2302// handles WM_BUTTON2UP
2303void DocumentViewer::wmButton2Up()
2304{
2305    if ( docDraggingStarted )
2306    {
2307        WinSetCapture( HWND_DESKTOP, NULLHANDLE );
2308        docDraggingStarted = false;
2309    }
2310}
2311
2312
2313// handles DM_DRAGOVER
2314MRESULT DocumentViewer::wmDragOver( PDRAGINFO dragInfo )
2315{
2316    PDRAGITEM dragItem;
2317    USHORT    usOp, usIndicator;
2318
2319    usOp = 0;
2320    usIndicator = DOR_NODROPOP;
2321
2322    DrgAccessDraginfo( dragInfo );
2323
2324    if ( dragInfo->usOperation == DO_DEFAULT )
2325    {
2326        dragItem = DrgQueryDragitemPtr( dragInfo, 0 );
2327        if ( DrgQueryDragitemCount( dragInfo ) == 1 )
2328        {
2329            if ( DrgVerifyRMF( dragItem, "DRM_OS2FILE", NULL ) &&
2330                 ( dragItem->hstrContainerName != NULLHANDLE ) &&
2331                 ( dragItem->hstrSourceName != NULLHANDLE ) )
2332            {
2333                char fname[ CCHMAXPATHCOMP ] = "";
2334                DrgQueryStrName( dragItem->hstrSourceName, CCHMAXPATHCOMP, fname );
2335                char *ext = strrchr( fname, '.' );
2336                if ( ext != NULL ) {
2337                    if ( pluginMan->createDocumentForExt( ext + 1, true ) != NULL ) {
2338                        usIndicator = DOR_DROP;
2339                        usOp = DO_UNKNOWN;
2340                    }
2341                }
2342            }
2343        }
2344    }
2345
2346    DrgFreeDraginfo( dragInfo );
2347    return MRFROM2SHORT( usIndicator, usOp );
2348}
2349
2350
2351// handles DM_DROP
2352void DocumentViewer::wmDrop( PDRAGINFO dragInfo )
2353{
2354    PDRAGITEM dragItem;
2355
2356    DrgAccessDraginfo( dragInfo );
2357    dragItem = DrgQueryDragitemPtr( dragInfo, 0 );
2358
2359    char fname[ CCHMAXPATHCOMP ] = "";
2360    char fpath[ CCHMAXPATH ] = "";
2361    DrgQueryStrName( dragItem->hstrSourceName, CCHMAXPATHCOMP, fname );
2362    DrgQueryStrName( dragItem->hstrContainerName, CCHMAXPATH, fpath );
2363    DrgFreeDraginfo( dragInfo );
2364
2365    strcat( fpath, fname );
2366    Lucide::loadDocument( fpath );
2367}
2368
2369// handles WM_TIMER
2370void DocumentViewer::wmTimer( USHORT idTimer )
2371{
2372    if ( idTimer == NO_MOUSE_TIMER )
2373    {
2374        secondsNoMouse++;
2375
2376        if ( fullscreen && !mouseHidden && ( secondsNoMouse > 3 ) )
2377        {
2378            WinShowPointer( HWND_DESKTOP, FALSE );
2379            mouseHidden = true;
2380        }
2381    }
2382}
2383
2384// handles WM_CONTROL
2385void DocumentViewer::wmControl( USHORT idControl, USHORT notifyCode, HWND hwndControl )
2386{
2387    // so far this comes only from hWndText
2388
2389    if ( notifyCode == EN_KILLFOCUS )
2390        hideTextField();
2391}
2392
2393// static, window procedure
2394MRESULT EXPENTRY DocumentViewer::docViewProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
2395{
2396    DocumentViewer *_this = (DocumentViewer *)WinQueryWindowULong( hwnd, QWL_USER );
2397
2398    switch ( msg )
2399    {
2400        case WM_CREATE:
2401        {
2402            // Save the mp1 into our user data so that subsequent calls have
2403            // access to the parent C++ object
2404            WinSetWindowULong( hwnd, QWL_USER, (ULONG)mp1 );
2405            _this = (DocumentViewer *)mp1;
2406            return (MRESULT)FALSE;
2407        }
2408
2409        case DM_DRAGOVER:
2410            return _this->wmDragOver( (PDRAGINFO)mp1 );
2411
2412        case DM_DROP:
2413            _this->wmDrop( (PDRAGINFO)mp1 );
2414            return (MRESULT)FALSE;
2415
2416        case WM_ERASEBACKGROUND:
2417            return (MRESULT)TRUE;
2418
2419        case WM_SIZE:
2420            _this->wmSize( hwnd, mp2 );
2421            return (MRESULT)FALSE;
2422
2423        case WM_HSCROLL:
2424            _this->horizScroll( hwnd, mp2 );
2425            break;
2426
2427        case WM_VSCROLL:
2428            _this->vertScroll( hwnd, mp2 );
2429            break;
2430
2431        case WM_PAINT:
2432            if ( _this->enableAsynchDraw ) {
2433                if ( _this->isContinuous() ) {
2434                    _this->wmPaintContAsynch( hwnd );
2435                } else {
2436                    _this->wmPaintAsynch( hwnd );
2437                }
2438            } else {
2439                if ( _this->isContinuous() ) {
2440                    _this->wmPaintCont( hwnd );
2441                } else {
2442                    _this->wmPaint( hwnd );
2443                }
2444            }
2445            return (MRESULT)FALSE;
2446
2447        case WM_BUTTON1DOWN:
2448            _this->wmButton1Down( hwnd, SHORT1FROMMP( mp1 ), SHORT2FROMMP( mp1 ) );
2449            break;
2450
2451        case WM_BUTTON1UP:
2452            _this->wmButton1Up();
2453            break;
2454
2455        case WM_BUTTON2DOWN:
2456            _this->wmButton2Down( hwnd, SHORT1FROMMP( mp1 ), SHORT2FROMMP( mp1 ) );
2457            break;
2458
2459        case WM_BUTTON2UP:
2460            _this->wmButton2Up();
2461            break;
2462
2463        case WM_MOUSEMOVE:
2464            if ( _this->wmMouseMove( hwnd, SHORT1FROMMP( mp1 ), SHORT2FROMMP( mp1 ) ) ) {
2465                return (MRESULT)TRUE;
2466            }
2467            break;
2468
2469        case WM_BUTTON1CLICK:
2470            if ( _this->wmClick( hwnd, SHORT1FROMMP( mp1 ), SHORT2FROMMP( mp1 ) ) ) {
2471                return (MRESULT)TRUE;
2472            }
2473            break;
2474
2475        case WM_BUTTON2CLICK:
2476            if ( _this->wmRightClick( hwnd, SHORT1FROMMP( mp1 ), SHORT2FROMMP( mp1 ) ) ) {
2477                return (MRESULT)TRUE;
2478            }
2479            break;
2480
2481        case WM_CHAR:
2482            if ( _this->wmChar( hwnd, mp1, mp2 ) ) {
2483                return (MRESULT)TRUE;
2484            }
2485            break;
2486
2487        case WM_FOCUSCHANGE:
2488            if ( SHORT1FROMMP( mp2 ) ) {
2489                Lucide::activeWindow = AwView;
2490            }
2491            break;
2492
2493        case WM_TIMER:
2494            _this->wmTimer( SHORT1FROMMP( mp1 ) );
2495            break;
2496
2497        case WM_CONTROL:
2498            _this->wmControl( SHORT1FROMMP( mp1 ), SHORT2FROMMP( mp1 ), HWNDFROMMP( mp2 ) );
2499            break;
2500    }
2501
2502    return WinDefWindowProc( hwnd, msg, mp1, mp2 );
2503}
2504
2505
2506// static, window procedure
2507MRESULT EXPENTRY DocumentViewer::docFrameProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
2508{
2509    DocumentViewer *_this = (DocumentViewer *)WinQueryWindowULong( hwnd, QWL_USER );
2510
2511    switch ( msg )
2512    {
2513        case WM_SYSCOMMAND:
2514            // Send WM_SYSCOMMAND messages to main frame
2515            WinSendMsg( _this->hMainFrame, WM_SYSCOMMAND, mp1, mp2 );
2516            return (MRESULT)FALSE;
2517    }
2518
2519    return _this->oldFrameProc( hwnd, msg, mp1, mp2 );
2520}
2521
Note: See TracBrowser for help on using the repository browser.