source: trunk/Lucide/SOURCE/gui/docViewer.cpp @ 72

Last change on this file since 72 was 72, checked in by Eugene Romanenko, 15 years ago

Fullscreen implemented (closes #6). Hotkey is Ctrl+L, like in Acrobat. Mozilla-like fullscreen (F11) renamed to 'Maximized view'. Currently page draws at upper-left corner, centered draw should be implemented.

File size: 58.8 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#define INCL_DOS
36#define INCL_WIN
37#define INCL_GPI
38#include <os2.h>
39
40#include <string>
41#include <process.h>
42#include <stdio.h>
43#include <ctype.h>
44
45#include <ludoc.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
54
55// OpenWatcom headers doesn't have GpiDrawBits() declaration
56extern "C" {
57    LONG APIENTRY GpiDrawBits(HPS hps, PVOID pBits, PBITMAPINFO2 pbmiInfoTable,
58                              LONG lCount, PPOINTL aptlPoints, LONG lRop, ULONG flOptions);
59}
60
61typedef LuDocument_LuRectSequence    *PLuRectSequence;
62typedef LuDocument_LuLinkMapSequence *PLuLinkMapSequence;
63
64#define LINE_HEIGHT     16
65
66// DocumentViewer constructor
67DocumentViewer::DocumentViewer( HAB _hab, HWND hWndFrame )
68{
69    hab         = _hab;
70    hMainFrame  = hWndFrame;
71    sHscrollMax = 0;
72    sVscrollMax = 0;
73    sHscrollPos = 0;
74    sVscrollPos = 0;
75    sVscrollInc = 0;
76    sHscrollInc = 0;
77    cxClient    = 0;
78    cyClient    = 0;
79    hWndDoc     = NULLHANDLE;
80    doc         = NULL;
81    totalpages  = 0;
82    currentpage = 0;
83    hpsBuffer   = NULLHANDLE;
84    hdcBuffer   = NULLHANDLE;
85    width       = 0;
86    height      = 0;
87    fullwidth   = 0;
88    fullheight  = 0;
89    bpp         = 0;
90    zoom        = 1.0;
91    realzoom    = 1.0;
92    rotation    = 0;
93    ev          = somGetGlobalEnvironment();
94    pixbuf      = NULL;
95    spos_x      = 0;
96    spos_y      = 0;
97    progressDlg = new ProgressDlg( hWndFrame );
98    drawareas   = NULL;
99    drawareaIndex = 0;
100    closed        = true;
101    // continuous view
102    continuous  = false;
103    pagesizes   = NULL;
104    realVscrollMax = 0;
105    VScrollStep = 1;
106    WinSetRectEmpty( hab, &savedRcl );
107    drawPS = false;
108    // asynch draw
109    abortAsynch = false;
110    termdraw    = false;
111    enableAsynchDraw = false;
112    DosCreateMutexSem( NULL, &todrawAccess, 0, FALSE );
113    DosCreateEventSem( NULL, &haveDraw, 0, FALSE );
114    // selection
115    mousePressed = false;
116    selectionStart.x = 0;  selectionStart.y = 0;
117    selectionEnd.x = 0;  selectionEnd.y = 0;
118    selection = NULL;
119    selrects = NULL;
120    // links
121    links = NULL;
122    handptr = WinLoadPointer( HWND_DESKTOP, NULLHANDLE, IDP_HAND );
123    // search
124    foundrects = NULL;
125    searchString = NULL;
126    abortSearch = false;
127
128    // create windows
129    ULONG dfFlags = FCF_VERTSCROLL | FCF_HORZSCROLL | FCF_NOBYTEALIGN;
130    hWndDocFrame = WinCreateStdWindow( hWndFrame, WS_VISIBLE, &dfFlags, NULL, NULL,
131                                       WS_VISIBLE, NULLHANDLE, 0, NULL );
132    WinSetWindowULong( hWndDocFrame, QWL_USER, (ULONG)this );
133    oldFrameProc = WinSubclassWindow( hWndDocFrame, docFrameProc );
134
135    hWndDoc = WinCreateWindow( hWndDocFrame, "er.docview", NULL,
136                               WS_VISIBLE | WS_TABSTOP, 0, 0, 0, 0, hWndDocFrame,
137                               HWND_TOP, FID_CLIENT, this, NULL );
138
139    hWndHscroll = WinWindowFromID( hWndDocFrame, FID_HORZSCROLL );
140    hWndVscroll = WinWindowFromID( hWndDocFrame, FID_VERTSCROLL );
141
142    drawThreadId = _beginthread( drawthread, NULL, 262144, this );
143}
144
145// DocumentViewer destructor
146DocumentViewer::~DocumentViewer()
147{
148    termdraw    = true;
149    abortAsynch = true;
150    DosPostEventSem( haveDraw );
151    DosWaitThread( &drawThreadId, DCWW_WAIT );
152    DosCloseMutexSem( todrawAccess );
153    DosCloseEventSem( haveDraw );
154
155    if ( doc != NULL ) {
156        freeRects( selrects );
157        freeRects( foundrects );
158        freeLinks();
159    }
160
161    WinDestroyPointer( handptr );
162
163    if ( ( hpsBuffer != NULLHANDLE ) && ( hdcBuffer != NULLHANDLE ) ) {
164        DestroyGraphicsBuffer( hpsBuffer, hdcBuffer );
165        hpsBuffer = hdcBuffer = NULLHANDLE;
166    }
167    delete pixbuf;
168    delete progressDlg;
169    delete searchString;
170    delete pagesizes;
171    delete selection;
172}
173
174
175// static, registration of a window class
176void DocumentViewer::registerClass( HAB hab )
177{
178    WinRegisterClass( hab, "er.docview", docViewProc, CS_SIZEREDRAW, sizeof( ULONG ) * 2 );
179}
180
181// sets the document for viewing
182void DocumentViewer::setDocument( LuDocument *_doc )
183{
184    close();
185    doc = _doc;
186
187    if ( doc != NULL )
188    {
189        closed = false;
190
191        totalpages = doc->getPageCount( ev );
192        bpp = doc->getBpp( ev );
193        if ( !doc->isScalable( ev ) ) {
194            zoom = 1;
195        }
196
197        pagesizes = new LuSize[ totalpages ];
198        countPagesizes();
199
200        selrects = new PLuRectSequence[ totalpages ];
201        memset( selrects, 0, sizeof( PLuRectSequence ) * totalpages );
202
203        foundrects = new PLuRectSequence[ totalpages ];
204        memset( foundrects, 0, sizeof( PLuRectSequence ) * totalpages );
205
206        links = new PLuLinkMapSequence[ totalpages ];
207        memset( links, 0, sizeof( PLuLinkMapSequence ) * totalpages );
208
209        selection = new LuRectangle[ totalpages ];
210        memset( selection, 0, sizeof( LuRectangle ) * totalpages );
211
212        drawPS = doc->isRenderIntoPS( ev );
213        if ( drawPS ) {
214            enableAsynchDraw = false;
215        }
216        else {
217            enableAsynchDraw = doc->isAsynchRenderingSupported( ev );
218        }
219        goToPage( 0 );
220
221        if ( continuous ) {
222            drawPage();
223        }
224    }
225}
226
227void DocumentViewer::countPagesizes()
228{
229    for ( long i = 0; i < totalpages; i++ )
230    {
231        doc->getPageSize( ev, i, &pagesizes[i].x, &pagesizes[i].y );
232        if ( ( rotation == 90 ) || ( rotation == 270 ) ) {
233            double tmp = pagesizes[i].x;
234            pagesizes[i].x = pagesizes[i].y;
235            pagesizes[i].y = tmp;
236        }
237        fullwidth = __max( fullwidth, pagesizes[i].x );
238        fullheight += pagesizes[i].y;
239    }
240}
241
242// closes the document
243void DocumentViewer::close()
244{
245    if ( closed ) {
246        return;
247    }
248
249    closed = true;
250    abortAsynch = true;
251    DosRequestMutexSem( todrawAccess, SEM_INDEFINITE_WAIT );
252
253    delete drawareas;
254    drawareas = NULL;
255
256    delete pagesizes;
257    pagesizes   = NULL;
258
259    delete selection;
260    selection   = NULL;
261
262    freeRects( foundrects );
263    delete foundrects;
264    foundrects  = NULL;
265
266    freeRects( selrects );
267    delete selrects;
268    selrects    = NULL;
269
270    freeLinks();
271
272    doc         = NULL;
273    totalpages  = 0;
274    currentpage = 0;
275    fullwidth   = 0;
276    fullheight  = 0;
277
278    DosReleaseMutexSem( todrawAccess );
279}
280
281// sets the page layout
282void DocumentViewer::setPageLayout( PgLayout layout )
283{
284    continuous = ( layout == Continuous );
285    if ( doc != NULL ) {
286        long pg = currentpage;
287        drawPage();
288        if ( continuous ) {
289            goToPage( pg );
290        }
291    }
292}
293
294void DocumentViewer::freeRects( LuDocument_LuRectSequence **rects )
295{
296    if ( rects != NULL )
297    {
298        for ( long i = 0; i < totalpages; i++ ) {
299            if ( rects[ i ] != NULL ) {
300                LuDocument::freeRectangles( ev, rects[ i ] );
301                rects[ i ] = NULL;
302            }
303        }
304    }
305}
306
307void DocumentViewer::freeLinks()
308{
309    if ( links != NULL )
310    {
311        for ( long i = 0; i < totalpages; i++ ) {
312            if ( links[ i ] != NULL ) {
313                LuDocument::freeLinkMapping( ev, links[ i ] );
314                links[ i ] = NULL;
315            }
316        }
317
318        delete links;
319        links = NULL;
320    }
321}
322
323
324// switch view to specified page
325void DocumentViewer::goToPage( long page )
326{
327    if ( ( page < 0 ) || ( page >= totalpages ) ) {
328        return;
329    }
330
331    if ( continuous && ( doc != NULL ) )
332    {
333        bool needRedraw = ( page == currentpage );
334        double pgpos = pagenumToPos( page ) / VScrollStep;
335        vertScroll( hWndDoc, MPFROM2SHORT( pgpos, SB_SLIDERPOSITION ), NULLHANDLE );
336        if ( needRedraw ) {
337            drawPage();
338        }
339    }
340    else
341    {
342        currentpage = page;
343        sVscrollPos = 0;
344        if ( doc != NULL ) {
345            drawPage();
346            Lucide::checkNavigationMenus();
347        }
348    }
349}
350
351// Sets the zoom level
352// _zoom - actual zoom level or:
353//         -1 - fit width
354//         -2 - fit page
355void DocumentViewer::setZoom( double _zoom )
356{
357
358    if ( doc != NULL ) {
359        if ( doc->isScalable( ev ) ) {
360            zoom = _zoom;
361            drawPage();
362        }
363    }
364    else {
365        zoom = _zoom;
366    }
367}
368
369// Sets the rotation
370// rotation may be 0, 90, 180 or 270 degrees
371// -90 will be changed to 270, 360 to 0
372void DocumentViewer::setRotation( long _rotation )
373{
374    if ( _rotation == -90 ) {
375        _rotation = 270;
376    }
377    if ( _rotation == 360 ) {
378        _rotation = 0;
379    }
380
381    if ( doc != NULL )
382    {
383        if ( doc->isRotable( ev ) )
384        {
385            rotation = _rotation;
386            countPagesizes();
387            drawPage();
388        }
389    }
390    else {
391        rotation = _rotation;
392    }
393}
394
395void DocumentViewer::setFullscreen( bool _fullscreen )
396{
397    fullscreen = _fullscreen;
398    ULONG ulFrameStyle = WinQueryWindowULong( hWndDocFrame, QWL_STYLE );
399
400    if ( fullscreen )
401    {
402        pglSave = getPageLayout();
403        zoomSave = getZoom();
404        setPageLayout( SinglePage );
405        setZoom( -2 );
406        WinSetParent( hWndHscroll, HWND_OBJECT, FALSE );
407        WinSetParent( hWndVscroll, HWND_OBJECT, FALSE );
408        ulFrameStyle &= ~FS_SIZEBORDER;
409    }
410    else
411    {
412        setPageLayout( pglSave );
413        setZoom( zoomSave );
414        WinSetParent( hWndHscroll, hWndDocFrame, FALSE );
415        WinSetParent( hWndVscroll, hWndDocFrame, FALSE );
416        ulFrameStyle &= ~FS_SIZEBORDER;
417    }
418
419    WinSetWindowULong( hWndDocFrame, QWL_STYLE, ulFrameStyle );
420    WinSendMsg( hWndDocFrame, WM_UPDATEFRAME,
421                MPFROMLONG( FCF_VERTSCROLL | FCF_HORZSCROLL | FCF_SIZEBORDER ), MPVOID );
422}
423
424// copy selected text to clipboard
425void DocumentViewer::copyToClipbrd()
426{
427    if ( continuous )
428    {
429        std::string txt = "";
430        for ( long i = 0; i < totalpages; i++ ) {
431            if ( selrects[ i ] != NULL ) {
432                txt += doc->getText( ev, i, &(selection[ i ]) );
433            }
434        }
435        textToClipbrd( hab, txt.c_str() );
436    }
437    else {
438        char *t = doc->getText( ev, currentpage, &(selection[ currentpage ]) );
439        textToClipbrd( hab, t );
440    }
441}
442
443// select all text (continuous view) or current page (single page view)
444void DocumentViewer::selectAll()
445{
446    if ( continuous )
447    {
448        for ( long i = 0; i < totalpages; i++ )
449        {
450            selection[ i ].x1 = 0;
451            selection[ i ].y1 = 0;
452            selection[ i ].x2 = pagesizes[ i ].x;
453            selection[ i ].y2 = pagesizes[ i ].y;
454            LuDocument::freeRectangles( ev, selrects[ i ] );
455            selrects[ i ] = doc->getSelectionRectangles( ev, i, &(selection[i]) );
456        }
457    }
458    else
459    {
460        selection[ currentpage ].x1 = 0;
461        selection[ currentpage ].y1 = 0;
462        selection[ currentpage ].x2 = pagesizes[ currentpage ].x;
463        selection[ currentpage ].y2 = pagesizes[ currentpage ].y;
464        LuDocument::freeRectangles( ev, selrects[ currentpage ] );
465        selrects[ currentpage ] = doc->getSelectionRectangles( ev, currentpage, &(selection[currentpage]) );
466    }
467
468    Lucide::enableCopy( true );
469    WinInvalidateRect( hWndDoc, NULL, FALSE );
470}
471
472// perform search in document
473void DocumentViewer::searchDocument( const char *_searchString, bool _caseSensitive,
474                                     bool _continueSearch )
475{
476    abortSearch = false;
477    if ( !continueSearch ) {
478        freeRects( foundrects );
479    }
480
481    delete searchString;
482    searchString = newstrdup( _searchString );
483    caseSensitive = _caseSensitive;
484    continueSearch = _continueSearch;
485
486    progressDlg->setBreakFunc( searchabort, this );
487    progressDlg->setText( "" );
488    progressDlg->show( searchthread, this );
489}
490
491// static method, cancels asynch rendering if abortAsynch is true
492void DocumentViewer::searchabort( void *data )
493{
494    ((DocumentViewer *)data)->abortSearch = true;
495}
496
497// static method, thread for asynchronous searching
498void DocumentViewer::searchthread( void *p )
499{
500    DosSetPriority( PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MINIMUM, 0 );
501    DocumentViewer *_this = (DocumentViewer *)p;
502
503    HAB thab = WinInitialize( 0 );
504    HMQ thmq = WinCreateMsgQueue( thab, 0 );
505
506    long i = _this->currentpage;
507    if ( _this->continueSearch && ( _this->currentpage < ( _this->totalpages - 1 ) ) ) {
508        i = _this->currentpage + 1;
509    }
510
511    bool found = false;
512    for ( ; i < _this->totalpages; i++ )
513    {
514        char *fmt = newstrdupL( FIND_SEARCH_PAGE_OF );
515        char *buf = new char[ 255 ];
516        snprintf( buf, 255, fmt, i + 1, _this->totalpages );
517        _this->progressDlg->setText( buf );
518        delete fmt;
519        delete buf;
520
521        _this->foundrects[ i ] = _this->doc->searchText( _this->ev, i,
522                                        (char *)_this->searchString, _this->caseSensitive );
523        if ( _this->foundrects[ i ] != NULL )
524        {
525            found = true;
526            _this->progressDlg->hide();
527            _this->goToPage( i );
528            if ( _this->foundrects[i]->_length > 0 ) {
529                RECTL r;
530                _this->docPosToWinPos( i, &(_this->foundrects[i]->_buffer[0]), &r );
531                _this->scrollToPos( _this->hWndDoc, NULLHANDLE, r.xLeft, r.yBottom, false );
532            }
533            break;
534        }
535
536        if ( _this->abortSearch ) {
537            break;
538        }
539    }
540    _this->progressDlg->hide();
541
542    if ( !found && !_this->abortSearch )
543    {
544        char *notfound = newstrdupL( FIND_NOT_FOUND );
545        WinMessageBox( HWND_DESKTOP, _this->hMainFrame, notfound, NULL,
546                       1, MB_OK | MB_ICONEXCLAMATION | MB_MOVEABLE );
547        delete notfound;
548    }
549
550    WinDestroyMsgQueue( thmq );
551    WinTerminate( thab );
552    _endthread();
553}
554
555// count real zoom level based on specified
556void DocumentViewer::adjustSize()
557{
558    if ( doc != NULL )
559    {
560        width  = pagesizes[ currentpage ].x;
561        height = pagesizes[ currentpage ].y;
562
563        fullwidth = 0;
564        fullheight = 0;
565        for ( long i = 0; i < totalpages; i++ ) {
566            fullwidth = __max( fullwidth, pagesizes[i].x );
567            fullheight += pagesizes[i].y;
568        }
569
570        if ( zoom == -1 ) { // fit width
571            realzoom = (double)cxClient / ( continuous ? fullwidth : width );
572        }
573        else if ( zoom == -2 ) { // fit page
574            realzoom = __min( (double)cxClient / width, (double)cyClient / height );
575        }
576        else {
577            realzoom = zoom;
578        }
579        width *= realzoom;
580        height *= realzoom;
581        fullwidth *= realzoom;
582        fullheight *= realzoom;
583    }
584}
585
586// page redraw
587void DocumentViewer::drawPage()
588{
589    if ( !continuous )
590    {
591        LuDocument::freeRectangles( ev, selrects[ currentpage ] );
592        selrects[ currentpage ] = NULL;
593
594        if ( links != NULL ) {
595            if ( links[ currentpage ] == NULL ) {
596                links[ currentpage ] = doc->getLinkMapping( ev, currentpage );
597            }
598        }
599
600        Lucide::enableCopy( false );
601    }
602    WinSendMsg( hWndDoc, WM_SIZE, MPFROM2SHORT( cxClient, cyClient ),
603                MPFROM2SHORT( cxClient, cyClient ) );
604    WinInvalidateRect( hWndDoc, NULL, FALSE );
605}
606
607
608// handles vertical scrolling
609MRESULT DocumentViewer::vertScroll( HWND hwnd, MPARAM mp2, HRGN hrgn )
610{
611    if ( fullscreen ) {
612        return ( MRFROMLONG( 0 ) );
613    }
614
615    sVscrollInc = 0;
616
617    switch ( SHORT2FROMMP( mp2 ) )
618    {
619        case SB_LINEUP:
620            sVscrollInc = -(__max( LINE_HEIGHT, VScrollStep ));
621            break ;
622        case SB_LINEDOWN:
623            sVscrollInc = __max( LINE_HEIGHT, VScrollStep );
624            break;
625        case SB_PAGEUP:
626            sVscrollInc = __min( -1, -( cyClient - LINE_HEIGHT ) );
627            break;
628        case SB_PAGEDOWN:
629            sVscrollInc = __max( 1, cyClient - LINE_HEIGHT );
630            break;
631        case SB_SLIDERTRACK:
632        case SB_SLIDERPOSITION:
633            sVscrollInc = ( SHORT1FROMMP( mp2 ) - sVscrollPos ) * VScrollStep;
634            break;
635    }
636
637    sVscrollInc = __max( -sVscrollPos * VScrollStep, __min( sVscrollInc,
638                              ( sVscrollMax - sVscrollPos ) * VScrollStep ) );
639
640    if ( sVscrollInc != 0 )
641    {
642        sVscrollPos += (SHORT)( sVscrollInc / VScrollStep );
643        WinScrollWindow( hwnd, 0, sVscrollInc, NULL, NULL, hrgn, NULL, SW_INVALIDATERGN );
644        WinSendMsg( hWndVscroll, SBM_SETPOS, MPFROMSHORT( sVscrollPos ), MPVOID );
645        WinUpdateWindow( hwnd );
646        sVscrollInc = 0;
647    }
648    return ( MRFROMLONG( 0 ) );
649}
650
651// handles horizontal scrolling
652MRESULT DocumentViewer::horizScroll( HWND hwnd, MPARAM mp2, HRGN hrgn )
653{
654    if ( fullscreen ) {
655        return ( MRFROMLONG( 0 ) );
656    }
657
658    sHscrollInc = 0;
659
660    switch ( SHORT2FROMMP( mp2 ) )
661    {
662        case SB_LINELEFT:
663            sHscrollInc = -LINE_HEIGHT;
664            break;
665        case SB_LINERIGHT:
666            sHscrollInc = LINE_HEIGHT;
667            break;
668        case SB_PAGELEFT:
669            sHscrollInc = __min( -1, -( cxClient - LINE_HEIGHT ) );
670            break;
671        case SB_PAGERIGHT:
672            sHscrollInc = __max( 1, cxClient - LINE_HEIGHT );
673            break;
674        case SB_SLIDERTRACK:
675        case SB_SLIDERPOSITION:
676            sHscrollInc = SHORT1FROMMP( mp2 ) - sHscrollPos;
677            break;
678    }
679
680    sHscrollInc = __max( -sHscrollPos, __min( sHscrollInc, sHscrollMax - sHscrollPos ) );
681
682    if ( sHscrollInc != 0 )
683    {
684        sHscrollPos += (SHORT)sHscrollInc;
685        WinScrollWindow( hwnd, -sHscrollInc, 0, NULL, NULL, hrgn, NULL, SW_INVALIDATERGN );
686        WinSendMsg( hWndHscroll, SBM_SETPOS, MPFROMSHORT( sHscrollPos ), MPVOID );
687        WinUpdateWindow( hwnd );
688        sHscrollInc = 0;
689    }
690    return ( MRFROMLONG( 0 ) );
691}
692
693
694// handles WM_SIZE message
695// creates appropriate hps buffer, sets scrollbars limits
696void DocumentViewer::wmSize( HWND hwnd, MPARAM mp2 )
697{
698    if ( !WinIsWindowShowing( hwnd ) ) {
699        return;
700    }
701
702    cxClient = SHORT1FROMMP( mp2 );
703    cyClient = SHORT2FROMMP( mp2 );
704
705    double relativeScrollPos = ( sVscrollMax == 0 ) ? 0 :
706                                    (double)sVscrollPos / (double)sVscrollMax;
707
708    adjustSize();
709
710    if ( ( hpsBuffer != NULLHANDLE ) && ( hdcBuffer != NULLHANDLE ) ) {
711        DestroyGraphicsBuffer( hpsBuffer, hdcBuffer );
712        hpsBuffer = hdcBuffer = NULLHANDLE;
713    }
714
715    HPS hps = WinGetPS( hwnd );
716    RECTL rectl = { 0, 0, cxClient, cyClient };
717    CreateGraphicsBuffer( hab, &rectl, hps, &hpsBuffer, &hdcBuffer );
718    WinReleasePS( hps );
719
720    if ( fullscreen )
721    {
722        sHscrollMax = 0;
723        sHscrollPos = 0;
724        realVscrollMax = 0;
725        VScrollStep = 1;
726        sVscrollPos = 0;
727    }
728    else
729    {
730        sHscrollMax = (SHORT)__max( 0, ( continuous ? fullwidth : width ) - cxClient );
731        sHscrollPos = __min( sHscrollPos, sHscrollMax );
732
733        WinSendMsg( hWndHscroll, SBM_SETSCROLLBAR,
734                    MPFROMSHORT(sHscrollPos), MPFROM2SHORT(0, sHscrollMax) );
735        WinSendMsg( hWndHscroll, SBM_SETTHUMBSIZE,
736                    MPFROM2SHORT( cxClient, width ), MPVOID );
737        WinEnableWindow( hWndHscroll, (BOOL)( sHscrollMax != 0 ) );
738
739        if ( continuous )
740        {
741            realVscrollMax = __max( 0, fullheight - cyClient );
742            VScrollStep = LINE_HEIGHT;
743            ULONG ssize = realVscrollMax / VScrollStep;
744            while ( ssize > 32000 ) {
745                VScrollStep += LINE_HEIGHT;
746                ssize = realVscrollMax / VScrollStep;
747            }
748
749            sVscrollMax = (SHORT)ssize;
750        }
751        else {
752            realVscrollMax = sVscrollMax = (SHORT)__max( 0, height - cyClient );
753            VScrollStep = 1;
754        }
755        sVscrollPos = __min( sVscrollPos, sVscrollMax );
756
757        WinSendMsg( hWndVscroll, SBM_SETSCROLLBAR,
758                    MPFROMSHORT(sVscrollPos), MPFROM2SHORT(0, sVscrollMax) );
759        if ( continuous ) {
760            WinSendMsg( hWndVscroll, SBM_SETTHUMBSIZE,
761                        MPFROM2SHORT( cyClient/VScrollStep, fullheight/VScrollStep ), MPVOID );
762        }
763        else {
764            WinSendMsg( hWndVscroll, SBM_SETTHUMBSIZE,
765                        MPFROM2SHORT( cyClient, height ), MPVOID );
766        }
767        WinEnableWindow( hWndVscroll, (BOOL)( sVscrollMax != 0 ) );
768
769        SHORT realScrollPos = (SHORT)(sVscrollMax * relativeScrollPos);
770        vertScroll( hWndDoc, MPFROM2SHORT( realScrollPos, SB_SLIDERPOSITION ), NULLHANDLE );
771    }
772}
773
774// returns true if subrect inside rect
775inline bool isSubrect( PRECTL rect, PRECTL subrect )
776{
777    return ( ( subrect->xLeft >= rect->xLeft ) &&
778             ( subrect->yBottom >= rect->yBottom ) &&
779             ( subrect->xRight <= rect->xRight ) &&
780             ( subrect->yTop <= rect->yTop ) );
781}
782
783// static method, cancels asynch rendering if abortAsynch is true
784long _System DocumentViewer::asynchCallbackFnAbort( void *data )
785{
786    return (long)(((DocumentViewer *)data)->abortAsynch);
787}
788
789// static method, draws area during asynch rendering
790long _System DocumentViewer::asynchCallbackFnDraw( void *data )
791{
792    DocumentViewer *_this = (DocumentViewer *)data;
793    HPS hps = WinGetPS( _this->hWndDoc );
794    if ( hps != NULLHANDLE )
795    {
796        PRECTL drawRect = &((*_this->drawareas)[_this->drawareaIndex].drawrect);
797        LONG rclx = drawRect->xRight - drawRect->xLeft;
798        LONG rcly = drawRect->yTop - drawRect->yBottom;
799
800        POINTL aptlPoints[4]={ drawRect->xLeft, drawRect->yBottom,
801                               drawRect->xRight-1, drawRect->yTop-1,
802                               0, 0, rclx, rcly };
803
804        LONG lRop = ROP_SRCCOPY;
805        BITMAPINFO2 pbmi;
806        pbmi.cbFix = 16L;
807        pbmi.cx = rclx;
808        pbmi.cy = rcly;
809        pbmi.cPlanes = 1;
810        pbmi.cBitCount = _this->bpp * 8;
811        GpiDrawBits( hps, _this->pixbuf->getDataPtr( _this->ev ), &pbmi, 4L,
812                     aptlPoints, lRop, BBO_IGNORE );
813
814        WinReleasePS( hps );
815    }
816    return 0;
817}
818
819// static method, thread for asynchronous rendering
820void DocumentViewer::drawthread( void *p )
821{
822    DosSetPriority( PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MINIMUM, 0 );
823    DocumentViewer *_this = (DocumentViewer *)p;
824
825    HAB thab = WinInitialize( 0 );
826    HMQ thmq = WinCreateMsgQueue( thab, 0 );
827
828    ULONG postCnt;
829    while ( !_this->termdraw )
830    {
831        DosWaitEventSem( _this->haveDraw, SEM_INDEFINITE_WAIT );
832        DosResetEventSem( _this->haveDraw, &postCnt );
833        _this->abortAsynch = false;
834
835        if ( ( _this->drawareas != NULL ) && ( _this->doc != NULL ) )
836        {
837            DosRequestMutexSem( _this->todrawAccess, SEM_INDEFINITE_WAIT );
838
839            for ( _this->drawareaIndex = 0;
840                  _this->drawareaIndex < _this->drawareas->size();
841                  _this->drawareaIndex++ )
842            {
843                PageDrawArea *pda = &(*_this->drawareas)[ _this->drawareaIndex ];
844
845                LONG rclx = pda->drawrect.xRight - pda->drawrect.xLeft;
846                LONG rcly = pda->drawrect.yTop - pda->drawrect.yBottom;
847                _this->pixbuf = new LuPixbuf( _this->ev, rclx, rcly, _this->bpp );
848                _this->doc->renderPageToPixbufAsynch( _this->ev, pda->pagenum,
849                       pda->startpos.x, pda->startpos.y, rclx, rcly, _this->realzoom,
850                       _this->rotation, _this->pixbuf,
851                       asynchCallbackFnDraw, asynchCallbackFnAbort, p );
852                delete _this->pixbuf;
853                _this->pixbuf = NULL;
854
855                if ( _this->abortAsynch ) {
856                    break;  // TODO: remove completed areas from drawareas
857                }
858            }
859
860            if ( !_this->abortAsynch )
861            {
862                HPS hps = WinGetPS( _this->hWndDoc );
863                if ( hps != NULLHANDLE ) {
864                    for ( int i = 0; i < _this->drawareas->size(); i++ )
865                    {
866                        PageDrawArea *pda = &(*_this->drawareas)[ i ];
867
868                        _this->drawSelection( pda->pagenum, hps, &pda->drawrect );
869                        _this->drawFound( pda->pagenum, hps, &pda->drawrect );
870                    }
871                    WinReleasePS( hps );
872                }
873                WinSetRectEmpty( _this->hab, &_this->savedRcl );
874                delete _this->drawareas;
875                _this->drawareas = NULL;
876            }
877
878            DosReleaseMutexSem( _this->todrawAccess );
879        }
880    }
881    WinDestroyMsgQueue( thmq );
882    WinTerminate( thab );
883    _endthread();
884}
885
886// handles WM_PAINT if document supports asynch rendering.
887// posts events to drawthread
888void DocumentViewer::wmPaintAsynch( HWND hwnd )
889{
890    RECTL rcl;
891    HPS hps = WinBeginPaint( hwnd, 0L, &rcl );
892    RECTL rclWin = { 0 };
893    WinQueryWindowRect( hwnd, &rclWin );
894    if ( WinEqualRect( hab, &rcl, &rclWin ) ) {
895        GpiErase( hps );
896    }
897    WinEndPaint( hps );
898
899    if ( doc != NULL )
900    {
901        RECTL rclPage = { 0, 0, width, height };
902        if ( height < cyClient )
903        {
904            rclPage.yBottom = cyClient - height;
905            rclPage.yTop = cyClient;
906        }
907        RECTL rclDraw = { 0 };
908        if ( WinIntersectRect( hab, &rclDraw, &rcl, &rclPage ) )
909        {
910            if ( ( drawareas != NULL ) && ( drawareas->size() > 0 ) ) {
911                if ( isSubrect( &((*drawareas)[0].drawrect), &rclDraw ) &&
912                     ( sVscrollInc == 0 ) && ( sHscrollInc == 0 ) ) {
913                    return;
914                }
915            }
916
917            abortAsynch = true;
918            DosRequestMutexSem( todrawAccess, SEM_INDEFINITE_WAIT );
919
920            if ( drawareas == NULL ) {
921                drawareas = new DrawAreas;
922            }
923            if ( drawareas->size() == 0 ) {
924                PageDrawArea pda;
925                memset( &pda, 0, sizeof( pda ) );
926                pda.pagenum = currentpage;
927                drawareas->push_back( pda );
928            }
929
930            PageDrawArea *ppda = &((*drawareas)[0]);
931
932            if ( !WinIsRectEmpty( hab, &ppda->drawrect ) )
933            {
934                if ( sVscrollInc > 0 ) {
935                    ppda->drawrect.yTop    += sVscrollInc;
936                } else if ( sVscrollInc < 0 ) {
937                    ppda->drawrect.yBottom += sVscrollInc;
938                }
939                if ( sHscrollInc > 0 ) {
940                    ppda->drawrect.xLeft  -= sHscrollInc;
941                } else if ( sHscrollInc < 0 ) {
942                    ppda->drawrect.xRight -= sHscrollInc;
943                }
944            }
945            WinUnionRect( hab, &ppda->drawrect, &ppda->drawrect, &rclDraw );
946            ppda->startpos.x = sHscrollPos + ppda->drawrect.xLeft;
947            ppda->startpos.y = ( cyClient - ppda->drawrect.yTop ) + sVscrollPos;
948
949            // workaround ?
950            ppda->drawrect.xRight++;
951            ppda->drawrect.yTop++;
952
953            DosReleaseMutexSem( todrawAccess );
954            DosPostEventSem( haveDraw );
955        }
956    }
957}
958
959
960// handles WM_PAINT if continuous asynchronous rendering used
961void DocumentViewer::wmPaintContAsynch( HWND hwnd )
962{
963    RECTL rcl, rclWin, rclDraw = { 0 };
964    HPS hps = WinBeginPaint( hwnd, 0L, &rcl );
965    GpiErase( hpsBuffer );
966    BlitGraphicsBuffer( hps, hpsBuffer, &rcl );
967    WinEndPaint( hps );
968
969    if ( doc != NULL )
970    {
971        if ( isSubrect( &savedRcl, &rcl ) && ( sVscrollInc == 0 ) && ( sHscrollInc == 0 ) ) {
972            return;
973        }
974
975        abortAsynch = true;
976        DosRequestMutexSem( todrawAccess, SEM_INDEFINITE_WAIT );
977
978        WinQueryWindowRect( hwnd, &rclWin );
979        WinUnionRect( hab, &rcl, &rcl, &savedRcl );
980
981        if ( sVscrollInc > 0 ) {
982            rcl.yTop    += sVscrollInc;
983        } else if ( sVscrollInc < 0 ) {
984            rcl.yBottom += sVscrollInc;
985        }
986        if ( sHscrollInc > 0 ) {
987            rcl.xLeft  -= sHscrollInc;
988        } else if ( sHscrollInc < 0 ) {
989            rcl.xRight -= sHscrollInc;
990        }
991
992        WinIntersectRect( hab, &rclDraw, &rcl, &rclWin );
993        WinCopyRect( hab, &rcl, &rclDraw );
994        WinCopyRect( hab, &savedRcl, &rcl );
995
996        delete drawareas;
997        drawareas = findDrawAreas( &rcl );
998
999        for ( int i = 0; i < drawareas->size(); i++ )
1000        {
1001            PageDrawArea *pda = &(*drawareas)[ i ];
1002
1003            // load links for page if not loaded before
1004            if ( links[ pda->pagenum ] == NULL ) {
1005                links[ pda->pagenum ] = doc->getLinkMapping( ev, pda->pagenum );
1006            }
1007        }
1008        DosReleaseMutexSem( todrawAccess );
1009        DosPostEventSem( haveDraw );
1010
1011        determineCurrentPage();
1012    }
1013}
1014
1015
1016// handles WM_PAINT if single-page synchronous rendering used
1017void DocumentViewer::wmPaint( HWND hwnd )
1018{
1019    RECTL rcl;
1020    HPS hps = WinBeginPaint( hwnd, 0L, &rcl );
1021    GpiErase( hpsBuffer );
1022
1023    if ( doc != NULL )
1024    {
1025        RECTL rclPage = { 0, 0, width, height };
1026        if ( height < cyClient )
1027        {
1028            rclPage.yBottom = cyClient - height;
1029            rclPage.yTop = cyClient;
1030        }
1031        RECTL rclDraw = { 0 };
1032        if ( WinIntersectRect( hab, &rclDraw, &rcl, &rclPage ) )
1033        {
1034            spos_x = sHscrollPos + rclDraw.xLeft;
1035            spos_y = (cyClient - rclDraw.yTop) + sVscrollPos;
1036            LONG rclx = rclDraw.xRight - rclDraw.xLeft;
1037            LONG rcly = rclDraw.yTop - rclDraw.yBottom;
1038
1039            if ( drawPS )
1040            {
1041                doc->renderPageToPS( ev, currentpage, spos_x, spos_y, rclx, rcly,
1042                                     realzoom, rotation, hpsBuffer, &rclDraw );
1043            }
1044            else
1045            {
1046                pixbuf = new LuPixbuf( ev, rclx, rcly, bpp );
1047                POINTL aptlPoints[4]={ rclDraw.xLeft, rclDraw.yBottom,
1048                                       rclDraw.xRight-1, rclDraw.yTop-1,
1049                                       0, 0, rclx, rcly };
1050
1051                doc->renderPageToPixbuf( ev, currentpage, spos_x, spos_y,
1052                                         rclx, rcly, realzoom, rotation, pixbuf );
1053                LONG lRop = ROP_SRCCOPY;
1054                BITMAPINFO2 pbmi;
1055                pbmi.cbFix = 16L;
1056                pbmi.cx = rclx;
1057                pbmi.cy = rcly;
1058                pbmi.cPlanes = 1;
1059                pbmi.cBitCount = bpp * 8;
1060                GpiDrawBits( hpsBuffer, pixbuf->getDataPtr( ev ), &pbmi, 4L,
1061                             aptlPoints, lRop, BBO_IGNORE );
1062                delete pixbuf;
1063                pixbuf = NULL;
1064            }
1065
1066            drawSelection( currentpage, hpsBuffer, &rclDraw );
1067            drawFound( currentpage, hpsBuffer, &rclDraw );
1068
1069            BlitGraphicsBuffer( hps, hpsBuffer, &rcl );
1070        }
1071    }
1072    else {
1073        BlitGraphicsBuffer( hps, hpsBuffer, &rcl );
1074    }
1075    WinEndPaint( hps );
1076}
1077
1078
1079// founds number of page at specified vertical position
1080// for continuous view only
1081long DocumentViewer::posToPagenum( LONG yPosWin, double *pageRest )
1082{
1083    double yPos = ( cyClient - yPosWin ) + ( sVscrollPos * VScrollStep );
1084    double pgend = 0;
1085    for ( long i = 0; i < totalpages; i++ )
1086    {
1087        pgend += ( pagesizes[ i ].y * realzoom );
1088        if ( yPos < pgend ) {
1089            *pageRest = pgend - yPos;
1090            return i;
1091        }
1092    }
1093    return 0;
1094}
1095
1096// founds vertical position of specified
1097// for continuous view only
1098double DocumentViewer::pagenumToPos( long pagenum )
1099{
1100    double ypos = 0;
1101    for ( long i = 0; i < pagenum; i++ ) {
1102        ypos += pagesizes[ i ].y;
1103    }
1104    return ypos * realzoom;
1105}
1106
1107// founds pages and it's areas to draw
1108// for continuous view only
1109DrawAreas *DocumentViewer::findDrawAreas( PRECTL r )
1110{
1111    DrawAreas *areas = new DrawAreas;
1112    if ( doc != NULL )
1113    {
1114        long foundpage = -1;
1115        double pageRest;
1116        for ( LONG i = r->yTop; i >= r->yBottom; i-- )
1117        {
1118            pageRest = 0;
1119            long pg = posToPagenum( i, &pageRest );
1120            if ( pg != foundpage )
1121            {
1122                PageDrawArea pda;
1123                pda.pagenum = pg;
1124                pda.drawrect.xLeft   = __min( pagesizes[ pg ].x * realzoom, r->xLeft );
1125                pda.drawrect.yBottom = __max( i - pageRest, r->yBottom );
1126                pda.drawrect.xRight  = __min( pagesizes[ pg ].x * realzoom, r->xRight );
1127                pda.drawrect.yTop    = i;
1128
1129                pda.startpos.x = sHscrollPos + pda.drawrect.xLeft;
1130                pda.startpos.y = ( pagesizes[ pg ].y * realzoom ) - pageRest;
1131
1132                areas->push_back( pda );
1133                foundpage = pg;
1134                i -= pageRest;
1135            }
1136        }
1137    }
1138
1139    return areas;
1140}
1141
1142
1143// found current page in continuous view mode.
1144// it's a page which occupes a most larger area in the window.
1145void DocumentViewer::determineCurrentPage()
1146{
1147    RECTL rcl = { 0 };
1148    WinQueryWindowRect( hWndDoc, &rcl );
1149    DrawAreas *areas = findDrawAreas( &rcl );
1150    long pg = 0;
1151    long sz = 0;
1152    for ( int i = 0; i < areas->size(); i++ )
1153    {
1154        PageDrawArea *pda = &(*areas)[ i ];
1155        long pgsz = pda->drawrect.yTop - pda->drawrect.yBottom;
1156        if ( pgsz > sz ) {
1157            pg = pda->pagenum;
1158            sz = pgsz;
1159        }
1160    }
1161    delete areas;
1162
1163    if ( pg != currentpage ) {
1164        currentpage = pg;
1165        Lucide::checkNavigationMenus();
1166    }
1167}
1168
1169
1170// handles WM_PAINT if continuous synchronous rendering used
1171void DocumentViewer::wmPaintCont( HWND hwnd )
1172{
1173    RECTL rcl;
1174    HPS hps = WinBeginPaint( hwnd, 0L, &rcl );
1175    GpiErase( hpsBuffer );
1176
1177    if ( doc != NULL )
1178    {
1179        delete drawareas;
1180        drawareas = findDrawAreas( &rcl );
1181
1182        for ( int i = 0; i < drawareas->size(); i++ )
1183        {
1184            PageDrawArea *pda = &(*drawareas)[ i ];
1185
1186            // load links for page if not loaded before
1187            if ( links[ pda->pagenum ] == NULL ) {
1188                links[ pda->pagenum ] = doc->getLinkMapping( ev, pda->pagenum );
1189            }
1190
1191            spos_x = pda->startpos.x;
1192            //spos_y = ( cyClient - pda->drawrect.yTop ) + ( sVscrollPos * VScrollStep );
1193            spos_y = pda->startpos.y;
1194            LONG rclx = pda->drawrect.xRight - pda->drawrect.xLeft;
1195            LONG rcly = pda->drawrect.yTop - pda->drawrect.yBottom;
1196
1197            if ( drawPS )
1198            {
1199                doc->renderPageToPS( ev, pda->pagenum, spos_x, spos_y, rclx, rcly,
1200                                     realzoom, rotation, hpsBuffer, &(pda->drawrect) );
1201            }
1202            else
1203            {
1204                pixbuf = new LuPixbuf( ev, rclx, rcly, bpp );
1205                POINTL aptlPoints[4]={ pda->drawrect.xLeft, pda->drawrect.yBottom,
1206                                       pda->drawrect.xRight-1, pda->drawrect.yTop-1,
1207                                       0, 0, rclx, rcly };
1208                doc->renderPageToPixbuf( ev, pda->pagenum, spos_x, spos_y,
1209                                         rclx, rcly, realzoom, rotation, pixbuf );
1210                LONG lRop = ROP_SRCCOPY;
1211                BITMAPINFO2 pbmi;
1212                pbmi.cbFix = 16L;
1213                pbmi.cx = rclx;
1214                pbmi.cy = rcly;
1215                pbmi.cPlanes = 1;
1216                pbmi.cBitCount = bpp * 8;
1217                GpiDrawBits( hpsBuffer, pixbuf->getDataPtr( ev ), &pbmi, 4L,
1218                             aptlPoints, lRop, BBO_IGNORE );
1219                delete pixbuf;
1220                pixbuf = NULL;
1221            }
1222
1223            drawSelection( pda->pagenum, hpsBuffer, &pda->drawrect );
1224            drawFound( pda->pagenum, hpsBuffer, &pda->drawrect );
1225        }
1226        delete drawareas;
1227        drawareas = NULL;
1228    }
1229    BlitGraphicsBuffer( hps, hpsBuffer, &rcl );
1230    WinEndPaint( hps );
1231
1232    if ( doc != NULL ) {
1233        determineCurrentPage();
1234    }
1235}
1236
1237
1238// Rotates document rectangle
1239void DocumentViewer::rotateRectangle( long pagenum, LuRectangle *r )
1240{
1241    double tmp_x1 = r->x1;
1242    double tmp_y1 = r->y1;
1243    double tmp_x2 = r->x2;
1244    double tmp_y2 = r->y2;
1245
1246    double w = pagesizes[ pagenum ].x;
1247    double h = pagesizes[ pagenum ].y;
1248
1249    if ( rotation == 90 ) {
1250        r->x1 = tmp_y1;
1251        r->y1 = w - tmp_x1;
1252        r->x2 = tmp_y2;
1253        r->y2 = w - tmp_x2;
1254    }
1255    else if ( rotation == 180 )
1256    {
1257        r->x1 = w - tmp_x2;
1258        r->y1 = h - tmp_y2;
1259        r->x2 = w - tmp_x1;
1260        r->y2 = h - tmp_y1;
1261    }
1262    else if ( rotation == 270 )
1263    {
1264        r->x1 = h - tmp_y1;
1265        r->y1 = tmp_x1;
1266        r->x2 = h - tmp_y2;
1267        r->y2 = tmp_x2;
1268    }
1269
1270    if ( r->x1 > r->x2 ) {
1271        double tmp = r->x1;
1272        r->x1 = r->x2;
1273        r->x2 = tmp;
1274    }
1275
1276    if ( r->y1 > r->y2 ) {
1277        double tmp = r->y1;
1278        r->y1 = r->y2;
1279        r->y2 = tmp;
1280    }
1281}
1282
1283// converts window position to document position
1284// single page mode only
1285void DocumentViewer::winPosToDocPos( PPOINTL startpoint, PPOINTL endpoint, LuRectangle *r )
1286{
1287    r->x1 = ( startpoint->x + sHscrollPos ) / realzoom;
1288    r->y1 = ( ( cyClient - startpoint->y ) + sVscrollPos ) / realzoom;
1289    r->x2 = ( endpoint->x + sHscrollPos ) / realzoom;
1290    r->y2 = ( ( cyClient - endpoint->y ) + sVscrollPos ) / realzoom;
1291
1292    rotateRectangle( currentpage, r );
1293}
1294
1295// converts window position to document position
1296// continuous view mode only
1297void DocumentViewer::winPosToDocPos( PageDrawArea *pda, LuRectangle *r )
1298{
1299    r->x1 = ( sHscrollPos + pda->drawrect.xLeft ) / realzoom;;
1300    r->y1 = pda->startpos.y / realzoom;
1301    r->x2 = ( ( pda->drawrect.xRight - pda->drawrect.xLeft ) / realzoom ) + r->x1;
1302    r->y2 = ( ( pda->drawrect.yTop - pda->drawrect.yBottom ) / realzoom ) + r->y1;
1303
1304    rotateRectangle( pda->pagenum, r );
1305}
1306
1307// converts document position to window position
1308void DocumentViewer::docPosToWinPos( long pagenum, LuRectangle *r, PRECTL rcl )
1309{
1310    double yplus = continuous ? pagenumToPos( pagenum ) : 0;
1311    double w = pagesizes[ pagenum ].x;
1312    double h = pagesizes[ pagenum ].y;
1313
1314    double tmp_x1 = r->x1;
1315    double tmp_y1 = r->y1;
1316    double tmp_x2 = r->x2;
1317    double tmp_y2 = r->y2;
1318
1319    if ( rotation == 90 )
1320    {
1321        tmp_x1 = w - r->y2;
1322        tmp_y1 = r->x1;
1323        tmp_x2 = w - r->y1;
1324        tmp_y2 = r->x2;
1325    }
1326    else if ( rotation == 180 )
1327    {
1328        tmp_x1 = w - r->x2;
1329        tmp_y1 = h - r->y2;
1330        tmp_x2 = w - r->x1;
1331        tmp_y2 = h - r->y1;
1332    }
1333    else if ( rotation == 270 )
1334    {
1335        tmp_x1 = r->y1;
1336        tmp_y1 = h - r->x2;
1337        tmp_x2 = r->y2;
1338        tmp_y2 = h - r->x1;
1339    }
1340
1341    rcl->xLeft   = ( tmp_x1 * realzoom ) - sHscrollPos;
1342    rcl->yBottom = cyClient - ( yplus + ( tmp_y2 * realzoom ) ) + ( sVscrollPos * VScrollStep );
1343    rcl->xRight  = ( tmp_x2 * realzoom ) - sHscrollPos;
1344    rcl->yTop    = cyClient - ( yplus + ( tmp_y1 * realzoom ) ) + ( sVscrollPos * VScrollStep );
1345}
1346
1347// creates region from sequence of rectangles
1348HRGN DocumentViewer::rectsToRegion( long pagenum, HPS hps, LuDocument_LuRectSequence *rects )
1349{
1350    HRGN hrgn = GpiCreateRegion( hps, 0, NULL );
1351    if ( rects != NULL )
1352    {
1353        RECTL r = {0};
1354        for ( int i = 0; i < rects->_length; i++ )
1355        {
1356            docPosToWinPos( pagenum, &(rects->_buffer[i]), &r );
1357            HRGN tmprgn = GpiCreateRegion( hps, 1, &r );
1358            GpiCombineRegion( hps, hrgn, hrgn, tmprgn, CRGN_OR );
1359            GpiDestroyRegion( hps, tmprgn );
1360        }
1361    }
1362    return hrgn;
1363}
1364
1365// draws selected area in window, using XOR mix
1366// drawing area may be restricted by r rectangle
1367void DocumentViewer::drawSelection( long pagenum, HPS hps, PRECTL r )
1368{
1369    GpiSetMix( hps, FM_XOR );
1370    GpiSetColor( hps, CLR_YELLOW );
1371    HRGN selectRegion = rectsToRegion( pagenum, hps, selrects[ pagenum ] );
1372    if ( r != NULL )
1373    {
1374        HRGN tmprgn = GpiCreateRegion( hps, 1, r );
1375        GpiCombineRegion( hps, selectRegion, selectRegion, tmprgn, CRGN_AND );
1376        GpiDestroyRegion( hps, tmprgn );
1377    }
1378    GpiPaintRegion( hps, selectRegion );
1379    GpiDestroyRegion( hps, selectRegion );
1380}
1381
1382void DocumentViewer::drawFound( long pagenum, HPS hps, PRECTL r )
1383{
1384    GpiSetMix( hps, FM_XOR );
1385    GpiSetColor( hps, CLR_CYAN );
1386    HRGN selectRegion = rectsToRegion( pagenum, hps, foundrects[ pagenum ] );
1387    if ( r != NULL )
1388    {
1389        HRGN tmprgn = GpiCreateRegion( hps, 1, r );
1390        GpiCombineRegion( hps, selectRegion, selectRegion, tmprgn, CRGN_AND );
1391        GpiDestroyRegion( hps, tmprgn );
1392    }
1393    GpiPaintRegion( hps, selectRegion );
1394    GpiDestroyRegion( hps, selectRegion );
1395}
1396
1397// scrolls window to specified pos (optionally with text selection)
1398void DocumentViewer::scrollToPos( HWND hwnd, HRGN hrgn, LONG xpos, LONG ypos,
1399                                  bool withSelection )
1400{
1401    SHORT xinc = 0;
1402    SHORT yinc = 0;
1403
1404    if ( ( xpos < 0 ) && ( sHscrollPos > 0 ) ) {
1405        xinc = __max( sHscrollPos * -1, xpos );
1406    } else if ( ( xpos > cxClient ) && ( sHscrollPos < sHscrollMax ) ) {
1407        xinc = __min( sHscrollMax - sHscrollPos, xpos - cxClient );
1408    }
1409    if ( ( ypos < 0 ) && ( sVscrollPos < sVscrollMax ) ) {
1410        yinc = __min( ( sVscrollMax - sVscrollPos ) * VScrollStep, ypos * -1 );
1411    }
1412    else if ( ( ypos > cyClient ) && ( sVscrollPos > 0 ) ) {
1413        yinc = __max( ( sVscrollPos * -1 ) * VScrollStep, cyClient - ypos );
1414    }
1415
1416    if ( xinc != 0 ) {
1417        horizScroll( hwnd, MPFROM2SHORT( sHscrollPos + xinc, SB_SLIDERPOSITION ), hrgn );
1418        if ( withSelection ) {
1419            selectionStart.x -= xinc;
1420        }
1421    }
1422
1423    if ( yinc != 0 )
1424    {
1425        SHORT remainder = yinc % VScrollStep;
1426        if ( remainder != 0 ) {
1427            SHORT add = VScrollStep - remainder;
1428            yinc += ( ( yinc > 0 ) ? add : -add );
1429        }
1430
1431        vertScroll( hwnd, MPFROM2SHORT( ( ( sVscrollPos * VScrollStep ) + yinc ) / VScrollStep,
1432                                        SB_SLIDERPOSITION ), hrgn );
1433        if ( withSelection ) {
1434            selectionStart.y += yinc;
1435        }
1436    }
1437}
1438
1439// handles WM_MOUSEMOVE
1440// performs text selection if mouse button pressed
1441// changes mouse ptr to 'hand' if it moves over link area
1442BOOL DocumentViewer::wmMouseMove( HWND hwnd, SHORT xpos, SHORT ypos )
1443{
1444    if ( mousePressed && ( doc != NULL ) )
1445    {
1446        selectionEnd.x = xpos;
1447        selectionEnd.y = ypos;
1448
1449        if ( continuous )
1450        {
1451            scrollToPos( hwnd, NULLHANDLE, xpos, ypos, true );
1452
1453            RECTL selRect = {
1454                selectionStart.x < selectionEnd.x ? selectionStart.x : selectionEnd.x,
1455                selectionStart.y < selectionEnd.y ? selectionStart.y : selectionEnd.y,
1456                selectionStart.x < selectionEnd.x ? selectionEnd.x : selectionStart.x,
1457                selectionStart.y < selectionEnd.y ? selectionEnd.y : selectionStart.y
1458            };
1459
1460            DrawAreas *areas = findDrawAreas( &selRect );
1461
1462            HPS hps = WinGetPS( hwnd );
1463            GpiSetMix( hps, FM_XOR );
1464            GpiSetColor( hps, CLR_YELLOW );
1465
1466            for ( int i = 0; i < areas->size(); i++ )
1467            {
1468                PageDrawArea *pda = &(*areas)[ i ];
1469
1470                winPosToDocPos( pda, &(selection[pda->pagenum]) );
1471
1472                HRGN clearRegion = rectsToRegion( pda->pagenum, hps, selrects[ pda->pagenum ] );
1473                LuDocument::freeRectangles( ev, selrects[ pda->pagenum ] );
1474                selrects[ pda->pagenum ] = doc->getSelectionRectangles( ev, pda->pagenum, &(selection[pda->pagenum]) );
1475                HRGN selectRegion = rectsToRegion( pda->pagenum, hps, selrects[ pda->pagenum ] );
1476                GpiCombineRegion( hps, selectRegion, selectRegion, clearRegion, CRGN_XOR );
1477                GpiPaintRegion( hps, selectRegion );
1478                GpiDestroyRegion( hps, clearRegion );
1479                GpiDestroyRegion( hps, selectRegion );
1480            }
1481
1482            WinReleasePS( hps );
1483            delete areas;
1484        }
1485        else
1486        {
1487            winPosToDocPos( &selectionStart, &selectionEnd, &(selection[currentpage]) );
1488
1489            HPS hps = WinGetPS( hwnd );
1490            HRGN scrolledRegion = NULLHANDLE; //GpiCreateRegion( hps, 0, NULL );
1491
1492            scrollToPos( hwnd, scrolledRegion, xpos, ypos, true );
1493
1494            // 127/191/255
1495            //LONG lclr = ( 127 << 16 ) | ( 191 << 8 ) | 255;
1496            //LONG lclr = ( 128 << 16 ) | ( 64 << 8 );
1497            //LONG ltabl[ 1 ] = { lclr };
1498            //GpiCreateLogColorTable( hps, 0, LCOLF_CONSECRGB, 100, 1, ltabl );
1499
1500            GpiSetMix( hps, FM_XOR );
1501            GpiSetColor( hps, CLR_YELLOW );
1502            //GpiSetColor( hps, 100 );
1503
1504            HRGN clearRegion = rectsToRegion( currentpage, hps, selrects[ currentpage ] );
1505            LuDocument::freeRectangles( ev, selrects[ currentpage ] );
1506            if ( ( selectionStart.x == selectionEnd.x ) &&
1507                 ( selectionStart.y == selectionEnd.y ) ) {
1508                selrects[ currentpage ] = NULL;
1509                memset( &(selection[ currentpage ]), 0, sizeof( LuRectangle ) );
1510            }
1511            else {
1512                selrects[ currentpage ] = doc->getSelectionRectangles( ev, currentpage, &(selection[currentpage]) );
1513            }
1514            HRGN selectRegion = rectsToRegion( currentpage, hps, selrects[ currentpage ] );
1515            GpiCombineRegion( hps, selectRegion, selectRegion, clearRegion, CRGN_XOR );
1516            //GpiCombineRegion( hps, selectRegion, selectRegion, scrolledRegion, CRGN_DIFF );
1517            GpiPaintRegion( hps, selectRegion );
1518            GpiDestroyRegion( hps, clearRegion );
1519            GpiDestroyRegion( hps, selectRegion );
1520            //GpiDestroyRegion( hps, scrolledRegion );
1521
1522            WinReleasePS( hps );
1523        }
1524    }
1525    else if ( links != NULL )
1526    {
1527        long pg = currentpage;
1528        if ( continuous ) {
1529            double tmp;
1530            pg = posToPagenum( ypos, &tmp );
1531        }
1532
1533        if ( links[ pg ] != NULL )
1534        {
1535            for ( int i = 0; i < links[ pg ]->_length; i++ )
1536            {
1537                RECTL r = {0};
1538                docPosToWinPos( pg, &(links[ pg ]->_buffer[i].area), &r );
1539
1540                POINTL ptl = { xpos, ypos };
1541                if ( WinPtInRect( hab, &r, &ptl ) ) {
1542                    WinSetPointer( HWND_DESKTOP, handptr );
1543                    return TRUE;
1544                }
1545            }
1546        }
1547    }
1548
1549    return FALSE;
1550}
1551
1552// handles WM_BUTTON1CLICK
1553BOOL DocumentViewer::wmClick( HWND hwnd, SHORT xpos, SHORT ypos )
1554{
1555    if ( links == NULL ) {
1556        return FALSE;
1557    }
1558
1559    long pg = currentpage;
1560    if ( continuous ) {
1561        double tmp;
1562        pg = posToPagenum( ypos, &tmp );
1563    }
1564
1565    if ( links[ pg ] != NULL )
1566    {
1567        for ( int i = 0; i < links[ pg ]->_length; i++ )
1568        {
1569            RECTL r = {0};
1570            docPosToWinPos( pg, &(links[ pg ]->_buffer[i].area), &r );
1571
1572            POINTL ptl = { xpos, ypos };
1573            if ( WinPtInRect( hab, &r, &ptl ) )
1574            {
1575                if ( links[ pg ]->_buffer[i].link.type == LU_LINK_TYPE_EXTERNAL_URI )
1576                {
1577                    WinMessageBox( HWND_DESKTOP, hMainFrame,
1578                        links[ pg ]->_buffer[i].link.uri, "URI", 1,
1579                        MB_OK | MB_INFORMATION | MB_MOVEABLE );
1580                }
1581                else if ( links[ pg ]->_buffer[i].link.type == LU_LINK_TYPE_TITLE )
1582                {
1583                    char *title = links[ pg ]->_buffer[i].link.title;
1584                    if ( title == NULL ) {
1585                        title = "???";
1586                    }
1587                    WinMessageBox( HWND_DESKTOP, hMainFrame,
1588                        title, "?", 1, MB_OK | MB_INFORMATION | MB_MOVEABLE );
1589                }
1590                else if ( links[ pg ]->_buffer[i].link.type == LU_LINK_TYPE_PAGE )
1591                {
1592                    goToPage( links[ pg ]->_buffer[i].link.page );
1593                }
1594
1595                return TRUE;
1596            }
1597        }
1598    }
1599
1600    return FALSE;
1601}
1602
1603
1604BOOL DocumentViewer::wmChar( HWND hwnd, MPARAM mp1, MPARAM mp2 )
1605{
1606    USHORT fsflags = SHORT1FROMMP( mp1 );
1607    USHORT usch = SHORT1FROMMP( mp2 );
1608    USHORT usvk = SHORT2FROMMP( mp2 );
1609
1610    if ( ( fsflags & KC_VIRTUALKEY ) && !( fsflags & KC_KEYUP ) )
1611    {
1612        switch ( usvk )
1613        {
1614            case VK_UP:
1615                WinSendMsg( hwnd, WM_VSCROLL, MPVOID, MPFROM2SHORT( 0, SB_LINEUP ) );
1616                return TRUE;
1617
1618            case VK_DOWN:
1619                WinSendMsg( hwnd, WM_VSCROLL, MPVOID, MPFROM2SHORT( 0, SB_LINEDOWN ) );
1620                return TRUE;
1621
1622            case VK_PAGEUP:
1623                if ( fsflags & KC_CTRL )
1624                {
1625                    if ( fullscreen ) {
1626                        goToPage( 0 );
1627                    } else {
1628                        vertScroll( hwnd, MPFROM2SHORT( 0, SB_SLIDERPOSITION ), NULLHANDLE );
1629                    }
1630                }
1631                else
1632                {
1633                    if ( fullscreen ) {
1634                        goToPage( currentpage - 1 );
1635                    } else {
1636                        WinSendMsg( hwnd, WM_VSCROLL, MPVOID, MPFROM2SHORT( 0, SB_PAGEUP ) );
1637                    }
1638                }
1639                return TRUE;
1640
1641            case VK_PAGEDOWN:
1642                if ( fsflags & KC_CTRL )
1643                {
1644                    if ( fullscreen ) {
1645                        goToPage( totalpages - 1 );
1646                    } else {
1647                        vertScroll( hwnd, MPFROM2SHORT( sVscrollMax, SB_SLIDERPOSITION ), NULLHANDLE );
1648                    }
1649                }
1650                else
1651                {
1652                    if ( fullscreen ) {
1653                        goToPage( currentpage + 1 );
1654                    } else {
1655                        WinSendMsg( hwnd, WM_VSCROLL, MPVOID, MPFROM2SHORT( 0, SB_PAGEDOWN ) );
1656                    }
1657                }
1658                return TRUE;
1659
1660            case VK_LEFT:
1661                WinSendMsg( hwnd, WM_HSCROLL, MPVOID, MPFROM2SHORT( 0, SB_LINELEFT ) );
1662                return TRUE;
1663
1664            case VK_RIGHT:
1665                WinSendMsg( hwnd, WM_HSCROLL, MPVOID, MPFROM2SHORT( 0, SB_LINERIGHT ) );
1666                return TRUE;
1667
1668            case VK_HOME:
1669                horizScroll( hwnd, MPFROM2SHORT( 0, SB_SLIDERPOSITION ), NULLHANDLE );
1670                return TRUE;
1671
1672            case VK_END:
1673                horizScroll( hwnd, MPFROM2SHORT( sHscrollMax, SB_SLIDERPOSITION ), NULLHANDLE );
1674                return TRUE;
1675        }
1676    }
1677
1678    // Ctrl+L
1679    if ( ( fsflags & KC_CTRL ) && !( fsflags & KC_KEYUP ) && ( toupper( usch ) == 'L' ) )
1680    {
1681        Lucide::toggleFullscreen();
1682        return TRUE;
1683    }
1684
1685    // +
1686    if ( ( fsflags & KC_CHAR ) && !( fsflags & KC_KEYUP ) && ( usch == '+' ) ) {
1687        goToPage( currentpage + 1 );
1688        return TRUE;
1689    }
1690    // -
1691    if ( ( fsflags & KC_CHAR ) && !( fsflags & KC_KEYUP ) && ( usch == '-' ) ) {
1692        goToPage( currentpage - 1 );
1693        return TRUE;
1694    }
1695
1696    return FALSE;
1697}
1698
1699// handles WM_BUTTON1DOWN
1700void DocumentViewer::wmButton1Down( HWND hwnd, SHORT xpos, SHORT ypos )
1701{
1702    if ( continuous && ( doc != NULL ) )
1703    {
1704        // clear selection
1705        RECTL rcl = { 0 };
1706        WinQueryWindowRect( hwnd, &rcl );
1707        DrawAreas *areas = findDrawAreas( &rcl );
1708
1709        HPS hps = WinGetPS( hwnd );
1710        GpiSetMix( hps, FM_XOR );
1711        GpiSetColor( hps, CLR_YELLOW );
1712
1713        for ( int i = 0; i < areas->size(); i++ )
1714        {
1715            PageDrawArea *pda = &(*areas)[ i ];
1716
1717            HRGN clearRegion = rectsToRegion( pda->pagenum, hps, selrects[ pda->pagenum ] );
1718            GpiPaintRegion( hps, clearRegion );
1719            GpiDestroyRegion( hps, clearRegion );
1720        }
1721        WinReleasePS( hps );
1722        delete areas;
1723
1724        freeRects( selrects );
1725
1726        memset( selection, 0, sizeof( LuRectangle ) * totalpages );
1727    }
1728
1729    WinSetCapture( HWND_DESKTOP, hwnd );
1730    mousePressed = true;
1731    selectionStart.x = xpos;
1732    selectionStart.y = ypos;
1733}
1734
1735// handles WM_BUTTON1UP
1736void DocumentViewer::wmButton1Up()
1737{
1738    WinSetCapture( HWND_DESKTOP, NULLHANDLE );
1739    mousePressed = false;
1740
1741    bool haveSelection = false;
1742    for ( long i = 0; i < totalpages; i++ ) {
1743        if ( selrects[ i ] != NULL ) {
1744            haveSelection = true;
1745            break;
1746        }
1747    }
1748
1749    Lucide::enableCopy( haveSelection );
1750}
1751
1752
1753// handles DM_DRAGOVER
1754MRESULT DocumentViewer::wmDragOver( PDRAGINFO dragInfo )
1755{
1756    PDRAGITEM dragItem;
1757    USHORT    usOp, usIndicator;
1758
1759    usOp = 0;
1760    usIndicator = DOR_NODROPOP;
1761
1762    DrgAccessDraginfo( dragInfo );
1763
1764    if ( dragInfo->usOperation == DO_DEFAULT )
1765    {
1766        dragItem = DrgQueryDragitemPtr( dragInfo, 0 );
1767        if ( DrgQueryDragitemCount( dragInfo ) == 1 )
1768        {
1769            if ( DrgVerifyRMF( dragItem, "DRM_OS2FILE", NULL ) &&
1770                 ( dragItem->hstrContainerName != NULLHANDLE ) &&
1771                 ( dragItem->hstrSourceName != NULLHANDLE ) )
1772            {
1773                char fname[ CCHMAXPATHCOMP ] = "";
1774                DrgQueryStrName( dragItem->hstrSourceName, CCHMAXPATHCOMP, fname );
1775                char *ext = strrchr( fname, '.' );
1776                if ( ext != NULL ) {
1777                    if ( pluginMan->createDocumentForExt( ext + 1, true ) != NULL ) {
1778                        usIndicator = DOR_DROP;
1779                        usOp = DO_UNKNOWN;
1780                    }
1781                }
1782            }
1783        }
1784    }
1785
1786    DrgFreeDraginfo( dragInfo );
1787    return MRFROM2SHORT( usIndicator, usOp );
1788}
1789
1790
1791// handles DM_DROP
1792void DocumentViewer::wmDrop( PDRAGINFO dragInfo )
1793{
1794    PDRAGITEM dragItem;
1795
1796    DrgAccessDraginfo( dragInfo );
1797    dragItem = DrgQueryDragitemPtr( dragInfo, 0 );
1798
1799    char fname[ CCHMAXPATHCOMP ] = "";
1800    char fpath[ CCHMAXPATH ] = "";
1801    DrgQueryStrName( dragItem->hstrSourceName, CCHMAXPATHCOMP, fname );
1802    DrgQueryStrName( dragItem->hstrContainerName, CCHMAXPATH, fpath );
1803    DrgFreeDraginfo( dragInfo );
1804
1805    strcat( fpath, fname );
1806    Lucide::loadDocument( fpath );
1807}
1808
1809
1810// static, window procedure
1811MRESULT EXPENTRY DocumentViewer::docViewProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
1812{
1813    DocumentViewer *_this = (DocumentViewer *)WinQueryWindowULong( hwnd, QWL_USER );
1814
1815    switch ( msg )
1816    {
1817        case WM_CREATE:
1818        {
1819            // Save the mp1 into our user data so that subsequent calls have
1820            // access to the parent C++ object
1821            WinSetWindowULong( hwnd, QWL_USER, (ULONG)mp1 );
1822            _this = (DocumentViewer *)mp1;
1823            return (MRESULT)FALSE;
1824        }
1825
1826        case DM_DRAGOVER:
1827            return _this->wmDragOver( (PDRAGINFO)mp1 );
1828
1829        case DM_DROP:
1830            _this->wmDrop( (PDRAGINFO)mp1 );
1831            return (MRESULT)FALSE;
1832
1833        case WM_ERASEBACKGROUND:
1834            return (MRESULT)TRUE;
1835
1836        case WM_SIZE:
1837            _this->wmSize( hwnd, mp2 );
1838            return (MRESULT)FALSE;
1839
1840        case WM_HSCROLL:
1841            _this->horizScroll( hwnd, mp2, NULLHANDLE );
1842            break;
1843
1844        case WM_VSCROLL:
1845            _this->vertScroll( hwnd, mp2, NULLHANDLE );
1846            break;
1847
1848        case WM_PAINT:
1849            if ( _this->enableAsynchDraw ) {
1850                if ( _this->continuous ) {
1851                    _this->wmPaintContAsynch( hwnd );
1852                } else {
1853                    _this->wmPaintAsynch( hwnd );
1854                }
1855            } else {
1856                if ( _this->continuous ) {
1857                    _this->wmPaintCont( hwnd );
1858                } else {
1859                    _this->wmPaint( hwnd );
1860                }
1861            }
1862            return (MRESULT)FALSE;
1863
1864        case WM_BUTTON1DOWN:
1865            _this->wmButton1Down( hwnd, SHORT1FROMMP( mp1 ), SHORT2FROMMP( mp1 ) );
1866            break;
1867
1868        case WM_BUTTON1UP:
1869            _this->wmButton1Up();
1870            break;
1871
1872        case WM_MOUSEMOVE:
1873            if ( _this->wmMouseMove( hwnd, SHORT1FROMMP( mp1 ), SHORT2FROMMP( mp1 ) ) ) {
1874                return (MRESULT)TRUE;
1875            }
1876            break;
1877
1878        case WM_BUTTON1CLICK:
1879            if ( _this->wmClick( hwnd, SHORT1FROMMP( mp1 ), SHORT2FROMMP( mp1 ) ) ) {
1880                return (MRESULT)TRUE;
1881            }
1882            break;
1883
1884        case WM_CHAR:
1885            if ( _this->wmChar( hwnd, mp1, mp2 ) ) {
1886                return (MRESULT)TRUE;
1887            }
1888            break;
1889    }
1890
1891    return WinDefWindowProc( hwnd, msg, mp1, mp2 );
1892}
1893
1894
1895// static, window procedure
1896MRESULT EXPENTRY DocumentViewer::docFrameProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
1897{
1898    DocumentViewer *_this = (DocumentViewer *)WinQueryWindowULong( hwnd, QWL_USER );
1899
1900    switch ( msg )
1901    {
1902        case WM_SYSCOMMAND:
1903            // Send WM_SYSCOMMAND messages to main frame
1904            WinSendMsg( _this->hMainFrame, WM_SYSCOMMAND, mp1, mp2 );
1905            return (MRESULT)FALSE;
1906    }
1907
1908    return _this->oldFrameProc( hwnd, msg, mp1, mp2 );
1909}
1910
Note: See TracBrowser for help on using the repository browser.