home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pwrgu2.zip / POWERGU2.EXE / DM / LBOXDRAG / LBOXITEM.CPP < prev    next >
Text File  |  1995-07-23  |  12KB  |  422 lines

  1. //************************************************************
  2. // Direct Manipulation - List Box Example
  3. //
  4. // Copyright (C) 1994, Law, Leong, Love, Olson, Tsuji.
  5. // All Rights Reserved.
  6. //************************************************************
  7. #define INCL_WIN
  8. #define INCL_GPI
  9. #include <os2.h>
  10.  
  11.  
  12. #include <ilistbox.hpp>
  13. #include <istring.hpp>
  14. #include <ipoint.hpp>
  15. #include <ifont.hpp>
  16. #include <idmimage.hpp>
  17. #include <idmevent.hpp>
  18. #include <idmtgth.hpp>
  19. #include <ihandle.hpp>
  20. #include <itrace.hpp>
  21.  
  22. #include "lboxitem.hpp"
  23. #include "operfix.hpp"
  24.  
  25. static const unsigned
  26.   nil = 0xffffffffu;
  27.  
  28. ListBoxItem :: ListBoxItem ( IDMSourceOperation *srcOp,
  29.                              IListBox           *srcLB,
  30.                              unsigned            index )
  31.   : IDMItem( srcOp,
  32.              IDM::text,
  33.              IDMItem:: moveable | IDMItem::copyable,
  34.              none )
  35.   {
  36.   // Item contents is the list box item text.
  37.   this -> setContents( srcLB->itemText( index ) );
  38.   // Item object is the item index.
  39.   this -> setObject( (void*)index );
  40.   // Try to use rfText...
  41.   IString
  42.     name = this -> generateSourceName(),
  43.     rfs   = rfForThisProcess();
  44.   if ( name.length() )
  45.     { // Text fits, use rfText.
  46.     this -> setSourceName( name );
  47.     rfs += IString( "," ) + IDM::rfText;
  48.     }
  49.   else
  50.     { // Text doesn't fit, use rfSharedMem instead.
  51.     rfs += IString( "," ) + IDM::rfSharedMem;
  52.     this -> setRequiresPreparation();
  53.     }
  54.   // Set up RMFs; we support dropping on shredder, too.
  55.   this -> setRMFs( rmfsFrom( IDM::rmLibrary, rfs ) );
  56.   this -> addRMF( IDM::rmDiscard, IDM::rfUnknown );
  57.  
  58.   // Use text icon when dragged.
  59.   ISystemPointerHandle
  60.     icon( ISystemPointerHandle::text );
  61.   IDMImage
  62.     image( icon );
  63.   this -> setImage( image );
  64.   }  
  65.  
  66. ListBoxItem :: ListBoxItem ( const IDMItem::Handle &dragItem )
  67.   : IDMItem( dragItem )
  68.   {
  69.   // We only support copy and move.
  70.   this -> enableLink( false );
  71.   }             
  72.  
  73. ListBoxItem :: ~ListBoxItem ( )
  74.   {
  75.   }
  76.  
  77. Boolean ListBoxItem :: generateSourceItems( IDMSourceOperation *srcOp )
  78.   {
  79.   Boolean
  80.     result = false;
  81.   IListBox
  82.    *srcLB = (IListBox*)( srcOp->sourceWindow() );
  83.   // Get index of dragged item:
  84.   unsigned
  85.     index = sourceIndex( srcLB, srcOp->position() );
  86.   if ( index != nil )
  87.     { // Not dragging from white space, add appropriate item:
  88.     srcOp -> addItem( new ListBoxItem( srcOp, srcLB, index ) );
  89.     srcOp -> setImageStyle( IDM::stack3AndFade );
  90.     result = true;
  91.     }
  92.   return result;
  93.   }
  94.  
  95. Boolean ListBoxItem :: targetDrop ( IDMTargetDropEvent &event )
  96.   {
  97.   IListBox
  98.    *tgtLB = (IListBox*)( event.window() );
  99.  
  100.   // Turn off target emphasis:
  101.   ListBoxItemProvider
  102.    *provider = (ListBoxItemProvider*)( tgtLB->itemProvider() );
  103.   provider -> drawEmphasis( tgtLB, TgtLocation( after, nil ) );
  104.  
  105.   // Calculate where dropped on:
  106.   TgtLocation
  107.     dropLoc = targetLocation( tgtLB, event.dropPosition() );
  108.   // Add or replace, based on drop location:
  109.   switch ( dropLoc.type )
  110.     {
  111.     case before:
  112.       tgtLB -> add( dropLoc.index, contents() );
  113.       break;
  114.     case on:
  115.       tgtLB -> setItemText( dropLoc.index, contents() );
  116.       break;
  117.     case after:
  118.       tgtLB -> add( dropLoc.index + 1, contents() );
  119.       break;
  120.     }
  121.   // If source and target are the same and item moved
  122.   // forward, update source index.
  123.   IDMTargetOperation::Handle
  124.     tgtOp = IDMTargetOperation::targetOperation();
  125.   if ( tgtOp->sourceWindow() == event.window()
  126.        &&
  127.        tgtOp->operation() == IDMOperation::move )
  128.     {
  129.     IDMItem::Handle
  130.       srcItem = IDMItem::sourceItemFor( tgtOp->item( 1 ) );
  131.     unsigned
  132.       srcIndex = (unsigned)( srcItem->object() );
  133.     if ( dropLoc.type != on 
  134.          && 
  135.          dropLoc.index < srcIndex )
  136.       srcItem->setObject( (void*)( srcIndex + 1 ) );
  137.     }
  138.  
  139.   return true;
  140.   }
  141.  
  142. Boolean ListBoxItem :: sourceEnd ( IDMSourceEndEvent &event )
  143.   {
  144.   // If move completed OK (and not to shredder),
  145.   // delete source item.
  146.   if ( event.wasTargetSuccessful()
  147.        &&
  148.        (unsigned long)( object() ) != nil
  149.        &&
  150.        OperFix::dropOperation( event ) != IDMOperation::copy )
  151.     {
  152.     unsigned
  153.       index = (unsigned)( this->object() );
  154.     ( (IListBox*)( event.window() ) ) -> remove( index );
  155.     }
  156.   return true;
  157.   }
  158.  
  159. Boolean ListBoxItem :: sourceDiscard ( IDMSourceDiscardEvent &event )
  160.   {
  161.   IListBox
  162.    *srcLB = (IListBox*)( event.window() );
  163.  
  164.   // Get index of dragged item back out.
  165.   unsigned
  166.     index = (unsigned)( this->object() );
  167.  
  168.   // Delete that item.
  169.   srcLB->remove( index );
  170.  
  171.   // Mark deleted so sourceEnd doesn't delete it again.
  172.   setObject( (void*)nil );
  173.  
  174.   return true;
  175.   }
  176.  
  177. unsigned ListBoxItem :: sourceIndex ( IListBox *pLB, 
  178.                                       const IPoint &pt ) 
  179.   {
  180.   // If there are no items, indicate no match.
  181.   if ( pLB->isEmpty() )
  182.     return nil;
  183.  
  184.   // Calculate index of dragged item:
  185.   unsigned
  186.     dy = pLB->rect().height() - pt.y(),
  187.     i  = itemHeight(pLB),
  188.     row = dy / i,
  189.     index = pLB->top() + row;
  190.  
  191.   // If off end, indicate that.
  192.   if ( index >= pLB->count() )
  193.     index = nil;
  194.  
  195.   return index;
  196.   }
  197.  
  198. ListBoxItem::TgtLocation
  199.   ListBoxItem :: targetLocation ( IListBox *pLB, const IPoint &pt )
  200.   {
  201.   // Target position is in desktiop coordinates.
  202.   // We must map this to listbox window coordinates.
  203.   IPoint
  204.     lbPt = IWindow::mapPoint( pt,
  205.                               IWindow::desktopWindow()->handle(),
  206.                               pLB->handle() );
  207.   // Get index of target item:
  208.   unsigned
  209.     index = sourceIndex( pLB, lbPt );
  210.   LocType
  211.     type = on;
  212.   if ( index != nil )
  213.     { // Drop at item, see if before or after:
  214.     unsigned
  215.       dy  = pLB->rect().height() - lbPt.y(),
  216.       i   = itemHeight(pLB),
  217.       rem = dy % i;
  218.     if ( rem < i/4 )
  219.       type = before;
  220.     else if ( rem > 3*i/4 )
  221.       type = after;
  222.     }
  223.   else
  224.     { // Drop off end.
  225.     type = after;
  226.     index = pLB->count() - 1;
  227.     }
  228.   return TgtLocation( type, index );
  229.   }
  230.  
  231. unsigned ListBoxItem :: itemHeight( IListBox *pLB )
  232.   {
  233.   // Get item height presuming standard usage
  234.   // based on current font.  Note that this is 
  235.   // may be wrong if the listbox is owner-draw
  236.   // or somebody has called setHeight().
  237.   IFont
  238.     font( pLB );
  239.   unsigned 
  240.     result = font.maxCharHeight() + font.externalLeading();
  241.   // Don't return 0 since that would cause divide-by-zero error.
  242.   if ( !result )
  243.     result++;
  244.   return result;
  245.   }
  246.  
  247. ListBoxItem::TgtLocation :: TgtLocation ( LocType type, 
  248.                                           unsigned index )
  249.   : type( type ),
  250.     index( index )
  251.   {
  252.   }
  253.  
  254. Boolean ListBoxItem::TgtLocation :: 
  255.   operator == ( const TgtLocation &loc ) const
  256.   {
  257.   if ( index == loc.index )
  258.     return type == loc.type;
  259.   else if ( index == loc.index-1 )
  260.     return ( type == after && loc.type == before );
  261.   else if ( index == loc.index+1 )
  262.     return ( type == before && loc.type == after );
  263.   else
  264.     return false;
  265.   }
  266.  
  267. ListBoxItemProvider :: ListBoxItemProvider ( IListBox *listBox )
  268.   {
  269.   if ( listBox )
  270.     this -> provideItemsFor( listBox );
  271.   }
  272.  
  273. ListBoxItemProvider 
  274.  &ListBoxItemProvider :: provideItemsFor( IListBox *listBox )
  275.   {
  276.   listBox -> setItemProvider( this );
  277.   return *this;
  278.   }      
  279.  
  280. typedef ListBoxItem::TgtLocation TgtLocation;
  281.  
  282. static TgtLocation
  283.   lastTarget( ListBoxItem::after, nil );
  284.  
  285. static void draw ( IPresSpaceHandle hps,
  286.                    IListBox *lb, 
  287.                    const TgtLocation &target )
  288.   {
  289.   if ( target.index != nil )
  290.     {
  291.     // First, get offset from top of listbox:
  292.     unsigned
  293.       offset = target.index - lb->top() + 1,
  294.       height = ListBoxItem::itemHeight( lb );
  295.     // Next, adjust if before this item:
  296.     if ( target.type == ListBoxItem::before )
  297.       offset--;
  298.     // Calculate that item's rectangle's bottom:
  299.     unsigned
  300.       bottom = lb->rect().height() - height * offset;
  301.     // Lower 2 pels 'cause it looks better!
  302.     bottom -= 2;
  303.     // Draw line or box:
  304.     IPoint
  305.       origin( 0, bottom );
  306.     if ( target.type == ListBoxItem::on )
  307.       {
  308.       IPoint
  309.         topRight( lb->rect().width(), bottom + height );
  310.       origin += 1;
  311.       topRight -= IPoint( WinQuerySysValue( HWND_DESKTOP,
  312.                                             SV_CXVSCROLL ) + 1, 1 );
  313.       GpiMove( hps, PPOINTL( &origin ) );
  314.       GpiBox( hps, DRO_OUTLINE, PPOINTL( &topRight ), 0, 0 );
  315.       }
  316.     else
  317.       {
  318.       IPoint
  319.         end( lb->rect().width(), bottom );
  320.       GpiMove( hps, PPOINTL( &origin ) );
  321.       GpiLine( hps, PPOINTL( &end ) );
  322.       }
  323.     }
  324.   }
  325.  
  326. ListBoxItemProvider 
  327.  &ListBoxItemProvider :: drawEmphasis ( IListBox *listBox, 
  328.                                         const TgtLocation &target ) 
  329.   {
  330.   // If same target, then it's already drawn.
  331.   if ( target == lastTarget )
  332.     return *this;
  333.  
  334.   // Get presentation space and make it mode INVERT.
  335.   IPresSpaceHandle
  336.     hps = DrgGetPS( listBox->handle() );
  337.   GpiSetMix( hps, FM_INVERT );
  338.  
  339.   // "Undraw" current target emphasis:
  340.   draw( hps, listBox, lastTarget );
  341.  
  342.   // Set new target and draw it.
  343.   lastTarget = target;
  344.   draw( hps, listBox, lastTarget );
  345.  
  346.   DrgReleasePS( hps );
  347.   return *this;
  348.   }
  349.  
  350. Boolean ListBoxItemProvider ::
  351.   provideLeaveSupport ( IDMTargetLeaveEvent &event )
  352.   {
  353.   IListBox
  354.    *listBox = (IListBox*)( event.window() );
  355.   this -> drawEmphasis( listBox, 
  356.                         TgtLocation( ListBoxItem::after, nil ) );
  357.   return false;
  358.   }
  359.  
  360. Boolean ListBoxItemProvider :: 
  361.   provideEnterSupport ( IDMTargetEnterEvent &event )
  362.   {
  363.   // Get default dragover result:
  364.   Inherited::provideEnterSupport( event );
  365.  
  366.   IDMTargetOperation::Handle
  367.     tgtOp = IDMTargetOperation::targetOperation();
  368.  
  369.   IListBox
  370.    *lb = (IListBox*)( event.window() );
  371.  
  372.   ListBoxItem::TgtLocation
  373.     tgtLocation 
  374.       = ListBoxItem::targetLocation( lb, event.position() );
  375.  
  376.   if ( event.dropIndicator() == IDM::ok 
  377.        &&
  378.        tgtOp->sourceWindow() == event.window() )
  379.     { // Source==target, prohibit dropping on same item.
  380.     IDMItem::Handle
  381.       srcItem = IDMItem::sourceItemFor( tgtOp->item( 1 ) );
  382.     unsigned
  383.       srcIndex = (unsigned)( srcItem->object() );
  384.  
  385.     // Disable conflicting drop on source window:
  386.     unsigned long
  387.       op = tgtOp->operation();
  388.     if ( op == IDMOperation::drag )
  389.       op = IDMOperation::move; // Default;
  390.     if ( op == IDMOperation::copy )
  391.       { // Can't copy to self.
  392.       if ( srcIndex == tgtLocation.index
  393.            &&
  394.            tgtLocation.type == ListBoxItem::on )
  395.         event.setDropIndicator( IDM::notOk );
  396.       }
  397.     else if ( op == IDMOperation::move )
  398.       { // No sense moving to same place.
  399.       if ( srcIndex == tgtLocation.index 
  400.            ||
  401.            ( tgtLocation.type == ListBoxItem::before
  402.              &&
  403.              srcIndex == tgtLocation.index - 1 )
  404.            ||
  405.            ( tgtLocation.type == ListBoxItem::after
  406.              &&
  407.              srcIndex == tgtLocation.index + 1 ) )
  408.         event.setDropIndicator( IDM::notOk );
  409.       }
  410.     }
  411.  
  412.   // Draw target emphasis:
  413.   drawEmphasis( lb, tgtLocation );
  414.  
  415.   return true;
  416.   }
  417.  
  418. ListBoxItemProvider *ListBoxItemProvider :: operator & ()
  419.   {
  420.   return this;
  421.   }
  422.