source: trunk/Lucide/gui/docViewer.cpp @ 491

Last change on this file since 491 was 491, checked in by dmik, 10 years ago

Fixed annoying flicker when switching pages in documents with dark page backgrounds.

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