home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / cmd / macfe / rdfui / CRDFCoordinator.cp < prev    next >
Encoding:
Text File  |  1998-04-08  |  18.8 KB  |  590 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. //
  20. // Mike Pinkerton, Netscape Communications
  21. //
  22. // The CRDFCoordinator is the view that contains the
  23. // "selector widget," object that selects the different RDF "views" stored
  24. // in the RDF database, and the view that displays the "hyper tree"
  25. // representation of the selected RDF view.
  26. //
  27. // It's responsible for coordinating the UI with the HT/RDF XP code. How does it
  28. // do that? Let's take a look at the interactions.
  29. //
  30. // There are two kinds of messages that we need to respond to, and the goal is to
  31. // keep them as separate as possible. The first kind are PowerPlant messages, sent
  32. // via the broadcaster/listener architecture. These messages should denote that
  33. // an event in the front end (eg, a user click) caused something to change and HT
  34. // (along with the rest of the UI) needs to know about it. The second kind of messages
  35. // come from HT itself, and are the result of a change within HT by calling HT functions
  36. // directly (or possibly by javascript, etc).
  37. //
  38. // Let's say the shelf is closed, and no workspaces are selected. The user then clicks
  39. // on one of the workspace icons. The CNavCenterSelectorPane figures out which HT view
  40. // corresponds to this icon and updates its internal state. In doing this, a
  41. // "active selection changed" message is broadcast to the coordinator. When this message
  42. // is received by the coordinator (CRDFCoordinator::ListenToMessage()), it tells the tree 
  43. // view to match the contents of the current HT view (CHyperTreeFlexTable::OpenView()). 
  44. // The coordinator also tells HT about the new active workspace by calling HT_SetSelectedView(),
  45. // but first it turns off HT notifications because we don't need to be told about this event
  46. // again (it has already been handled by PP's messages in the FE). Control then returns to the
  47. // selector pane which broadcasts a "shelf state changed" message to open the shelf. This 
  48. // is again received by the coordinator which tells the Navcenter shelf to spring out. Note
  49. // that this is done _after_ the tree view has been correctly set to avoid tons of asserts 
  50. // in the flex table code.
  51. //
  52. // When the user clicks either the close box or that same workspace icon, a message is sent to
  53. // the coordinator to close the shelf. The coordinator then tells the selector about the change
  54. // by calling SetActiveWorkspace(NULL) and closes the shelf.
  55. //
  56. // There are several times when HT wants to set the active workspace on its own, the most notable
  57. // occurs when a new window is created at startup. HT remembers the last active workspace
  58. // when the user quits and will reset when the new window is created. It does this by making a
  59. // call to HT_SetSelectedView() which sends an HT event to the coordinator (handled in 
  60. // CRDFCoordinator::HandleNotification()). The first thing the coordinator does is to call
  61. // SelectView() which opens the tree and ensures that the selector is up to date with the
  62. // current selection. Then it sends messages to open the shelf (if need be) and to update
  63. // the title in the title bar. Note that these last two things are done in different places
  64. // when the change comes from the FE so we don't end up doing them over and over again when
  65. // HT makes a change.
  66. //
  67. // My first stab at all this was to have one single flow of control that could handle view selection
  68. // from both the user and HT in one line. While possible (the last implementation did it), it was
  69. // very confusing. Hopefully this new world will make things somewhat easier to follow because
  70. // the messages are more separated based on where the action that kicked it off came from.
  71. //
  72.  
  73. #include "CRDFCoordinator.h"
  74.  
  75. #include "CHyperTreeFlexTable.h"
  76. #include "CNavCenterTitle.h"
  77.  
  78.  
  79. // XP headers
  80. #include "xp_str.h"
  81. #include "htrdf.h"
  82. #include "xp_ncent.h"
  83.  
  84. // MacFE specific headers
  85. #include "URDFUtilities.h"
  86. #include "CNavCenterSelectorPane.h"
  87. #include "Netscape_Constants.h"
  88. #include "URobustCreateWindow.h"
  89. #include "BookmarksDialogs.h"
  90. #include "divview.h"
  91. #include "LGAIconSuiteControl.h"
  92.  
  93.  
  94. const char* CRDFCoordinator::Pref_EditWorkspace = "browser.editWorkspace";
  95. const char* CRDFCoordinator::Pref_ShowNavCenterSelector = "browser.chrome.show_navcenter_selector";
  96. const char* CRDFCoordinator::Pref_ShowNavCenterShelf = "browser.chrome.show_navcenter_shelf";
  97.  
  98. CRDFCoordinator::CRDFCoordinator(LStream* inStream)
  99. :    LView(inStream),
  100.     LDragAndDrop ( GetMacPort(), this ),
  101.     mSelectorPane(NULL),
  102.     mTreePane(NULL),
  103.     mHTPane(NULL),
  104.     mIsInChrome(false),
  105.     mNavCenter(NULL),
  106.     mSelector(NULL)
  107. {
  108.     *inStream >> mSelectorPaneID;
  109.     *inStream >> mTreePaneID;
  110.     
  111. } // constructor
  112.  
  113.  
  114. CRDFCoordinator::~CRDFCoordinator()
  115. {
  116.     // if we don't do this, the LCommander destructor will try to clear the selection and
  117.     // of course, the HTPane won't be around anymore to update the selection....boom....
  118.     SwitchTarget(nil);
  119.     HT_SetNotificationMask ( mHTPane, HT_EVENT_NO_NOTIFICATION_MASK );
  120.     
  121.     delete mNavCenter;
  122.     delete mSelector;
  123.     
  124.     UnregisterNavCenter();
  125.     HT_DeletePane ( mHTPane );
  126.     
  127. } // destructor
  128.  
  129.  
  130. void
  131. CRDFCoordinator::FinishCreateSelf()
  132. {
  133.     mSelectorPane = dynamic_cast<CNavCenterSelectorPane*>(FindPaneByID(mSelectorPaneID));
  134.     mTreePane = dynamic_cast<CHyperTreeFlexTable*>(FindPaneByID(mTreePaneID));
  135.  
  136.     Assert_((mSelectorPane != NULL) && (mTreePane != NULL));
  137.  
  138.     // initialize the navCenter shelf. If we are a standalone window, we won't have
  139.     // a LDividedView so no expando-collapso stuff happens.
  140.     LDividedView* navCenter = dynamic_cast<LDividedView*>(FindPaneByID('ExCt'));
  141.     if ( navCenter ) {
  142.         mIsInChrome = true;
  143.         mSelectorPane->SetEmbedded(true);
  144.         mNavCenter = new CShelf ( navCenter, Pref_ShowNavCenterShelf );
  145.     }
  146.     
  147.     // initialize the navCenter selector shelf. Again, if we're standalone, there won't
  148.     // be a LDividedView.
  149.     LDividedView* navCenterSelector = dynamic_cast<LDividedView*>(FindPaneByID('ExSe'));
  150.     if ( navCenterSelector )
  151.         mSelector = new CShelf ( navCenterSelector, Pref_ShowNavCenterSelector );
  152.     
  153.     // Register the title bar as a listener to both this class and the selector bar. It will
  154.     // receive messages from the selector when the _user_ changes the current workspace and 
  155.     // will receive messages from this class when _HT_ changes the current workspace. Either
  156.     // way it needs to know so it can update the title string.
  157.     CNavCenterTitle* titleBar =
  158.             dynamic_cast<CNavCenterTitle*>(FindPaneByID(CNavCenterTitle::pane_ID));
  159.     if ( titleBar ) {
  160.         AddListener(titleBar);
  161.         if ( mSelectorPane )
  162.             mSelectorPane->AddListener(titleBar);    
  163.     }
  164.         
  165.     // If the close box is there, register this class as a listener so we get the
  166.     // close message. It won't be there in the standalone window version        
  167.     LGAIconSuiteControl* closeBox = 
  168.             dynamic_cast<LGAIconSuiteControl*>(FindPaneByID(CNavCenterTitle::kCloseBoxPaneID));
  169.     if ( closeBox )
  170.         closeBox->AddListener(this);
  171.             
  172.     // setting view selection comes via CRDFNotificationHandler, so don't do it here.
  173.     mHTPane = CreateHTPane();
  174.     if (mHTPane)
  175.     {
  176.         if (mSelectorPane)
  177.         {
  178.             mSelectorPane->SetHTPane ( mHTPane );
  179.             mSelectorPane->AddListener(this);
  180.             // fill selector pane with list of RDF "views"
  181.             Uint32 numViews = HT_GetViewListCount(mHTPane);
  182.             for (Uint32 i = 0; i < numViews; i++)
  183.             {
  184.                 HT_View view = HT_GetNthView(mHTPane, i);
  185.                 SelectorData* selector = new SelectorData(view);
  186.                 HT_SetViewFEData ( view, selector );
  187.             }            
  188.         }
  189.             
  190.         // receive notifications from the tree view
  191.         if (mTreePane)
  192.             mTreePane->AddListener(this);
  193.  
  194.     } // if HT pane is valid
  195.     
  196. } // FinishCreateSelf
  197.  
  198.  
  199. //
  200. // SavePlace
  201. //
  202. // Pass through to the tree view so it can save the shelf width
  203. //
  204. void
  205. CRDFCoordinator :: SavePlace ( LStream* outStreamData )
  206. {
  207.     if ( !outStreamData )
  208.         return;
  209.         
  210.     if ( mIsInChrome )
  211.         mNavCenter->GetShelf()->SavePlace(outStreamData);
  212.     
  213. } // SavePlace
  214.  
  215.  
  216. //
  217. // RestorePlace
  218. //
  219. // Pass through to the tree view so it can restore the shelf width
  220. //
  221. void
  222. CRDFCoordinator :: RestorePlace ( LStream* inStreamData )
  223. {
  224.     if ( !inStreamData )    // if reading from new/empty prefs, the stream will be null
  225.         return;
  226.         
  227.     if ( mIsInChrome )
  228.         mNavCenter->GetShelf()->RestorePlace(inStreamData);
  229.     
  230. } // RestorePlace
  231.  
  232.  
  233. //
  234. // HandleNotification
  235. //
  236. // Process notification events from HT. Things like nodes being added or deleted, etc come
  237. // here and then are farmed out to the appropriate subview.
  238. //
  239. void CRDFCoordinator::HandleNotification(
  240.     HT_Notification    /*notifyStruct*/,
  241.     HT_Resource        node,
  242.     HT_Event        event)
  243. {
  244.     PRBool isOpen;
  245.     HT_Error err;
  246.  
  247.     HT_View view = HT_GetView(node);        // everyone needs this, more or less
  248.     
  249.     switch (event)
  250.     {
  251.         case HT_EVENT_NODE_ADDED:
  252.         {
  253.             if ( view == mTreePane->GetHTView() ) { 
  254.                 TableIndexT index = HT_GetNodeIndex(view, node);
  255.                 mTreePane->InsertRows(1, index + 1, NULL, 0, true);
  256.                 mTreePane->SyncSelectionWithHT();
  257.             }
  258.             break;
  259.         }
  260.  
  261.         case HT_EVENT_VIEW_SELECTED:
  262.         {
  263.             // if the current view is changed by HT (not interactively by the user), this
  264.             // is the only place in the call chain where we will get notification. Make sure
  265.             // the shelf is open/closed accordingly and the titlebar is updated.
  266.             SelectView(view);
  267.             
  268.             bool openShelf = (view != NULL);
  269.             ListenToMessage ( CNavCenterSelectorPane::msg_ShelfStateShouldChange, &openShelf );
  270.             BroadcastMessage ( CNavCenterSelectorPane::msg_ActiveSelectorChanged, view );
  271.             break;
  272.         }
  273.         
  274.         //
  275.         // we get this event before the node opens/closes. This is useful for closing so
  276.         // we can compute the number of visible children of the node being closed before
  277.         // all that info goes away.
  278.         //
  279.         case HT_EVENT_NODE_OPENCLOSE_CHANGING:
  280.             err = HT_GetOpenState(node, &isOpen);
  281.             if (isOpen)
  282.                 CollapseNode(node);
  283.             break;
  284.         
  285.         //
  286.         // we get this event after the node has finished opening/closing.
  287.         //
  288.         case HT_EVENT_NODE_OPENCLOSE_CHANGED:
  289.             err = HT_GetOpenState(node, &isOpen);
  290.             if (isOpen)
  291.                 ExpandNode(node);
  292.             break;
  293.             
  294.         case HT_EVENT_NODE_DELETED_DATA:
  295.         case HT_EVENT_NODE_DELETED_NODATA:
  296.         {
  297.             // delete FE data if any is there....
  298.             break;
  299.         }
  300.  
  301.         //
  302.         // we get this event when a new node is created and is to be put in "inline edit" mode.
  303.         //
  304.         case HT_EVENT_NODE_EDIT:
  305.         {
  306.             //ÑÑÑ There are currently some problems with redraw here because of the way that
  307.             // the drawing code and the inline editing code interact. You can uncomment the
  308.             // line below and see that the cell does not draw correctly because it never gets
  309.             // a drawCellContents() called on it (since it is the cell being edited). This
  310.             // needs some work.....
  311.             if ( view == mTreePane->GetHTView() ) {
  312.                 TableIndexT rowToEdit = URDFUtilities::HTRowToPPRow( HT_GetNodeIndex(view, node) );
  313. //                mTreePane->DoInlineEditing(    rowToEdit );            
  314.             }
  315.             break;
  316.         }
  317.                     
  318.         case HT_EVENT_NODE_VPROP_CHANGED:
  319.         {
  320.             //ÑÑÑoptimization? only redraw the cell that changed
  321. //            TableIndexT index = HT_GetNodeIndex(HT_GetView(node), node);
  322.             mTreePane->Refresh();
  323.             break;
  324.         }
  325.         
  326.         case HT_EVENT_NODE_SELECTION_CHANGED:
  327.             mTreePane->SyncSelectionWithHT();
  328.             break;
  329.             
  330.         case HT_EVENT_VIEW_REFRESH:
  331.         {
  332.             // update the sitemap icon if anything has changed
  333.             if ( view == HT_GetViewType(mHTPane, HT_VIEW_SITEMAP) ) {
  334.                 //.....update the icon, but we don't have an icon or an API yet....
  335.             }
  336.  
  337.             // only update if current view
  338.             //
  339.             // ÑÑÑ There are some fairly noticable redraw problems here. For example, when
  340.             // nodes are deleted or added, the entire view is redrawn (and flashes). We
  341.             // need some help from HT to prevent this.
  342.             if ( view == mTreePane->GetHTView() ) { 
  343.                 uint32 newRowsInView = HT_GetItemListCount ( view );
  344.                 TableIndexT numRows, numCols;
  345.                 mTreePane->GetTableSize ( numRows, numCols );
  346.             
  347.                 int32 delta = newRowsInView - numRows;
  348.                 if ( delta > 0 )
  349.                     mTreePane->InsertRows ( delta, 1 );
  350.                 else
  351.                     mTreePane->RemoveRows ( abs(delta), 1, false );
  352.                 mTreePane->SyncSelectionWithHT();
  353.                 mTreePane->Refresh();
  354.             } // if refresh for current view
  355.             break;
  356.         }
  357.         
  358.         
  359.         case HT_EVENT_VIEW_WORKSPACE_REFRESH:
  360.             mSelectorPane->Refresh();
  361.             break;
  362.             
  363.         case HT_EVENT_VIEW_ADDED:
  364.         {
  365.             //ÑÑÑ adds new view at end because we don't have enough data from HT to
  366.             //ÑÑÑ do it right
  367.             SelectorData* selector = new SelectorData(view);
  368.             HT_SetViewFEData ( view, selector );
  369.             mSelectorPane->Refresh();
  370.             break;
  371.         }
  372.                     
  373.         case HT_EVENT_VIEW_DELETED:
  374.         {
  375.             SelectorData* sel = static_cast<SelectorData*>(HT_GetViewFEData(view));
  376.             delete sel;
  377.             break;
  378.         }
  379.         
  380.     } // case of which HT event
  381.  
  382. } // HandleNotification
  383.  
  384.  
  385. //
  386. // SelectView
  387. //
  388. // Make the given view the current view and ensure that the selector widget 
  389. // is up to date.
  390. //
  391. void CRDFCoordinator::SelectView(HT_View view)
  392. {
  393.     if ( view )
  394.         mTreePane->OpenView(view);
  395.     
  396.     // find the appropriate workspace and make it active if it has not yet been set
  397.     // (such as when HT sets it explicitly). We have to turn off listening to the selector 
  398.     // pane to avoid infinite loops (changing the selector will send us a message that the 
  399.     // active selector changed). This code should not be executed when the view change
  400.     // is made by the FE.
  401.     if ( !mSelectorPane->GetActiveWorkspace() || mSelectorPane->GetActiveWorkspace() != view ) {
  402.         StopListening();
  403.         mSelectorPane->SetActiveWorkspace(view);
  404.         StartListening();
  405.     } // if no selection or current selection outdated
  406.     
  407. } // SelectView
  408.  
  409.  
  410. //
  411. // SelectView
  412. //
  413. // Make the given type of view the current view and (by calling the above routine)
  414. // ensure that the selector widget is up to date. HT_SetSelectedView() will send
  415. // us a notification event to open the given view, so we don't have to do it here.
  416. //
  417. void CRDFCoordinator::SelectView ( HT_ViewType inPane )
  418. {
  419.     HT_View view = HT_GetViewType ( mHTPane, inPane );
  420.     HT_SetSelectedView ( mHTPane, view );
  421.  
  422. } // SelectView
  423.  
  424.  
  425.  
  426. void CRDFCoordinator::ExpandNode(HT_Resource node)
  427. {
  428.     mTreePane->ExpandNode(node);
  429. }
  430.  
  431. void CRDFCoordinator::CollapseNode(HT_Resource node)
  432. {
  433.     mTreePane->CollapseNode(node);
  434. }
  435.  
  436.  
  437. //
  438. // ListenToMessage
  439. //
  440. // Process the various messages we get from the FE, such as requests to open/close the tree shelf
  441. // or change which workspace is currently selected.
  442. void CRDFCoordinator::ListenToMessage(
  443.     MessageT        inMessage,
  444.     void            *ioParam)
  445. {
  446.     switch (inMessage) {
  447.     
  448.         // the user clicked in the selector pane to change the selected workspace. Tell
  449.         // the backend about it, but before we do that, turn off HT events so we don't actually
  450.         // get the notification back -- we don't need it because the view change was caused
  451.         // by the FE.
  452.         case CNavCenterSelectorPane::msg_ActiveSelectorChanged:
  453.         {
  454.             HT_View newView = reinterpret_cast<HT_View>(ioParam);
  455.             URDFUtilities::StHTEventMasking saveMask(mHTPane, HT_EVENT_NO_NOTIFICATION_MASK);
  456.             HT_SetSelectedView(mHTPane, newView);
  457.             SelectView(newView);
  458.             break;
  459.         }
  460.         
  461.         // expand/collapse the shelf to the state pointed to by |ioParam|. If we don't
  462.         // switch the target, we run into the problem where we are still the active
  463.         // commander and get called on to handle the menus. Since there will be no
  464.         // view, HT will barf.
  465.         case CNavCenterSelectorPane::msg_ShelfStateShouldChange:
  466.             if ( mIsInChrome ) {
  467.                 bool nowOpen = *(reinterpret_cast<bool*>(ioParam));
  468.                 mNavCenter->SetShelfState ( nowOpen );
  469.                 if ( nowOpen ) {
  470.                     mTreePane->SetRightmostVisibleColumn(1);    //ÑÑ╩avoid annoying columns
  471.                     SwitchTarget(this);
  472.                 }
  473.                 else
  474.                     SwitchTarget(GetSuperCommander());
  475.             }
  476.             break;
  477.         
  478.         // similar to above, but can cut out the crap because we are closing things
  479.         // down explicitly. Also make sure to tell the selector pane that nothing is 
  480.         // active, which the above message cannot do because it is responding to the
  481.         // code that just changed the workspace.
  482.         case CNavCenterTitle::msg_CloseShelfNow:
  483.             mNavCenter->SetShelfState ( false );
  484.             mSelectorPane->SetActiveWorkspace ( NULL );
  485.             SwitchTarget(GetSuperCommander());
  486.             break;
  487.             
  488.     } // case of which message
  489.     
  490. } // ListenToMessage
  491.  
  492.  
  493. //
  494. // ObeyCommand
  495. //
  496. // Do what PowerPlant tells us. =)
  497. //
  498. Boolean
  499. CRDFCoordinator :: ObeyCommand ( CommandT inCommand, void* ioParam )
  500. {
  501.     Boolean cmdHandled = false;
  502.  
  503.     if ( inCommand >= cmd_NavCenterBase && inCommand <= cmd_NavCenterCap ) {
  504.         // Ñmake sure nav center is open???
  505.         HT_Error err = HT_DoMenuCmd ( mHTPane, (HT_MenuCmd)(inCommand - cmd_NavCenterBase) );
  506.         Assert_( err == HT_NoErr );
  507.         return true;
  508.     }
  509.  
  510.     switch ( inCommand ) {
  511.     
  512.         //
  513.         // handle commands that we have to share with other parts of the UI
  514.         //
  515.         case cmd_Cut:
  516.             HT_DoMenuCmd(mHTPane, HT_CMD_CUT );
  517.             cmdHandled = true;
  518.             break;
  519.         case cmd_Copy:
  520.             HT_DoMenuCmd(mHTPane, HT_CMD_COPY );
  521.             cmdHandled = true;
  522.             break;
  523.         case cmd_Paste:
  524.             HT_DoMenuCmd(mHTPane, HT_CMD_PASTE );
  525.             cmdHandled = true;
  526.             break;
  527.         case cmd_Clear:
  528.             HT_DoMenuCmd(mHTPane, HT_CMD_DELETE_FILE );
  529.             cmdHandled = true;
  530.             break;
  531.  
  532.         case cmd_NCFind:
  533.             URobustCreateWindow::CreateWindow ( CBookmarksFindDialog::res_ID, nil );
  534.             cmdHandled = true;
  535.             break;
  536.         
  537.         default:
  538.             cmdHandled = LCommander::ObeyCommand ( inCommand, ioParam );
  539.             break;
  540.             
  541.     } // case on command
  542.  
  543.     return cmdHandled;
  544.     
  545. } // ObeyCommand
  546.  
  547.  
  548. //
  549. // HandleKeyPress
  550. //
  551. // Handle changing the nav center view on cmd-tab
  552. // 
  553. Boolean
  554. CRDFCoordinator :: HandleKeyPress(const EventRecord &inKeyEvent)
  555. {
  556.     char key = inKeyEvent.message & charCodeMask;
  557.     if ( inKeyEvent.modifiers & cmdKey && key == kTabCharCode )
  558.         mSelectorPane->CycleCurrentWorkspace();
  559.     else
  560.         return LCommander::HandleKeyPress(inKeyEvent);
  561.  
  562.     return true;
  563.     
  564. } // HandleKeyPress
  565.  
  566.  
  567. //
  568. // RegisterNavCenter
  569. //
  570. // Tell XP about this navCenter & context for site map stuff
  571. //
  572. void
  573. CRDFCoordinator :: RegisterNavCenter ( MWContext* inContext )
  574. {
  575.     XP_RegisterNavCenter ( mHTPane, inContext );
  576.  
  577. } // RegisterNavCenter
  578.  
  579.  
  580. //
  581. // UnregisterNavCenter
  582. //
  583. // Tell XP this window is going away.
  584. //
  585. void
  586. CRDFCoordinator :: UnregisterNavCenter ( )
  587. {
  588.     XP_UnregisterNavCenter ( mHTPane );    
  589.  
  590. } // UnregisterNavCenter