/* * Turbo Vision - Version 2.0 * * Copyright (c) 1994 by Borland International * All Rights Reserved. * Modified by Robert H”hne to be used for RHIDE. Modified by Vadim Beloborodov to be used on WIN32 console Modified by Salvador E. Tropea to add functionality. * * */ // SET: Moved the standard headers here because according to DJ // they can inconditionally declare symbols like NULL #define Uses_string #define Uses_AllocLocal #define Uses_TKeys #define Uses_TListViewer #define Uses_TScrollBar #define Uses_TDrawBuffer #define Uses_TPoint #define Uses_TEvent #define Uses_TGroup #define Uses_opstream #define Uses_ipstream #define Uses_TStreamableClass #define Uses_TPalette #include #define cpListViewer "\x1A\x1A\x1B\x1C\x1D" // SET: By default I make it report more messages than original TV. In this // case the focusItem(ccIndex item) member generates a broadcast. unsigned TListViewer::extraOptions=ofBeVerbose; TListViewer::TListViewer( const TRect& bounds, ushort aNumCols, TScrollBar *aHScrollBar, TScrollBar *aVScrollBar) : TView( bounds ), topItem( 0 ), focused( 0 ), range( 0 ), handleSpace(True) { options |= ofFirstClick | ofSelectable | extraOptions; eventMask |= evBroadcast; hScrollBar = aHScrollBar; vScrollBar = aVScrollBar; center = False; setNumCols(aNumCols); } // SET: Moved here to allow changes from sub-classes void TListViewer::setNumCols(int aNumCols) { int arStep,pgStep; numCols=aNumCols; // Compute the scroll bar changes. if (vScrollBar!=0) { if (numCols==1) { pgStep=size.y-1; arStep=1; } else { pgStep=size.y*numCols; arStep=size.y; } vScrollBar->setStep(pgStep,arStep); } if (hScrollBar) hScrollBar->setStep(size.x/numCols,1); } void TListViewer::changeBounds( const TRect& bounds ) { TView::changeBounds( bounds ); if( hScrollBar != 0 ) hScrollBar->setStep( size.x / numCols, 1 ); } void TListViewer::draw() { short i, j; ccIndex item; ushort normalColor, selectedColor, focusedColor=0, color; short colWidth, curCol, indent; TDrawBuffer b; uchar scOff; if( (state&(sfSelected | sfActive)) == (sfSelected | sfActive)) { normalColor = getColor(1); focusedColor = getColor(3); selectedColor = getColor(4); } else { normalColor = getColor(2); selectedColor = getColor(4); } if( hScrollBar != 0 ) indent = hScrollBar->value; else indent = 0; colWidth = size.x / numCols + 1; for( i = 0; i < size.y; i++ ) { for( j = 0; j < numCols; j++ ) { int width;; item = j * size.y + i + topItem; curCol = j * colWidth; if (j == numCols-1) width = size.x - curCol + 1; else width = colWidth; if( (state & (sfSelected | sfActive)) == (sfSelected | sfActive) && focused == item && range > 0) { color = focusedColor; setCursor( curCol + 1, i ); scOff = 0; } else if( item < range && isSelected(item) ) { color = selectedColor; scOff = 2; } else { color = normalColor; scOff = 4; } b.moveChar( curCol, ' ', color, width ); if( item < range ) { // This was probably the // reason for a bug, because // getText assumes a buffer // with a length of maxLen + 1 AllocLocalStr(text,width+indent+1); AllocLocalStr(buf,width+1); getText( text, item, width + indent ); int tl = strlen(text); if (tl <= indent) buf[0] = 0; else { memcpy( buf, text+indent, width ); buf[tl-indent] = EOS; } b.moveStr( curCol+1, buf, color ); if( showMarkers ) { b.putChar( curCol, specialChars[scOff] ); b.putChar( curCol+width-2, specialChars[scOff+1] ); } } else if( i == 0 && j == 0 ) b.moveStr( curCol+1, _(""), getColor(1) ); // It was a really nasty thing, this call used 179 instead of // a configurable value like now. b.moveChar( curCol+width-1, columnSeparator, getColor(5), 1 ); } writeLine( 0, i, size.x, 1, b ); } } void TListViewer::focusItem( ccIndex item ) { focused = item; if( item < topItem ) { if( numCols == 1 ) topItem = item; else topItem = item - item % size.y; } else { if( item >= topItem + size.y*numCols ) { if( numCols == 1 ) topItem = item - size.y + 1; else topItem = item - item % size.y - (size.y * (numCols-1)); } } if( vScrollBar != 0 ) vScrollBar->setValue( item ); else drawView(); if (owner && (options & ofBeVerbose)) message(owner,evBroadcast,cmListItemFocused,this); } /**[txh]******************************************************************** Description: That's a variant of focusItem that tries to center the focused item when the list have only one column. ***************************************************************************/ void TListViewer::focusItemCentered( ccIndex item ) { if( numCols != 1 ) { focusItem( item ); return; } center = True; focused = item; if( item < topItem ) { topItem = item - size.y/2; if( topItem < 0) topItem = 0; } else { if( item >= topItem + size.y*numCols ) { topItem = item - size.y/2; if( topItem + size.y >= range && range > size.y) topItem = range - size.y; } } if( vScrollBar != 0 ) vScrollBar->setValue( item ); else drawView(); if (owner && (options & ofBeVerbose)) message(owner,evBroadcast,cmListItemFocused,this); center = False; } void TListViewer::focusItemNum( ccIndex item ) { if( item < 0 ) item = 0; else if( item >= range && range > 0 ) item = range - 1; if( range != 0 ) if( center ) focusItemCentered( item ); else focusItem( item ); } TPalette& TListViewer::getPalette() const { static TPalette palette( cpListViewer, sizeof( cpListViewer )-1 ); return palette; } void TListViewer::getText( char *dest, ccIndex, short ) { *dest = EOS; } Boolean TListViewer::isSelected( ccIndex item ) { return Boolean( item == focused ); } void TListViewer::handleEvent( TEvent& event ) { TPoint mouse; ushort colWidth; ccIndex oldItem, newItem; ushort count; int mouseAutosToSkip = 4; TView::handleEvent(event); if( event.what == evMouseDown ) { // They must be before doubleClick to avoid "b4 double click" if( event.mouse.buttons == mbButton4 ) { focusItemNum(focused - size.y * numCols); clearEvent( event ); return; } if( event.mouse.buttons == mbButton5 ) { focusItemNum(focused + size.y * numCols); clearEvent( event ); return; } if( event.mouse.doubleClick && range > focused ) { selectItem( focused ); clearEvent( event ); return; } colWidth = size.x / numCols + 1; oldItem = focused; mouse = makeLocal( event.mouse.where ); newItem = mouse.y + (size.y * (mouse.x / colWidth)) + topItem; count = 0; do { if( newItem != oldItem ) focusItemNum( newItem ); oldItem = newItem; mouse = makeLocal( event.mouse.where ); if( mouseInView( event.mouse.where ) ) newItem = mouse.y + (size.y * (mouse.x / colWidth)) + topItem; else { if( numCols == 1 ) { if( event.what == evMouseAuto ) count++; if( count == mouseAutosToSkip ) { count = 0; if( mouse.y < 0 ) newItem = focused - 1; else if( mouse.y >= size.y ) newItem = focused + 1; } } else { if( event.what == evMouseAuto ) count++; if( count == mouseAutosToSkip ) { count = 0; if( mouse.x < 0 ) newItem = focused - size.y; else if( mouse.x >= size.x ) newItem = focused + size.y; else if( mouse.y < 0 ) newItem = focused - focused % size.y; else if( mouse.y > size.y ) newItem = focused - focused % size.y + size.y - 1; } } } } while( mouseEvent( event, evMouseMove | evMouseAuto ) ); focusItemNum( newItem ); if( event.mouse.doubleClick && range > focused ) selectItem( focused ); clearEvent( event ); } else if( event.what == evKeyDown ) { if ((handleSpace == True) && (event.keyDown.charScan.charCode == ' ') && focused < range ) { selectItem( focused ); newItem = focused; } else { switch (ctrlToArrow(event.keyDown.keyCode)) { case kbUp: newItem = focused - 1; break; case kbDown: newItem = focused + 1; break; case kbRight: if( numCols > 1 ) newItem = focused + size.y; else { // SET: if the user put a scroll bar with one column // that's what he wants if (hScrollBar) hScrollBar->handleEvent(event); return; } break; case kbLeft: if( numCols > 1 ) newItem = focused - size.y; else { // SET: see kbRight if (hScrollBar) hScrollBar->handleEvent(event); return; } break; case kbPgDn: newItem = focused + size.y * numCols; break; case kbPgUp: newItem = focused - size.y * numCols; break; case kbHome: newItem = topItem; break; case kbEnd: newItem = topItem + (size.y * numCols) - 1; break; case kbCtrlPgDn: newItem = range - 1; break; case kbCtrlPgUp: newItem = 0; break; default: return; } focusItemNum(newItem); } clearEvent(event); } else if( event.what == evBroadcast ) { if( (options & ofSelectable) != 0 ) { if( event.message.command == cmScrollBarClicked && ( event.message.infoPtr == hScrollBar || event.message.infoPtr == vScrollBar ) ) select(); else if( event.message.command == cmScrollBarChanged ) { if( vScrollBar == event.message.infoPtr ) { focusItemNum( vScrollBar->value ); drawView(); } else if( hScrollBar == event.message.infoPtr ) drawView(); } } } } void TListViewer::selectItem( ccIndex ) { message( owner, evBroadcast, cmListItemSelected, this ); } void TListViewer::setRange( ccIndex aRange ) { range = aRange; if (focused >= aRange) focused = (aRange - 1 >= 0) ? aRange - 1 : 0; if( vScrollBar != 0 ) { vScrollBar->setParams( focused, 0, aRange - 1, vScrollBar->pgStep, vScrollBar->arStep ); } else drawView(); } void TListViewer::setState( ushort aState, Boolean enable) { TView::setState( aState, enable ); if( (aState & (sfSelected | sfActive)) != 0 ) { if( hScrollBar != 0 ) if( getState(sfActive) ) hScrollBar->show(); else hScrollBar->hide(); if( vScrollBar != 0 ) if( getState(sfActive) ) vScrollBar->show(); else vScrollBar->hide(); drawView(); } } void TListViewer::shutDown() { hScrollBar = 0; vScrollBar = 0; TView::shutDown(); } #if !defined( NO_STREAM ) void TListViewer::write( opstream& os ) { TView::write( os ); os << hScrollBar << vScrollBar << numCols << topItem << focused << range; } void *TListViewer::read( ipstream& is ) { TView::read( is ); is >> hScrollBar >> vScrollBar >> numCols >> topItem >> focused >> range; return this; } TStreamable *TListViewer::build() { return new TListViewer( streamableInit ); } TListViewer::TListViewer( StreamableInit ) : TView( streamableInit ) { } #endif // NO_STREAM