home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / cmd / macfe / central / divview.cp < prev    next >
Encoding:
Text File  |  1998-04-08  |  15.7 KB  |  580 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. #include "divview.h"
  20.  
  21. #include "resgui.h"
  22. #include "macutil.h"
  23. #include "macgui.h"
  24. #include "CBevelView.h"
  25.  
  26. #include <Sound.h>
  27.  
  28.  
  29. //-----------------------------------
  30. LDividedView::LDividedView( LStream* inStream )
  31. //-----------------------------------
  32. :    LView( inStream )
  33. ,    fFirstView(nil)
  34. ,    fSecondView(nil)
  35. ,    mCollapseFirstByDragging(false)
  36. ,    mCollapseSecondByDragging(false)
  37. ,    mDividerPosBeforeCollapsing(100)
  38. {
  39.     *inStream >> mFirstSubview >> mSecondSubview;
  40.         
  41.     *inStream >> mDivSize;
  42.     *inStream >> mFirstIndent;
  43.     if ( mFirstIndent > mDivSize )
  44.         mFirstIndent = mDivSize;
  45.     *inStream >> mSecondIndent;
  46.     if ( mSecondIndent > mDivSize )
  47.         mSecondIndent = mDivSize;
  48.     *inStream >> mMinFirstSize;
  49.     *inStream >> mMinSecondSize;
  50.     *inStream >> mSlidesHorizontally;
  51. }
  52.  
  53. //-----------------------------------
  54. LDividedView::~LDividedView()
  55. //-----------------------------------
  56. {
  57. }
  58.  
  59. //-----------------------------------
  60. void LDividedView::FinishCreateSelf()
  61. //-----------------------------------
  62. {
  63.     LView::FinishCreateSelf();
  64.             
  65.     fFirstView = (LView*)this->FindPaneByID( mFirstSubview );
  66.     fSecondView = (LView*)this->FindPaneByID( mSecondSubview );
  67.     LBroadcaster* zapButton = dynamic_cast<LBroadcaster*>(GetZapButton());
  68.     if (zapButton)
  69.     {
  70.         zapButton->AddListener(this);
  71.         // Make sure the first pane can be collapsed
  72.         Boolean collapseFirst, collapseSecond;
  73.         GetCollapseByDragging(collapseFirst, collapseSecond);
  74.         SetCollapseByDragging(true, collapseSecond);
  75.     }
  76.     this->SyncFrameBindings();
  77.     mDividerPosBeforeCollapsing = GetDividerPosition();
  78. }
  79.  
  80. //-----------------------------------
  81. LPane* LDividedView::GetZapButton()
  82. //-----------------------------------
  83. {
  84.     return FindPaneByID('Zap!');
  85. }
  86.  
  87. //-----------------------------------
  88. void LDividedView::ToggleFirstPane()
  89. //-----------------------------------
  90. {
  91.     Int16 delta;
  92.     if (IsFirstPaneCollapsed())
  93.         delta = 1; // at left, restore to center.
  94.     else if (IsSecondPaneCollapsed())
  95.         delta = -1; // at right, restore to center
  96.     else
  97.         delta =  - GetDividerPosition(); // collapse left
  98.     StValueChanger<Boolean> save(mCollapseFirstByDragging, true);
  99.     ChangeDividerPosition(delta);
  100. }
  101.     
  102. //-----------------------------------
  103. void LDividedView::ListenToMessage(MessageT inMessage, void* /*ioParam*/)
  104. //-----------------------------------
  105. {
  106.     if (inMessage == 'Zap!')
  107.         ToggleFirstPane();
  108. }
  109.     
  110. //-----------------------------------
  111. void LDividedView::SyncFrameBindings()
  112. //-----------------------------------
  113. {
  114.     SBooleanRect    firstBindings;
  115.     SBooleanRect    secondBindings;
  116.  
  117.     firstBindings.top = firstBindings.bottom = firstBindings.left = firstBindings.right = TRUE;
  118.     secondBindings.top = secondBindings.bottom = secondBindings.left = secondBindings.right = TRUE;
  119.     
  120.     if ( mSlidesHorizontally )
  121.         firstBindings.right = FALSE;
  122.     else
  123.         firstBindings.bottom = FALSE;    
  124.     
  125.     fFirstView->SetFrameBinding( firstBindings );
  126.     fSecondView->SetFrameBinding( secondBindings );
  127. }
  128.  
  129. void LDividedView::PositionZapButton()
  130. {
  131.     SDimension16 zapFrameSize;
  132.     LPane* zapButton = GetZapButton();
  133.     if (!zapButton)
  134.         return;
  135.     zapButton->GetFrameSize(zapFrameSize);
  136.     SDimension16 firstFrameSize;
  137.     fFirstView->GetFrameSize( firstFrameSize );
  138.     if (mSlidesHorizontally)
  139.         zapButton->PlaceInSuperFrameAt(
  140.             firstFrameSize.width + mFirstIndent,
  141.             (mFrameSize.height >> 1) - (zapFrameSize.height >> 1), 
  142.             FALSE );            
  143.     else
  144.         zapButton->PlaceInSuperFrameAt(
  145.             (mFrameSize.width >> 1) - (zapFrameSize.width >> 1), 
  146.             firstFrameSize.height + mFirstIndent,
  147.             FALSE );            
  148.         
  149. }
  150.  
  151. void LDividedView::PositionViews( Boolean willBeHoriz )
  152. {
  153.     if ( mSlidesHorizontally == willBeHoriz )            
  154.         return;
  155.  
  156.     this->FocusDraw();
  157.  
  158.     SDimension16 firstFrameSize;
  159.     fFirstView->GetFrameSize( firstFrameSize );
  160.     if ( mSlidesHorizontally )
  161.     {
  162.         Int16 subpaneSize = ( mFrameSize.height - mDivSize ) / 2;
  163.         
  164.         fFirstView->ResizeFrameTo(    mFrameSize.width - ( mFirstIndent * 2 ),
  165.                                     subpaneSize - mFirstIndent,
  166.                                     FALSE );
  167.         fSecondView->ResizeFrameTo( mFrameSize.width - ( mSecondIndent * 2 ),
  168.                                     subpaneSize - mSecondIndent,
  169.                                     FALSE );
  170.         
  171.         fFirstView->PlaceInSuperFrameAt( mFirstIndent, mFirstIndent, FALSE );                            
  172.         fSecondView->PlaceInSuperFrameAt(    mSecondIndent,
  173.                                             firstFrameSize.height + mFirstIndent + mDivSize,
  174.                                             FALSE );
  175.     }
  176.     else
  177.     {
  178.         Int16 subpaneSize = ( mFrameSize.width - mDivSize ) / 2;
  179.  
  180.         fFirstView->ResizeFrameTo(    subpaneSize - mFirstIndent,
  181.                                     mFrameSize.height - ( mFirstIndent * 2 ),
  182.                                     FALSE );
  183.         fSecondView->ResizeFrameTo(    subpaneSize - mSecondIndent,
  184.                                     mFrameSize.height - ( mSecondIndent * 2),
  185.                                     FALSE );
  186.  
  187.         fFirstView->PlaceInSuperFrameAt( mFirstIndent, mFirstIndent, FALSE );    
  188.         fSecondView->PlaceInSuperFrameAt(    firstFrameSize.width + mFirstIndent + mDivSize,
  189.                                             mSecondIndent,
  190.                                             FALSE );
  191.     }
  192.     PositionZapButton();
  193.     mSlidesHorizontally = willBeHoriz;
  194.     this->SyncFrameBindings();
  195.     BroadcastMessage( msg_DividerChangedPosition, this );
  196.     CBevelView::SubPanesChanged(this, true);    
  197.     this->Refresh();
  198. } // LDividedView::PositionViews
  199.  
  200. // Ñ╩set whether we are vertical or horizontal
  201. void LDividedView::SetSlidesHorizontally( Boolean isHoriz )
  202. {
  203.     if ( mSlidesHorizontally != isHoriz )
  204.         this->PositionViews( isHoriz );
  205. }
  206.  
  207. Int32 LDividedView::GetDividerPosition() const
  208. {
  209.     Rect    firstFrame;
  210.     
  211.     GetSubpaneRect( (LView*)this, fFirstView, firstFrame );
  212.     if ( this->GetSlidesHorizontally() )
  213.         return firstFrame.right;
  214.     else
  215.         return firstFrame.bottom;
  216. }
  217.  
  218. void LDividedView::CalcRectBetweenPanes( Rect& invRect )
  219. {
  220.     Rect        firstFrame;
  221.     Rect        secondFrame;
  222.     
  223.     GetSubpaneRect( this, fFirstView, firstFrame );    
  224.     GetSubpaneRect( this, fSecondView, secondFrame );
  225.  
  226.     this->CalcLocalFrameRect( invRect );
  227.  
  228.     if ( mSlidesHorizontally )
  229.     {    
  230.         invRect.left = firstFrame.right;
  231.         invRect.right = secondFrame.left;
  232.     }
  233.     else
  234.     {
  235.         invRect.top = firstFrame.bottom;
  236.         invRect.bottom = secondFrame.top;
  237.     }
  238. }
  239.  
  240. void LDividedView::CalcDividerRect( Rect& outRect )
  241. {
  242.     Rect        firstFrame;
  243.     Rect        secondFrame;
  244.     
  245.     GetSubpaneRect( this, fFirstView, firstFrame );    
  246.     GetSubpaneRect( this, fSecondView, secondFrame );
  247.  
  248.     this->CalcLocalFrameRect( outRect );
  249.     
  250.     if ( mSlidesHorizontally )
  251.     {    
  252.         Int16 mid = ( firstFrame.right + secondFrame.left ) / 2;
  253.         outRect.left = mid - 1;
  254.         outRect.right = mid + 1;
  255.         outRect.top = ( firstFrame.top < secondFrame.top ) ? firstFrame.top : secondFrame.top;
  256.         outRect.bottom = ( firstFrame.bottom > secondFrame.bottom ) ? firstFrame.bottom : secondFrame.bottom;
  257.     }
  258.     else
  259.     {
  260.         Int16 mid = ( firstFrame.bottom + secondFrame.top ) / 2;
  261.         outRect.top = mid - 1;
  262.         outRect.bottom = mid + 1;
  263.         outRect.left = ( firstFrame.left < secondFrame.left ) ? firstFrame.left : secondFrame.left;
  264.         outRect.right = ( firstFrame.right > secondFrame.right ) ? firstFrame.right : secondFrame.right;
  265.     }
  266. }
  267.  
  268. // Ñ this is somewhat hackish, but this routine calculates the limit rect
  269. //        for dragging -- the hackish part is that it looks at its subviews
  270. //        pane IDs to see if they begin with a 'D' and if so, assumes they
  271. //        are also LDividedViews, and then does limiting based on that
  272. //    Well, I fixed this by using dynamic_cast - jrm 97/10/17
  273. void LDividedView::CalcLimitRect( Rect& lRect )
  274. {
  275.     this->CalcLocalFrameRect( lRect );
  276.  
  277.     LDividedView* other = dynamic_cast<LDividedView*>(fFirstView);
  278.     if (other)
  279.     {
  280.         if ( other->GetSlidesHorizontally() == mSlidesHorizontally )
  281.         {
  282.             Point        local;
  283.             local.h = local.v = 0;
  284.             if ( other->GetSlidesHorizontally() )
  285.                 local.h = other->GetDividerPosition();
  286.             else
  287.                 local.v = other->GetDividerPosition();
  288.         
  289.             other->LocalToPortPoint( local );
  290.             this->PortToLocalPoint( local );
  291.             
  292.             if ( mSlidesHorizontally )
  293.                 lRect.left = local.h;
  294.             else
  295.                 lRect.top = local.v;
  296.         }                    
  297.     }
  298.  
  299.     other = dynamic_cast<LDividedView*>(fSecondView);
  300.     if (other)
  301.     {
  302.         if ( other->GetSlidesHorizontally() == mSlidesHorizontally )
  303.         {
  304.             Point        local;
  305.             local.h = local.v = 0;
  306.             if ( other->GetSlidesHorizontally() )
  307.                 local.h = other->GetDividerPosition();
  308.             else
  309.                 local.v = other->GetDividerPosition();
  310.         
  311.             other->LocalToPortPoint( local );
  312.             this->PortToLocalPoint( local );
  313.             
  314.             if ( mSlidesHorizontally )
  315.                 lRect.right = local.h;
  316.             else
  317.                 lRect.bottom = local.v;
  318.         }                    
  319.     }
  320.  
  321.     if ( mSlidesHorizontally )
  322.     {
  323.         if (!mCollapseFirstByDragging)
  324.             lRect.left += mMinFirstSize;
  325.         if (!mCollapseSecondByDragging)
  326.             lRect.right -= mMinSecondSize;
  327.     }
  328.     else
  329.     {
  330.         if (!mCollapseFirstByDragging)
  331.             lRect.top += mMinFirstSize;
  332.         if (!mCollapseSecondByDragging)
  333.             lRect.bottom -= mMinSecondSize;
  334.     }
  335. }
  336.  
  337. void LDividedView::CalcSlopRect( Rect& sRect )
  338. {
  339.     LWindow*        myWin;
  340.     
  341.     myWin = LWindow::FetchWindowObject( this->GetMacPort() );
  342.     if ( myWin )
  343.     {
  344.         myWin->CalcPortFrameRect( sRect );
  345.         sRect = PortToLocalRect( this, sRect );
  346.     }
  347.     else
  348.         this->CalcLocalFrameRect( sRect );
  349.     // If we're allowed to drag/collapse, allow a drag beyond the frame
  350.     if (mCollapseFirstByDragging)
  351.         if (mSlidesHorizontally)
  352.             sRect.left -= 100;
  353.         else
  354.             sRect.top -= 100;
  355.     else if (mCollapseSecondByDragging)
  356.         if (mSlidesHorizontally)
  357.             sRect.right += 100;
  358.         else
  359.             sRect.bottom += 100;
  360. }
  361.  
  362. void LDividedView::ClickSelf( const SMouseDownEvent& inMouseDown )
  363. {
  364.     Rect    frame;
  365.  
  366.     this->CalcRectBetweenPanes( frame );
  367.     if ( PtInRect( inMouseDown.whereLocal, &frame ) )
  368.     {
  369.         this->FocusDraw();
  370.         if (GetClickCount() > 1)
  371.         {    
  372.             // we don't necessarily want users double-clicking to close the pane.
  373.             // If it makes sense, we can turn this back on.
  374. //            ListenToMessage('Zap!', this);
  375.             return;
  376.         }
  377.         
  378.         // if the first pane is closed and dragging to open is turned off,
  379.         // bail before we get to the dragging code.
  380.         if ( !GetDividerPosition() && !CanExpandByDragging() )
  381.             return;
  382.             
  383.         StRegion    myRgn;
  384.         this->CalcDividerRect( frame );
  385.         ::RectRgn( myRgn, &frame );
  386.         
  387.         Rect lRect, sRect;
  388.         this->CalcLimitRect( lRect );
  389.         this->CalcSlopRect( sRect );                
  390.  
  391.         long result = ::DragGrayRgn(    myRgn, inMouseDown.whereLocal, 
  392.                                 &lRect, &sRect, 
  393.                                 mSlidesHorizontally ? hAxisOnly : vAxisOnly, nil );
  394.         
  395.         Int16 delta = mSlidesHorizontally ? LoWord( result ) : HiWord( result );
  396.         
  397.         if ( ( result == kOutsideSlop ) || ( delta == 0 ) ) 
  398.             return;
  399.         
  400.         this->ChangeDividerPosition( delta );
  401.     }
  402. }
  403.  
  404. #define SAVE_VERSION    28
  405.  
  406. void LDividedView::SavePlace( LStream* inStream )
  407. {
  408.     WriteVersionTag( inStream, SAVE_VERSION );
  409.  
  410.     *inStream << mSlidesHorizontally;
  411.     Int32 divPos = this->GetDividerPosition();
  412.     *inStream << divPos;
  413.     *inStream << mDividerPosBeforeCollapsing;
  414. }
  415.  
  416. void LDividedView::RestorePlace( LStream* inStream )
  417. {
  418.     if ( !ReadVersionTag( inStream, SAVE_VERSION ) )
  419.         throw (OSErr)rcDBWrongVersion;
  420.         
  421.     Int32        shouldBe;
  422.     Boolean        isHoriz;
  423.     
  424.     *inStream >> isHoriz;
  425.     this->PositionViews( isHoriz );
  426.     Int32 divPos = this->GetDividerPosition();
  427.     *inStream >> shouldBe;
  428.     this->ChangeDividerPosition( shouldBe - divPos );
  429.     *inStream >> mDividerPosBeforeCollapsing;
  430. }
  431.  
  432. //-----------------------------------
  433. void LDividedView::PlaySound(ResIDT id)
  434. //-----------------------------------
  435. {
  436.     // Play the swishing sound, if available
  437.     SndListHandle soundH =
  438.         (SndListHandle)::GetResource('snd ', id);
  439.     if (soundH)
  440.     {
  441.         ::DetachResource((Handle)soundH);
  442.         ::SndPlay(nil, soundH, false);
  443.         ::DisposeHandle((Handle)soundH);
  444.     }
  445. }
  446.  
  447. //-----------------------------------
  448. void LDividedView::ChangeDividerPosition( Int16 delta )
  449. // Ñ move the divider, resize the top/left pane, and
  450. //        move the bottom/right one
  451. //-----------------------------------
  452. {
  453.     Int32 dividerPos = this->GetDividerPosition();
  454.     Int16 newPos = dividerPos + delta;
  455.     Int16 frameBound // drag beyond which causes slam
  456.         =    mSlidesHorizontally ? mFrameSize.width : mFrameSize.height;    
  457.     
  458.     Boolean showFirst = false;
  459.     Boolean showSecond = false;
  460.     if (newPos > frameBound - mMinSecondSize)
  461.     {
  462.         if (mCollapseSecondByDragging)
  463.         {
  464.             if (IsSecondPaneCollapsed() && delta <= 0)
  465.             {
  466.                 // Uncollapse from right
  467.                 delta = mDividerPosBeforeCollapsing - dividerPos;
  468.                 showSecond = true;
  469.             }
  470.             else
  471.             {
  472.                 // Collapse to right
  473.                 delta = frameBound - mDivSize - dividerPos;
  474.                 fSecondView->Hide();
  475.                 if (dividerPos == 0) // user dragged from one extreme to the other
  476.                     showFirst = true;
  477.                 else
  478.                     mDividerPosBeforeCollapsing = dividerPos;
  479.                 if (IsVisible())
  480.                     PlayClosingSound();
  481.             }
  482.         }
  483.         else
  484.             return;
  485.     }
  486.     else if (newPos < mMinFirstSize)
  487.     {
  488.         if (mCollapseFirstByDragging)
  489.         {
  490.             if (IsFirstPaneCollapsed() && delta >= 0)
  491.             {
  492.                 // Uncollapse from left
  493.                 delta = mDividerPosBeforeCollapsing - dividerPos;
  494.                 showFirst = true;
  495.             }
  496.             else
  497.             {
  498.                 // Collapse to left
  499.                 delta = - dividerPos;
  500.                 fFirstView->Hide();
  501.                 if (dividerPos == frameBound - mDivSize)
  502.                     showSecond = true; // user dragged from one extreme to the other
  503.                 else
  504.                     mDividerPosBeforeCollapsing = dividerPos;
  505.                 if (IsVisible())
  506.                     PlayClosingSound();
  507.             }
  508.         }
  509.         else
  510.             return;
  511.     }
  512.     else if (IsSecondPaneCollapsed() && delta <= 0)
  513.         showSecond = true;
  514.     else if (IsFirstPaneCollapsed() && delta >= 0)
  515.         showFirst = true;
  516.  
  517.     LPane* zapButton = GetZapButton();
  518.     if ( mSlidesHorizontally )
  519.     {
  520.         fFirstView->ResizeFrameBy( delta, 0, FALSE );
  521.         fSecondView->MoveBy( delta, 0, FALSE );
  522.         fSecondView->ResizeFrameBy( -delta, 0, FALSE );
  523.         if (zapButton)
  524.             zapButton->MoveBy(delta, 0, FALSE);
  525.     }
  526.     else
  527.     {
  528.         fFirstView->ResizeFrameBy( 0, delta, FALSE );
  529.         fSecondView->MoveBy( 0, delta, FALSE );
  530.         fSecondView->ResizeFrameBy( 0, -delta, FALSE );
  531.         if (zapButton)
  532.             zapButton->MoveBy(0, delta, FALSE);
  533.     }
  534.     
  535.     BroadcastMessage( msg_DividerChangedPosition, this );
  536.     
  537.     if (showFirst)
  538.         fFirstView->Show();
  539.     if (showSecond)
  540.         fSecondView->Show();
  541.     CBevelView::SubPanesChanged(this, false);    
  542.     if ((showFirst || showSecond) && IsVisible())
  543.         PlayOpeningSound();
  544.     this->Refresh();
  545. } // LDividedView::ChangeDividerPosition
  546.  
  547. void
  548. LDividedView::ResizeFrameBy(
  549.     Int16        inWidthDelta,
  550.     Int16        inHeightDelta,
  551.     Boolean        inRefresh)
  552. {
  553.     // The big flaw in this class is that the situations when the two subpanes change
  554.     // have to be handled case by case.  This is but another.
  555.     LView::ResizeFrameBy(inWidthDelta, inHeightDelta, inRefresh);
  556.     PositionZapButton();
  557.     CBevelView::SubPanesChanged(this, inRefresh);
  558. }
  559.  
  560. void LDividedView::AdjustCursorSelf( Point inPortPt, const EventRecord& inMacEvent )
  561. {
  562.     Rect        frame;
  563.     
  564.     // if the pane is closed and drag-to-expand is turned off, bail.
  565.     if ( !GetDividerPosition() && !CanExpandByDragging() )
  566.         return;
  567.     
  568.     this->CalcRectBetweenPanes( frame );
  569.     this->PortToLocalPoint( inPortPt );
  570.     if ( PtInRect( inPortPt, &frame ) )
  571.     {
  572.         if ( mSlidesHorizontally )
  573.             ::SetCursor( *(::GetCursor( curs_HoriDrag ) ) );
  574.         else
  575.             ::SetCursor( *(::GetCursor( curs_VertDrag ) ) );
  576.     }
  577.     else
  578.         LView::AdjustCursorSelf(inPortPt, inMacEvent);
  579. }
  580.