2017-02-10 23:10:17 +01:00
/*
interactionDirector - Global UI Manager
Copyright ( C ) Lumiera . org
2017 , Hermann Vosseler < Ichthyostega @ web . de >
This program is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation ; either version 2 of
the License , or ( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/** @file interaction-director.cpp
* * Implementation parts of the top - level controller within the UI .
*/
2017-02-10 23:41:47 +01:00
# include "gui/gtk-base.hpp"
# include "gui/ui-bus.hpp"
# include "gui/ctrl/bus-term.hpp"
2017-02-19 03:46:00 +01:00
# include "gui/ctrl/global-ctx.hpp"
# include "gui/interact/interaction-director.hpp"
2017-09-02 19:28:57 +02:00
# include "gui/interact/view-locator.hpp"
2017-02-19 03:46:00 +01:00
# include "gui/interact/spot-locator.hpp"
# include "gui/interact/navigator.hpp"
# include "gui/interact/focus-tracker.hpp"
2018-04-04 03:29:26 +02:00
# include "gui/interact/ui-coord-resolver.hpp"
2017-04-23 18:28:32 +02:00
# include "gui/dialog/preferences-dialog.hpp"
# include "gui/dialog/render.hpp"
# include "gui/workspace/workspace-window.hpp"
2017-02-19 03:46:00 +01:00
# include "gui/ctrl/ui-state.hpp"
# include "gui/setting/asset-controller.hpp"
# include "gui/timeline/timeline-controller.hpp"
2017-02-10 23:41:47 +01:00
# include "proc/mobject/session/root.hpp"
2017-04-17 23:16:57 +02:00
# include "proc/asset/sequence.hpp" ///////////////////////////////////////////////////////////TICKET #1096 : avoid direct inclusion to reduce compile times
# include "proc/mobject/session/fork.hpp" ///////////////////////////////////////////////////////////TICKET #1096 : avoid direct inclusion to reduce compile times
2017-04-17 21:54:36 +02:00
# include "proc/cmd.hpp"
# include "backend/real-clock.hpp"
2017-02-10 23:41:47 +01:00
# include "lib/diff/tree-mutator.hpp"
2017-04-17 21:54:36 +02:00
# include "lib/format-obj.hpp"
2017-02-10 23:41:47 +01:00
//#include "gui/ui-bus.hpp"
//#include "lib/util.hpp"
2017-02-10 23:10:17 +01:00
2017-02-10 23:41:47 +01:00
//#include <memory>
//#include <list>
2017-02-10 23:10:17 +01:00
2017-02-10 23:41:47 +01:00
//using util::isnil;
//using std::list;
//using std::shared_ptr;
2017-04-17 23:16:57 +02:00
using lib : : idi : : EntryID ;
using lib : : hash : : LuidH ;
2017-02-10 23:41:47 +01:00
using lib : : diff : : TreeMutator ;
2017-04-17 21:54:36 +02:00
using util : : toString ;
2017-02-10 23:10:17 +01:00
namespace gui {
2017-02-17 21:16:42 +01:00
namespace interact {
2017-02-19 03:46:00 +01:00
namespace session = proc : : mobject : : session ;
2017-04-17 21:54:36 +02:00
namespace cmd = proc : : cmd ;
2017-02-19 03:46:00 +01:00
2017-02-17 21:16:42 +01:00
using ctrl : : GlobalCtx ;
2017-02-19 03:46:00 +01:00
using ctrl : : UiState ;
using setting : : AssetController ;
using timeline : : TimelineController ;
2017-02-10 23:10:17 +01:00
2017-02-19 03:46:00 +01:00
// emit dtors of children here...
2017-02-10 23:10:17 +01:00
InteractionDirector : : ~ InteractionDirector ( )
{ }
2018-08-02 17:23:28 +02:00
/**
* Setup and initialise all representations of " global-ness " .
* @ param globals wiring to the circle of top - level UI managers ( including ourselves )
* @ warning this ctor is performed within the UI thread , yet _prior_ to entering the GTK event loop .
* For this reason , all initialisation performed here must be wiring - only ; any tasks requiring an
* actually operative UI need to be scheduled , by means of the NotificationService .
* @ todo 7 / 2018 STOP no , can ' t be the NotificationService . ////////////////////////////////////////////TICKET #1151 : Need a new dedicated service in UiManager
*/
2017-02-14 03:31:12 +01:00
InteractionDirector : : InteractionDirector ( GlobalCtx & globals )
2017-02-19 03:46:00 +01:00
: model : : Controller ( session : : Root : : getID ( ) , globals . uiBus_ . getAccessPoint ( ) )
2017-02-14 03:31:12 +01:00
, globalCtx_ ( globals )
2018-04-07 02:55:47 +02:00
, viewLocator_ { new ViewLocator }
2017-02-19 03:46:00 +01:00
, spotLocator_ { new SpotLocator }
2018-04-04 03:29:26 +02:00
, navigator_ { * spotLocator_ , * viewLocator_ } // Service exposed as Depend<LocationQuery>
2017-02-19 03:46:00 +01:00
, tracker_ { new FocusTracker { * navigator_ } }
2017-02-19 04:46:13 +01:00
, uiState_ { new UiState { globals . uiBus_ . getStateManager ( ) , * tracker_ } }
2017-02-19 03:46:00 +01:00
, assets_ { new AssetController { session : : Root : : getAssetID ( ) , this - > uiBus_ } }
, timelines_ { }
{
}
2017-02-10 23:10:17 +01:00
2017-04-23 18:46:58 +02:00
namespace { // Temporary Junk
inline void
unimplemented ( const char * todo )
{
WARN ( gui , " %s is not yet implemented. So sorry. " , todo ) ;
}
}
2017-02-10 23:10:17 +01:00
void
2017-02-10 23:41:47 +01:00
InteractionDirector : : buildMutator ( TreeMutator : : Handle buffer )
2017-02-10 23:10:17 +01:00
{
2017-02-10 23:41:47 +01:00
// using Attrib = std::pair<const string,string>;
// using lib::diff::collection;
2017-02-10 23:10:17 +01:00
2017-02-10 23:41:47 +01:00
buffer . create (
TreeMutator : : build ( )
) ;
2017-04-23 18:46:58 +02:00
unimplemented ( " create a sensible binding between root-controller and root-model element " ) ;
2017-02-10 23:10:17 +01:00
}
2017-03-02 23:08:01 +01:00
/**
* setup a new editing project , possibly close the current one .
* This action launches the project setup UI , for the user to define
* the storage location and further parameters of the new project .
* A currently opened project will be deactivated asynchronously ,
* without saving a snapshot , while retaining the last actions
* in the project ' s command log .
*/
void
InteractionDirector : : newProject ( )
{
2017-04-23 18:46:58 +02:00
unimplemented ( " launch project setup UI " ) ;
2017-03-02 23:08:01 +01:00
}
/**
* Save a snapshot of the current project ' s contents and the UI state .
* @ note any command issued against a session will always be logged in
* the project ' s log . A snapshot is a marked reference point and
* additionally includes a capture of UI state into the project .
*/
void
InteractionDirector : : saveSnapshot ( )
{
2017-04-17 21:54:36 +02:00
string snapshotID { " snap- " + toString ( backend : : RealClock : : now ( ) ) } ;
invoke ( cmd : : session_saveSnapshot , snapshotID ) ;
2017-03-02 23:08:01 +01:00
}
/**
* Continue evolution of the currently active project under a new identity .
* From a user ' s point of view , this is the " save as... " action , but due to
* the nature of Lumiera ' s projects , it is in fact more complicated . Thus
* this action launches the project setup UI and preselects the " fork " option
* there . Optionally this allows to copy the project ' s history into the forked
* new project , or alternatively just to start with a snapshot . Another option
* there is to set up a new storage location , or to replace the existing project
* under a new name .
* @ note any commands issued since saving the last snapshot will be retained at least
* in the existing project ' s log ( unless the old project is replaced / rewritten )
*/
void
InteractionDirector : : forkProject ( )
{
2017-04-23 18:46:58 +02:00
unimplemented ( " launch project setup UI to create a fork of the project under new name " ) ;
2017-03-02 23:08:01 +01:00
}
2017-04-23 18:28:32 +02:00
/**
* Edit global configuration and setup .
* This action will launch the project setup UI , which allows to adjust configuration
* - for this installation of Lumiera
* - for the current project
* - for this user ' s session in this project
* @ todo 4 / 2017 not yet implemented , delegate to the AssetControler , which represents
* all global aspects of the application , session and project
*/
void
InteractionDirector : : editSetup ( )
{
dialog : : PreferencesDialog dialog ( getWorkspaceWindow ( ) ) ;
dialog . run ( ) ;
2017-04-23 18:46:58 +02:00
unimplemented ( " edit global configuration " ) ;
2017-04-23 18:28:32 +02:00
}
2017-03-02 23:08:01 +01:00
/**
* Select and open a file to perform a suitable operation .
* This action will launch the fileOpen UI . Depending on the selected file ' s meaning ,
* the actual operation will be either to integrate the data somehow into the current
* session , or to deactivate the current session and replace it with an other session
* persisted on storage .
*/
void
InteractionDirector : : openFile ( )
{
2017-04-23 18:46:58 +02:00
unimplemented ( " open file " ) ;
2017-03-02 23:08:01 +01:00
}
2017-04-23 18:28:32 +02:00
/**
* Start a render process . This action will launch the render setup UI .
* Depending on the current Spot , a suitable object to render will be preselected ,
* typically the current timeline .
* @ todo 4 / 2017 not yet implemented , and it is not clear how to access the functionality .
* Do we send a command ? Or do we access the Play - Out subsystem directly , to create
* some kind of dedicated player instance ?
*/
void
InteractionDirector : : render ( )
{
dialog : : Render dialog ( getWorkspaceWindow ( ) ) ; //////global -> InteractionDirector
dialog . run ( ) ;
2017-04-23 18:46:58 +02:00
unimplemented ( " start render " ) ;
2017-04-23 18:28:32 +02:00
}
2017-03-14 04:30:02 +01:00
/**
* Establish a pristine new sequence within the session .
* The goal is to create a new _playground_ for the user to add content .
* Actually , not only a new sequence is created , but also a new fork ( track tree )
* and a new timeline to hold that sequence . And finally , this new timeline is opened for editing .
* This action invokes a command into the session , which in turn is responsible for figuring out
* all the contextual details sensibly .
2017-04-17 23:16:57 +02:00
* @ todo 4 / 2017 using the session - root ( = this ) as anchor for the moment ,
* but should actually figure out the current context dynamically . . . //////////////////////////////TICKET #1082 : actually access the interaction state to get "current scope"
2017-03-14 04:30:02 +01:00
*/
void
InteractionDirector : : newSequence ( )
{
2017-04-17 23:16:57 +02:00
LuidH anchor { * this } ; /////////////////////////////////////////////////////////////////TICKET #1082 : actually access the interaction state to get "current scope"
LuidH newSeqID { EntryID < proc : : asset : : Sequence > ( ) . getHash ( ) } ; ////////////////////////////////////////////TICKET #1096 : a better Idea what to send over the wire?
invoke ( cmd : : session_newSequence , anchor , newSeqID ) ;
2017-03-14 04:30:02 +01:00
}
/**
* Establish a empty new track close to the current scope .
* Like for # newSequence , the goal is to create a new empty workspace .
* But since a track can be attached anywhere within the fork ( track tree ) , the currently active element
* is used to establish a current scope , which in turn is used as anchor to attach the new track in a
* sensible way , with a preference to add the new track as a sibling to the current scope . The actual
* details of this decision are delegated to the session , but the command invoked by this action does
* need a current element as argument , and this current element thus needs to be figured out from
* the context of invocation ( current focus and possibly selection )
* @ todo as of 3 / 2017 this is an initial draft : It is not clear yet , if this lookup of context
* will always be treated implicitly , or if it is better to have a public _content discovery operation_
* on InteractionDirector and pass the current element explicitly as argument
2017-04-17 23:16:57 +02:00
* @ todo 4 / 2017 using the session - root ( = this ) as anchor for the moment ,
* but should actually figure out the current context dynamically . . . //////////////////////////////TICKET #1082 : actually access the interaction state to get "current scope"
2017-03-14 04:30:02 +01:00
*/
void
InteractionDirector : : newTrack ( )
{
2017-04-17 23:16:57 +02:00
LuidH anchor { * this } ; /////////////////////////////////////////////////////////////////TICKET #1082 : actually access the interaction state to get "current scope"
LuidH newTrackID { EntryID < proc : : mobject : : session : : Fork > ( ) . getHash ( ) } ; ///////////////////////////////////TICKET #1096 : a better Idea what to send over the wire?
invoke ( cmd : : sequence_newTrack , anchor , newTrackID ) ;
2017-03-14 04:30:02 +01:00
}
2017-04-23 18:28:32 +02:00
workspace : : WorkspaceWindow &
InteractionDirector : : getWorkspaceWindow ( )
{
2017-09-02 18:36:58 +02:00
return globalCtx_ . windowLoc_ . findActiveWindow ( ) ;
2017-04-23 18:28:32 +02:00
}
2017-02-10 23:10:17 +01:00
2017-02-17 21:16:42 +01:00
} } // namespace gui::interact