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

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

Fixed: Launching Lucide in a background session (e.g. from a fullscreen session) would cause the document pages to have zero width and height and be effectively invisible until the Lucide window is resized. Closes #163.

File size: 88.9 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    BOOL sizeChanged = cxClient != SHORT1FROMMP( mp2 ) ||
916                       cyClient != SHORT2FROMMP( mp2 );
917
918    cxClient = SHORT1FROMMP( mp2 );
919    cyClient = SHORT2FROMMP( mp2 );
920
921    double relativeScrollPos = ( sVscrollMax == 0 ) ? 0 :
922                                    (double)sVscrollPos / (double)sVscrollMax;
923
924    adjustSize();
925
926    if ( sizeChanged || hpsBuffer == NULLHANDLE )
927    {
928        if ( ( hpsBuffer != NULLHANDLE ) && ( hdcBuffer != NULLHANDLE ) )
929        {
930            DestroyGraphicsBuffer( hpsBuffer, hdcBuffer );
931            hpsBuffer = hdcBuffer = NULLHANDLE;
932        }
933
934        HPS hps = WinGetPS( hwnd );
935        RECTL rectl = { 0, 0, cxClient, cyClient };
936        CreateGraphicsBuffer( hab, &rectl, hps, &hpsBuffer, &hdcBuffer );
937        WinReleasePS( hps );
938
939        GpiCreateLogColorTable( hpsBuffer, 0, LCOLF_RGB, 0, 0, NULL );
940    }
941
942    sHscrollMax = (SHORT)std::max( 0., ( isContinuous() ? fullwidth : width ) - cxClient );
943    sHscrollPos = std::min( sHscrollPos, sHscrollMax );
944
945    WinSendMsg( hWndHscroll, SBM_SETSCROLLBAR,
946                MPFROMSHORT(sHscrollPos), MPFROM2SHORT(0, sHscrollMax) );
947    WinSendMsg( hWndHscroll, SBM_SETTHUMBSIZE,
948                MPFROM2SHORT( cxClient, width ), MPVOID );
949    WinEnableWindow( hWndHscroll, (BOOL)( sHscrollMax != 0 ) );
950
951    VScrollStep = 1;
952    if ( isContinuous() )
953    {
954        realVscrollMax = std::max( 0., fullheight - cyClient );
955        ULONG ssize = realVscrollMax / VScrollStep;
956        while ( ssize > 32000 ) {
957            VScrollStep += LINE_HEIGHT;
958            ssize = realVscrollMax / VScrollStep;
959        }
960
961        sVscrollMax = (SHORT)ssize;
962        if ( realVscrollMax > ( sVscrollMax * VScrollStep ) ) {
963            sVscrollMax += 1;
964        }
965    }
966    else {
967        realVscrollMax = sVscrollMax = (SHORT)std::max( 0., height - cyClient );
968    }
969    sVscrollPos = std::min( sVscrollPos, sVscrollMax );
970
971    WinSendMsg( hWndVscroll, SBM_SETSCROLLBAR,
972                MPFROMSHORT(sVscrollPos), MPFROM2SHORT(0, sVscrollMax) );
973    if ( isContinuous() ) {
974        WinSendMsg( hWndVscroll, SBM_SETTHUMBSIZE,
975                    MPFROM2SHORT( cyClient/VScrollStep, fullheight/VScrollStep ), MPVOID );
976    }
977    else {
978        WinSendMsg( hWndVscroll, SBM_SETTHUMBSIZE,
979                    MPFROM2SHORT( cyClient, height ), MPVOID );
980    }
981    WinEnableWindow( hWndVscroll, (BOOL)( sVscrollMax != 0 ) );
982
983    SHORT realScrollPos = (SHORT)(sVscrollMax * relativeScrollPos);
984    vertScroll( hWndDoc, MPFROM2SHORT( realScrollPos, SB_SLIDERPOSITION ) );
985
986    positionTextField();
987}
988
989// returns true if subrect inside rect
990inline bool isSubrect( PRECTL rect, PRECTL subrect )
991{
992    return ( ( subrect->xLeft >= rect->xLeft ) &&
993             ( subrect->yBottom >= rect->yBottom ) &&
994             ( subrect->xRight <= rect->xRight ) &&
995             ( subrect->yTop <= rect->yTop ) );
996}
997
998// static method, cancels asynch rendering if abortAsynch is true
999long _System DocumentViewer::asynchCallbackFnAbort( void *data )
1000{
1001    return (long)(((DocumentViewer *)data)->abortAsynch);
1002}
1003
1004// static method, draws area during asynch rendering
1005long _System DocumentViewer::asynchCallbackFnDraw( void *data )
1006{
1007    DocumentViewer *_this = (DocumentViewer *)data;
1008    HPS hps = WinGetPS( _this->hWndDoc );
1009    if ( hps != NULLHANDLE )
1010    {
1011        PRECTL drawRect = &((*_this->drawareas)[_this->drawareaIndex].drawrect);
1012        LONG rclx = drawRect->xRight - drawRect->xLeft;
1013        LONG rcly = drawRect->yTop - drawRect->yBottom;
1014
1015        POINTL aptlPoints[4]={ drawRect->xLeft, drawRect->yBottom,
1016                               drawRect->xRight-1, drawRect->yTop-1,
1017                               0, 0, rclx, rcly };
1018
1019        LONG lRop = ROP_SRCCOPY;
1020        BITMAPINFO2 pbmi;
1021        pbmi.cbFix = 16L;
1022        pbmi.cx = rclx;
1023        pbmi.cy = rcly;
1024        pbmi.cPlanes = 1;
1025        pbmi.cBitCount = _this->bpp * 8;
1026        GpiDrawBits( hps, _this->pixbuf->getDataPtr( ev ), &pbmi, 4L,
1027                     aptlPoints, lRop, BBO_IGNORE );
1028
1029        WinReleasePS( hps );
1030    }
1031    return 0;
1032}
1033
1034// static method, thread for asynchronous rendering
1035void DocumentViewer::drawthread( void *p )
1036{
1037    DosSetPriority( PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MINIMUM, 0 );
1038    DocumentViewer *_this = (DocumentViewer *)p;
1039
1040    HAB thab = WinInitialize( 0 );
1041    HMQ thmq = WinCreateMsgQueue( thab, 0 );
1042
1043    ULONG postCnt;
1044    while ( !_this->termdraw )
1045    {
1046        DosWaitEventSem( _this->haveDraw, SEM_INDEFINITE_WAIT );
1047        DosResetEventSem( _this->haveDraw, &postCnt );
1048        _this->abortAsynch = false;
1049
1050        if ( ( _this->drawareas != NULL ) && ( _this->doc != NULL ) )
1051        {
1052            DosRequestMutexSem( _this->todrawAccess, SEM_INDEFINITE_WAIT );
1053
1054            for ( _this->drawareaIndex = 0;
1055                  _this->drawareaIndex < _this->drawareas->size();
1056                  _this->drawareaIndex++ )
1057            {
1058                long renderErrorCode = LU_RERR_NO_ERROR;
1059                char *renderError = NULL;
1060
1061                PageDrawArea *pda = &(*_this->drawareas)[ _this->drawareaIndex ];
1062
1063                LONG rclx = pda->drawrect.xRight - pda->drawrect.xLeft;
1064                LONG rcly = pda->drawrect.yTop - pda->drawrect.yBottom;
1065                _this->pixbuf = new LuPixbuf( ev, rclx, rcly, _this->bpp );
1066                _this->doc->renderPageToPixbufAsynch( ev, pda->pagenum,
1067                       pda->startpos.x, pda->startpos.y, rclx, rcly, _this->realzoom,
1068                       _this->rotation, _this->pixbuf,
1069                       (LuDocument_asynchCallbackFn)asynchCallbackFnDraw,
1070                       (LuDocument_asynchCallbackFn)asynchCallbackFnAbort, p,
1071                       &renderErrorCode, &renderError );
1072                delete _this->pixbuf;
1073                _this->pixbuf = NULL;
1074
1075                if ( renderErrorCode != LU_RERR_NO_ERROR )
1076                {
1077                    // TODO: display error/warning (renderErrorCode, renderError)
1078
1079                    // ...
1080
1081                    if ( renderError != NULL ) {
1082                        SOMFree( renderError );
1083                    }
1084                }
1085
1086                if ( _this->abortAsynch ) {
1087                    break;  // TODO: remove completed areas from drawareas (?)
1088                }
1089            }
1090
1091            if ( !_this->abortAsynch )
1092            {
1093                HPS hps = WinGetPS( _this->hWndDoc );
1094                if ( hps != NULLHANDLE ) {
1095                    for ( int i = 0; i < _this->drawareas->size(); i++ )
1096                    {
1097                        PageDrawArea *pda = &(*_this->drawareas)[ i ];
1098
1099                        _this->drawSelection( pda->pagenum, hps, &pda->drawrect );
1100                        _this->drawFound( pda->pagenum, hps, &pda->drawrect );
1101                    }
1102                    WinReleasePS( hps );
1103                }
1104                WinSetRectEmpty( thab, &_this->savedRcl );
1105                delete _this->drawareas;
1106                _this->drawareas = NULL;
1107            }
1108
1109            DosReleaseMutexSem( _this->todrawAccess );
1110        }
1111    }
1112    WinDestroyMsgQueue( thmq );
1113    WinTerminate( thab );
1114    _endthread();
1115}
1116
1117// handles WM_PAINT if single-page asynchronous rendering used
1118// posts events to drawthread
1119void DocumentViewer::wmPaintAsynch( HWND hwnd )
1120{
1121    LONG xPos = 0, yPos = 0;
1122    RECTL rclPage = { 0 };
1123    RECTL rcl;
1124    HPS hps = WinBeginPaint( hwnd, 0L, &rcl );
1125
1126#if FILL_PAGE_BACKGROUND
1127
1128    WinFillRect( hpsBuffer, &rcl, BORDER_COLOR );
1129    if ( doc != NULL )
1130    {
1131        if ( width < cxClient ) {
1132            xPos = ( cxClient - width ) / 2;
1133        }
1134        if ( height < cyClient ) {
1135            yPos = ( cyClient - height ) / 2;
1136        }
1137
1138        rclPage.xLeft   = xPos;
1139        rclPage.yBottom = yPos;
1140        rclPage.xRight  = width + xPos;
1141        rclPage.yTop    = height + yPos;
1142        WinFillRect( hpsBuffer, &rclPage, PAGEBACK_COLOR );
1143    }
1144    BlitGraphicsBuffer( hps, hpsBuffer, &rcl );
1145
1146#else
1147
1148    GpiCreateLogColorTable( hps, 0, LCOLF_RGB, 0, 0, NULL );
1149
1150    if ( doc != NULL )
1151    {
1152        if ( width < cxClient ) {
1153            xPos = ( cxClient - width ) / 2;
1154        }
1155        if ( height < cyClient ) {
1156            yPos = ( cyClient - height ) / 2;
1157        }
1158
1159        rclPage.xLeft   = xPos;
1160        rclPage.yBottom = yPos;
1161        rclPage.xRight  = width + xPos;
1162        rclPage.yTop    = height + yPos;
1163
1164        HRGN hrgnBorder = GpiCreateRegion( hps, 1, &rcl );
1165        HRGN hrgnPage = GpiCreateRegion( hps, 1, &rclPage );
1166        LONG res = GpiCombineRegion( hps, hrgnBorder, hrgnBorder, hrgnPage,
1167                                     CRGN_DIFF );
1168        if ( res != RGN_NULL )
1169        {
1170            GpiSetColor( hps, BORDER_COLOR );
1171            GpiPaintRegion( hps, hrgnBorder );
1172        }
1173        GpiDestroyRegion( hps, hrgnPage );
1174        GpiDestroyRegion( hps, hrgnBorder );
1175    }
1176    else
1177        WinFillRect( hps, &rcl, BORDER_COLOR );
1178
1179#endif
1180
1181    WinEndPaint( hps );
1182
1183    if ( doc != NULL )
1184    {
1185        RECTL rclDraw = { 0 };
1186        if ( WinIntersectRect( hab, &rclDraw, &rcl, &rclPage ) )
1187        {
1188            if ( ( drawareas != NULL ) && ( drawareas->size() > 0 ) ) {
1189                if ( isSubrect( &((*drawareas)[0].drawrect), &rclDraw ) &&
1190                     ( sVscrollInc == 0 ) && ( sHscrollInc == 0 ) ) {
1191                    return;
1192                }
1193            }
1194
1195            abortAsynch = true;
1196            DosRequestMutexSem( todrawAccess, SEM_INDEFINITE_WAIT );
1197
1198            if ( drawareas == NULL ) {
1199                drawareas = new DrawAreas;
1200            }
1201            if ( drawareas->size() == 0 ) {
1202                PageDrawArea pda;
1203                memset( &pda, 0, sizeof( pda ) );
1204                pda.pagenum = currentpage;
1205                drawareas->push_back( pda );
1206            }
1207
1208            PageDrawArea *ppda = &((*drawareas)[0]);
1209
1210            if ( !WinIsRectEmpty( hab, &ppda->drawrect ) )
1211            {
1212                if ( sVscrollInc > 0 ) {
1213                    ppda->drawrect.yTop    += sVscrollInc;
1214                } else if ( sVscrollInc < 0 ) {
1215                    ppda->drawrect.yBottom += sVscrollInc;
1216                }
1217                if ( sHscrollInc > 0 ) {
1218                    ppda->drawrect.xLeft  -= sHscrollInc;
1219                } else if ( sHscrollInc < 0 ) {
1220                    ppda->drawrect.xRight -= sHscrollInc;
1221                }
1222            }
1223            WinUnionRect( hab, &ppda->drawrect, &ppda->drawrect, &rclDraw );
1224            ppda->startpos.x = sHscrollPos + ppda->drawrect.xLeft - xPos;
1225            ppda->startpos.y = ( yPos > 0 ) ? rclPage.yTop - ppda->drawrect.yTop :
1226                    ( cyClient - ppda->drawrect.yTop ) + sVscrollPos;
1227
1228            DosReleaseMutexSem( todrawAccess );
1229            DosPostEventSem( haveDraw );
1230        }
1231    }
1232}
1233
1234
1235// handles WM_PAINT if continuous asynchronous rendering used
1236void DocumentViewer::wmPaintContAsynch( HWND hwnd )
1237{
1238    RECTL rcl, rclWin, rclDraw = { 0 };
1239    HPS hps = WinBeginPaint( hwnd, 0L, &rcl );
1240
1241#if FILL_PAGE_BACKGROUND
1242
1243    WinFillRect( hpsBuffer, &rcl, BORDER_COLOR );
1244
1245    if ( doc != NULL )
1246    {
1247        long foundpage = -1;
1248        double pageRest;
1249        for ( LONG i = rcl.yTop; i >= rcl.yBottom; i-- )
1250        {
1251            pageRest = 0;
1252            long pg = posToPagenum( i, &pageRest );
1253            if ( ( pg != foundpage ) && ( pg != -1 ) )
1254            {
1255                RECTL rclPage = { 0 };
1256                LuRectangle lr = { 0, 0,
1257                    isRotated() ? (pagesizes[ pg ].y - 1) : (pagesizes[ pg ].x - 1),
1258                    isRotated() ? (pagesizes[ pg ].x - 1) : (pagesizes[ pg ].y - 1) };
1259                docPosToWinPos( pg, &lr, &rclPage );
1260                WinFillRect( hpsBuffer, &rclPage, PAGEBACK_COLOR );
1261                foundpage = pg;
1262                i -= pageRest;
1263            }
1264        }
1265    }
1266
1267    BlitGraphicsBuffer( hps, hpsBuffer, &rcl );
1268
1269#else
1270
1271    GpiCreateLogColorTable( hps, 0, LCOLF_RGB, 0, 0, NULL );
1272
1273    if ( doc != NULL )
1274    {
1275        HRGN hrgnBorder = GpiCreateRegion( hps, 1, &rcl );
1276        HRGN hrgnPage = GpiCreateRegion( hps, 0, NULL );
1277        LONG res = RGN_NULL;
1278
1279        long foundpage = -1;
1280        double pageRest;
1281        for ( LONG i = rcl.yTop; i >= rcl.yBottom; i-- )
1282        {
1283            pageRest = 0;
1284            long pg = posToPagenum( i, &pageRest );
1285            if ( ( pg != foundpage ) && ( pg != -1 ) )
1286            {
1287                RECTL rclPage = { 0 };
1288                LuRectangle lr = { 0, 0,
1289                    isRotated() ? (pagesizes[ pg ].y - 1) : (pagesizes[ pg ].x - 1),
1290                    isRotated() ? (pagesizes[ pg ].x - 1) : (pagesizes[ pg ].y - 1) };
1291                docPosToWinPos( pg, &lr, &rclPage );
1292                GpiSetRegion( hps, hrgnPage, 1, &rclPage );
1293                res = GpiCombineRegion( hps, hrgnBorder, hrgnBorder, hrgnPage,
1294                                        CRGN_DIFF );
1295                foundpage = pg;
1296                i -= pageRest;
1297            }
1298        }
1299
1300        if ( res != RGN_NULL )
1301        {
1302            GpiSetColor( hps, BORDER_COLOR );
1303            GpiPaintRegion( hps, hrgnBorder );
1304        }
1305        GpiDestroyRegion( hps, hrgnPage );
1306        GpiDestroyRegion( hps, hrgnBorder );
1307    }
1308    else
1309        WinFillRect( hps, &rcl, BORDER_COLOR );
1310
1311#endif
1312
1313    WinEndPaint( hps );
1314
1315    if ( doc != NULL )
1316    {
1317        if ( isSubrect( &savedRcl, &rcl ) && ( sVscrollInc == 0 ) && ( sHscrollInc == 0 ) ) {
1318            return;
1319        }
1320
1321        abortAsynch = true;
1322        DosRequestMutexSem( todrawAccess, SEM_INDEFINITE_WAIT );
1323
1324        WinQueryWindowRect( hwnd, &rclWin );
1325        WinUnionRect( hab, &rcl, &rcl, &savedRcl );
1326
1327        if ( sVscrollInc > 0 ) {
1328            rcl.yTop    += sVscrollInc;
1329        } else if ( sVscrollInc < 0 ) {
1330            rcl.yBottom += sVscrollInc;
1331        }
1332        if ( sHscrollInc > 0 ) {
1333            rcl.xLeft  -= sHscrollInc;
1334        } else if ( sHscrollInc < 0 ) {
1335            rcl.xRight -= sHscrollInc;
1336        }
1337
1338        WinIntersectRect( hab, &rclDraw, &rcl, &rclWin );
1339        WinCopyRect( hab, &rcl, &rclDraw );
1340        WinCopyRect( hab, &savedRcl, &rcl );
1341
1342        delete drawareas;
1343        drawareas = findDrawAreas( &rcl );
1344
1345        for ( int i = 0; i < drawareas->size(); i++ )
1346        {
1347            PageDrawArea *pda = &(*drawareas)[ i ];
1348
1349            // load links for page if not loaded before
1350            if ( links != NULL ) {
1351                if ( links[ pda->pagenum ] == NULL ) {
1352                    links[ pda->pagenum ] = doc->getLinkMapping( ev, pda->pagenum );
1353                }
1354            }
1355
1356            // load input fields for page if not loaded before
1357            if ( inputFields != NULL ) {
1358                if ( inputFields[ pda->pagenum ].fields == NULL ) {
1359                    inputFields[ pda->pagenum ].fields = doc->getInputFields( ev, pda->pagenum );
1360                    unsigned long len = inputFields[ pda->pagenum ].fields->_length;
1361                    inputFields[ pda->pagenum ].cache = new PageInputFields::Cache[ len ];
1362                    memset( inputFields[ pda->pagenum ].cache, 0, sizeof( PageInputFields::Cache ) * len );
1363                }
1364            }
1365        }
1366        DosReleaseMutexSem( todrawAccess );
1367        DosPostEventSem( haveDraw );
1368
1369        determineCurrentPage();
1370    }
1371}
1372
1373
1374// handles WM_PAINT if single-page synchronous rendering used
1375void DocumentViewer::wmPaint( HWND hwnd )
1376{
1377    RECTL rcl;
1378    HPS hps = WinBeginPaint( hwnd, 0L, &rcl );
1379    WinFillRect( hpsBuffer, &rcl, BORDER_COLOR );
1380
1381    if ( doc != NULL )
1382    {
1383        LONG xPos = 0, yPos = 0;
1384        if ( width < cxClient ) {
1385            xPos = ( cxClient - width ) / 2;
1386        }
1387        if ( height < cyClient ) {
1388            yPos = ( cyClient - height ) / 2;
1389        }
1390
1391        RECTL rclPage = { xPos, yPos, width + xPos, height + yPos };
1392        RECTL rclDraw = { 0 };
1393        if ( WinIntersectRect( hab, &rclDraw, &rcl, &rclPage ) )
1394        {
1395            spos_x = sHscrollPos + rclDraw.xLeft - xPos;
1396            spos_y = ( height < cyClient ) ? rclPage.yTop - rclDraw.yTop : (cyClient - rclDraw.yTop) + sVscrollPos;
1397            LONG rclx = rclDraw.xRight - rclDraw.xLeft;
1398            LONG rcly = rclDraw.yTop - rclDraw.yBottom;
1399
1400            long renderErrorCode = LU_RERR_NO_ERROR;
1401            char *renderError = NULL;
1402
1403            if ( drawPS )
1404            {
1405                doc->renderPageToPS( ev, currentpage, spos_x, spos_y, rclx, rcly,
1406                                     realzoom, rotation, hpsBuffer, &rclDraw,
1407                                     &renderErrorCode, &renderError );
1408            }
1409            else
1410            {
1411                pixbuf = new LuPixbuf( ev, rclx, rcly, bpp );
1412                POINTL aptlPoints[4]={ rclDraw.xLeft, rclDraw.yBottom,
1413                                       rclDraw.xRight-1, rclDraw.yTop-1,
1414                                       0, 0, rclx, rcly };
1415
1416                doc->renderPageToPixbuf( ev, currentpage, spos_x, spos_y,
1417                                         rclx, rcly, realzoom, rotation, pixbuf,
1418                                         &renderErrorCode, &renderError );
1419                LONG lRop = ROP_SRCCOPY;
1420                BITMAPINFO2 pbmi;
1421                pbmi.cbFix = 16L;
1422                pbmi.cx = rclx;
1423                pbmi.cy = rcly;
1424                pbmi.cPlanes = 1;
1425                pbmi.cBitCount = bpp * 8;
1426                GpiDrawBits( hpsBuffer, pixbuf->getDataPtr( ev ), &pbmi, 4L,
1427                             aptlPoints, lRop, BBO_IGNORE );
1428                delete pixbuf;
1429                pixbuf = NULL;
1430            }
1431
1432            if ( renderErrorCode != LU_RERR_NO_ERROR )
1433            {
1434                // TODO: display error/warning (renderErrorCode, renderError)
1435
1436                // ...
1437
1438                if ( renderError != NULL ) {
1439                    SOMFree( renderError );
1440                }
1441            }
1442
1443            drawSelection( currentpage, hpsBuffer, &rclDraw );
1444            drawFound( currentpage, hpsBuffer, &rclDraw );
1445        }
1446    }
1447    BlitGraphicsBuffer( hps, hpsBuffer, &rcl );
1448    WinEndPaint( hps );
1449}
1450
1451
1452// founds number of page at specified vertical position
1453// for continuous view only
1454long DocumentViewer::posToPagenum( LONG yPosWin, double *pageRest )
1455{
1456    double yPos = ( cyClient - yPosWin ) + ( sVscrollPos * VScrollStep );
1457    double pgstart = 0;
1458    double pgend = 0;
1459    for ( long i = 0; i < totalpages; i++ )
1460    {
1461        pgend = pgstart + ( pagesizes[ i ].y * realzoom );
1462        if ( ( yPos >= pgstart ) && ( yPos < pgend ) ) {
1463            *pageRest = pgend - yPos;
1464            return i;
1465        }
1466        pgstart = ( pgend + VERT_SPACE );
1467    }
1468    return -1;
1469}
1470
1471// founds vertical position of specified
1472// for continuous view only
1473double DocumentViewer::pagenumToPos( long pagenum )
1474{
1475    double ypos = 0;
1476    for ( long i = 0; i < pagenum; i++ ) {
1477        ypos += pagesizes[ i ].y;
1478    }
1479    return ( ( ypos * realzoom ) + ( pagenum * VERT_SPACE ) );
1480}
1481
1482void DocumentViewer::showTextField( long page, long index, PRECTL r )
1483{
1484    // save the previous changes if any
1485    if ( textField != NULL )
1486        hideTextField();
1487
1488    textField =
1489        static_cast<LuInputText *>( inputFields[ page ].fields->_buffer[ index ] );
1490    textFieldPage = page;
1491    textFieldIndex = index;
1492
1493    positionTextField( r );
1494
1495    HWND hwnd;
1496    if ( textField->isMultiLine( ev ) ) {
1497        hwnd = hWndMLE;
1498        WinSendMsg( hwnd, MLM_SETTEXTLIMIT, MPFROMLONG( 65520 ), NULL );
1499    } else {
1500        hwnd = hWndEntry;
1501        // @todo uncomment this once it returns anything useful
1502        // int maxLen = textField->getMaximumLength( ev );
1503        WinSendMsg( hwnd, EM_SETTEXTLIMIT, MPFROMLONG( 65520 ), NULL );
1504    }
1505
1506    const char *contents = textField->getContents( ev );
1507    char *str = uniUtf8ToSys( contents, NULL, NULL );
1508    WinSetWindowText( hwnd, str );
1509    delete[] str;
1510
1511    WinShowWindow( hwnd, TRUE );
1512    WinSetFocus( HWND_DESKTOP, hwnd );
1513}
1514
1515void DocumentViewer::positionTextField( PRECTL r )
1516{
1517    if ( textField == NULL )
1518        return;
1519
1520    RECTL r2;
1521    if ( r == NULL ) {
1522        LuRectangle *rect = textField->getRectangle( ev );
1523        docPosToWinPos( textFieldPage, rect, &r2 );
1524    } else {
1525        r2 = *r;
1526    }
1527
1528    static LONG ulDpi = 0;
1529    if ( ulDpi == 0 ) {
1530        // DPI is constant beteen reboots
1531        HPS hps = WinGetScreenPS( HWND_DESKTOP );
1532        DevQueryCaps( GpiQueryDevice( hps ), CAPS_HORIZONTAL_FONT_RES,
1533                      1, &ulDpi );
1534        WinReleasePS( hps );
1535    }
1536
1537    LONG points = -1;
1538    HWND hwnd;
1539    if ( textField->isMultiLine( ev ) ) {
1540        hwnd = hWndMLE;
1541        // the font size for multi-line text does not change
1542    } else {
1543        hwnd = hWndEntry;
1544        // reduce the rectangle to compensate for the border
1545        r2.xLeft += 3;
1546        r2.yBottom += 3;
1547        r2.xRight -= 3;
1548        r2.yTop -= 3;
1549        // set the font size to match the field height
1550        points = ( r2.yTop - r2.yBottom ) * 72 / 120 - 2;
1551        if ( points < 1 )
1552            points = 1;
1553    }
1554
1555    if (points >= 0 ) {
1556        char font[ 32 ];
1557        sprintf( font, "%d.Helvetica Bold", points );
1558        WinSetPresParam( hWndEntry, PP_FONTNAMESIZE, strlen( font ) + 1, font );
1559    }
1560
1561    WinSetWindowPos( hwnd, HWND_TOP,
1562                     r2.xLeft, r2.yBottom,
1563                     r2.xRight - r2.xLeft,
1564                     r2.yTop - r2.yBottom,
1565                     SWP_MOVE | SWP_SIZE | SWP_ZORDER );
1566}
1567
1568void DocumentViewer::hideTextField( bool apply, PPOINTL ptl )
1569{
1570    if ( textField != NULL ) {
1571        HWND hwnd = textField->isMultiLine( ev ) ? hWndMLE : hWndEntry;
1572        SWP swp;
1573        WinQueryWindowPos( hwnd, &swp );
1574        RECTL r = { swp.x, swp.y, swp.x + swp.cx, swp.y + swp.cy };
1575        if ( ptl && WinPtInRect( hab, &r, ptl ) ) {
1576            // don't hide if the point is inside the field
1577            return;
1578        }
1579        if ( apply ) {
1580            LONG len = WinQueryWindowTextLength( hwnd );
1581            char *str = new char [ len + 1 ];
1582            *str = 0;
1583            WinQueryWindowText( hwnd, len + 1, str );
1584            str[ len ] = 0;
1585            char *contents = uniSysToUtf8( str, NULL, NULL );
1586            char *oldContents = textField->getContents( ev );
1587            if ( (oldContents == NULL && contents != NULL) ||
1588                 (oldContents != NULL && contents == NULL) ||
1589                 strcmp( textField->getContents( ev ), contents ) ) {
1590                // only modify the field if it differs
1591                textField->setContents( ev, contents );
1592                inputFields[ textFieldPage ].cache[ textFieldIndex ].modified = true;
1593            }
1594            delete[] contents;
1595            delete[] str;
1596        }
1597        textField = NULL;
1598        WinShowWindow( hwnd, FALSE );
1599        // repaint little bit more (rounding errors)
1600        r.xLeft -= 1; r.yBottom -= 1;
1601        r.xRight += 1; r.yTop += 1;
1602        WinInvalidateRect( hWndDoc, &r, TRUE );
1603        // remove the focus from the window we hid
1604        WinSetFocus( HWND_DESKTOP, hWndDoc );
1605    }
1606}
1607
1608// founds pages and it's areas to draw
1609// for continuous view only
1610DrawAreas *DocumentViewer::findDrawAreas( PRECTL r )
1611{
1612    DrawAreas *areas = new DrawAreas;
1613    if ( doc != NULL )
1614    {
1615        long foundpage = -1;
1616        double pageRest;
1617        for ( LONG i = r->yTop; i >= r->yBottom; i-- )
1618        {
1619            pageRest = 0;
1620            long pg = posToPagenum( i, &pageRest );
1621            if ( ( pg != foundpage ) && ( pg != -1 ) )
1622            {
1623                double w = pagesizes[ pg ].x * realzoom;
1624
1625                PageDrawArea pda = {0};
1626                pda.pagenum = pg;
1627
1628                LONG xPos = 0;
1629                if ( w < cxClient ) {
1630                    xPos = ( cxClient - w ) / 2;
1631                }
1632                RECTL rclPage = { 0 };
1633                LuRectangle lr = { 0, 0,
1634                    isRotated() ? (pagesizes[ pg ].y - 1) : (pagesizes[ pg ].x - 1),
1635                    isRotated() ? (pagesizes[ pg ].x - 1) : (pagesizes[ pg ].y - 1) };
1636                docPosToWinPos( pg, &lr, &rclPage );
1637                if ( WinIntersectRect( hab, &pda.drawrect, r, &rclPage ) )
1638                {
1639                    pda.startpos.x = sHscrollPos + pda.drawrect.xLeft - xPos;
1640                    pda.startpos.y = ( pagesizes[ pg ].y * realzoom ) - pageRest;
1641                    areas->push_back( pda );
1642                }
1643                foundpage = pg;
1644                i -= pageRest;
1645            }
1646        }
1647    }
1648
1649    return areas;
1650}
1651
1652
1653// found current page in continuous view mode.
1654// it's a page which occupes a most larger area in the window.
1655void DocumentViewer::determineCurrentPage()
1656{
1657    RECTL rcl = { 0 };
1658    WinQueryWindowRect( hWndDoc, &rcl );
1659    DrawAreas *areas = findDrawAreas( &rcl );
1660    long pg = 0;
1661    long sz = 0;
1662    for ( int i = 0; i < areas->size(); i++ )
1663    {
1664        PageDrawArea *pda = &(*areas)[ i ];
1665        long pgsz = pda->drawrect.yTop - pda->drawrect.yBottom;
1666        if ( pgsz > sz ) {
1667            pg = pda->pagenum;
1668            sz = pgsz;
1669        }
1670    }
1671    delete areas;
1672
1673    if ( pg != currentpage ) {
1674        currentpage = pg;
1675        Lucide::checkNavigationMenus();
1676    }
1677}
1678
1679
1680// handles WM_PAINT if continuous synchronous rendering used
1681void DocumentViewer::wmPaintCont( HWND hwnd )
1682{
1683    RECTL rcl;
1684    HPS hps = WinBeginPaint( hwnd, 0L, &rcl );
1685    WinFillRect( hpsBuffer, &rcl, BORDER_COLOR );
1686
1687    if ( doc != NULL )
1688    {
1689        delete drawareas;
1690        drawareas = findDrawAreas( &rcl );
1691
1692        for ( int i = 0; i < drawareas->size(); i++ )
1693        {
1694            PageDrawArea *pda = &(*drawareas)[ i ];
1695
1696            // load links for page if not loaded before
1697            if ( links != NULL ) {
1698                if ( links[ pda->pagenum ] == NULL ) {
1699                    links[ pda->pagenum ] = doc->getLinkMapping( ev, pda->pagenum );
1700                }
1701            }
1702
1703            // load input fields for page if not loaded before
1704            if ( inputFields != NULL ) {
1705                if ( inputFields[ pda->pagenum ].fields == NULL ) {
1706                    inputFields[ pda->pagenum ].fields = doc->getInputFields( ev, pda->pagenum );
1707                    unsigned long len = inputFields[ pda->pagenum ].fields->_length;
1708                    inputFields[ pda->pagenum ].cache = new PageInputFields::Cache[ len ];
1709                    memset( inputFields[ pda->pagenum ].cache, 0, sizeof( PageInputFields::Cache ) * len );
1710                }
1711            }
1712
1713            spos_x = pda->startpos.x;
1714            spos_y = pda->startpos.y;
1715            LONG rclx = pda->drawrect.xRight - pda->drawrect.xLeft;
1716            LONG rcly = pda->drawrect.yTop - pda->drawrect.yBottom;
1717
1718            long renderErrorCode = LU_RERR_NO_ERROR;
1719            char *renderError = NULL;
1720
1721            if ( drawPS )
1722            {
1723                doc->renderPageToPS( ev, pda->pagenum, spos_x, spos_y, rclx, rcly,
1724                                     realzoom, rotation, hpsBuffer, &(pda->drawrect),
1725                                     &renderErrorCode, &renderError );
1726            }
1727            else
1728            {
1729                pixbuf = new LuPixbuf( ev, rclx, rcly, bpp );
1730                POINTL aptlPoints[4]={ pda->drawrect.xLeft, pda->drawrect.yBottom,
1731                                       pda->drawrect.xRight-1, pda->drawrect.yTop-1,
1732                                       0, 0, rclx, rcly };
1733                doc->renderPageToPixbuf( ev, pda->pagenum, spos_x, spos_y,
1734                                         rclx, rcly, realzoom, rotation, pixbuf,
1735                                         &renderErrorCode, &renderError );
1736                LONG lRop = ROP_SRCCOPY;
1737                BITMAPINFO2 pbmi;
1738                pbmi.cbFix = 16L;
1739                pbmi.cx = rclx;
1740                pbmi.cy = rcly;
1741                pbmi.cPlanes = 1;
1742                pbmi.cBitCount = bpp * 8;
1743                GpiDrawBits( hpsBuffer, pixbuf->getDataPtr( ev ), &pbmi, 4L,
1744                             aptlPoints, lRop, BBO_IGNORE );
1745                delete pixbuf;
1746                pixbuf = NULL;
1747            }
1748
1749            if ( renderErrorCode != LU_RERR_NO_ERROR )
1750            {
1751                // TODO: display error/warning (renderErrorCode, renderError)
1752
1753                // ...
1754
1755                if ( renderError != NULL ) {
1756                    SOMFree( renderError );
1757                }
1758            }
1759
1760            drawSelection( pda->pagenum, hpsBuffer, &pda->drawrect );
1761            drawFound( pda->pagenum, hpsBuffer, &pda->drawrect );
1762        }
1763        delete drawareas;
1764        drawareas = NULL;
1765    }
1766    BlitGraphicsBuffer( hps, hpsBuffer, &rcl );
1767    WinEndPaint( hps );
1768
1769    if ( doc != NULL ) {
1770        determineCurrentPage();
1771    }
1772}
1773
1774
1775// Rotates document rectangle
1776void DocumentViewer::rotateRectangle( long pagenum, LuRectangle *r )
1777{
1778    double tmp_x1 = r->x1;
1779    double tmp_y1 = r->y1;
1780    double tmp_x2 = r->x2;
1781    double tmp_y2 = r->y2;
1782
1783    double w = pagesizes[ pagenum ].x;
1784    double h = pagesizes[ pagenum ].y;
1785
1786    if ( rotation == 90 ) {
1787        r->x1 = tmp_y1;
1788        r->y1 = w - tmp_x1;
1789        r->x2 = tmp_y2;
1790        r->y2 = w - tmp_x2;
1791    }
1792    else if ( rotation == 180 )
1793    {
1794        r->x1 = w - tmp_x2;
1795        r->y1 = h - tmp_y2;
1796        r->x2 = w - tmp_x1;
1797        r->y2 = h - tmp_y1;
1798    }
1799    else if ( rotation == 270 )
1800    {
1801        r->x1 = h - tmp_y1;
1802        r->y1 = tmp_x1;
1803        r->x2 = h - tmp_y2;
1804        r->y2 = tmp_x2;
1805    }
1806
1807    if ( r->x1 > r->x2 ) {
1808        double tmp = r->x1;
1809        r->x1 = r->x2;
1810        r->x2 = tmp;
1811    }
1812
1813    if ( r->y1 > r->y2 ) {
1814        double tmp = r->y1;
1815        r->y1 = r->y2;
1816        r->y2 = tmp;
1817    }
1818}
1819
1820// converts window position to document position
1821// single page mode only
1822void DocumentViewer::winPosToDocPos( PPOINTL startpoint, PPOINTL endpoint, LuRectangle *r )
1823{
1824    LONG sx = startpoint->x;
1825    LONG sy = startpoint->y;
1826    LONG ex = endpoint->x;
1827    LONG ey = endpoint->y;
1828    if ( width < cxClient ) {
1829        LONG xPos = ( cxClient - width ) / 2;
1830        sx -= xPos;
1831        ex -= xPos;
1832    }
1833    if ( height < cyClient ) {
1834        LONG yPos = ( cyClient - height ) / 2;
1835        sy += yPos;
1836        ey += yPos;
1837    }
1838
1839    r->x1 = ( sx + sHscrollPos ) / realzoom;
1840    r->y1 = ( ( cyClient - sy ) + sVscrollPos ) / realzoom;
1841    r->x2 = ( ex + sHscrollPos ) / realzoom;
1842    r->y2 = ( ( cyClient - ey ) + sVscrollPos ) / realzoom;
1843
1844    rotateRectangle( currentpage, r );
1845}
1846
1847// converts window position to document position
1848// continuous view mode only
1849void DocumentViewer::winPosToDocPos( PageDrawArea *pda, LuRectangle *r )
1850{
1851    LONG sx = pda->drawrect.xLeft;
1852    LONG ex = pda->drawrect.xRight;
1853    double w = pagesizes[ pda->pagenum ].x * realzoom;
1854    if ( w < cxClient ) {
1855        LONG xPos = ( cxClient - w ) / 2;
1856        sx -= xPos;
1857        ex -= xPos;
1858    }
1859
1860    r->x1 = ( sHscrollPos + sx ) / realzoom;;
1861    r->y1 = pda->startpos.y / realzoom;
1862    r->x2 = ( ( ex - sx ) / realzoom ) + r->x1;
1863    r->y2 = ( ( pda->drawrect.yTop - pda->drawrect.yBottom ) / realzoom ) + r->y1;
1864
1865    rotateRectangle( pda->pagenum, r );
1866}
1867
1868// converts document position to window position
1869void DocumentViewer::docPosToWinPos( long pagenum, LuRectangle *r, PRECTL rcl )
1870{
1871    double yplus = isContinuous() ? pagenumToPos( pagenum ) : 0;
1872    double w = pagesizes[ pagenum ].x;
1873    double h = pagesizes[ pagenum ].y;
1874
1875    double tmp_x1 = r->x1;
1876    double tmp_y1 = r->y1;
1877    double tmp_x2 = r->x2;
1878    double tmp_y2 = r->y2;
1879
1880    if ( rotation == 90 )
1881    {
1882        tmp_x1 = w - r->y2;
1883        tmp_y1 = r->x1;
1884        tmp_x2 = w - r->y1;
1885        tmp_y2 = r->x2;
1886    }
1887    else if ( rotation == 180 )
1888    {
1889        tmp_x1 = w - r->x2;
1890        tmp_y1 = h - r->y2;
1891        tmp_x2 = w - r->x1;
1892        tmp_y2 = h - r->y1;
1893    }
1894    else if ( rotation == 270 )
1895    {
1896        tmp_x1 = r->y1;
1897        tmp_y1 = h - r->x2;
1898        tmp_x2 = r->y2;
1899        tmp_y2 = h - r->x1;
1900    }
1901
1902    rcl->xLeft   = ( tmp_x1 * realzoom ) - sHscrollPos;
1903    rcl->yBottom = cyClient - ( yplus + ( tmp_y2 * realzoom ) ) + ( sVscrollPos * VScrollStep );
1904    rcl->xRight  = ( tmp_x2 * realzoom ) - sHscrollPos;
1905    rcl->yTop    = cyClient - ( yplus + ( tmp_y1 * realzoom ) ) + ( sVscrollPos * VScrollStep );
1906
1907    LONG pw = w * realzoom;
1908    if ( pw < cxClient ) {
1909        LONG xPos = ( cxClient - pw ) / 2;
1910        rcl->xLeft  += xPos;
1911        rcl->xRight += xPos;
1912    }
1913    if ( !isContinuous() )
1914    {
1915        LONG ph = h * realzoom;
1916        if ( ph < cyClient ) {
1917            LONG yPos = ( cyClient - ph ) / 2;
1918            rcl->yBottom -= yPos;
1919            rcl->yTop    -= yPos;
1920        }
1921    }
1922}
1923
1924// creates region from sequence of rectangles
1925HRGN DocumentViewer::rectsToRegion( long pagenum, HPS hps, LuDocument_LuRectSequence *rects )
1926{
1927    HRGN hrgn = GpiCreateRegion( hps, 0, NULL );
1928    if ( rects != NULL )
1929    {
1930        RECTL r = {0};
1931        for ( int i = 0; i < rects->_length; i++ )
1932        {
1933            docPosToWinPos( pagenum, &(rects->_buffer[i]), &r );
1934            HRGN tmprgn = GpiCreateRegion( hps, 1, &r );
1935            GpiCombineRegion( hps, hrgn, hrgn, tmprgn, CRGN_OR );
1936            GpiDestroyRegion( hps, tmprgn );
1937        }
1938    }
1939    return hrgn;
1940}
1941
1942// draws selected area in window, using XOR mix
1943// drawing area may be restricted by r rectangle
1944void DocumentViewer::drawSelection( long pagenum, HPS hps, PRECTL r )
1945{
1946    GpiSetMix( hps, FM_XOR );
1947    GpiSetColor( hps, CLR_YELLOW );
1948    HRGN selectRegion = rectsToRegion( pagenum, hps, selrects[ pagenum ] );
1949    if ( r != NULL )
1950    {
1951        HRGN tmprgn = GpiCreateRegion( hps, 1, r );
1952        GpiCombineRegion( hps, selectRegion, selectRegion, tmprgn, CRGN_AND );
1953        GpiDestroyRegion( hps, tmprgn );
1954    }
1955    GpiPaintRegion( hps, selectRegion );
1956    GpiDestroyRegion( hps, selectRegion );
1957}
1958
1959void DocumentViewer::drawFound( long pagenum, HPS hps, PRECTL r )
1960{
1961    GpiSetMix( hps, FM_XOR );
1962    GpiSetColor( hps, CLR_CYAN );
1963    HRGN selectRegion = rectsToRegion( pagenum, hps, foundrects[ pagenum ] );
1964    if ( r != NULL )
1965    {
1966        HRGN tmprgn = GpiCreateRegion( hps, 1, r );
1967        GpiCombineRegion( hps, selectRegion, selectRegion, tmprgn, CRGN_AND );
1968        GpiDestroyRegion( hps, tmprgn );
1969    }
1970    GpiPaintRegion( hps, selectRegion );
1971    GpiDestroyRegion( hps, selectRegion );
1972}
1973
1974// scrolls window to specified pos (optionally with text selection)
1975void DocumentViewer::scrollToPos( HWND hwnd, LONG xpos, LONG ypos,
1976                                  bool withSelection )
1977{
1978    SHORT xinc = 0;
1979    SHORT yinc = 0;
1980
1981    if ( ( xpos < 0 ) && ( sHscrollPos > 0 ) ) {
1982        xinc = std::max( (SHORT)( -sHscrollPos ), (SHORT)xpos );
1983    } else if ( ( xpos > cxClient ) && ( sHscrollPos < sHscrollMax ) ) {
1984        xinc = std::min( (SHORT)( sHscrollMax - sHscrollPos ), (SHORT)( xpos - cxClient ) );
1985    }
1986    if ( ( ypos < 0 ) && ( sVscrollPos < sVscrollMax ) ) {
1987        yinc = std::min( (SHORT)( ( sVscrollMax - sVscrollPos ) * VScrollStep ), (SHORT)( -ypos ) );
1988    }
1989    else if ( ( ypos > cyClient ) && ( sVscrollPos > 0 ) ) {
1990        yinc = std::max( (SHORT)( ( -sVscrollPos ) * VScrollStep ), (SHORT)( cyClient - ypos ) );
1991    }
1992
1993    if ( xinc != 0 ) {
1994        horizScroll( hwnd, MPFROM2SHORT( sHscrollPos + xinc, SB_SLIDERPOSITION ) );
1995        if ( withSelection ) {
1996            selectionStart.x -= xinc;
1997        }
1998    }
1999
2000    if ( yinc != 0 )
2001    {
2002        SHORT remainder = yinc % VScrollStep;
2003        if ( remainder != 0 ) {
2004            SHORT add = VScrollStep - remainder;
2005            yinc += ( ( yinc > 0 ) ? add : -add );
2006        }
2007
2008        vertScroll( hwnd, MPFROM2SHORT( ( ( sVscrollPos * VScrollStep ) + yinc ) / VScrollStep,
2009                                        SB_SLIDERPOSITION ) );
2010        if ( withSelection ) {
2011            selectionStart.y += yinc;
2012        }
2013    }
2014}
2015
2016// handles WM_MOUSEMOVE
2017// performs text selection if mouse button pressed
2018// changes mouse ptr to 'hand' if it moves over link area
2019BOOL DocumentViewer::wmMouseMove( HWND hwnd, SHORT xpos, SHORT ypos )
2020{
2021    if ( ( xpos != xLastPos ) || ( ypos != yLastPos ) ) // only if mouse really moved
2022    {
2023        unhideMouse();
2024    }
2025    xLastPos = xpos;
2026    yLastPos = ypos;
2027
2028    if ( zoomMode )
2029    {
2030        HPOINTER ptr = zoomInPtr;
2031        if ( WinGetPhysKeyState( HWND_DESKTOP, 0x1d ) & 0x8000 ) {
2032            ptr = zoomOutPtr;
2033        }
2034        WinSetPointer( HWND_DESKTOP, ptr );
2035        return TRUE;
2036    }
2037    else
2038    {
2039        if ( mousePressed && ( doc != NULL ) )
2040        {
2041            selectionEnd.x = xpos;
2042            selectionEnd.y = ypos;
2043
2044            if ( isContinuous() )
2045            {
2046                scrollToPos( hwnd, xpos, ypos, true );
2047
2048                RECTL selRect = {
2049                    selectionStart.x < selectionEnd.x ? selectionStart.x : selectionEnd.x,
2050                    selectionStart.y < selectionEnd.y ? selectionStart.y : selectionEnd.y,
2051                    selectionStart.x < selectionEnd.x ? selectionEnd.x : selectionStart.x,
2052                    selectionStart.y < selectionEnd.y ? selectionEnd.y : selectionStart.y
2053                };
2054
2055                DrawAreas *areas = findDrawAreas( &selRect );
2056
2057                HPS hps = WinGetPS( hwnd );
2058                GpiSetMix( hps, FM_XOR );
2059                GpiSetColor( hps, CLR_YELLOW );
2060
2061                for ( int i = 0; i < areas->size(); i++ )
2062                {
2063                    PageDrawArea *pda = &(*areas)[ i ];
2064
2065                    winPosToDocPos( pda, &(selection[pda->pagenum]) );
2066
2067                    HRGN clearRegion = rectsToRegion( pda->pagenum, hps, selrects[ pda->pagenum ] );
2068                    LuDocument::freeRectangles( ev, selrects[ pda->pagenum ] );
2069                    selrects[ pda->pagenum ] = doc->getSelectionRectangles( ev, pda->pagenum, &(selection[pda->pagenum]) );
2070                    HRGN selectRegion = rectsToRegion( pda->pagenum, hps, selrects[ pda->pagenum ] );
2071                    GpiCombineRegion( hps, selectRegion, selectRegion, clearRegion, CRGN_XOR );
2072                    GpiPaintRegion( hps, selectRegion );
2073                    GpiDestroyRegion( hps, clearRegion );
2074                    GpiDestroyRegion( hps, selectRegion );
2075                }
2076
2077                WinReleasePS( hps );
2078                delete areas;
2079            }
2080            else
2081            {
2082                winPosToDocPos( &selectionStart, &selectionEnd, &(selection[currentpage]) );
2083
2084                HPS hps = WinGetPS( hwnd );
2085
2086                scrollToPos( hwnd, xpos, ypos, true );
2087
2088                // 127/191/255
2089                //LONG lclr = ( 127 << 16 ) | ( 191 << 8 ) | 255;
2090                //LONG lclr = ( 128 << 16 ) | ( 64 << 8 );
2091                //LONG ltabl[ 1 ] = { lclr };
2092                //GpiCreateLogColorTable( hps, 0, LCOLF_CONSECRGB, 100, 1, ltabl );
2093
2094                GpiSetMix( hps, FM_XOR );
2095                GpiSetColor( hps, CLR_YELLOW );
2096                //GpiSetColor( hps, 100 );
2097
2098                HRGN clearRegion = rectsToRegion( currentpage, hps, selrects[ currentpage ] );
2099                LuDocument::freeRectangles( ev, selrects[ currentpage ] );
2100                if ( ( selectionStart.x == selectionEnd.x ) &&
2101                     ( selectionStart.y == selectionEnd.y ) ) {
2102                    selrects[ currentpage ] = NULL;
2103                    memset( &(selection[ currentpage ]), 0, sizeof( LuRectangle ) );
2104                }
2105                else {
2106                    selrects[ currentpage ] = doc->getSelectionRectangles( ev, currentpage, &(selection[currentpage]) );
2107                }
2108                HRGN selectRegion = rectsToRegion( currentpage, hps, selrects[ currentpage ] );
2109                GpiCombineRegion( hps, selectRegion, selectRegion, clearRegion, CRGN_XOR );
2110                GpiPaintRegion( hps, selectRegion );
2111                GpiDestroyRegion( hps, clearRegion );
2112                GpiDestroyRegion( hps, selectRegion );
2113
2114                WinReleasePS( hps );
2115            }
2116        }
2117        else if ( docDraggingStarted && ( doc != NULL ) )
2118        {
2119            WinSetPointer( HWND_DESKTOP, handClosedPtr );
2120            docDraggingEnd.x = xpos;
2121            docDraggingEnd.y = ypos;
2122
2123            SHORT hMove = -( docDraggingEnd.x - docDraggingStart.x );
2124            if ( abs( hMove ) > 5 )
2125            {
2126                horizScroll( hwnd, MPFROM2SHORT( hMove, SB_PAGEDRAG ) );
2127                docDraggingStart.x = xpos;
2128            }
2129
2130            SHORT vMove = docDraggingEnd.y - docDraggingStart.y;
2131            if ( abs( vMove ) > 5 )
2132            {
2133                vertScroll( hwnd, MPFROM2SHORT( vMove, SB_PAGEDRAG ) );
2134                docDraggingStart.y = ypos;
2135            }
2136            return TRUE;
2137        }
2138        else if ( links != NULL || inputFields != NULL )
2139        {
2140            long pg = currentpage;
2141            if ( isContinuous() ) {
2142                double tmp;
2143                pg = posToPagenum( ypos, &tmp );
2144            }
2145
2146            if ( links != NULL )
2147            {
2148                if ( ( pg != -1 ) && ( links[ pg ] != NULL ) )
2149                {
2150                    for ( int i = 0; i < links[ pg ]->_length; i++ )
2151                    {
2152                        RECTL r = {0};
2153                        docPosToWinPos( pg, &(links[ pg ]->_buffer[i].area), &r );
2154
2155                        POINTL ptl = { xpos, ypos };
2156                        if ( WinPtInRect( hab, &r, &ptl ) ) {
2157                            WinSetPointer( HWND_DESKTOP, handPtr );
2158                            return TRUE;
2159                        }
2160                    }
2161                }
2162            }
2163
2164            if ( inputFields != NULL )
2165            {
2166                if ( ( pg != -1 ) && ( inputFields[ pg ].fields != NULL ) )
2167                {
2168                    for ( int i = 0; i < inputFields[ pg ].fields->_length; i++ )
2169                    {
2170                        PageInputFields::Cache &entry = inputFields[ pg ].cache[ i ];
2171                        if ( entry.rect == NULL )
2172                            inputFields[ pg ].fillCache( i );
2173
2174                        if ( entry.supported ) {
2175                            RECTL r = {0};
2176                            docPosToWinPos( pg, entry.rect, &r );
2177
2178                            POINTL ptl = { xpos, ypos };
2179                            if ( WinPtInRect( hab, &r, &ptl ) ) {
2180                                WinSetPointer( HWND_DESKTOP,
2181                                               entry.type == LuInputField_Text ?
2182                                               textPtr : handPtr );
2183                                return TRUE;
2184                            }
2185                        }
2186                    }
2187                }
2188            }
2189        }
2190    }
2191    return FALSE;
2192}
2193
2194void DocumentViewer::zoomInOut( bool zoomIn )
2195{
2196    if ( ( doc != NULL ) && doc->isScalable( ev ) )
2197    {
2198        double z = getRealZoom() / 4;
2199        double zval = 0;
2200        if ( zoomIn ) {
2201            zval = getRealZoom() + z;
2202        } else {
2203            zval = getRealZoom() - z;
2204        }
2205        zval = (long)( zval * 20.0 ) / 20.0;   // Round to 0.05 (5%)
2206        if ( zval == getRealZoom() ) {
2207            zval += ( zoomIn ? 0.01 : -0.01 );
2208        }
2209        if ( zval > 0.1 ) {
2210            Lucide::setZoom( zval );
2211        }
2212    }
2213}
2214
2215void DocumentViewer::resetModifiedState()
2216{
2217    if ( inputFields != NULL ) {
2218        for ( long pg = 0; pg < totalpages; ++pg ) {
2219            if ( inputFields[ pg ].fields == NULL )
2220                continue;
2221            for ( unsigned long i = 0; i < inputFields[ pg ].fields->_length; ++i ) {
2222                inputFields[ pg ].cache[ i ].modified = false;
2223            }
2224        }
2225    }
2226}
2227
2228// handles WM_BUTTON1CLICK
2229BOOL DocumentViewer::wmClick( HWND hwnd, SHORT xpos, SHORT ypos )
2230{
2231    if ( zoomMode )
2232    {
2233        zoomInOut( ( WinGetPhysKeyState( HWND_DESKTOP, 0x1d ) & 0x8000 ) == 0 );
2234        return TRUE;
2235    }
2236    else
2237    {
2238        if ( links == NULL && inputFields == NULL ) {
2239            return FALSE;
2240        }
2241
2242        long pg = currentpage;
2243        if ( isContinuous() ) {
2244            double tmp;
2245            pg = posToPagenum( ypos, &tmp );
2246        }
2247
2248        if ( links != NULL )
2249        {
2250            if ( ( pg != -1 ) && ( links[ pg ] != NULL ) )
2251            {
2252                for ( int i = 0; i < links[ pg ]->_length; i++ )
2253                {
2254                    RECTL r = {0};
2255                    docPosToWinPos( pg, &(links[ pg ]->_buffer[i].area), &r );
2256
2257                    POINTL ptl = { xpos, ypos };
2258                    if ( WinPtInRect( hab, &r, &ptl ) )
2259                    {
2260                        if ( links[ pg ]->_buffer[i].link.type == LU_LINK_TYPE_EXTERNAL_URI )
2261                        {
2262                            if ( !startBrowser( links[ pg ]->_buffer[i].link.uri ) )
2263                            {
2264                                char *m = newstrdupL( MSGS_ERROR_STARTING_BROWSER );
2265                                WinMessageBox( HWND_DESKTOP, hMainFrame, m,
2266                                           NULL, 0, MB_OK | MB_ICONEXCLAMATION | MB_MOVEABLE );
2267                                delete m;
2268                            }
2269                        }
2270                        else if ( links[ pg ]->_buffer[i].link.type == LU_LINK_TYPE_EXTERNAL_FILE )
2271                        {
2272                            char *uri = links[ pg ]->_buffer[i].link.uri;
2273                            if ( uri != NULL ) {
2274                                Lucide::newWindow( uri, true );
2275                            }
2276                        }
2277                        else if ( links[ pg ]->_buffer[i].link.type == LU_LINK_TYPE_TITLE )
2278                        {
2279                            char *title = links[ pg ]->_buffer[i].link.title;
2280                            if ( title == NULL ) {
2281                                title = "???";
2282                            }
2283                            WinMessageBox( HWND_DESKTOP, hMainFrame,
2284                                title, "?", 1, MB_OK | MB_INFORMATION | MB_MOVEABLE );
2285                        }
2286                        else if ( links[ pg ]->_buffer[i].link.type == LU_LINK_TYPE_PAGE )
2287                        {
2288                            goToPage( links[ pg ]->_buffer[i].link.page );
2289                        }
2290
2291                        return TRUE;
2292                    }
2293                }
2294            }
2295        }
2296
2297        if ( inputFields != NULL )
2298        {
2299            if ( ( pg != -1 ) && ( inputFields[ pg ].fields != NULL ) )
2300            {
2301                for ( int i = 0; i < inputFields[ pg ].fields->_length; i++ )
2302                {
2303                    PageInputFields::Cache &entry = inputFields[ pg ].cache[ i ];
2304                    if ( entry.rect == NULL )
2305                        inputFields[ pg ].fillCache( i );
2306
2307                    if ( entry.supported ) {
2308                        RECTL r = {0};
2309                        docPosToWinPos( pg, entry.rect, &r );
2310
2311                        POINTL ptl = { xpos, ypos };
2312                        if ( WinPtInRect( hab, &r, &ptl ) )
2313                        {
2314                            LuInputField *field = inputFields[ pg ].fields->_buffer[ i ];
2315
2316                            switch ( entry.type )
2317                            {
2318                                case LuInputField_Button:
2319                                {
2320                                    LuInputButton *button = static_cast<LuInputButton *>( field );
2321                                    LuInputButton_ButtonType type = button->getButtonType( ev );
2322                                    if ( type == LuInputButton_Check || type == LuInputButton_Radio ) {
2323                                        boolean state = button->getState( ev );
2324                                        button->setState( ev, !state );
2325                                        WinInvalidateRect( hwnd, &r, FALSE );
2326                                        entry.modified = true;
2327                                    }
2328                                    break;
2329                                }
2330                                case LuInputField_Text:
2331                                {
2332                                    showTextField( pg, i, &r );
2333                                    break;
2334                                }
2335                                default:
2336                                    break;
2337                            }
2338
2339                            return TRUE;
2340                        }
2341                    }
2342                }
2343            }
2344        }
2345    }
2346    return FALSE;
2347}
2348
2349// handles WM_BUTTON2CLICK
2350BOOL DocumentViewer::wmRightClick( HWND hwnd, SHORT xpos, SHORT ypos )
2351{
2352    if ( zoomMode )
2353    {
2354        zoomInOut( false );
2355        return TRUE;
2356    }
2357    return FALSE;
2358}
2359
2360BOOL DocumentViewer::wmChar( HWND hwnd, MPARAM mp1, MPARAM mp2 )
2361{
2362    USHORT fsflags = SHORT1FROMMP( mp1 );
2363    USHORT usch = SHORT1FROMMP( mp2 );
2364    USHORT usvk = SHORT2FROMMP( mp2 );
2365
2366    if ( ( fsflags & KC_VIRTUALKEY ) && !( fsflags & KC_KEYUP ) )
2367    {
2368        switch ( usvk )
2369        {
2370            case VK_UP:
2371                if ( fsflags & KC_CTRL ) {
2372                    vertScroll( hwnd, MPFROM2SHORT( 0, SB_SLIDERPOSITION ) );
2373                } else {
2374                    WinSendMsg( hwnd, WM_VSCROLL, MPVOID, MPFROM2SHORT( 0, SB_LINEUP ) );
2375                }
2376                return TRUE;
2377
2378            case VK_DOWN:
2379                if ( fsflags & KC_CTRL ) {
2380                    vertScroll( hwnd, MPFROM2SHORT( sVscrollMax, SB_SLIDERPOSITION ) );
2381                } else {
2382                    WinSendMsg( hwnd, WM_VSCROLL, MPVOID, MPFROM2SHORT( 0, SB_LINEDOWN ) );
2383                }
2384                return TRUE;
2385
2386            case VK_LEFT:
2387                if ( fsflags & KC_CTRL ) {
2388                    horizScroll( hwnd, MPFROM2SHORT( 0, SB_SLIDERPOSITION ) );
2389                } else {
2390                    WinSendMsg( hwnd, WM_HSCROLL, MPVOID, MPFROM2SHORT( 0, SB_LINELEFT ) );
2391                }
2392                return TRUE;
2393
2394            case VK_RIGHT:
2395                if ( fsflags & KC_CTRL ) {
2396                    horizScroll( hwnd, MPFROM2SHORT( sHscrollMax, SB_SLIDERPOSITION ) );
2397                } else {
2398                    WinSendMsg( hwnd, WM_HSCROLL, MPVOID, MPFROM2SHORT( 0, SB_LINERIGHT ) );
2399                }
2400                return TRUE;
2401
2402
2403            case VK_PAGEUP:
2404                if ( !( fsflags & (KC_CTRL | KC_SHIFT | KC_ALT) ) )
2405                {
2406                    bool dojump = ( !isContinuous() && ( sVscrollPos == 0 )
2407                                        && ( currentpage > 0 ) );
2408
2409                    if ( dojump ) {
2410                        goToPage( currentpage - 1 );
2411                        vertScroll( hwnd, MPFROM2SHORT( sVscrollMax, SB_SLIDERPOSITION ) );
2412                    } else {
2413                        WinSendMsg( hwnd, WM_VSCROLL, MPVOID, MPFROM2SHORT( 0, SB_PAGEUP ) );
2414                    }
2415                    return TRUE;
2416                }
2417                break;
2418
2419            case VK_PAGEDOWN:
2420                if ( !( fsflags & (KC_CTRL | KC_SHIFT | KC_ALT) ) )
2421                {
2422                    bool dojump = ( !isContinuous() && ( sVscrollPos == sVscrollMax ) );
2423
2424                    if ( dojump ) {
2425                        goToPage( currentpage + 1 );
2426                    } else {
2427                        WinSendMsg( hwnd, WM_VSCROLL, MPVOID, MPFROM2SHORT( 0, SB_PAGEDOWN ) );
2428                    }
2429                    return TRUE;
2430                }
2431                break;
2432
2433            case VK_HOME:
2434                if ( !( fsflags & (KC_CTRL | KC_SHIFT | KC_ALT) ) )
2435                {
2436                    goToPage( 0 );
2437                    vertScroll( hwnd, MPFROM2SHORT( 0, SB_SLIDERPOSITION ) );
2438                    return TRUE;
2439                }
2440                break;
2441
2442            case VK_END:
2443                if ( !( fsflags & (KC_CTRL | KC_SHIFT | KC_ALT) ) )
2444                {
2445                    goToPage( totalpages - 1 );
2446                    vertScroll( hwnd, MPFROM2SHORT( sVscrollMax, SB_SLIDERPOSITION ) );
2447                    return TRUE;
2448                }
2449                break;
2450
2451            case VK_ESC:
2452                if ( textField != NULL ) {
2453                    hideTextField( false );
2454                    return TRUE;
2455                }
2456                break;
2457
2458            case VK_ENTER:
2459            case VK_NEWLINE:
2460                if ( textField != NULL ) {
2461                    hideTextField();
2462                    return TRUE;
2463                }
2464                break;
2465        }
2466    }
2467
2468    // Special case for Esc in presentation mode
2469    if ( presentation && !( fsflags & KC_KEYUP ) &&
2470         ( fsflags & KC_VIRTUALKEY ) && ( usvk == VK_ESC ) ) {
2471        Lucide::togglePresentation();
2472        return TRUE;
2473    }
2474
2475    // Ctrl && zoomMode
2476    if ( ( fsflags & KC_VIRTUALKEY ) && ( usvk == VK_CTRL ) && zoomMode ) {
2477        wmMouseMove( hwnd, 0, 0 ); // to switch mouse pointer if in zoomMode
2478    }
2479
2480    return FALSE;
2481}
2482
2483// handles WM_BUTTON1DOWN
2484void DocumentViewer::wmButton1Down( HWND hwnd, SHORT xpos, SHORT ypos )
2485{
2486    if ( isContinuous() && ( doc != NULL ) )
2487    {
2488        // clear selection
2489        RECTL rcl = { 0 };
2490        WinQueryWindowRect( hwnd, &rcl );
2491        DrawAreas *areas = findDrawAreas( &rcl );
2492
2493        HPS hps = WinGetPS( hwnd );
2494        GpiSetMix( hps, FM_XOR );
2495        GpiSetColor( hps, CLR_YELLOW );
2496
2497        for ( int i = 0; i < areas->size(); i++ )
2498        {
2499            PageDrawArea *pda = &(*areas)[ i ];
2500
2501            HRGN clearRegion = rectsToRegion( pda->pagenum, hps, selrects[ pda->pagenum ] );
2502            GpiPaintRegion( hps, clearRegion );
2503            GpiDestroyRegion( hps, clearRegion );
2504        }
2505        WinReleasePS( hps );
2506        delete areas;
2507
2508        freeRects( selrects );
2509
2510        memset( selection, 0, sizeof( LuRectangle ) * totalpages );
2511    }
2512
2513    POINTL ptl = { xpos, ypos };
2514    hideTextField( true, &ptl );
2515
2516    WinSetCapture( HWND_DESKTOP, hwnd );
2517    mousePressed = true;
2518    selectionStart.x = xpos;
2519    selectionStart.y = ypos;
2520}
2521
2522
2523// handles WM_BUTTON1UP
2524void DocumentViewer::wmButton1Up()
2525{
2526    WinSetCapture( HWND_DESKTOP, NULLHANDLE );
2527    mousePressed = false;
2528
2529    bool haveSelection = false;
2530    for ( long i = 0; i < totalpages; i++ ) {
2531        if ( selrects[ i ] != NULL ) {
2532            haveSelection = true;
2533            break;
2534        }
2535    }
2536
2537    Lucide::enableCopy( haveSelection );
2538}
2539
2540
2541// handles WM_BUTTON2DOWN
2542void DocumentViewer::wmButton2Down( HWND hwnd, SHORT xpos, SHORT ypos )
2543{
2544    if ( doc != NULL )
2545    {
2546        WinSetCapture( HWND_DESKTOP, hwnd );
2547        docDraggingStarted = true;
2548        docDraggingStart.x = xpos;
2549        docDraggingStart.y = ypos;
2550    }
2551}
2552
2553
2554// handles WM_BUTTON2UP
2555void DocumentViewer::wmButton2Up()
2556{
2557    if ( docDraggingStarted )
2558    {
2559        WinSetCapture( HWND_DESKTOP, NULLHANDLE );
2560        docDraggingStarted = false;
2561    }
2562}
2563
2564
2565// handles DM_DRAGOVER
2566MRESULT DocumentViewer::wmDragOver( PDRAGINFO dragInfo )
2567{
2568    PDRAGITEM dragItem;
2569    USHORT    usOp, usIndicator;
2570
2571    usOp = 0;
2572    usIndicator = DOR_NODROPOP;
2573
2574    DrgAccessDraginfo( dragInfo );
2575
2576    if ( dragInfo->usOperation == DO_DEFAULT )
2577    {
2578        dragItem = DrgQueryDragitemPtr( dragInfo, 0 );
2579        if ( DrgQueryDragitemCount( dragInfo ) == 1 )
2580        {
2581            if ( DrgVerifyRMF( dragItem, "DRM_OS2FILE", NULL ) &&
2582                 ( dragItem->hstrContainerName != NULLHANDLE ) &&
2583                 ( dragItem->hstrSourceName != NULLHANDLE ) )
2584            {
2585                char fname[ CCHMAXPATHCOMP ] = "";
2586                DrgQueryStrName( dragItem->hstrSourceName, CCHMAXPATHCOMP, fname );
2587                char *ext = strrchr( fname, '.' );
2588                if ( ext != NULL ) {
2589                    if ( pluginMan->createDocumentForExt( ext + 1, true ) != NULL ) {
2590                        usIndicator = DOR_DROP;
2591                        usOp = DO_UNKNOWN;
2592                    }
2593                }
2594            }
2595        }
2596    }
2597
2598    DrgFreeDraginfo( dragInfo );
2599    return MRFROM2SHORT( usIndicator, usOp );
2600}
2601
2602
2603// handles DM_DROP
2604void DocumentViewer::wmDrop( PDRAGINFO dragInfo )
2605{
2606    PDRAGITEM dragItem;
2607
2608    DrgAccessDraginfo( dragInfo );
2609    dragItem = DrgQueryDragitemPtr( dragInfo, 0 );
2610
2611    char fname[ CCHMAXPATHCOMP ] = "";
2612    char fpath[ CCHMAXPATH ] = "";
2613    DrgQueryStrName( dragItem->hstrSourceName, CCHMAXPATHCOMP, fname );
2614    DrgQueryStrName( dragItem->hstrContainerName, CCHMAXPATH, fpath );
2615    DrgFreeDraginfo( dragInfo );
2616
2617    strcat( fpath, fname );
2618    Lucide::loadDocument( fpath );
2619}
2620
2621// handles WM_TIMER
2622void DocumentViewer::wmTimer( USHORT idTimer )
2623{
2624    if ( idTimer == NO_MOUSE_TIMER )
2625    {
2626        if ( presentation && !mouseHidden && inFocus )
2627        {
2628            WinShowPointer( HWND_DESKTOP, FALSE );
2629            mouseHidden = true;
2630            WinStopTimer( hab, hWndDoc, NO_MOUSE_TIMER );
2631        }
2632    }
2633}
2634
2635// handles WM_CONTROL
2636void DocumentViewer::wmControl( USHORT idControl, USHORT notifyCode, HWND hwndControl )
2637{
2638    if ( ( idControl == DOC_ID_ENTRY && notifyCode == EN_KILLFOCUS &&
2639           textField != NULL && !textField->isMultiLine( ev ) ) ||
2640         ( idControl == DOC_ID_MLE && notifyCode == MLN_KILLFOCUS &&
2641           textField != NULL && textField->isMultiLine( ev ) ) )
2642        hideTextField();
2643}
2644
2645// static, window procedure
2646MRESULT EXPENTRY DocumentViewer::docViewProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
2647{
2648    DocumentViewer *_this = (DocumentViewer *)WinQueryWindowULong( hwnd, QWL_USER );
2649
2650    switch ( msg )
2651    {
2652        case WM_CREATE:
2653        {
2654            // Save the mp1 into our user data so that subsequent calls have
2655            // access to the parent C++ object
2656            WinSetWindowULong( hwnd, QWL_USER, (ULONG)mp1 );
2657            _this = (DocumentViewer *)mp1;
2658            return (MRESULT)FALSE;
2659        }
2660
2661        case WM_SETFOCUS:
2662            _this->inFocus = SHORT1FROMMP( mp2 ) == TRUE;
2663            _this->unhideMouse();
2664            break;
2665
2666        case DM_DRAGOVER:
2667            return _this->wmDragOver( (PDRAGINFO)mp1 );
2668
2669        case DM_DROP:
2670            _this->wmDrop( (PDRAGINFO)mp1 );
2671            return (MRESULT)FALSE;
2672
2673        case WM_ERASEBACKGROUND:
2674            return (MRESULT)TRUE;
2675
2676        case WM_SIZE:
2677            _this->wmSize( hwnd, mp2 );
2678            return (MRESULT)FALSE;
2679
2680        case WM_HSCROLL:
2681            _this->horizScroll( hwnd, mp2 );
2682            break;
2683
2684        case WM_VSCROLL:
2685            _this->vertScroll( hwnd, mp2 );
2686            break;
2687
2688        case WM_PAINT:
2689            if ( _this->enableAsynchDraw ) {
2690                if ( _this->isContinuous() ) {
2691                    _this->wmPaintContAsynch( hwnd );
2692                } else {
2693                    _this->wmPaintAsynch( hwnd );
2694                }
2695            } else {
2696                if ( _this->isContinuous() ) {
2697                    _this->wmPaintCont( hwnd );
2698                } else {
2699                    _this->wmPaint( hwnd );
2700                }
2701            }
2702            return (MRESULT)FALSE;
2703
2704        case WM_BUTTON1DOWN:
2705            _this->wmButton1Down( hwnd, SHORT1FROMMP( mp1 ), SHORT2FROMMP( mp1 ) );
2706            break;
2707
2708        case WM_BUTTON1UP:
2709            _this->wmButton1Up();
2710            break;
2711
2712        case WM_BUTTON2DOWN:
2713            _this->wmButton2Down( hwnd, SHORT1FROMMP( mp1 ), SHORT2FROMMP( mp1 ) );
2714            break;
2715
2716        case WM_BUTTON2UP:
2717            _this->wmButton2Up();
2718            break;
2719
2720        case WM_MOUSEMOVE:
2721            if ( _this->wmMouseMove( hwnd, SHORT1FROMMP( mp1 ), SHORT2FROMMP( mp1 ) ) ) {
2722                return (MRESULT)TRUE;
2723            }
2724            break;
2725
2726        case WM_BUTTON1CLICK:
2727            if ( _this->wmClick( hwnd, SHORT1FROMMP( mp1 ), SHORT2FROMMP( mp1 ) ) ) {
2728                return (MRESULT)TRUE;
2729            }
2730            break;
2731
2732        case WM_BUTTON2CLICK:
2733            if ( _this->wmRightClick( hwnd, SHORT1FROMMP( mp1 ), SHORT2FROMMP( mp1 ) ) ) {
2734                return (MRESULT)TRUE;
2735            }
2736            break;
2737
2738        case WM_CHAR:
2739            if ( _this->wmChar( hwnd, mp1, mp2 ) ) {
2740                return (MRESULT)TRUE;
2741            }
2742            break;
2743
2744        case WM_FOCUSCHANGE:
2745            if ( SHORT1FROMMP( mp2 ) ) {
2746                Lucide::activeWindow = AwView;
2747            }
2748            break;
2749
2750        case WM_TIMER:
2751            _this->wmTimer( SHORT1FROMMP( mp1 ) );
2752            break;
2753
2754        case WM_CONTROL:
2755            _this->wmControl( SHORT1FROMMP( mp1 ), SHORT2FROMMP( mp1 ), HWNDFROMMP( mp2 ) );
2756            break;
2757    }
2758
2759    return WinDefWindowProc( hwnd, msg, mp1, mp2 );
2760}
2761
2762
2763// static, window procedure
2764MRESULT EXPENTRY DocumentViewer::docFrameProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
2765{
2766    DocumentViewer *_this = (DocumentViewer *)WinQueryWindowULong( hwnd, QWL_USER );
2767
2768    switch ( msg )
2769    {
2770        case WM_SYSCOMMAND:
2771            // Send WM_SYSCOMMAND messages to the main frame so that the main
2772            // system menu works when the document frame (which doesn't actually
2773            // have a system menu) is in focus
2774            WinSendMsg( _this->hMainFrame, WM_SYSCOMMAND, mp1, mp2 );
2775            return (MRESULT)FALSE;
2776    }
2777
2778    return _this->oldFrameProc( hwnd, msg, mp1, mp2 );
2779}
2780
Note: See TracBrowser for help on using the repository browser.