You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ovdCInterfacedScenario.cpp 149KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699
  1. #include <boost/filesystem.hpp>
  2. #include "ovdCInterfacedScenario.h"
  3. #include "ovdCApplication.h"
  4. #include "ovdCBoxProxy.h"
  5. #include "ovdCCommentProxy.h"
  6. #include "ovdCLinkProxy.h"
  7. #include "ovdCConnectorEditor.h"
  8. #include "ovdCInterfacedObject.h"
  9. #include "ovdTAttributeHandler.h"
  10. #include "ovdCDesignerVisualization.h"
  11. #include "ovdCPlayerVisualization.h"
  12. #include "ovdCRenameDialog.h"
  13. #include "ovdCAboutPluginDialog.h"
  14. #include "ovdCAboutScenarioDialog.h"
  15. #include "ovdCSettingEditorDialog.h"
  16. #include "ovdCCommentEditorDialog.h"
  17. #include <vector>
  18. #include <fstream>
  19. #include <sstream>
  20. #include <streambuf>
  21. #include <algorithm>
  22. #include <gdk/gdkkeysyms.h>
  23. #include <fs/Files.h>
  24. #include <cstdlib>
  25. #if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
  26. #include <strings.h>
  27. #define _strcmpi strcasecmp
  28. #endif
  29. namespace OpenViBE {
  30. namespace Designer {
  31. extern std::map<size_t, GdkColor> gColors;
  32. static GtkTargetEntry targets[] = { { static_cast<gchar*>("STRING"), 0, 0 }, { static_cast<gchar*>("text/plain"), 0, 0 } };
  33. static GdkColor colorFromIdentifier(const CIdentifier& id, const bool isDeprecated = false)
  34. {
  35. GdkColor color;
  36. uint32_t value1 = 0;
  37. uint32_t value2 = 0;
  38. uint64_t res = 0;
  39. sscanf(id.str().c_str(), "(0x%08X, 0x%08X)", &value1, &value2);
  40. res += value1;
  41. res <<= 32;
  42. res += value2;
  43. color.pixel = guint16(0);
  44. color.red = guint16((res & 0xffff) | 0x8000);
  45. color.green = guint16(((res >> 16) & 0xffff) | 0x8000);
  46. color.blue = guint16(((res >> 32) & 0xffff) | 0x8000);
  47. if (isDeprecated)
  48. {
  49. color.blue = 2 * color.blue / 3;
  50. color.red = 2 * color.red / 3;
  51. color.green = 2 * color.green / 3;
  52. }
  53. return color;
  54. }
  55. static std::string getBoxAlgorithmURL(const std::string& in, const bool removeSlash = false)
  56. {
  57. std::string tmp(in);
  58. std::string out;
  59. bool lastWasSeparator = true;
  60. for (char c : tmp)
  61. {
  62. if (std::isalnum(c) || (!removeSlash && c == '/'))
  63. {
  64. if (c == '/') { out += "_"; }
  65. else
  66. {
  67. if (lastWasSeparator) { out += std::toupper(c); }
  68. else { out += c; }
  69. }
  70. lastWasSeparator = false;
  71. }
  72. else
  73. {
  74. // if(!lastWasSeparator) { out += "_"; }
  75. lastWasSeparator = true;
  76. }
  77. }
  78. return out;
  79. }
  80. static void count_widget_cb(GtkWidget* /*widget*/, gpointer data)
  81. {
  82. int* i = reinterpret_cast<int*>(data);
  83. if (i) { (*i)++; }
  84. }
  85. static int gtk_container_get_children_count(GtkContainer* container)
  86. {
  87. int res = 0;
  88. gtk_container_foreach(container, count_widget_cb, &res);
  89. return res;
  90. }
  91. static gboolean scenario_scrolledwindow_scroll_event_cb(GtkWidget* /*widget*/, GdkEventScroll* event)
  92. {
  93. guint state = event->state & gtk_accelerator_get_default_mod_mask();
  94. /* Shift+Wheel scrolls the in the perpendicular direction */
  95. if (state & GDK_SHIFT_MASK)
  96. {
  97. if (event->direction == GDK_SCROLL_UP) { event->direction = GDK_SCROLL_LEFT; }
  98. else if (event->direction == GDK_SCROLL_LEFT) { event->direction = GDK_SCROLL_UP; }
  99. else if (event->direction == GDK_SCROLL_DOWN) { event->direction = GDK_SCROLL_RIGHT; }
  100. else if (event->direction == GDK_SCROLL_RIGHT) { event->direction = GDK_SCROLL_DOWN; }
  101. event->state &= ~GDK_SHIFT_MASK;
  102. state &= ~GDK_SHIFT_MASK;
  103. }
  104. return FALSE;
  105. }
  106. static void scenario_drawing_area_expose_cb(GtkWidget* /*widget*/, GdkEventExpose* event, gpointer data)
  107. {
  108. static_cast<CInterfacedScenario*>(data)->scenarioDrawingAreaExposeCB(event);
  109. }
  110. static void scenario_drawing_area_drag_data_received_cb(GtkWidget* /*widget*/, GdkDragContext* dc, const gint x, const gint y, GtkSelectionData* selectionData,
  111. const guint info, const guint t, gpointer data)
  112. {
  113. static_cast<CInterfacedScenario*>(data)->scenarioDrawingAreaDragDataReceivedCB(dc, x, y, selectionData, info, t);
  114. }
  115. static gboolean scenario_drawing_area_motion_notify_cb(GtkWidget* widget, GdkEventMotion* event, gpointer data)
  116. {
  117. static_cast<CInterfacedScenario*>(data)->scenarioDrawingAreaMotionNotifyCB(widget, event);
  118. return FALSE;
  119. }
  120. static void scenario_drawing_area_button_pressed_cb(GtkWidget* widget, GdkEventButton* event, gpointer data)
  121. {
  122. static_cast<CInterfacedScenario*>(data)->scenarioDrawingAreaButtonPressedCB(widget, event);
  123. }
  124. static void scenario_drawing_area_button_released_cb(GtkWidget* widget, GdkEventButton* event, gpointer data)
  125. {
  126. static_cast<CInterfacedScenario*>(data)->scenarioDrawingAreaButtonReleasedCB(widget, event);
  127. }
  128. static void scenario_drawing_area_key_press_event_cb(GtkWidget* widget, GdkEventKey* event, gpointer data)
  129. {
  130. static_cast<CInterfacedScenario*>(data)->scenarioDrawingAreaKeyPressEventCB(widget, event);
  131. }
  132. static void scenario_drawing_area_key_release_event_cb(GtkWidget* widget, GdkEventKey* event, gpointer data)
  133. {
  134. static_cast<CInterfacedScenario*>(data)->scenarioDrawingAreaKeyReleaseEventCB(widget, event);
  135. }
  136. static void context_menu_cb(GtkMenuItem* /*item*/, CInterfacedScenario::box_ctx_menu_cb_t* cb)
  137. {
  138. //CInterfacedScenario::box_ctx_menu_cb_t* pContextMenuCB=static_cast < CInterfacedScenario::box_ctx_menu_cb_t* >(data);
  139. switch (cb->command)
  140. {
  141. case EContextMenu::SelectionCopy: cb->scenario->copySelection();
  142. break;
  143. case EContextMenu::SelectionCut: cb->scenario->cutSelection();
  144. break;
  145. case EContextMenu::SelectionPaste: cb->scenario->pasteSelection();
  146. break;
  147. case EContextMenu::SelectionDelete: cb->scenario->deleteSelection();
  148. break;
  149. case EContextMenu::BoxRename: cb->scenario->contextMenuBoxRenameCB(*cb->box);
  150. break;
  151. case EContextMenu::BoxUpdate:
  152. {
  153. cb->scenario->snapshotCB();
  154. cb->scenario->contextMenuBoxUpdateCB(*cb->box);
  155. cb->scenario->redraw();
  156. break;
  157. }
  158. case EContextMenu::BoxRemoveDeprecatedInterfacors:
  159. {
  160. cb->scenario->contextMenuBoxRemoveDeprecatedInterfacorsCB(*cb->box);
  161. cb->scenario->redraw();
  162. break;
  163. }
  164. //case EContextMenu::BoxRename: cb->pInterfacedScenario->contextMenuBoxRenameAllCB(); break;
  165. case EContextMenu::BoxDelete:
  166. {
  167. // If selection is empty delete the box under cursor
  168. if (cb->scenario->m_SelectedObjects.empty())
  169. {
  170. cb->scenario->deleteBox(cb->box->getIdentifier());
  171. cb->scenario->redraw();
  172. cb->scenario->snapshotCB();
  173. }
  174. else { cb->scenario->deleteSelection(); }
  175. break;
  176. }
  177. case EContextMenu::BoxAddInput: cb->scenario->contextMenuBoxAddInputCB(*cb->box);
  178. break;
  179. case EContextMenu::BoxEditInput: cb->scenario->contextMenuBoxEditInputCB(*cb->box, cb->index);
  180. break;
  181. case EContextMenu::BoxRemoveInput: cb->scenario->contextMenuBoxRemoveInputCB(*cb->box, cb->index);
  182. break;
  183. case EContextMenu::BoxAddOutput: cb->scenario->contextMenuBoxAddOutputCB(*cb->box);
  184. break;
  185. case EContextMenu::BoxEditOutput: cb->scenario->contextMenuBoxEditOutputCB(*cb->box, cb->index);
  186. break;
  187. case EContextMenu::BoxRemoveOutput: cb->scenario->contextMenuBoxRemoveOutputCB(*cb->box, cb->index);
  188. break;
  189. case EContextMenu::BoxConnectScenarioInput: cb->scenario->contextMenuBoxConnectScenarioInputCB(*cb->box, cb->index, cb->secondaryIndex);
  190. break;
  191. case EContextMenu::BoxConnectScenarioOutput: cb->scenario->contextMenuBoxConnectScenarioOutputCB(*cb->box, cb->index, cb->secondaryIndex);
  192. break;
  193. case EContextMenu::BoxDisconnectScenarioInput: cb->scenario->contextMenuBoxDisconnectScenarioInputCB(*cb->box, cb->index, cb->secondaryIndex);
  194. break;
  195. case EContextMenu::BoxDisconnectScenarioOutput: cb->scenario->contextMenuBoxDisconnectScenarioOutputCB(*cb->box, cb->index, cb->secondaryIndex);
  196. break;
  197. case EContextMenu::BoxAddSetting: cb->scenario->contextMenuBoxAddSettingCB(*cb->box);
  198. break;
  199. case EContextMenu::BoxEditSetting: cb->scenario->contextMenuBoxEditSettingCB(*cb->box, cb->index);
  200. break;
  201. case EContextMenu::BoxRemoveSetting: cb->scenario->contextMenuBoxRemoveSettingCB(*cb->box, cb->index);
  202. break;
  203. case EContextMenu::BoxConfigure: cb->scenario->contextMenuBoxConfigureCB(*cb->box);
  204. break;
  205. case EContextMenu::BoxAbout: cb->scenario->contextMenuBoxAboutCB(*cb->box);
  206. break;
  207. case EContextMenu::BoxEnable:
  208. {
  209. if (cb->scenario->m_SelectedObjects.empty()) { cb->scenario->contextMenuBoxEnableCB(*cb->box); }
  210. else { cb->scenario->contextMenuBoxEnableAllCB(); }
  211. break;
  212. }
  213. case EContextMenu::BoxDisable:
  214. {
  215. if (cb->scenario->m_SelectedObjects.empty())
  216. {
  217. cb->scenario->contextMenuBoxDisableCB(*cb->box);
  218. break;
  219. }
  220. cb->scenario->contextMenuBoxDisableAllCB();
  221. break;
  222. }
  223. case EContextMenu::BoxDocumentation: cb->scenario->contextMenuBoxDocumentationCB(*cb->box);
  224. break;
  225. case EContextMenu::BoxEditMetabox: cb->scenario->contextMenuBoxEditMetaboxCB(*cb->box);
  226. break;
  227. case EContextMenu::ScenarioAbout: cb->scenario->contextMenuScenarioAboutCB();
  228. break;
  229. case EContextMenu::ScenarioAddComment: cb->scenario->contextMenuScenarioAddCommentCB();
  230. break;
  231. default: break;
  232. }
  233. // Redraw in any case, as some of the actual callbacks can forget to redraw. As this callback is only called after the user has accessed
  234. // the right-click menu, so its not a large overhead to do it in general. @TODO might remove the individual redraws.
  235. cb->scenario->redraw();
  236. }
  237. static void gdk_draw_rounded_rectangle(GdkDrawable* drawable, GdkGC* drawGC, const gboolean fill, const gint x, const gint y, const gint width,
  238. const gint height, const gint radius = 8)
  239. {
  240. if (fill != 0)
  241. {
  242. #if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
  243. gdk_draw_rectangle(drawable, drawGC, TRUE, x + radius, y, width - 2 * radius, height);
  244. gdk_draw_rectangle(drawable, drawGC, TRUE, x, y + radius, width, height - 2 * radius);
  245. #elif defined TARGET_OS_Windows
  246. gdk_draw_rectangle(drawable, drawGC, TRUE, x + radius, y, width - 2 * radius + 1, height + 1);
  247. gdk_draw_rectangle(drawable, drawGC, TRUE, x, y + radius, width + 1, height - 2 * radius + 1);
  248. #else
  249. #pragma error("you should give a version of this function for your OS")
  250. #endif
  251. }
  252. else
  253. {
  254. gdk_draw_line(drawable, drawGC, x + radius, y, x + width - radius, y);
  255. gdk_draw_line(drawable, drawGC, x + radius, y + height, x + width - radius, y + height);
  256. gdk_draw_line(drawable, drawGC, x, y + radius, x, y + height - radius);
  257. gdk_draw_line(drawable, drawGC, x + width, y + radius, x + width, y + height - radius);
  258. }
  259. #if defined TARGET_OS_Linux || defined TARGET_OS_MacOS
  260. gdk_draw_arc(drawable, drawGC, fill, x + width - radius * 2, y, radius * 2, radius * 2, 0 * 64, 90 * 64);
  261. gdk_draw_arc(drawable, drawGC, fill, x, y, radius * 2, radius * 2, 90 * 64, 90 * 64);
  262. gdk_draw_arc(drawable, drawGC, fill, x, y + height - radius * 2, radius * 2, radius * 2, 180 * 64, 90 * 64);
  263. gdk_draw_arc(drawable, drawGC, fill, x + width - radius * 2, y + height - radius * 2, radius * 2, radius * 2, 270 * 64, 90 * 64);
  264. #elif defined TARGET_OS_Windows
  265. gdk_draw_arc(drawable, drawGC, fill, x + width - radius * 2, y, radius * 2 + (fill != 0 ? 2 : 1), radius * 2 + (fill != 0 ? 2 : 1), 0 * 64, 90 * 64);
  266. gdk_draw_arc(drawable, drawGC, fill, x, y, radius * 2 + (fill != 0 ? 2 : 1), radius * 2 + (fill != 0 ? 2 : 1), 90 * 64, 90 * 64);
  267. gdk_draw_arc(drawable, drawGC, fill, x, y + height - radius * 2, radius * 2 + (fill != 0 ? 2 : 1), radius * 2 + (fill != 0 ? 2 : 1), 180 * 64, 90 * 64);
  268. gdk_draw_arc(drawable, drawGC, fill, x + width - radius * 2, y + height - radius * 2, radius * 2 + (fill != 0 ? 2 : 1), radius * 2 + (fill != 0 ? 2 : 1),
  269. 270 * 64, 90 * 64);
  270. #else
  271. #pragma error("you should give a version of this function for your OS")
  272. #endif
  273. }
  274. static void scenario_title_button_close_cb(GtkButton* /*button*/, gpointer data)
  275. {
  276. static_cast<CInterfacedScenario*>(data)->m_Application.closeScenarioCB(static_cast<CInterfacedScenario*>(data));
  277. }
  278. static gboolean editable_widget_focus_in_cb(GtkWidget* /*widget*/, GdkEvent* /*event*/, CApplication* app)
  279. {
  280. gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object(app->m_Builder, "openvibe-menu_edit")), 0);
  281. return 0;
  282. }
  283. static gboolean editable_widget_focus_out_cb(GtkWidget* /*widget*/, GdkEvent* /*event*/, CApplication* app)
  284. {
  285. gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object(app->m_Builder, "openvibe-menu_edit")), 1);
  286. return 0;
  287. }
  288. //static void scenario_configuration_add_setting_cb(GtkWidget*, CInterfacedScenario* pInterfacedScenario) { pInterfacedScenario->addScenarioSettingCB(); }
  289. static void modify_scenario_setting_value_cb(GtkWidget* /*widget*/, CInterfacedScenario::setting_cb_data_t* data)
  290. {
  291. CIdentifier typeID = CIdentifier::undefined();
  292. data->scenario->m_Scenario.getSettingType(data->index, typeID);
  293. data->scenario->m_Scenario.setSettingValue(data->index, data->scenario->m_SettingHelper->getValue(typeID, data->widgetValue));
  294. data->scenario->m_HasBeenModified = true;
  295. data->scenario->updateScenarioLabel();
  296. }
  297. static void modify_scenario_setting_default_value_cb(GtkWidget* /*widget*/, CInterfacedScenario::setting_cb_data_t* data)
  298. {
  299. CIdentifier typeID = CIdentifier::undefined();
  300. data->scenario->m_Scenario.getSettingType(data->index, typeID);
  301. data->scenario->m_Scenario.setSettingDefaultValue(data->index, data->scenario->m_SettingHelper->getValue(typeID, data->widgetValue));
  302. // We also se the 'actual' value to this
  303. data->scenario->m_Scenario.setSettingValue(data->index, data->scenario->m_SettingHelper->getValue(typeID, data->widgetValue));
  304. data->scenario->m_HasBeenModified = true;
  305. data->scenario->updateScenarioLabel();
  306. }
  307. static void modify_scenario_setting_move_up_cb(GtkWidget* /*widget*/, CInterfacedScenario::setting_cb_data_t* data)
  308. {
  309. if (data->index == 0) { return; }
  310. data->scenario->swapScenarioSettings(data->index - 1, data->index);
  311. }
  312. static void modify_scenario_setting_move_down_cb(GtkWidget* /*widget*/, CInterfacedScenario::setting_cb_data_t* data)
  313. {
  314. if (data->index >= data->scenario->m_Scenario.getSettingCount() - 1) { return; }
  315. data->scenario->swapScenarioSettings(data->index, data->index + 1);
  316. }
  317. static void modify_scenario_setting_revert_to_default_cb(GtkWidget* /*widget*/, CInterfacedScenario::setting_cb_data_t* data)
  318. {
  319. CString value;
  320. data->scenario->m_Scenario.getSettingDefaultValue(data->index, value);
  321. data->scenario->m_Scenario.setSettingValue(data->index, value);
  322. data->scenario->redrawScenarioSettings();
  323. }
  324. static void copy_scenario_setting_token_cb(GtkWidget* /*widget*/, CInterfacedScenario::setting_cb_data_t* data)
  325. {
  326. CString name;
  327. data->scenario->m_Scenario.getSettingName(data->index, name);
  328. name = CString("$var{") + name + CString("}");
  329. GtkClipboard* defaultClipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
  330. gtk_clipboard_set_text(defaultClipboard, name.toASCIIString(), -1);
  331. // On X11 there is another clipboard that it is useful to set as well
  332. GtkClipboard* x11Clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
  333. gtk_clipboard_set_text(x11Clipboard, name.toASCIIString(), -1);
  334. }
  335. static void modify_scenario_setting_type_cb(GtkWidget* combobox, CInterfacedScenario::setting_cb_data_t* data)
  336. {
  337. GtkBuilder* builder = gtk_builder_new();
  338. gtk_builder_add_from_string(builder, data->scenario->m_SerializedSettingGUIXML.c_str(), data->scenario->m_SerializedSettingGUIXML.length(), nullptr);
  339. gtk_widget_destroy(data->widgetValue);
  340. const CIdentifier typeID = data->scenario->m_SettingTypes[gtk_combo_box_get_active_text(GTK_COMBO_BOX(combobox))];
  341. data->scenario->m_Scenario.setSettingType(data->index, typeID);
  342. const CString name = data->scenario->m_SettingHelper->getSettingWidgetName(typeID);
  343. GtkWidget* value = GTK_WIDGET(gtk_builder_get_object(builder, name.toASCIIString()));
  344. gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(value)), value);
  345. gtk_table_attach_defaults(GTK_TABLE(data->container), value, 1, 5, 1, 2);
  346. // Set the value and connect GUI callbacks (because, yes, setValue connects callbacks like a ninja)
  347. CString str;
  348. data->scenario->m_Scenario.getSettingDefaultValue(data->index, str);
  349. data->scenario->m_SettingHelper->setValue(typeID, value, str);
  350. // add callbacks to disable the Edit menu in openvibe designer, which will in turn enable using stuff like copy-paste inside the widget
  351. const CString entryName = data->scenario->m_SettingHelper->getSettingEntryWidgetName(typeID);
  352. GtkWidget* entryValue = GTK_WIDGET(gtk_builder_get_object(builder, entryName.toASCIIString()));
  353. data->widgetValue = value;
  354. data->widgetEntryValue = entryValue;
  355. g_signal_connect(entryValue, "changed", G_CALLBACK(modify_scenario_setting_default_value_cb), data);
  356. g_object_unref(builder);
  357. }
  358. static void delete_scenario_setting_cb(GtkWidget* /*button*/, CInterfacedScenario::setting_cb_data_t* data)
  359. {
  360. data->scenario->m_Scenario.removeSetting(data->index);
  361. data->scenario->redrawConfigureScenarioSettingsDialog();
  362. }
  363. static void modify_scenario_setting_name_cb(GtkWidget* entry, CInterfacedScenario::setting_cb_data_t* data)
  364. {
  365. data->scenario->m_Scenario.setSettingName(data->index, gtk_entry_get_text(GTK_ENTRY(entry)));
  366. }
  367. static void reset_scenario_setting_identifier_cb(GtkWidget* /*button*/, CInterfacedScenario::setting_cb_data_t* data)
  368. {
  369. const CIdentifier id = data->scenario->m_Scenario.getUnusedSettingIdentifier(CIdentifier::undefined());
  370. if (id != CIdentifier::undefined())
  371. {
  372. data->scenario->m_Scenario.updateInterfacorIdentifier(Kernel::EBoxInterfacorType::Setting, data->index, id);
  373. data->scenario->redrawConfigureScenarioSettingsDialog();
  374. }
  375. }
  376. static void modify_scenario_setting_identifier_cb(GtkWidget* entry, CInterfacedScenario::setting_cb_data_t* data)
  377. {
  378. CIdentifier id;
  379. if (id.fromString(gtk_entry_get_text(GTK_ENTRY(entry))))
  380. {
  381. data->scenario->m_Scenario.updateInterfacorIdentifier(Kernel::EBoxInterfacorType::Setting, data->index, id);
  382. }
  383. }
  384. static void edit_scenario_link_cb(GtkWidget* /*widget*/, CInterfacedScenario::link_cb_data_t* data)
  385. {
  386. if (data->input) { data->scenario->editScenarioInputCB(data->index); }
  387. else { data->scenario->editScenarioOutputCB(data->index); }
  388. data->scenario->redraw();
  389. }
  390. static void modify_scenario_link_move_up_cb(GtkWidget* /*widget*/, CInterfacedScenario::link_cb_data_t* data)
  391. {
  392. if (data->index == 0) { return; }
  393. if (data->input) { data->scenario->swapScenarioInputs(data->index - 1, data->index); }
  394. else { data->scenario->swapScenarioOutputs(data->index - 1, data->index); }
  395. data->scenario->snapshotCB();
  396. }
  397. static void modify_scenario_link_move_down_cb(GtkWidget* /*widget*/, CInterfacedScenario::link_cb_data_t* data)
  398. {
  399. const auto interfacorType = data->input ? Kernel::Input : Kernel::Output;
  400. if (data->scenario->m_Scenario.getInterfacorCount(interfacorType) < 2
  401. || data->index >= data->scenario->m_Scenario.getInterfacorCount(interfacorType) - 1) { return; }
  402. if (data->input) { data->scenario->swapScenarioInputs(data->index, data->index + 1); }
  403. else { data->scenario->swapScenarioOutputs(data->index, data->index + 1); }
  404. data->scenario->snapshotCB();
  405. }
  406. static void delete_scenario_link_cb(GtkButton* /*button*/, CInterfacedScenario::link_cb_data_t* data)
  407. {
  408. if (data->input)
  409. {
  410. data->scenario->m_Scenario.removeScenarioInput(data->index);
  411. data->scenario->redrawScenarioInputSettings();
  412. }
  413. else
  414. {
  415. data->scenario->m_Scenario.removeScenarioOutput(data->index);
  416. data->scenario->redrawScenarioOutputSettings();
  417. }
  418. data->scenario->snapshotCB();
  419. data->scenario->redraw();
  420. }
  421. /*
  422. static void modify_scenario_link_name_cb(GtkWidget* entry, CInterfacedScenario::link_cb_data_t* data)
  423. {
  424. if (data->m_isInput) { data->m_interfacedScenario->m_scenario.setInputName(data->m_uiLinkIdx, gtk_entry_get_text(GTK_ENTRY(entry))); }
  425. else { data->m_interfacedScenario->m_scenario.setOutputName(data->m_uiLinkIdx, gtk_entry_get_text(GTK_ENTRY(entry))); }
  426. }
  427. static void modify_scenario_link_type_cb(GtkWidget* comboBox, CInterfacedScenario::link_cb_data_t* data)
  428. {
  429. const CIdentifier typeID = data->m_interfacedScenario->m_mStreamType[gtk_combo_box_get_active_text(GTK_COMBO_BOX(comboBox))];
  430. if (data->m_isInput) { data->m_interfacedScenario->m_scenario.setInputType(data->m_uiLinkIdx, typeID); }
  431. else { data->m_interfacedScenario->m_scenario.setOutputType(data->m_uiLinkIdx, typeID); }
  432. data->m_interfacedScenario->redraw();
  433. }
  434. //*/
  435. // Redraw Static Helper
  436. static std::array<GdkPoint, 4> get4PointsInterfacorRedraw(const int size, const int shiftX, const int shiftY)
  437. {
  438. std::array<GdkPoint, 4> points;
  439. points[0].x = size >> 1;
  440. points[0].y = size;
  441. points[1].x = 0;
  442. points[1].y = 0;
  443. points[2].x = size - 1;
  444. points[2].y = 0;
  445. for (int j = 0; j < 3; ++j)
  446. {
  447. points[j].x += shiftX;
  448. points[j].y += shiftY;
  449. }
  450. return points;
  451. }
  452. static void drawScenarioTextIOIndex(GtkWidget* widget, GdkGC* gcline, const size_t index, const gint xText, const gint yText,
  453. const gint xL1, const gint yL1, const gint xL2, const gint yL2)
  454. {
  455. PangoContext* pangoCtx = gtk_widget_get_pango_context(widget);
  456. PangoLayout* pangoLayout = pango_layout_new(pangoCtx);
  457. pango_layout_set_alignment(pangoLayout, PANGO_ALIGN_CENTER);
  458. pango_layout_set_markup(pangoLayout, std::to_string(index + 1).c_str(), -1);
  459. gdk_draw_layout(widget->window, widget->style->text_gc[GTK_WIDGET_STATE(widget)], xText, yText, pangoLayout);
  460. g_object_unref(pangoLayout);
  461. gdk_draw_line(widget->window, gcline, xL1, yL1, xL2, yL2);
  462. }
  463. static void drawBorderInterfacor(GtkWidget* widget, GdkGC* gc, GdkColor& color, std::array<GdkPoint, 4> points, const int border, const bool isDeprecated)
  464. {
  465. gdk_gc_set_rgb_fg_color(gc, &color);
  466. gdk_draw_polygon(widget->window, gc, TRUE, points.data(), 3);
  467. if (isDeprecated) { gdk_gc_set_rgb_fg_color(gc, &gColors[Color_LinkInvalid]); }
  468. else { gdk_gc_set_rgb_fg_color(gc, &gColors[border]); }
  469. gdk_draw_polygon(widget->window, gc, FALSE, points.data(), 3);
  470. }
  471. static void drawCircleWithBorder(GtkWidget* widget, GdkGC* gc, GdkColor& bgColor, GdkColor& fgColor, const gint x, const gint y, const gint radius)
  472. {
  473. gdk_gc_set_rgb_fg_color(gc, &bgColor);
  474. gdk_draw_arc(widget->window, gc, TRUE, x, y, radius, radius, 0, 64 * 360);
  475. gdk_gc_set_rgb_fg_color(gc, &fgColor);
  476. gdk_draw_arc(widget->window, gc, FALSE, x, y, radius, radius, 0, 64 * 360);
  477. }
  478. static void linkHandler(Kernel::ILink* link, const int x, const int y, const CIdentifier& attX, const CIdentifier& attY)
  479. {
  480. if (link)
  481. {
  482. TAttributeHandler handler(*link);
  483. if (!handler.hasAttribute(attX)) { handler.addAttribute<int>(attX, x); }
  484. else { handler.setAttributeValue<int>(attX, x); }
  485. if (!handler.hasAttribute(attY)) { handler.addAttribute<int>(attY, y); }
  486. else { handler.setAttributeValue<int>(attY, y); }
  487. }
  488. }
  489. CInterfacedScenario::CInterfacedScenario(const Kernel::IKernelContext& ctx, CApplication& application, Kernel::IScenario& scenario, CIdentifier& scenarioID,
  490. GtkNotebook& notebook, const char* guiFilename, const char* guiSettingsFilename)
  491. : m_PlayerStatus(Kernel::EPlayerStatus::Stop), m_ScenarioID(scenarioID), m_Application(application), m_Scenario(scenario),
  492. m_kernelCtx(ctx), m_notebook(notebook), m_guiFilename(guiFilename), m_guiSettingsFilename(guiSettingsFilename)
  493. {
  494. m_guiBuilder = gtk_builder_new();
  495. gtk_builder_add_from_file(m_guiBuilder, m_guiFilename.c_str(), nullptr);
  496. gtk_builder_connect_signals(m_guiBuilder, nullptr);
  497. std::ifstream settingGUIFilestream;
  498. FS::Files::openIFStream(settingGUIFilestream, m_guiSettingsFilename.c_str());
  499. m_SerializedSettingGUIXML = std::string((std::istreambuf_iterator<char>(settingGUIFilestream)), std::istreambuf_iterator<char>());
  500. m_SettingHelper = new CSettingCollectionHelper(m_kernelCtx, m_guiSettingsFilename.c_str());
  501. // We will need to access setting types by their name later
  502. CIdentifier typeID;
  503. while ((typeID = m_kernelCtx.getTypeManager().getNextTypeIdentifier(typeID)) != CIdentifier::undefined())
  504. {
  505. if (!m_kernelCtx.getTypeManager().isStream(typeID)) { m_SettingTypes[m_kernelCtx.getTypeManager().getTypeName(typeID).toASCIIString()] = typeID; }
  506. else { m_streamTypes[m_kernelCtx.getTypeManager().getTypeName(typeID).toASCIIString()] = typeID; }
  507. }
  508. m_notebookPageTitle = GTK_WIDGET(gtk_builder_get_object(m_guiBuilder, "openvibe_scenario_notebook_title"));
  509. m_notebookPageContent = GTK_WIDGET(gtk_builder_get_object(m_guiBuilder, "openvibe_scenario_notebook_scrolledwindow"));
  510. gtk_notebook_remove_page(GTK_NOTEBOOK(gtk_builder_get_object(m_guiBuilder, "openvibe-scenario_notebook")), 0);
  511. gtk_notebook_remove_page(GTK_NOTEBOOK(gtk_builder_get_object(m_guiBuilder, "openvibe-scenario_notebook")), 0);
  512. gtk_notebook_append_page(&m_notebook, m_notebookPageContent, m_notebookPageTitle);
  513. gtk_notebook_set_tab_reorderable(&m_notebook, m_notebookPageContent, 1);
  514. GtkWidget* closeWidget = GTK_WIDGET(gtk_builder_get_object(m_guiBuilder, "openvibe-scenario_button_close"));
  515. g_signal_connect(G_OBJECT(closeWidget), "clicked", G_CALLBACK(scenario_title_button_close_cb), this);
  516. m_scenarioDrawingArea = GTK_DRAWING_AREA(gtk_builder_get_object(m_guiBuilder, "openvibe-scenario_drawing_area"));
  517. m_scenarioViewport = GTK_VIEWPORT(gtk_builder_get_object(m_guiBuilder, "openvibe-scenario_viewport"));
  518. gtk_drag_dest_set(GTK_WIDGET(m_scenarioDrawingArea), GTK_DEST_DEFAULT_ALL, targets, sizeof(targets) / sizeof(GtkTargetEntry), GDK_ACTION_COPY);
  519. g_signal_connect(G_OBJECT(m_scenarioDrawingArea), "expose_event", G_CALLBACK(scenario_drawing_area_expose_cb), this);
  520. g_signal_connect(G_OBJECT(m_scenarioDrawingArea), "drag_data_received", G_CALLBACK(scenario_drawing_area_drag_data_received_cb), this);
  521. g_signal_connect(G_OBJECT(m_scenarioDrawingArea), "motion_notify_event", G_CALLBACK(scenario_drawing_area_motion_notify_cb), this);
  522. g_signal_connect(G_OBJECT(m_scenarioDrawingArea), "button_press_event", G_CALLBACK(scenario_drawing_area_button_pressed_cb), this);
  523. g_signal_connect(G_OBJECT(m_scenarioDrawingArea), "button_release_event", G_CALLBACK(scenario_drawing_area_button_released_cb), this);
  524. g_signal_connect(G_OBJECT(m_scenarioDrawingArea), "key-press-event", G_CALLBACK(scenario_drawing_area_key_press_event_cb), this);
  525. g_signal_connect(G_OBJECT(m_scenarioDrawingArea), "key-release-event", G_CALLBACK(scenario_drawing_area_key_release_event_cb), this);
  526. g_signal_connect(G_OBJECT(m_notebookPageContent), "scroll-event", G_CALLBACK(scenario_scrolledwindow_scroll_event_cb), this);
  527. m_mensiaLogoPixbuf = gdk_pixbuf_new_from_file(Directories::getDataDir() + "/applications/designer/mensia-decoration.png", nullptr);
  528. #if defined TARGET_OS_Windows
  529. // add drag-n-drop capabilities onto the scenario notebook to open new scenario
  530. gtk_drag_dest_add_uri_targets(GTK_WIDGET(m_scenarioDrawingArea));
  531. #endif
  532. //retrieve visualization tree
  533. m_Application.m_VisualizationMgr->createVisualizationTree(m_TreeID);
  534. m_Tree = &m_Application.m_VisualizationMgr->getVisualizationTree(m_TreeID);
  535. m_Tree->init(&m_Scenario);
  536. //create window manager
  537. m_DesignerVisualization = new CDesignerVisualization(m_kernelCtx, *m_Tree, *this);
  538. m_DesignerVisualization->init(std::string(guiFilename));
  539. m_configureSettingsDialog = GTK_WIDGET(gtk_builder_get_object(m_Application.m_Builder, "dialog_scenario_configuration"));
  540. m_settingsVBox = GTK_WIDGET(gtk_builder_get_object(m_Application.m_Builder, "dialog_scenario_configuration-vbox"));
  541. m_noHelpDialog = GTK_WIDGET(gtk_builder_get_object(m_Application.m_Builder, "dialog_no_help"));
  542. m_errorPendingDeprecatedInterfacorsDialog = GTK_WIDGET(gtk_builder_get_object(m_Application.m_Builder, "dialog_pending_deprecated_interfacors"));
  543. this->redrawScenarioSettings();
  544. this->redrawScenarioInputSettings();
  545. this->redrawScenarioOutputSettings();
  546. m_StateStack.reset(new CScenarioStateStack(ctx, *this, scenario));
  547. CInterfacedScenario::updateScenarioLabel();
  548. // Output a log message if any box of the scenario is in some special state
  549. CIdentifier boxID = CIdentifier::undefined();
  550. bool warningUpdate = false;
  551. bool warningDeprecated = false;
  552. bool warningUnknown = false;
  553. while ((boxID = m_Scenario.getNextBoxIdentifier(boxID)) != CIdentifier::undefined())
  554. {
  555. //const IBox *box = m_scenario.getBoxDetails(l_oBoxID);
  556. //const CBoxProxy proxy(m_kernelCtx, *box);
  557. const CBoxProxy proxy(m_kernelCtx, m_Scenario, boxID);
  558. if (!warningUpdate && !proxy.isUpToDate())
  559. {
  560. m_kernelCtx.getLogManager() << Kernel::LogLevel_Warning <<
  561. "Scenario requires 'update' of some box(es). You need to replace these boxes or the scenario may not work correctly.\n";
  562. warningUpdate = true;
  563. }
  564. if (!warningDeprecated && proxy.isDeprecated())
  565. {
  566. m_kernelCtx.getLogManager() << Kernel::LogLevel_Warning << "Scenario constains deprecated box(es). Please consider using other boxes instead.\n";
  567. warningDeprecated = true;
  568. }
  569. // if (!noteUnstable && proxy.isUnstable())
  570. // {
  571. // m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "Scenario contains unstable box(es).\n";
  572. // noteUnstable = true;
  573. // }
  574. if (!warningUnknown && !proxy.isBoxAlgorithmPluginPresent())
  575. {
  576. m_kernelCtx.getLogManager() << Kernel::LogLevel_Warning << "Scenario contains unknown box algorithm(s).\n";
  577. if (proxy.isMetabox())
  578. {
  579. CString mPath = m_kernelCtx.getConfigurationManager().expand("${Kernel_Metabox}");
  580. m_kernelCtx.getLogManager() << Kernel::LogLevel_Warning << "Some Metaboxes could not be found in [" << mPath << "]\n";
  581. }
  582. warningUnknown = true;
  583. }
  584. }
  585. }
  586. CInterfacedScenario::~CInterfacedScenario()
  587. {
  588. //delete window manager
  589. delete m_DesignerVisualization;
  590. if (m_stencilBuffer != nullptr) { g_object_unref(m_stencilBuffer); }
  591. g_object_unref(m_guiBuilder);
  592. /*
  593. g_object_unref(m_builder);
  594. g_object_unref(m_builder);
  595. */
  596. gtk_notebook_remove_page(&m_notebook, gtk_notebook_page_num(&m_notebook, m_notebookPageContent));
  597. }
  598. void CInterfacedScenario::redraw()
  599. {
  600. if (GDK_IS_WINDOW(GTK_WIDGET(m_scenarioDrawingArea)->window)) { gdk_window_invalidate_rect(GTK_WIDGET(m_scenarioDrawingArea)->window, nullptr, 1); }
  601. }
  602. // This function repaints the dialog which opens when configuring settings
  603. void CInterfacedScenario::redrawConfigureScenarioSettingsDialog()
  604. {
  605. if (m_HasFileName)
  606. {
  607. char filename[1024];
  608. FS::Files::getFilename(m_Filename.c_str(), filename);
  609. const std::string title = std::string("Settings for \"") + filename + "\"";
  610. gtk_window_set_title(GTK_WINDOW(m_configureSettingsDialog), title.c_str());
  611. }
  612. else { gtk_window_set_title(GTK_WINDOW(m_configureSettingsDialog), "Settings for an unnamed scenario"); }
  613. GList* widgets = gtk_container_get_children(GTK_CONTAINER(m_settingsVBox));
  614. for (GList* it = widgets; it != nullptr; it = g_list_next(it)) { gtk_widget_destroy(GTK_WIDGET(it->data)); }
  615. g_list_free(widgets);
  616. m_settingConfigCBDatas.clear();
  617. m_settingConfigCBDatas.resize(m_Scenario.getSettingCount());
  618. if (m_Scenario.getSettingCount() == 0)
  619. {
  620. GtkWidget* widget = gtk_label_new("This scenario has no settings");
  621. gtk_box_pack_start(GTK_BOX(m_settingsVBox), widget, TRUE, TRUE, 5);
  622. }
  623. else
  624. {
  625. for (size_t i = 0; i < m_Scenario.getSettingCount(); ++i)
  626. {
  627. GtkBuilder* builder = gtk_builder_new();
  628. gtk_builder_add_from_string(builder, m_SerializedSettingGUIXML.c_str(), m_SerializedSettingGUIXML.length(), nullptr);
  629. GtkWidget* container = GTK_WIDGET(gtk_builder_get_object(builder, "scenario_configuration_setting-table"));
  630. // this has to be done since the widget is already inside a parent in the gtkbuilder
  631. gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(container)), container);
  632. gtk_box_pack_start(GTK_BOX(m_settingsVBox), container, FALSE, FALSE, 5);
  633. GtkWidget* entryName = GTK_WIDGET(gtk_builder_get_object(builder, "scenario_configuration_setting-entry_name"));
  634. GtkWidget* comboBoxType = GTK_WIDGET(gtk_builder_get_object(builder, "scenario_configuration_setting-combobox_type"));
  635. GtkWidget* buttonUp = GTK_WIDGET(gtk_builder_get_object(builder, "scenario_configuration_setting-button_move_up"));
  636. GtkWidget* buttonDown = GTK_WIDGET(gtk_builder_get_object(builder, "scenario_configuration_setting-button_move_down"));
  637. GtkWidget* buttonDelete = GTK_WIDGET(gtk_builder_get_object(builder, "scenario_configuration_setting-button_delete"));
  638. GtkWidget* entryID = GTK_WIDGET(gtk_builder_get_object(builder, "scenario_configuration_setting-entry_identifier"));
  639. GtkWidget* buttonResetID = GTK_WIDGET(gtk_builder_get_object(builder, "scenario_configuration_setting-button_reset_identifier"));
  640. // fill the type dropdown
  641. CIdentifier typeID = CIdentifier::undefined();
  642. m_Scenario.getSettingType(i, typeID);
  643. CIdentifier id;
  644. CString str;
  645. gint idx = 0;
  646. while ((id = m_kernelCtx.getTypeManager().getNextTypeIdentifier(id)) != CIdentifier::undefined())
  647. {
  648. if (!m_kernelCtx.getTypeManager().isStream(id))
  649. {
  650. gtk_combo_box_append_text(GTK_COMBO_BOX(comboBoxType), m_kernelCtx.getTypeManager().getTypeName(id).toASCIIString());
  651. if (id == typeID) { gtk_combo_box_set_active(GTK_COMBO_BOX(comboBoxType), idx); }
  652. idx++;
  653. }
  654. }
  655. // Set name
  656. m_Scenario.getSettingName(i, str);
  657. gtk_entry_set_text(GTK_ENTRY(entryName), str.toASCIIString());
  658. // Set the identifer
  659. m_Scenario.getInterfacorIdentifier(Kernel::EBoxInterfacorType::Setting, i, id);
  660. gtk_entry_set_text(GTK_ENTRY(entryID), id.str().c_str());
  661. // Add widget for the actual setting
  662. str = m_SettingHelper->getSettingWidgetName(typeID);
  663. GtkWidget* defaultValue = GTK_WIDGET(gtk_builder_get_object(builder, str.toASCIIString()));
  664. gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(defaultValue)), defaultValue);
  665. gtk_table_attach_defaults(GTK_TABLE(container), defaultValue, 1, 5, 1, 2);
  666. // Set the value and connect GUI callbacks (because, yes, setValue connects callbacks like a ninja)
  667. m_Scenario.getSettingDefaultValue(i, str);
  668. m_SettingHelper->setValue(typeID, defaultValue, str);
  669. // add callbacks to disable the Edit menu in openvibe designer, which will in turn enable using stuff like copy-paste inside the widget
  670. str = m_SettingHelper->getSettingEntryWidgetName(typeID);
  671. GtkWidget* entryDefaultValue = GTK_WIDGET(gtk_builder_get_object(builder, str.toASCIIString()));
  672. // Set the callbacks
  673. setting_cb_data_t cbData;
  674. cbData.scenario = this;
  675. cbData.index = i;
  676. cbData.widgetValue = defaultValue;
  677. cbData.widgetEntryValue = entryDefaultValue;
  678. cbData.container = container;
  679. m_settingConfigCBDatas[i] = cbData;
  680. // Connect signals of the container
  681. g_signal_connect(G_OBJECT(comboBoxType), "changed", G_CALLBACK(modify_scenario_setting_type_cb), &m_settingConfigCBDatas[i]);
  682. g_signal_connect(G_OBJECT(buttonDelete), "clicked", G_CALLBACK(delete_scenario_setting_cb), &m_settingConfigCBDatas[i]);
  683. g_signal_connect(G_OBJECT(buttonUp), "clicked", G_CALLBACK(modify_scenario_setting_move_up_cb), &m_settingConfigCBDatas[i]);
  684. g_signal_connect(G_OBJECT(buttonDown), "clicked", G_CALLBACK(modify_scenario_setting_move_down_cb), &m_settingConfigCBDatas[i]);
  685. g_signal_connect(G_OBJECT(entryName), "changed", G_CALLBACK(modify_scenario_setting_name_cb), &m_settingConfigCBDatas[i]);
  686. g_signal_connect(G_OBJECT(entryID), "activate", G_CALLBACK(modify_scenario_setting_identifier_cb), &m_settingConfigCBDatas[i]);
  687. g_signal_connect(G_OBJECT(buttonResetID), "clicked", G_CALLBACK(reset_scenario_setting_identifier_cb), &m_settingConfigCBDatas[i]);
  688. // these callbacks assure that we can use copy/paste and undo within editable fields
  689. // as otherwise the keyboard shortucts are stolen by the designer
  690. g_signal_connect(G_OBJECT(entryName), "focus-in-event", G_CALLBACK(editable_widget_focus_in_cb), &m_Application);
  691. g_signal_connect(G_OBJECT(entryName), "focus-out-event", G_CALLBACK(editable_widget_focus_out_cb), &m_Application);
  692. g_signal_connect(G_OBJECT(entryDefaultValue), "focus-in-event", G_CALLBACK(editable_widget_focus_in_cb), &m_Application);
  693. g_signal_connect(G_OBJECT(entryDefaultValue), "focus-out-event", G_CALLBACK(editable_widget_focus_out_cb), &m_Application);
  694. // add callbacks for setting the settings
  695. g_signal_connect(entryDefaultValue, "changed", G_CALLBACK(modify_scenario_setting_default_value_cb), &m_settingConfigCBDatas[i]);
  696. g_object_unref(builder);
  697. }
  698. }
  699. }
  700. // This function, similar to the previous one, repaints the settings handling sidebar
  701. void CInterfacedScenario::redrawScenarioSettings()
  702. {
  703. GtkWidget* widget = GTK_WIDGET(gtk_builder_get_object(m_Application.m_Builder, "openvibe-scenario_configuration_vbox"));
  704. GList* widgets = gtk_container_get_children(GTK_CONTAINER(widget));
  705. for (GList* settingIterator = widgets; settingIterator != nullptr; settingIterator = g_list_next(settingIterator))
  706. {
  707. gtk_widget_destroy(GTK_WIDGET(settingIterator->data));
  708. }
  709. g_list_free(widgets);
  710. m_settingCBDatas.clear();
  711. m_settingCBDatas.resize(m_Scenario.getSettingCount());
  712. if (m_Scenario.getSettingCount() == 0)
  713. {
  714. GtkWidget* label = gtk_label_new("This scenario has no settings");
  715. gtk_box_pack_start(GTK_BOX(widget), label, TRUE, TRUE, 5);
  716. }
  717. else
  718. {
  719. for (size_t i = 0; i < m_Scenario.getSettingCount(); ++i)
  720. {
  721. GtkBuilder* builder = gtk_builder_new();
  722. gtk_builder_add_from_string(builder, m_SerializedSettingGUIXML.c_str(), m_SerializedSettingGUIXML.length(), nullptr);
  723. GtkWidget* container = GTK_WIDGET(gtk_builder_get_object(builder, "scenario_setting-table"));
  724. // this has to be done since the widget is already inside a parent in the gtkbuilder
  725. gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(container)), container);
  726. gtk_box_pack_start(GTK_BOX(widget), container, FALSE, FALSE, 5);
  727. GtkWidget* labelName = GTK_WIDGET(gtk_builder_get_object(builder, "scenario_setting-label"));
  728. GtkWidget* buttonDefault = GTK_WIDGET(gtk_builder_get_object(builder, "scenario_setting-button_default"));
  729. GtkWidget* buttonCopy = GTK_WIDGET(gtk_builder_get_object(builder, "scenario_setting-button_copy"));
  730. // Set name
  731. CString str;
  732. m_Scenario.getSettingName(i, str);
  733. gtk_label_set_text(GTK_LABEL(labelName), str.toASCIIString());
  734. gtk_misc_set_alignment(GTK_MISC(labelName), 0.0, 0.5);
  735. // Add widget for the actual setting
  736. CIdentifier typeID = CIdentifier::undefined();
  737. m_Scenario.getSettingType(i, typeID);
  738. str = m_SettingHelper->getSettingWidgetName(typeID);
  739. GtkWidget* value = GTK_WIDGET(gtk_builder_get_object(builder, str.toASCIIString()));
  740. gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(value)), value);
  741. gtk_table_attach_defaults(GTK_TABLE(container), value, 0, 1, 1, 2);
  742. // Set the value and connect GUI callbacks (because, yes, setValue connects callbacks like a ninja)
  743. m_Scenario.getSettingValue(i, str);
  744. m_SettingHelper->setValue(typeID, value, str);
  745. // add callbacks to disable the Edit menu in openvibe designer, which will in turn enable using stuff like copy-paste inside the widget
  746. str = m_SettingHelper->getSettingEntryWidgetName(typeID);
  747. GtkWidget* entryValue = GTK_WIDGET(gtk_builder_get_object(builder, str.toASCIIString()));
  748. // Set the callbacks
  749. setting_cb_data_t cbData;
  750. cbData.scenario = this;
  751. cbData.index = i;
  752. cbData.widgetValue = value;
  753. cbData.widgetEntryValue = entryValue;
  754. cbData.container = container;
  755. m_settingCBDatas[i] = cbData;
  756. // these callbacks assure that we can use copy/paste and undo within editable fields
  757. // as otherwise the keyboard shortucts are stolen by the designer
  758. g_signal_connect(G_OBJECT(entryValue), "focus-in-event", G_CALLBACK(editable_widget_focus_in_cb), &m_Application);
  759. g_signal_connect(G_OBJECT(entryValue), "focus-out-event", G_CALLBACK(editable_widget_focus_out_cb), &m_Application);
  760. // add callbacks for setting the settings
  761. g_signal_connect(entryValue, "changed", G_CALLBACK(modify_scenario_setting_value_cb), &m_settingCBDatas[i]);
  762. g_signal_connect(buttonDefault, "clicked", G_CALLBACK(modify_scenario_setting_revert_to_default_cb), &m_settingCBDatas[i]);
  763. g_signal_connect(buttonCopy, "clicked", G_CALLBACK(copy_scenario_setting_token_cb), &m_settingCBDatas[i]);
  764. g_object_unref(builder);
  765. }
  766. }
  767. gtk_widget_show_all(widget);
  768. }
  769. void CInterfacedScenario::redrawScenarioInputSettings()
  770. {
  771. size_t (Kernel::IScenario::* getNLink)() const = &Kernel::IScenario::getInputCount;
  772. bool (Kernel::IScenario::* getLinkName)(size_t, CString&) const = &Kernel::IScenario::getInputName;
  773. bool (Kernel::IScenario::* getLinkType)(size_t, CIdentifier&) const = &Kernel::IScenario::getInputType;
  774. this->redrawScenarioLinkSettings(m_Application.m_Inputs, true, m_scenarioInputCBDatas, getNLink, getLinkName, getLinkType);
  775. }
  776. void CInterfacedScenario::redrawScenarioOutputSettings()
  777. {
  778. size_t (Kernel::IScenario::* getNLink)() const = &Kernel::IScenario::getOutputCount;
  779. bool (Kernel::IScenario::* getLinkName)(size_t, CString&) const = &Kernel::IScenario::getOutputName;
  780. bool (Kernel::IScenario::* getLinkType)(size_t, CIdentifier&) const = &Kernel::IScenario::getOutputType;
  781. this->redrawScenarioLinkSettings(m_Application.m_Outputs, false, m_scenarioOutputCBDatas, getNLink, getLinkName, getLinkType);
  782. }
  783. // Redraws the tab containing inputs or outputs of the scenario
  784. // This method receives pointers to methods that manipulate either intpus or outputs so it can be generic
  785. void CInterfacedScenario::redrawScenarioLinkSettings(GtkWidget* links, const bool isInput, std::vector<link_cb_data_t>& linkCBDatas,
  786. size_t (Kernel::IScenario::* getNLink)() const,
  787. bool (Kernel::IScenario::* getLinkName)(size_t, CString&) const,
  788. bool (Kernel::IScenario::* getLinkType)(size_t, CIdentifier&) const)
  789. {
  790. GList* widgets = gtk_container_get_children(GTK_CONTAINER(links));
  791. for (GList* it = widgets; it != nullptr; it = g_list_next(it)) { gtk_widget_destroy(GTK_WIDGET(it->data)); }
  792. g_list_free(widgets);
  793. const size_t nLink = (m_Scenario.*getNLink)();
  794. linkCBDatas.clear();
  795. linkCBDatas.resize(nLink);
  796. gtk_table_resize(GTK_TABLE(links), nLink == 0 ? 1 : nLink, 7);
  797. if (nLink == 0)
  798. {
  799. GtkWidget* settingPlaceholderLabel = gtk_label_new("This scenario has none");
  800. gtk_table_attach_defaults(GTK_TABLE(links), settingPlaceholderLabel, 0, 1, 0, 1);
  801. }
  802. else
  803. {
  804. for (size_t i = 0; i < nLink; ++i)
  805. {
  806. GtkBuilder* builder = gtk_builder_new();
  807. gtk_builder_add_from_string(builder, m_SerializedSettingGUIXML.c_str(), m_SerializedSettingGUIXML.length(), nullptr);
  808. GtkWidget* container = GTK_WIDGET(gtk_builder_get_object(builder, "scenario_io_setting-table"));
  809. // this has to be done since the widget is already inside a parent in the gtkbuilder
  810. gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(container)), container);
  811. GtkWidget* entryLinkName = GTK_WIDGET(gtk_builder_get_object(builder, "scenario_io_setting-label"));
  812. gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(entryLinkName)), entryLinkName);
  813. GtkWidget* comboBoxType = GTK_WIDGET(gtk_builder_get_object(builder, "scenario_io_setting-combobox_type"));
  814. gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(comboBoxType)), comboBoxType);
  815. // fill the type dropdown
  816. CIdentifier typeID = CIdentifier::undefined();
  817. (m_Scenario.*getLinkType)(i, typeID);
  818. CIdentifier id;
  819. gint idx = 0;
  820. while ((id = m_kernelCtx.getTypeManager().getNextTypeIdentifier(id)) != CIdentifier::undefined())
  821. {
  822. if (m_kernelCtx.getTypeManager().isStream(id))
  823. {
  824. gtk_combo_box_append_text(GTK_COMBO_BOX(comboBoxType), m_kernelCtx.getTypeManager().getTypeName(id).toASCIIString());
  825. if (id == typeID) { gtk_combo_box_set_active(GTK_COMBO_BOX(comboBoxType), idx); }
  826. idx++;
  827. }
  828. }
  829. gtk_combo_box_set_button_sensitivity(GTK_COMBO_BOX(comboBoxType), GTK_SENSITIVITY_OFF);
  830. GtkWidget* buttonUp = GTK_WIDGET(gtk_builder_get_object(builder, "scenario_io_setting-button_move_up"));
  831. gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(buttonUp)), buttonUp);
  832. GtkWidget* buttonDown = GTK_WIDGET(gtk_builder_get_object(builder, "scenario_io_setting-button_move_down"));
  833. gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(buttonDown)), buttonDown);
  834. GtkWidget* buttonEdit = GTK_WIDGET(gtk_builder_get_object(builder, "scenario_io_setting-button_edit"));
  835. gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(buttonEdit)), buttonEdit);
  836. GtkWidget* buttonDelete = GTK_WIDGET(gtk_builder_get_object(builder, "scenario_io_setting-button_delete"));
  837. gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(buttonDelete)), buttonDelete);
  838. // Set name
  839. CString str;
  840. (m_Scenario.*getLinkName)(i, str);
  841. gtk_label_set_text(GTK_LABEL(entryLinkName), str.toASCIIString());
  842. gtk_misc_set_alignment(GTK_MISC(entryLinkName), 0.0, 0.5);
  843. gtk_widget_set_sensitive(GTK_WIDGET(entryLinkName), GTK_SENSITIVITY_OFF);
  844. gtk_table_attach(GTK_TABLE(links), entryLinkName, 0, 1, i, i + 1, GtkAttachOptions(GTK_EXPAND | GTK_FILL), GTK_SHRINK, 4, 4);
  845. gtk_table_attach(GTK_TABLE(links), comboBoxType, 1, 2, i, i + 1, GTK_SHRINK, GTK_SHRINK, 4, 4);
  846. gtk_table_attach(GTK_TABLE(links), buttonUp, 3, 4, i, i + 1, GTK_SHRINK, GTK_SHRINK, 4, 4);
  847. gtk_table_attach(GTK_TABLE(links), buttonDown, 4, 5, i, i + 1, GTK_SHRINK, GTK_SHRINK, 4, 4);
  848. gtk_table_attach(GTK_TABLE(links), buttonEdit, 5, 6, i, i + 1, GTK_SHRINK, GTK_SHRINK, 4, 4);
  849. gtk_table_attach(GTK_TABLE(links), buttonDelete, 6, 7, i, i + 1, GTK_SHRINK, GTK_SHRINK, 4, 4);
  850. // Set the callbacks
  851. link_cb_data_t cbData;
  852. cbData.scenario = this;
  853. cbData.index = i;
  854. cbData.input = isInput;
  855. linkCBDatas[i] = cbData;
  856. g_signal_connect(G_OBJECT(buttonDelete), "clicked", G_CALLBACK(delete_scenario_link_cb), &linkCBDatas[i]);
  857. g_signal_connect(G_OBJECT(buttonEdit), "clicked", G_CALLBACK(edit_scenario_link_cb), &linkCBDatas[i]);
  858. g_signal_connect(G_OBJECT(buttonUp), "clicked", G_CALLBACK(modify_scenario_link_move_up_cb), &linkCBDatas[i]);
  859. g_signal_connect(G_OBJECT(buttonDown), "clicked", G_CALLBACK(modify_scenario_link_move_down_cb), &linkCBDatas[i]);
  860. g_object_unref(builder);
  861. }
  862. }
  863. gtk_widget_show_all(links);
  864. }
  865. void CInterfacedScenario::updateScenarioLabel()
  866. {
  867. GtkLabel* gtkLabel = GTK_LABEL(gtk_builder_get_object(m_guiBuilder, "openvibe-scenario_label"));
  868. std::string label;
  869. std::string filename = m_Filename;
  870. std::string labelUntrimmed = "unsaved document";
  871. std::string::size_type pos;
  872. while ((pos = filename.find('\\')) != std::string::npos) { filename[pos] = '/'; }
  873. label += m_HasBeenModified ? "*" : "";
  874. label += " ";
  875. // trimming file name if the number of character is above ${Designer_ScenarioFileNameTrimmingLimit}
  876. // trim only unselected scenarios
  877. if (m_HasFileName)
  878. {
  879. labelUntrimmed = filename;
  880. filename = filename.substr(filename.rfind('/') + 1);
  881. size_t trimLimit = size_t(m_kernelCtx.getConfigurationManager().expandAsUInteger("${Designer_ScenarioFileNameTrimmingLimit}", 25));
  882. if (trimLimit > 3) { trimLimit -= 3; } // limit should include the '...'
  883. // default = we trim everything but the current scenario filename
  884. // if {we are stacking horizontally the scenarios, we trim also } current filename to avoid losing too much of the edition panel.
  885. if (filename.size() > trimLimit)
  886. {
  887. if (m_Application.getCurrentInterfacedScenario() == this
  888. && m_kernelCtx.getConfigurationManager().expandAsBoolean("${Designer_ScenarioTabsVerticalStack}", false))
  889. {
  890. filename = "..." + filename.substr(filename.size() - trimLimit, trimLimit);
  891. }
  892. if (m_Application.getCurrentInterfacedScenario() != this)
  893. {
  894. filename = filename.substr(0, trimLimit);
  895. filename += "...";
  896. }
  897. }
  898. label += filename;
  899. }
  900. else { label += "(untitled)"; }
  901. label += " ";
  902. label += m_HasBeenModified ? "*" : "";
  903. gtk_label_set_text(gtkLabel, label.c_str());
  904. label = labelUntrimmed;
  905. pos = 0;
  906. while ((pos = label.find('&', pos)) != std::string::npos)
  907. {
  908. label.replace(pos, 1, "&amp;");
  909. pos += 5;
  910. }
  911. gtk_widget_set_tooltip_markup(GTK_WIDGET(gtkLabel), ("<i>" + label + (m_HasBeenModified ? " - unsaved" : "") + "</i>").c_str());
  912. }
  913. #define UPDATE_STENCIL_IDX(id,stencilgc) { (id)++; ::GdkColor sc={0, guint16(((id)&0xff0000)>>8), guint16((id)&0xff00), guint16(((id)&0xff)<<8) }; gdk_gc_set_rgb_fg_color(stencilgc, &sc); }
  914. void CInterfacedScenario::redraw(Kernel::IBox& box)
  915. {
  916. GtkWidget* widget = GTK_WIDGET(m_scenarioDrawingArea);
  917. GdkGC* stencilGC = gdk_gc_new(GDK_DRAWABLE(m_stencilBuffer));
  918. GdkGC* drawGC = gdk_gc_new(widget->window);
  919. const int marginX = int(round(5 * m_currentScale));
  920. const int marginY = int(round(5 * m_currentScale));
  921. const int circleSize = int(round(11 * m_currentScale));
  922. const int circleSpace = int(round(4 * m_currentScale));
  923. //CBoxProxy proxy(m_kernelCtx, box);
  924. CBoxProxy proxy(m_kernelCtx, m_Scenario, box.getIdentifier());
  925. if (box.getAlgorithmClassIdentifier() == OVP_ClassId_BoxAlgorithm_Metabox)
  926. {
  927. CIdentifier metaboxId;
  928. metaboxId.fromString(box.getAttributeValue(OVP_AttributeId_Metabox_ID));
  929. proxy.setBoxAlgorithmDescriptorOverride(
  930. static_cast<const Plugins::IBoxAlgorithmDesc*>(m_kernelCtx.getMetaboxManager().getMetaboxObjectDesc(metaboxId)));
  931. }
  932. int sizeX = int(round(proxy.getWidth(GTK_WIDGET(m_scenarioDrawingArea)) * m_currentScale) + marginX * 2);
  933. int sizeY = int(round(proxy.getHeight(GTK_WIDGET(m_scenarioDrawingArea)) * m_currentScale) + marginY * 2);
  934. int startX = int(round(proxy.getXCenter() * m_currentScale + m_viewOffsetX - (sizeX >> 1)));
  935. int startY = int(round(proxy.getYCenter() * m_currentScale + m_viewOffsetY - (sizeY >> 1)));
  936. UPDATE_STENCIL_IDX(m_interfacedObjectId, stencilGC);
  937. gdk_draw_rounded_rectangle(GDK_DRAWABLE(m_stencilBuffer), stencilGC, TRUE, startX, startY, sizeX, sizeY, gint(round(8.0 * m_currentScale)));
  938. m_interfacedObjects[m_interfacedObjectId] = CInterfacedObject(box.getIdentifier());
  939. bool canCreate = proxy.isBoxAlgorithmPluginPresent();
  940. bool upToDate = canCreate ? proxy.isUpToDate() : true;
  941. bool pendingDeprecatedInterfacors = proxy.hasPendingDeprecatedInterfacors();
  942. bool deprecated = canCreate && proxy.isDeprecated();
  943. bool metabox = canCreate && proxy.isMetabox();
  944. bool disabled = proxy.isDisabled();
  945. // Add a thick dashed border around selected boxes
  946. if (m_SelectedObjects.count(box.getIdentifier()))
  947. {
  948. int offsetTL = 2; // Offset Top Left
  949. #if defined TARGET_OS_Windows
  950. int offsetBR = 4; // Offset Bottom Right
  951. #elif defined TARGET_OS_Linux || defined TARGET_OS_MacOS
  952. int offsetBR = 5; // Offset Bottom Right
  953. #else
  954. int offsetBR = 4; // Offset Bottom Right
  955. #endif
  956. if (metabox)
  957. {
  958. offsetTL = 3;
  959. offsetBR = 6;
  960. }
  961. gdk_gc_set_rgb_fg_color(drawGC, &gColors[Color_BoxBorderSelected]);
  962. gdk_gc_set_line_attributes(drawGC, 1, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_ROUND);
  963. gdk_draw_rounded_rectangle(widget->window, drawGC, TRUE, startX - offsetTL, startY - offsetTL, sizeX + offsetBR, sizeY + offsetBR);
  964. }
  965. if (!this->isLocked() || !m_DebugCPUUsage)
  966. {
  967. //if(m_currentObject[box.getIdentifier()]) { gdk_gc_set_rgb_fg_color(drawGC, &gColors[Color_BoxBackgroundSelected]); }
  968. //else
  969. if (!canCreate) { gdk_gc_set_rgb_fg_color(drawGC, &gColors[Color_BoxBackgroundMissing]); }
  970. else if (disabled) { gdk_gc_set_rgb_fg_color(drawGC, &gColors[Color_BoxBackgroundDisabled]); }
  971. else if (deprecated) { gdk_gc_set_rgb_fg_color(drawGC, &gColors[Color_BoxBackgroundDeprecated]); }
  972. else if (!upToDate || pendingDeprecatedInterfacors) { gdk_gc_set_rgb_fg_color(drawGC, &gColors[Color_BoxBackgroundOutdated]); }
  973. //else if(metabox) { gdk_gc_set_rgb_fg_color(drawGC, &gColors[Color_BoxBackgroundMetabox]); }
  974. else { gdk_gc_set_rgb_fg_color(drawGC, &gColors[Color_BoxBackground]); }
  975. }
  976. else
  977. {
  978. CIdentifier timeID;
  979. timeID.fromString(box.getAttributeValue(OV_AttributeId_Box_ComputationTimeLastSecond));
  980. uint64_t time = (timeID == CIdentifier::undefined() ? 0 : timeID.id());
  981. uint64_t reference = (1LL << 32) / (m_nBox == 0 ? 1 : m_nBox);
  982. GdkColor color;
  983. if (time < reference)
  984. {
  985. color.pixel = 0;
  986. color.red = guint16((time << 16) / reference);
  987. color.green = 32768;
  988. color.blue = 0;
  989. }
  990. else
  991. {
  992. if (time < reference * 4)
  993. {
  994. color.pixel = 0;
  995. color.red = 65535;
  996. color.green = guint16(32768 - ((time << 15) / (reference * 4)));
  997. color.blue = 0;
  998. }
  999. else
  1000. {
  1001. color.pixel = 0;
  1002. color.red = 65535;
  1003. color.green = 0;
  1004. color.blue = 0;
  1005. }
  1006. }
  1007. gdk_gc_set_rgb_fg_color(drawGC, &color);
  1008. }
  1009. gdk_draw_rounded_rectangle(widget->window, drawGC, TRUE, startX, startY, sizeX, sizeY, gint(round(8.0 * m_currentScale)));
  1010. int borderColor = Color_BoxBorder;
  1011. gdk_gc_set_rgb_fg_color(drawGC, &gColors[borderColor]);
  1012. gdk_gc_set_line_attributes(drawGC, 1, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_ROUND);
  1013. gdk_draw_rounded_rectangle(widget->window, drawGC, FALSE, startX, startY, sizeX, sizeY, gint(round(8.0 * m_currentScale)));
  1014. if (metabox)
  1015. {
  1016. gdk_gc_set_rgb_fg_color(drawGC, &gColors[borderColor]);
  1017. gdk_gc_set_line_attributes(drawGC, 1, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_ROUND);
  1018. gdk_draw_rounded_rectangle(widget->window, drawGC, FALSE, startX - 3, startY - 3, sizeX + 6, sizeY + 6, gint(round(8.0 * m_currentScale)));
  1019. }
  1020. TAttributeHandler handler(box);
  1021. int offset = sizeX / 2 - int(box.getInputCount()) * (circleSpace + circleSize) / 2 + circleSize / 4;
  1022. for (size_t i = 0; i < box.getInterfacorCountIncludingDeprecated(Kernel::Input); ++i)
  1023. {
  1024. CIdentifier id;
  1025. bool isDeprecated;
  1026. box.getInputType(i, id);
  1027. box.getInterfacorDeprecatedStatus(Kernel::Input, i, isDeprecated);
  1028. GdkColor color = colorFromIdentifier(id, isDeprecated);
  1029. const auto points = get4PointsInterfacorRedraw(circleSize, startX + int(i) * (circleSpace + circleSize) + offset, startY - (circleSize >> 1));
  1030. UPDATE_STENCIL_IDX(m_interfacedObjectId, stencilGC);
  1031. gdk_draw_polygon(GDK_DRAWABLE(m_stencilBuffer), stencilGC, TRUE, points.data(), 3);
  1032. m_interfacedObjects[m_interfacedObjectId] = CInterfacedObject(box.getIdentifier(), Box_Input, i);
  1033. drawBorderInterfacor(widget, drawGC, color, points, Color_BoxInputBorder, isDeprecated);
  1034. int x = startX + int(i) * (circleSpace + circleSize) + (circleSize >> 1) - m_viewOffsetX + offset;
  1035. int y = startY - (circleSize >> 1) - m_viewOffsetY;
  1036. id = m_Scenario.getNextLinkIdentifierToBoxInput(CIdentifier::undefined(), box.getIdentifier(), i);
  1037. while (id != CIdentifier::undefined())
  1038. {
  1039. Kernel::ILink* link = m_Scenario.getLinkDetails(id);
  1040. linkHandler(link, x, y, OV_AttributeId_Link_XDst, OV_AttributeId_Link_YDst);
  1041. id = m_Scenario.getNextLinkIdentifierToBoxInput(id, box.getIdentifier(), i);
  1042. }
  1043. // Display a circle above inputs that are linked to the box inputs
  1044. for (size_t j = 0; j < m_Scenario.getInputCount(); j++)
  1045. {
  1046. size_t boxInputIdx;
  1047. m_Scenario.getScenarioInputLink(j, id, boxInputIdx);
  1048. if (id == box.getIdentifier() && boxInputIdx == i)
  1049. {
  1050. // Since the circle representing the input is quite large, we are going to offset each other one
  1051. int offsetDisc = int(i % 2) * circleSize * 2;
  1052. const int left = startX + int(i) * (circleSpace + circleSize) + offset - int(circleSize * 0.5);
  1053. const int top = startY - (circleSize >> 1) - circleSize * 3 - offsetDisc;
  1054. this->m_Scenario.getInputType(j, id);
  1055. color = colorFromIdentifier(id, false);
  1056. UPDATE_STENCIL_IDX(m_interfacedObjectId, stencilGC);
  1057. gdk_draw_arc(GDK_DRAWABLE(m_stencilBuffer), stencilGC, TRUE, left, top, circleSize * 2, circleSize * 2, 0, 64 * 360);
  1058. m_interfacedObjects[m_interfacedObjectId] = CInterfacedObject(box.getIdentifier(), Box_ScenarioInput, i);
  1059. drawCircleWithBorder(widget, drawGC, color, gColors[Color_BoxInputBorder], left, top, circleSize * 2);
  1060. // Draw the text indicating the scenario input index
  1061. drawScenarioTextIOIndex(widget, drawGC, j, left + marginX, top + marginY,
  1062. startX + int(i) * (circleSpace + circleSize) + offset + (circleSize >> 1), top + circleSize * 2,
  1063. startX + int(i) * (circleSpace + circleSize) + offset + (circleSize >> 1), startY - (circleSize >> 1));
  1064. }
  1065. }
  1066. }
  1067. gdk_gc_set_line_attributes(drawGC, 1, GDK_LINE_SOLID, GDK_CAP_BUTT, GDK_JOIN_ROUND);
  1068. offset = sizeX / 2 - int(box.getOutputCount()) * (circleSpace + circleSize) / 2 + circleSize / 4;
  1069. for (size_t i = 0; i < box.getInterfacorCountIncludingDeprecated(Kernel::Output); ++i)
  1070. {
  1071. CIdentifier id;
  1072. bool isDeprecated;
  1073. box.getOutputType(i, id);
  1074. box.getInterfacorDeprecatedStatus(Kernel::Output, i, isDeprecated);
  1075. GdkColor color = colorFromIdentifier(id, isDeprecated);
  1076. const auto points = get4PointsInterfacorRedraw(circleSize, startX + int(i) * (circleSpace + circleSize) + offset, startY - (circleSize >> 1) + sizeY);
  1077. UPDATE_STENCIL_IDX(m_interfacedObjectId, stencilGC);
  1078. gdk_draw_polygon(GDK_DRAWABLE(m_stencilBuffer), stencilGC, TRUE, points.data(), 3);
  1079. m_interfacedObjects[m_interfacedObjectId] = CInterfacedObject(box.getIdentifier(), Box_Output, i);
  1080. drawBorderInterfacor(widget, drawGC, color, points, Color_BoxOutputBorder, isDeprecated);
  1081. int x = startX + int(i) * (circleSpace + circleSize) + (circleSize >> 1) - m_viewOffsetX + offset;
  1082. int y = startY + sizeY + (circleSize >> 1) + 1 - m_viewOffsetY;
  1083. id = m_Scenario.getNextLinkIdentifierFromBoxOutput(CIdentifier::undefined(), box.getIdentifier(), i);
  1084. while (id != CIdentifier::undefined())
  1085. {
  1086. Kernel::ILink* link = m_Scenario.getLinkDetails(id);
  1087. if (link)
  1088. {
  1089. TAttributeHandler attHandler(*link);
  1090. linkHandler(link, x, y, OV_AttributeId_Link_XSrc, OV_AttributeId_Link_YSrc);
  1091. }
  1092. id = m_Scenario.getNextLinkIdentifierFromBoxOutput(id, box.getIdentifier(), i);
  1093. }
  1094. // Display a circle below outputs that are linked to the box outputs
  1095. for (size_t j = 0; j < m_Scenario.getOutputCount(); j++)
  1096. {
  1097. size_t boxOutputIdx;
  1098. m_Scenario.getScenarioOutputLink(j, id, boxOutputIdx);
  1099. if (id == box.getIdentifier() && boxOutputIdx == i)
  1100. {
  1101. // Since the circle representing the Output is quite large, we are going to offset each other one
  1102. int offsetDisc = (int(i) % 2) * circleSize * 2;
  1103. const int left = startX + int(i) * (circleSpace + circleSize) + offset - int(circleSize * 0.5);
  1104. const int top = startY - (circleSize >> 1) + sizeY + offsetDisc + circleSize * 2;
  1105. this->m_Scenario.getOutputType(j, id);
  1106. color = colorFromIdentifier(id);
  1107. UPDATE_STENCIL_IDX(m_interfacedObjectId, stencilGC);
  1108. gdk_draw_arc(GDK_DRAWABLE(m_stencilBuffer), stencilGC, TRUE, left, top, circleSize * 2, circleSize * 2, 0, 64 * 360);
  1109. m_interfacedObjects[m_interfacedObjectId] = CInterfacedObject(box.getIdentifier(), Box_ScenarioOutput, i);
  1110. drawCircleWithBorder(widget, drawGC, color, gColors[Color_BoxOutputBorder], left, top, circleSize * 2);
  1111. // Draw the text indicating the scenario output index
  1112. // This is somewhat the bottom of the triangle indicating a box output
  1113. drawScenarioTextIOIndex(widget, drawGC, j, left + marginX, top + marginY,
  1114. startX + int(i) * (circleSpace + circleSize) + offset + (circleSize >> 1), top,
  1115. startX + int(i) * (circleSpace + circleSize) + offset + (circleSize >> 1), startY + (circleSize >> 2) + sizeY + 2);
  1116. }
  1117. }
  1118. }
  1119. // Draw labels
  1120. PangoContext* ctx = gtk_widget_get_pango_context(widget);
  1121. PangoLayout* layout = pango_layout_new(ctx);
  1122. // Draw box label
  1123. PangoRectangle labelRect;
  1124. pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
  1125. pango_layout_set_markup(layout, proxy.getLabel(), -1);
  1126. pango_layout_get_pixel_extents(layout, nullptr, &labelRect);
  1127. gdk_draw_layout(widget->window, widget->style->text_gc[GTK_WIDGET_STATE(widget)], startX + marginX, startY + marginY, layout);
  1128. // Draw box status label
  1129. PangoRectangle statusRect;
  1130. pango_layout_set_markup(layout, proxy.getStatusLabel(), -1);
  1131. pango_layout_get_pixel_extents(layout, nullptr, &statusRect);
  1132. int shiftX = (std::max(labelRect.width, statusRect.width) - std::min(labelRect.width, statusRect.width)) / 2;
  1133. UPDATE_STENCIL_IDX(m_interfacedObjectId, stencilGC);
  1134. gdk_draw_rectangle(GDK_DRAWABLE(m_stencilBuffer), stencilGC, TRUE, startX + shiftX + marginX, startY + labelRect.height + marginY, statusRect.width,
  1135. statusRect.height);
  1136. m_interfacedObjects[m_interfacedObjectId] = CInterfacedObject(box.getIdentifier(), Box_Update, 0);
  1137. gdk_draw_layout(widget->window, widget->style->text_gc[GTK_WIDGET_STATE(widget)], startX + shiftX + marginX, startY + labelRect.height + marginY, layout);
  1138. g_object_unref(layout);
  1139. g_object_unref(drawGC);
  1140. g_object_unref(stencilGC);
  1141. }
  1142. void CInterfacedScenario::redraw(Kernel::IComment& comment)
  1143. {
  1144. GtkWidget* widget = GTK_WIDGET(m_scenarioDrawingArea);
  1145. GdkGC* stencilGC = gdk_gc_new(GDK_DRAWABLE(m_stencilBuffer));
  1146. GdkGC* drawGC = gdk_gc_new(widget->window);
  1147. // size_t i;
  1148. const int marginX = static_cast<const int>(round(16 * m_currentScale));
  1149. const int marginY = static_cast<const int>(round(16 * m_currentScale));
  1150. const CCommentProxy proxy(m_kernelCtx, comment);
  1151. const int sizeX = proxy.getWidth(GTK_WIDGET(m_scenarioDrawingArea)) + marginX * 2;
  1152. const int sizeY = proxy.getHeight(GTK_WIDGET(m_scenarioDrawingArea)) + marginY * 2;
  1153. const int startX = int(round(proxy.getXCenter() * m_currentScale + m_viewOffsetX - (sizeX >> 1)));
  1154. const int startY = int(round(proxy.getYCenter() * m_currentScale + m_viewOffsetY - (sizeY >> 1)));
  1155. UPDATE_STENCIL_IDX(m_interfacedObjectId, stencilGC);
  1156. gdk_draw_rounded_rectangle(GDK_DRAWABLE(m_stencilBuffer), stencilGC, TRUE, startX, startY, sizeX, sizeY, gint(round(16.0 * m_currentScale)));
  1157. m_interfacedObjects[m_interfacedObjectId] = CInterfacedObject(comment.getIdentifier());
  1158. gdk_gc_set_rgb_fg_color(drawGC, &gColors[m_SelectedObjects.count(comment.getIdentifier()) ? Color_CommentBackgroundSelected : Color_CommentBackground]);
  1159. gdk_draw_rounded_rectangle(widget->window, drawGC, TRUE, startX, startY, sizeX, sizeY, gint(round(16.0 * m_currentScale)));
  1160. gdk_gc_set_rgb_fg_color(drawGC, &gColors[m_SelectedObjects.count(comment.getIdentifier()) ? Color_CommentBorderSelected : Color_CommentBorder]);
  1161. gdk_draw_rounded_rectangle(widget->window, drawGC, FALSE, startX, startY, sizeX, sizeY, gint(round(16.0 * m_currentScale)));
  1162. PangoContext* ctx = gtk_widget_get_pango_context(widget);
  1163. PangoLayout* layout = pango_layout_new(ctx);
  1164. pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER);
  1165. if (pango_parse_markup(comment.getText().toASCIIString(), -1, 0, nullptr, nullptr, nullptr, nullptr))
  1166. {
  1167. pango_layout_set_markup(layout, comment.getText().toASCIIString(), -1);
  1168. }
  1169. else { pango_layout_set_text(layout, comment.getText().toASCIIString(), -1); }
  1170. gdk_draw_layout(widget->window, widget->style->text_gc[GTK_WIDGET_STATE(widget)], startX + marginX, startY + marginY, layout);
  1171. g_object_unref(layout);
  1172. g_object_unref(drawGC);
  1173. g_object_unref(stencilGC);
  1174. }
  1175. void CInterfacedScenario::redraw(Kernel::ILink& link)
  1176. {
  1177. GtkWidget* widget = GTK_WIDGET(m_scenarioDrawingArea);
  1178. GdkGC* stencilGC = gdk_gc_new(GDK_DRAWABLE(m_stencilBuffer));
  1179. GdkGC* drawGC = gdk_gc_new(widget->window);
  1180. const CLinkProxy proxy(link);
  1181. CIdentifier srcOutputTypeID;
  1182. CIdentifier dstInputTypeID;
  1183. m_Scenario.getBoxDetails(link.getSourceBoxIdentifier())->getOutputType(link.getSourceBoxOutputIndex(), srcOutputTypeID);
  1184. m_Scenario.getBoxDetails(link.getTargetBoxIdentifier())->getInputType(link.getTargetBoxInputIndex(), dstInputTypeID);
  1185. if (link.hasAttribute(OV_AttributeId_Link_Invalid)) { gdk_gc_set_rgb_fg_color(drawGC, &gColors[Color_LinkInvalid]); }
  1186. else if (m_SelectedObjects.count(link.getIdentifier())) { gdk_gc_set_rgb_fg_color(drawGC, &gColors[Color_LinkSelected]); }
  1187. else if (dstInputTypeID == srcOutputTypeID) { gdk_gc_set_rgb_fg_color(drawGC, &gColors[Color_Link]); }
  1188. else
  1189. {
  1190. if (m_kernelCtx.getTypeManager().isDerivedFromStream(srcOutputTypeID, dstInputTypeID))
  1191. {
  1192. gdk_gc_set_rgb_fg_color(drawGC, &gColors[Color_LinkDownCast]);
  1193. }
  1194. else if (m_kernelCtx.getTypeManager().isDerivedFromStream(dstInputTypeID, srcOutputTypeID))
  1195. {
  1196. gdk_gc_set_rgb_fg_color(drawGC, &gColors[Color_LinkUpCast]);
  1197. }
  1198. else { gdk_gc_set_rgb_fg_color(drawGC, &gColors[Color_LinkInvalid]); }
  1199. }
  1200. UPDATE_STENCIL_IDX(m_interfacedObjectId, stencilGC);
  1201. gdk_draw_line(GDK_DRAWABLE(m_stencilBuffer), stencilGC, proxy.getXSource() + m_viewOffsetX, proxy.getYSource() + m_viewOffsetY,
  1202. proxy.getXTarget() + m_viewOffsetX, proxy.getYTarget() + m_viewOffsetY);
  1203. gdk_draw_line(widget->window, drawGC, proxy.getXSource() + m_viewOffsetX, proxy.getYSource() + m_viewOffsetY, proxy.getXTarget() + m_viewOffsetX,
  1204. proxy.getYTarget() + m_viewOffsetY);
  1205. m_interfacedObjects[m_interfacedObjectId] = CInterfacedObject(link.getIdentifier(), Box_Link, 0);
  1206. g_object_unref(drawGC);
  1207. g_object_unref(stencilGC);
  1208. }
  1209. #undef UPDATE_STENCIL_IDX
  1210. size_t CInterfacedScenario::pickInterfacedObject(const int x, const int y) const
  1211. {
  1212. if (!GDK_DRAWABLE(m_stencilBuffer)) { return size_t(0xffffffff); }
  1213. int maxX;
  1214. int maxY;
  1215. uint32_t res = 0xffffffff;
  1216. gdk_drawable_get_size(GDK_DRAWABLE(m_stencilBuffer), &maxX, &maxY);
  1217. if (x >= 0 && y >= 0 && x < maxX && y < maxY)
  1218. {
  1219. GdkPixbuf* pixbuf = gdk_pixbuf_get_from_drawable(nullptr, GDK_DRAWABLE(m_stencilBuffer), nullptr, x, y, 0, 0, 1, 1);
  1220. if (!pixbuf)
  1221. {
  1222. m_kernelCtx.getLogManager() << Kernel::LogLevel_ImportantWarning <<
  1223. "Could not get pixbuf from stencil buffer - couldn't pick object... this should never happen !\n";
  1224. return size_t(0xffffffff);
  1225. }
  1226. guchar* pixels = gdk_pixbuf_get_pixels(pixbuf);
  1227. if (!pixels)
  1228. {
  1229. m_kernelCtx.getLogManager() << Kernel::LogLevel_ImportantWarning <<
  1230. "Could not get pixels from pixbuf - couldn't pick object... this should never happen !\n";
  1231. return 0xffffffff;
  1232. }
  1233. res = 0;
  1234. res += (pixels[0] << 16);
  1235. res += (pixels[1] << 8);
  1236. res += (pixels[2]);
  1237. g_object_unref(pixbuf);
  1238. }
  1239. return size_t(res);
  1240. }
  1241. bool CInterfacedScenario::pickInterfacedObject(const int x, const int y, int sizeX, int sizeY)
  1242. {
  1243. if (!GDK_DRAWABLE(m_stencilBuffer))
  1244. {
  1245. // m_kernelCtx.getLogManager() << Kernel::LogLevel_ImportantWarning << "No stencil buffer defined - couldn't pick object... this should never happen !\n";
  1246. return false;
  1247. }
  1248. int maxX;
  1249. int maxY;
  1250. gdk_drawable_get_size(GDK_DRAWABLE(m_stencilBuffer), &maxX, &maxY);
  1251. int startX = x;
  1252. int startY = y;
  1253. int endX = x + sizeX;
  1254. int endY = y + sizeY;
  1255. // crops according to drawing area boundings
  1256. if (startX < 0) { startX = 0; }
  1257. if (startY < 0) { startY = 0; }
  1258. if (endX < 0) { endX = 0; }
  1259. if (endY < 0) { endY = 0; }
  1260. if (startX >= maxX - 1) { startX = maxX - 1; }
  1261. if (startY >= maxY - 1) { startY = maxY - 1; }
  1262. if (endX >= maxX - 1) { endX = maxX - 1; }
  1263. if (endY >= maxY - 1) { endY = maxY - 1; }
  1264. // recompute new size
  1265. sizeX = endX - startX + 1;
  1266. sizeY = endY - startY + 1;
  1267. GdkPixbuf* pixbuf = gdk_pixbuf_get_from_drawable(nullptr, GDK_DRAWABLE(m_stencilBuffer), nullptr, startX, startY, 0, 0, sizeX, sizeY);
  1268. if (!pixbuf)
  1269. {
  1270. m_kernelCtx.getLogManager() << Kernel::LogLevel_ImportantWarning
  1271. << "Could not get pixbuf from stencil buffer - couldn't pick object... this should never happen !\n";
  1272. return false;
  1273. }
  1274. guchar* pixels = gdk_pixbuf_get_pixels(pixbuf);
  1275. if (!pixels)
  1276. {
  1277. m_kernelCtx.getLogManager() << Kernel::LogLevel_ImportantWarning
  1278. << "Could not get pixels from pixbuf - couldn't pick object... this should never happen !\n";
  1279. return false;
  1280. }
  1281. const int nRowBytes = gdk_pixbuf_get_rowstride(pixbuf);
  1282. const int nChannel = gdk_pixbuf_get_n_channels(pixbuf);
  1283. for (int j = 0; j < sizeY; ++j)
  1284. {
  1285. for (int i = 0; i < sizeX; ++i)
  1286. {
  1287. size_t idx = 0;
  1288. idx += (pixels[j * nRowBytes + i * nChannel + 0] << 16);
  1289. idx += (pixels[j * nRowBytes + i * nChannel + 1] << 8);
  1290. idx += (pixels[j * nRowBytes + i * nChannel + 2]);
  1291. if (m_interfacedObjects[idx].m_ID != CIdentifier::undefined()) { m_SelectedObjects.insert(m_interfacedObjects[idx].m_ID); }
  1292. }
  1293. }
  1294. g_object_unref(pixbuf);
  1295. return true;
  1296. }
  1297. #define OV_ClassId_Selected CIdentifier(0xC67A01DC, 0x28CE06C1)
  1298. void CInterfacedScenario::undoCB(const bool manageModifiedStatusFlag)
  1299. {
  1300. // When a box gets updated we generate a snapshot beforehand to enable undo in all cases
  1301. // This will result in two indentical undo states, in order to avoid weird Redo, we drop the
  1302. // reduntant state at this moment
  1303. bool shouldDropLastState = false;
  1304. if (m_Scenario.containsBoxWithDeprecatedInterfacors()) { shouldDropLastState = true; }
  1305. if (m_StateStack->undo())
  1306. {
  1307. CIdentifier id;
  1308. m_SelectedObjects.clear();
  1309. while ((id = m_Scenario.getNextBoxIdentifier(id)) != CIdentifier::undefined())
  1310. {
  1311. if (m_Scenario.getBoxDetails(id)->hasAttribute(OV_ClassId_Selected)) { m_SelectedObjects.insert(id); }
  1312. }
  1313. while ((id = m_Scenario.getNextLinkIdentifier(id)) != CIdentifier::undefined())
  1314. {
  1315. if (m_Scenario.getLinkDetails(id)->hasAttribute(OV_ClassId_Selected)) { m_SelectedObjects.insert(id); }
  1316. }
  1317. if (m_DesignerVisualization) { m_DesignerVisualization->load(); }
  1318. if (manageModifiedStatusFlag) { m_HasBeenModified = true; }
  1319. this->redrawScenarioSettings();
  1320. this->redrawScenarioInputSettings();
  1321. this->redrawScenarioOutputSettings();
  1322. this->redraw();
  1323. if (shouldDropLastState) { m_StateStack->dropLastState(); }
  1324. gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object(this->m_Application.m_Builder, "openvibe-button_redo")), m_StateStack->isRedoPossible());
  1325. gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object(this->m_Application.m_Builder, "openvibe-button_undo")), m_StateStack->isUndoPossible());
  1326. }
  1327. else
  1328. {
  1329. m_kernelCtx.getLogManager() << Kernel::LogLevel_Trace << "Can not undo\n";
  1330. GtkWidget* undoButton = GTK_WIDGET(gtk_builder_get_object(this->m_Application.m_Builder, "openvibe-button_undo"));
  1331. gtk_widget_set_sensitive(undoButton, false);
  1332. }
  1333. }
  1334. void CInterfacedScenario::redoCB(const bool manageModifiedStatusFlag)
  1335. {
  1336. if (m_StateStack->redo())
  1337. {
  1338. CIdentifier id;
  1339. m_SelectedObjects.clear();
  1340. while ((id = m_Scenario.getNextBoxIdentifier(id)) != CIdentifier::undefined())
  1341. {
  1342. if (m_Scenario.getBoxDetails(id)->hasAttribute(OV_ClassId_Selected)) { m_SelectedObjects.insert(id); }
  1343. }
  1344. while ((id = m_Scenario.getNextLinkIdentifier(id)) != CIdentifier::undefined())
  1345. {
  1346. if (m_Scenario.getLinkDetails(id)->hasAttribute(OV_ClassId_Selected)) { m_SelectedObjects.insert(id); }
  1347. }
  1348. if (m_DesignerVisualization) { m_DesignerVisualization->load(); }
  1349. if (manageModifiedStatusFlag) { m_HasBeenModified = true; }
  1350. this->redrawScenarioSettings();
  1351. this->redrawScenarioInputSettings();
  1352. this->redrawScenarioOutputSettings();
  1353. this->redraw();
  1354. gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object(this->m_Application.m_Builder, "openvibe-button_redo")), m_StateStack->isRedoPossible());
  1355. gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object(this->m_Application.m_Builder, "openvibe-button_undo")), m_StateStack->isUndoPossible());
  1356. }
  1357. else
  1358. {
  1359. gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object(this->m_Application.m_Builder, "openvibe-button_redo")), false);
  1360. m_kernelCtx.getLogManager() << Kernel::LogLevel_Trace << "Can not redo\n";
  1361. }
  1362. }
  1363. void CInterfacedScenario::snapshotCB(const bool manageModifiedStatusFlag)
  1364. {
  1365. if (m_Scenario.containsBoxWithDeprecatedInterfacors())
  1366. {
  1367. OV_WARNING("Scenario containing boxes with deprecated I/O or Settings does not support undo", m_kernelCtx.getLogManager());
  1368. }
  1369. else
  1370. {
  1371. CIdentifier id;
  1372. while ((id = m_Scenario.getNextBoxIdentifier(id)) != CIdentifier::undefined())
  1373. {
  1374. if (m_SelectedObjects.count(id)) { m_Scenario.getBoxDetails(id)->addAttribute(OV_ClassId_Selected, ""); }
  1375. else { m_Scenario.getBoxDetails(id)->removeAttribute(OV_ClassId_Selected); }
  1376. }
  1377. while ((id = m_Scenario.getNextLinkIdentifier(id)) != CIdentifier::undefined())
  1378. {
  1379. if (m_SelectedObjects.count(id)) { m_Scenario.getLinkDetails(id)->addAttribute(OV_ClassId_Selected, ""); }
  1380. else { m_Scenario.getLinkDetails(id)->removeAttribute(OV_ClassId_Selected); }
  1381. }
  1382. if (manageModifiedStatusFlag) { m_HasBeenModified = true; }
  1383. this->updateScenarioLabel();
  1384. m_StateStack->snapshot();
  1385. }
  1386. gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object(this->m_Application.m_Builder, "openvibe-button_redo")), m_StateStack->isRedoPossible());
  1387. gtk_widget_set_sensitive(GTK_WIDGET(gtk_builder_get_object(this->m_Application.m_Builder, "openvibe-button_undo")), m_StateStack->isUndoPossible());
  1388. }
  1389. void CInterfacedScenario::addCommentCB(int x, int y)
  1390. {
  1391. CIdentifier id;
  1392. m_Scenario.addComment(id, CIdentifier::undefined());
  1393. if (x == -1 || y == -1)
  1394. {
  1395. GtkWidget* scrolledWindow = gtk_widget_get_parent(gtk_widget_get_parent(GTK_WIDGET(m_scenarioDrawingArea)));
  1396. GtkAdjustment* adjustmentH = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(scrolledWindow));
  1397. GtkAdjustment* adjustmentV = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(scrolledWindow));
  1398. #if defined TARGET_OS_Linux && !defined TARGET_OS_MacOS
  1399. x = int(gtk_adjustment_get_value(adjustmentH) + gtk_adjustment_get_page_size(adjustmentH) / 2);
  1400. y = int(gtk_adjustment_get_value(adjustmentV) + gtk_adjustment_get_page_size(adjustmentV) / 2);
  1401. #elif defined TARGET_OS_Windows
  1402. gint wx, wy;
  1403. ::gdk_window_get_size(gtk_widget_get_parent(GTK_WIDGET(m_scenarioDrawingArea))->window, &wx, &wy);
  1404. x = int(gtk_adjustment_get_value(adjustmentH) + int(wx / 2));
  1405. y = int(gtk_adjustment_get_value(adjustmentV) + int(wy / 2));
  1406. #else
  1407. x = int(gtk_adjustment_get_value(adjustmentH) + 32);
  1408. y = int(gtk_adjustment_get_value(adjustmentV) + 32);
  1409. #endif
  1410. }
  1411. CCommentProxy proxy(m_kernelCtx, m_Scenario, id);
  1412. proxy.setCenter(x - m_viewOffsetX, y - m_viewOffsetY);
  1413. // Aligns comemnts on grid
  1414. proxy.setCenter(int((proxy.getXCenter() + 8) & 0xfffffff0L), int((proxy.getYCenter() + 8) & 0xfffffff0L));
  1415. // Applies modifications before snapshot
  1416. proxy.apply();
  1417. CCommentEditorDialog dialog(m_kernelCtx, *m_Scenario.getCommentDetails(id), m_guiFilename.c_str());
  1418. if (!dialog.run()) { m_Scenario.removeComment(id); }
  1419. else
  1420. {
  1421. m_SelectedObjects.clear();
  1422. m_SelectedObjects.insert(id);
  1423. this->snapshotCB();
  1424. }
  1425. this->redraw();
  1426. }
  1427. void CInterfacedScenario::configureScenarioSettingsCB()
  1428. {
  1429. this->snapshotCB();
  1430. // construct the dialog
  1431. this->redrawConfigureScenarioSettingsDialog();
  1432. gtk_widget_show_all(m_settingsVBox);
  1433. const gint response = gtk_dialog_run(GTK_DIALOG(m_configureSettingsDialog));
  1434. if (response == GTK_RESPONSE_CANCEL) { this->undoCB(false); }
  1435. else { this->snapshotCB(); }
  1436. gtk_widget_hide(m_configureSettingsDialog);
  1437. this->redrawScenarioSettings();
  1438. }
  1439. void CInterfacedScenario::addScenarioSettingCB()
  1440. {
  1441. const std::string name = "Setting " + std::to_string(m_Scenario.getSettingCount() + 1);
  1442. m_Scenario.addSetting(name.c_str(), OVTK_TypeId_Integer, "0", size_t(-1), false, m_Scenario.getUnusedSettingIdentifier(CIdentifier::undefined()));
  1443. this->redrawConfigureScenarioSettingsDialog();
  1444. }
  1445. void CInterfacedScenario::addScenarioInputCB()
  1446. {
  1447. const std::string name = "Input " + std::to_string(m_Scenario.getInputCount() + 1);
  1448. // scenario I/O are identified by name/type combination value, at worst uniq in the scope of the inputs of the box.
  1449. m_Scenario.addInput(name.c_str(), OVTK_TypeId_StreamedMatrix, m_Scenario.getUnusedInputIdentifier(CIdentifier::undefined()));
  1450. CConnectorEditor editor(m_kernelCtx, m_Scenario, Box_Input, m_Scenario.getInputCount() - 1, "Add Input", m_guiFilename.c_str());
  1451. if (editor.run()) { this->snapshotCB(); }
  1452. else { m_Scenario.removeInput(m_Scenario.getInputCount() - 1); }
  1453. this->redrawScenarioInputSettings();
  1454. }
  1455. void CInterfacedScenario::editScenarioInputCB(const size_t index)
  1456. {
  1457. CConnectorEditor editor(m_kernelCtx, m_Scenario, Box_Input, index, "Edit Input", m_guiFilename.c_str());
  1458. if (editor.run()) { this->snapshotCB(); }
  1459. this->redrawScenarioInputSettings();
  1460. }
  1461. void CInterfacedScenario::addScenarioOutputCB()
  1462. {
  1463. const std::string name = "Output " + std::to_string(m_Scenario.getOutputCount() + 1);
  1464. // scenario I/O are identified by name/type combination value, at worst uniq in the scope of the outputs of the box.
  1465. m_Scenario.addOutput(name.c_str(), OVTK_TypeId_StreamedMatrix, m_Scenario.getUnusedOutputIdentifier(CIdentifier::undefined()));
  1466. CConnectorEditor editor(m_kernelCtx, m_Scenario, Box_Output, m_Scenario.getOutputCount() - 1, "Add Output", m_guiFilename.c_str());
  1467. if (editor.run()) { this->snapshotCB(); }
  1468. else { m_Scenario.removeOutput(m_Scenario.getOutputCount() - 1); }
  1469. this->redrawScenarioOutputSettings();
  1470. }
  1471. void CInterfacedScenario::editScenarioOutputCB(const size_t index)
  1472. {
  1473. CConnectorEditor editor(m_kernelCtx, m_Scenario, Box_Output, index, "Edit Output", m_guiFilename.c_str());
  1474. if (editor.run()) { this->snapshotCB(); }
  1475. this->redrawScenarioOutputSettings();
  1476. }
  1477. void CInterfacedScenario::swapScenarioSettings(const size_t indexA, const size_t indexB)
  1478. {
  1479. m_Scenario.swapSettings(indexA, indexB);
  1480. this->redrawConfigureScenarioSettingsDialog();
  1481. }
  1482. void CInterfacedScenario::swapScenarioInputs(const size_t indexA, const size_t indexB)
  1483. {
  1484. CIdentifier idA, idB;
  1485. size_t idxA, idxB;
  1486. m_Scenario.getScenarioInputLink(indexA, idA, idxA);
  1487. m_Scenario.getScenarioInputLink(indexB, idB, idxB);
  1488. m_Scenario.swapInputs(indexA, indexB);
  1489. m_Scenario.setScenarioInputLink(indexB, idA, idxA);
  1490. m_Scenario.setScenarioInputLink(indexA, idB, idxB);
  1491. this->redrawScenarioInputSettings();
  1492. this->redraw();
  1493. }
  1494. void CInterfacedScenario::swapScenarioOutputs(const size_t indexA, const size_t indexB)
  1495. {
  1496. CIdentifier idA, idB;
  1497. size_t idxA, idxB;
  1498. m_Scenario.getScenarioOutputLink(indexA, idA, idxA);
  1499. m_Scenario.getScenarioOutputLink(indexB, idB, idxB);
  1500. m_Scenario.swapOutputs(indexA, indexB);
  1501. m_Scenario.setScenarioOutputLink(indexB, idA, idxA);
  1502. m_Scenario.setScenarioOutputLink(indexA, idB, idxB);
  1503. this->redrawScenarioOutputSettings();
  1504. this->redraw();
  1505. }
  1506. void CInterfacedScenario::scenarioDrawingAreaExposeCB(GdkEventExpose* /*event*/)
  1507. {
  1508. if (m_currentMode == Mode_None)
  1509. {
  1510. gint viewportX = -1, viewportY = -1;
  1511. gint minX = 0x7fff, maxX = -0x7fff;
  1512. gint minY = 0x7fff, maxY = -0x7fff;
  1513. const gint marginX = gint(round(32.0 * m_currentScale));
  1514. const gint marginY = gint(round(32.0 * m_currentScale));
  1515. CIdentifier id;
  1516. while ((id = m_Scenario.getNextBoxIdentifier(id)) != CIdentifier::undefined())
  1517. {
  1518. //CBoxProxy proxy(m_kernelCtx, *m_scenario.getBoxDetails(l_oBoxID));
  1519. CBoxProxy proxy(m_kernelCtx, m_Scenario, id);
  1520. minX = std::min(minX, gint((proxy.getXCenter() - 1.0 * proxy.getWidth(GTK_WIDGET(m_scenarioDrawingArea)) / 2) * m_currentScale));
  1521. maxX = std::max(maxX, gint((proxy.getXCenter() + 1.0 * proxy.getWidth(GTK_WIDGET(m_scenarioDrawingArea)) / 2) * m_currentScale));
  1522. minY = std::min(minY, gint((proxy.getYCenter() - 1.0 * proxy.getHeight(GTK_WIDGET(m_scenarioDrawingArea)) / 2) * m_currentScale));
  1523. maxY = std::max(maxY, gint((proxy.getYCenter() + 1.0 * proxy.getHeight(GTK_WIDGET(m_scenarioDrawingArea)) / 2) * m_currentScale));
  1524. }
  1525. while ((id = m_Scenario.getNextCommentIdentifier(id)) != CIdentifier::undefined())
  1526. {
  1527. CCommentProxy proxy(m_kernelCtx, *m_Scenario.getCommentDetails(id));
  1528. minX = std::min(minX, gint((proxy.getXCenter() - 1.0 * proxy.getWidth(GTK_WIDGET(m_scenarioDrawingArea)) / 2) * m_currentScale));
  1529. maxX = std::max(maxX, gint((proxy.getXCenter() + 1.0 * proxy.getWidth(GTK_WIDGET(m_scenarioDrawingArea)) / 2) * m_currentScale));
  1530. minY = std::min(minY, gint((proxy.getYCenter() - 1.0 * proxy.getHeight(GTK_WIDGET(m_scenarioDrawingArea)) / 2) * m_currentScale));
  1531. maxY = std::max(maxY, gint((proxy.getYCenter() + 1.0 * proxy.getHeight(GTK_WIDGET(m_scenarioDrawingArea)) / 2) * m_currentScale));
  1532. }
  1533. const gint newSizeX = maxX - minX;
  1534. const gint newSizeY = maxY - minY;
  1535. gint oldSizeX = -1;
  1536. gint oldSizeY = -1;
  1537. gdk_window_get_size(GTK_WIDGET(m_scenarioViewport)->window, &viewportX, &viewportY);
  1538. gtk_widget_get_size_request(GTK_WIDGET(m_scenarioDrawingArea), &oldSizeX, &oldSizeY);
  1539. if (newSizeX >= 0 && newSizeY >= 0)
  1540. {
  1541. if (oldSizeX != newSizeX + 2 * marginX || oldSizeY != newSizeY + 2 * marginY)
  1542. {
  1543. gtk_widget_set_size_request(GTK_WIDGET(m_scenarioDrawingArea), newSizeX + 2 * marginX, newSizeY + 2 * marginY);
  1544. }
  1545. m_viewOffsetX = std::min(m_viewOffsetX, -maxX - marginX + std::max(viewportX, newSizeX + 2 * marginX));
  1546. m_viewOffsetX = std::max(m_viewOffsetX, -minX + marginX);
  1547. m_viewOffsetY = std::min(m_viewOffsetY, -maxY - marginY + std::max(viewportY, newSizeY + 2 * marginY));
  1548. m_viewOffsetY = std::max(m_viewOffsetY, -minY + marginY);
  1549. }
  1550. }
  1551. gint x, y;
  1552. gdk_window_get_size(GTK_WIDGET(m_scenarioDrawingArea)->window, &x, &y);
  1553. if (m_stencilBuffer) { g_object_unref(m_stencilBuffer); }
  1554. m_stencilBuffer = gdk_pixmap_new(GTK_WIDGET(m_scenarioDrawingArea)->window, x, y, -1);
  1555. GdkGC* stencilGC = gdk_gc_new(m_stencilBuffer);
  1556. GdkColor color = { 0, 0, 0, 0 };
  1557. gdk_gc_set_rgb_fg_color(stencilGC, &color);
  1558. gdk_draw_rectangle(GDK_DRAWABLE(m_stencilBuffer), stencilGC, TRUE, 0, 0, x, y);
  1559. g_object_unref(stencilGC);
  1560. if (this->isLocked())
  1561. {
  1562. color.pixel = 0;
  1563. color.red = 0x0f00;
  1564. color.green = 0x0f00;
  1565. color.blue = 0x0f00;
  1566. GdkGC* drawGC = gdk_gc_new(GTK_WIDGET(m_scenarioDrawingArea)->window);
  1567. gdk_gc_set_rgb_fg_color(drawGC, &color);
  1568. gdk_gc_set_function(drawGC, GDK_XOR);
  1569. gdk_draw_rectangle(GTK_WIDGET(m_scenarioDrawingArea)->window, drawGC, TRUE, 0, 0, x, y);
  1570. g_object_unref(drawGC);
  1571. }
  1572. // TODO: optimize this as this will be called endlessly
  1573. /*
  1574. else if (false) //m_scenario.containsBoxWithDeprecatedInterfacors()
  1575. {
  1576. color.pixel = 0;
  1577. color.red = 0xffff;
  1578. color.green = 0xefff;
  1579. color.blue = 0xefff;
  1580. GdkGC* drawGC = gdk_gc_new(GTK_WIDGET(m_scenarioDrawingArea)->window);
  1581. gdk_gc_set_rgb_fg_color(drawGC, &color);
  1582. gdk_gc_set_function(drawGC, GDK_AND);
  1583. gdk_draw_rectangle(GTK_WIDGET(m_pScenarioDrawingArea)->window, drawGC, TRUE, 0, 0, x, y);
  1584. g_object_unref(l_pDrawGC);
  1585. }
  1586. */
  1587. m_interfacedObjectId = 0;
  1588. m_interfacedObjects.clear();
  1589. size_t count = 0;
  1590. CIdentifier id;
  1591. while ((id = m_Scenario.getNextCommentIdentifier(id)) != CIdentifier::undefined())
  1592. {
  1593. redraw(*m_Scenario.getCommentDetails(id));
  1594. count++;
  1595. }
  1596. m_nComment = count;
  1597. count = 0;
  1598. while ((id = m_Scenario.getNextBoxIdentifier(id)) != CIdentifier::undefined())
  1599. {
  1600. redraw(*m_Scenario.getBoxDetails(id));
  1601. count++;
  1602. }
  1603. m_nBox = count;
  1604. count = 0;
  1605. while ((id = m_Scenario.getNextLinkIdentifier(id)) != CIdentifier::undefined())
  1606. {
  1607. redraw(*m_Scenario.getLinkDetails(id));
  1608. count++;
  1609. }
  1610. m_nLink = count;
  1611. if (m_currentMode == Mode_Selection || m_currentMode == Mode_SelectionAdd)
  1612. {
  1613. const int startX = int(std::min(m_pressMouseX, m_currentMouseX));
  1614. const int startY = int(std::min(m_pressMouseY, m_currentMouseY));
  1615. const int sizeX = int(std::max(m_pressMouseX - m_currentMouseX, m_currentMouseX - m_pressMouseX));
  1616. const int sizeY = int(std::max(m_pressMouseY - m_currentMouseY, m_currentMouseY - m_pressMouseY));
  1617. GtkWidget* widget = GTK_WIDGET(m_scenarioDrawingArea);
  1618. GdkGC* drawGC = gdk_gc_new(widget->window);
  1619. gdk_gc_set_function(drawGC, GDK_OR);
  1620. gdk_gc_set_rgb_fg_color(drawGC, &gColors[Color_SelectionArea]);
  1621. gdk_draw_rectangle(widget->window, drawGC, TRUE, startX, startY, sizeX, sizeY);
  1622. gdk_gc_set_function(drawGC, GDK_COPY);
  1623. gdk_gc_set_rgb_fg_color(drawGC, &gColors[Color_SelectionAreaBorder]);
  1624. gdk_draw_rectangle(widget->window, drawGC, FALSE, startX, startY, sizeX, sizeY);
  1625. g_object_unref(drawGC);
  1626. }
  1627. if (m_currentMode == Mode_Connect)
  1628. {
  1629. GtkWidget* widget = GTK_WIDGET(m_scenarioDrawingArea);
  1630. GdkGC* drawGC = gdk_gc_new(widget->window);
  1631. gdk_gc_set_rgb_fg_color(drawGC, &gColors[Color_Link]);
  1632. gdk_draw_line(widget->window, drawGC, int(m_pressMouseX), int(m_pressMouseY), int(m_currentMouseX), int(m_currentMouseY));
  1633. g_object_unref(drawGC);
  1634. }
  1635. }
  1636. // This method inserts a box into the scenario upon receiving data
  1637. void CInterfacedScenario::scenarioDrawingAreaDragDataReceivedCB(GdkDragContext* dc, const gint x, const gint y, GtkSelectionData* selectionData,
  1638. guint /*info*/, guint /*t*/)
  1639. {
  1640. if (this->isLocked()) { return; }
  1641. // two cases: dragged from inside the program = a box ...
  1642. if (dc->protocol == GDK_DRAG_PROTO_LOCAL || dc->protocol == GDK_DRAG_PROTO_XDND)
  1643. {
  1644. CIdentifier boxID;
  1645. CIdentifier boxAlgorithmClassID;
  1646. // The drag data only contains one string, for a normal box this string is its algorithmClassIdentifier
  1647. // However since all metaboxes have the same identifier, we have added the 'identifier' of a metabox after this string
  1648. // The identifier itself is the name of the scenario which created the metabox
  1649. std::string str(reinterpret_cast<const char*>(gtk_selection_data_get_text(selectionData)));
  1650. // check that there is an identifier inside the string, its form is (0xXXXXXXXX, 0xXXXXXXXX)
  1651. if (str.find(')') != std::string::npos) { boxAlgorithmClassID.fromString(str.substr(0, str.find(')')).c_str()); }
  1652. Kernel::IBox* box = nullptr;
  1653. const Plugins::IPluginObjectDesc* pod = nullptr;
  1654. if (boxAlgorithmClassID == CIdentifier::undefined())
  1655. {
  1656. m_currentMouseX = x;
  1657. m_currentMouseY = y;
  1658. return;
  1659. }
  1660. if (boxAlgorithmClassID == OVP_ClassId_BoxAlgorithm_Metabox)
  1661. {
  1662. // extract the name of the metabox from the drag data string
  1663. CIdentifier id;
  1664. id.fromString(CString(str.substr(str.find(')') + 1).c_str()));
  1665. //m_kernelCtx.getLogManager() << Kernel::LogLevel_Info << "This is a metabox with ID " << metaboxID << "\n";
  1666. pod = m_kernelCtx.getMetaboxManager().getMetaboxObjectDesc(id);
  1667. // insert a box into the scenario, initialize it from the proxy-descriptor from the metabox loader
  1668. m_Scenario.addBox(boxID, *static_cast<const Plugins::IBoxAlgorithmDesc*>(pod), CIdentifier::undefined());
  1669. box = m_Scenario.getBoxDetails(boxID);
  1670. box->addAttribute(OVP_AttributeId_Metabox_ID, id.toString());
  1671. }
  1672. else
  1673. {
  1674. m_Scenario.addBox(boxID, boxAlgorithmClassID, CIdentifier::undefined());
  1675. box = m_Scenario.getBoxDetails(boxID);
  1676. const CIdentifier id = box->getAlgorithmClassIdentifier();
  1677. pod = m_kernelCtx.getPluginManager().getPluginObjectDescCreating(id);
  1678. }
  1679. m_SelectedObjects.clear();
  1680. m_SelectedObjects.insert(boxID);
  1681. // If a visualization box was dropped, add it in window manager
  1682. if (pod && pod->hasFunctionality(Plugins::EPluginFunctionality::Visualization))
  1683. {
  1684. // Let window manager know about new box
  1685. if (m_DesignerVisualization) { m_DesignerVisualization->onVisualizationBoxAdded(box); }
  1686. }
  1687. CBoxProxy proxy(m_kernelCtx, m_Scenario, boxID);
  1688. proxy.setCenter(x - m_viewOffsetX, y - m_viewOffsetY);
  1689. // Aligns boxes on grid
  1690. proxy.setCenter(int((proxy.getXCenter() + 8) & 0xfffffff0L), int((proxy.getYCenter() + 8) & 0xfffffff0L));
  1691. // Applies modifications before snapshot
  1692. proxy.apply();
  1693. this->snapshotCB();
  1694. m_currentMouseX = x;
  1695. m_currentMouseY = y;
  1696. }
  1697. // ... or dragged from outside the application = a file
  1698. // ONLY AVAILABLE ON WINDOWS (known d'n'd protocol)
  1699. #if defined TARGET_OS_Windows
  1700. if (dc->protocol == GDK_DRAG_PROTO_WIN32_DROPFILES)
  1701. {
  1702. // we get the content of the buffer: the list of files URI:
  1703. // file:///path/to/file.ext\r\n
  1704. // file:///path/to/file.ext\r\n
  1705. // ...
  1706. const std::string draggedFilesPath(reinterpret_cast<const char*>(gtk_selection_data_get_data(selectionData)));
  1707. std::stringstream ss(draggedFilesPath);
  1708. std::string line;
  1709. std::vector<std::string> filesToOpen;
  1710. while (std::getline(ss, line))
  1711. {
  1712. // the path starts with file:/// and ends with \r\n once parsed line after line, a \r remains on Windows
  1713. line = line.substr(8, line.length() - 9);
  1714. // uri to path (to remove %xx escape characters):
  1715. line = g_uri_unescape_string(line.c_str(), nullptr);
  1716. filesToOpen.push_back(line);
  1717. }
  1718. for (auto& file : filesToOpen) { m_Application.openScenario(file.c_str()); }
  1719. }
  1720. #endif
  1721. }
  1722. void CInterfacedScenario::scenarioDrawingAreaMotionNotifyCB(GtkWidget* /*widget*/, GdkEventMotion* event)
  1723. {
  1724. // m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "scenarioDrawingAreaMotionNotifyCB\n";
  1725. if (this->isLocked()) { return; }
  1726. GtkWidget* tooltip = GTK_WIDGET(gtk_builder_get_object(m_guiBuilder, "tooltip"));
  1727. gtk_widget_set_name(tooltip, "gtk-tooltips");
  1728. const size_t objIdx = pickInterfacedObject(int(event->x), int(event->y));
  1729. CInterfacedObject& obj = m_interfacedObjects[objIdx];
  1730. if (obj.m_ID != CIdentifier::undefined() && obj.m_ConnectorType != Box_Link && obj.m_ConnectorType != Box_None)
  1731. {
  1732. Kernel::IBox* boxDetails = m_Scenario.getBoxDetails(obj.m_ID);
  1733. if (boxDetails)
  1734. {
  1735. CString name;
  1736. CString type;
  1737. if (obj.m_ConnectorType == Box_Input)
  1738. {
  1739. CIdentifier typeID;
  1740. boxDetails->getInputName(obj.m_ConnectorIdx, name);
  1741. boxDetails->getInputType(obj.m_ConnectorIdx, typeID);
  1742. type = m_kernelCtx.getTypeManager().getTypeName(typeID);
  1743. type = CString("[") + type + CString("]");
  1744. }
  1745. else if (obj.m_ConnectorType == Box_Output)
  1746. {
  1747. CIdentifier typeID;
  1748. boxDetails->getOutputName(obj.m_ConnectorIdx, name);
  1749. boxDetails->getOutputType(obj.m_ConnectorIdx, typeID);
  1750. type = m_kernelCtx.getTypeManager().getTypeName(typeID);
  1751. type = CString("[") + type + CString("]");
  1752. }
  1753. else if (obj.m_ConnectorType == Box_Update)
  1754. {
  1755. //m_scenario.updateBox(boxDetails->getIdentifier());
  1756. name = CString("Right click for");
  1757. type = "box update";
  1758. }
  1759. else if (obj.m_ConnectorType == Box_ScenarioInput)
  1760. {
  1761. CIdentifier typeID;
  1762. boxDetails->getInputName(obj.m_ConnectorIdx, name);
  1763. boxDetails->getInputType(obj.m_ConnectorIdx, typeID);
  1764. for (size_t i = 0; i < m_Scenario.getInputCount(); i++)
  1765. {
  1766. CIdentifier id;
  1767. size_t idx;
  1768. m_Scenario.getScenarioInputLink(i, id, idx);
  1769. if (id == boxDetails->getIdentifier() && idx == obj.m_ConnectorIdx)
  1770. {
  1771. m_Scenario.getInputName(i, name);
  1772. name = CString("Connected to \n") + name;
  1773. m_Scenario.getInputType(i, typeID);
  1774. }
  1775. }
  1776. type = m_kernelCtx.getTypeManager().getTypeName(typeID);
  1777. type = CString("[") + type + CString("]");
  1778. }
  1779. else if (obj.m_ConnectorType == Box_ScenarioOutput)
  1780. {
  1781. CIdentifier typeID;
  1782. boxDetails->getOutputName(obj.m_ConnectorIdx, name);
  1783. boxDetails->getOutputType(obj.m_ConnectorIdx, typeID);
  1784. for (size_t i = 0; i < m_Scenario.getOutputCount(); i++)
  1785. {
  1786. CIdentifier id;
  1787. size_t idx;
  1788. m_Scenario.getScenarioOutputLink(i, id, idx);
  1789. if (id == boxDetails->getIdentifier() && idx == obj.m_ConnectorIdx)
  1790. {
  1791. m_Scenario.getOutputName(i, name);
  1792. name = CString("Connected to \n") + name;
  1793. m_Scenario.getOutputType(i, typeID);
  1794. }
  1795. }
  1796. type = m_kernelCtx.getTypeManager().getTypeName(typeID);
  1797. type = CString("[") + type + CString("]");
  1798. }
  1799. gtk_label_set_text(GTK_LABEL(gtk_builder_get_object(m_guiBuilder, "tooltip-label_name_content")), name);
  1800. gtk_label_set_text(GTK_LABEL(gtk_builder_get_object(m_guiBuilder, "tooltip-label_type_content")), type);
  1801. gtk_window_move(GTK_WINDOW(tooltip), gint(event->x_root), gint(event->y_root) + 40);
  1802. gtk_widget_show(tooltip);
  1803. }
  1804. }
  1805. else { gtk_widget_hide(tooltip); }
  1806. if (m_currentMode != Mode_None)
  1807. {
  1808. if (m_currentMode == Mode_MoveScenario)
  1809. {
  1810. m_viewOffsetX += int(event->x - m_currentMouseX);
  1811. m_viewOffsetY += int(event->y - m_currentMouseY);
  1812. }
  1813. else if (m_currentMode == Mode_MoveSelection)
  1814. {
  1815. if (m_controlPressed) { m_SelectedObjects.insert(m_currentObject.m_ID); }
  1816. else
  1817. {
  1818. if (!m_SelectedObjects.count(m_currentObject.m_ID))
  1819. {
  1820. m_SelectedObjects.clear();
  1821. m_SelectedObjects.insert(m_currentObject.m_ID);
  1822. }
  1823. }
  1824. for (auto& id : m_SelectedObjects)
  1825. {
  1826. if (m_Scenario.isBox(id))
  1827. {
  1828. CBoxProxy proxy(m_kernelCtx, m_Scenario, id);
  1829. proxy.setCenter(proxy.getXCenter() + int(event->x - m_currentMouseX), proxy.getYCenter() + int(event->y - m_currentMouseY));
  1830. }
  1831. if (m_Scenario.isComment(id))
  1832. {
  1833. CCommentProxy proxy(m_kernelCtx, m_Scenario, id);
  1834. proxy.setCenter(proxy.getXCenter() + int(event->x - m_currentMouseX), proxy.getYCenter() + int(event->y - m_currentMouseY));
  1835. }
  1836. }
  1837. }
  1838. this->redraw();
  1839. }
  1840. m_currentMouseX = event->x;
  1841. m_currentMouseY = event->y;
  1842. }
  1843. namespace {
  1844. void gtk_menu_add_separator_menu_item(GtkMenu* menu)
  1845. {
  1846. GtkSeparatorMenuItem* menuitem = GTK_SEPARATOR_MENU_ITEM(gtk_separator_menu_item_new());
  1847. gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
  1848. }
  1849. GtkImageMenuItem* gtk_menu_add_new_image_menu_item(GtkMenu* menu, const char* icon, const char* label)
  1850. {
  1851. GtkImageMenuItem* menuitem = GTK_IMAGE_MENU_ITEM(gtk_image_menu_item_new_with_label(label));
  1852. gtk_image_menu_item_set_image(menuitem, gtk_image_new_from_stock(icon, GTK_ICON_SIZE_MENU));
  1853. gtk_menu_shell_append(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
  1854. return menuitem;
  1855. }
  1856. } // namespace
  1857. GtkImageMenuItem* CInterfacedScenario::addNewImageMenuItemWithCBGeneric(GtkMenu* menu, const char* icon, const char* label, const menu_cb_function_t cb,
  1858. Kernel::IBox* box, const EContextMenu command, const size_t index, const size_t index2)
  1859. {
  1860. GtkImageMenuItem* menuItem = gtk_menu_add_new_image_menu_item(menu, icon, label);
  1861. box_ctx_menu_cb_t menuCB;
  1862. menuCB.command = command;
  1863. menuCB.index = index;
  1864. menuCB.secondaryIndex = index2;
  1865. menuCB.box = box;
  1866. menuCB.scenario = this;
  1867. const auto idx = m_boxCtxMenuCBs.size();
  1868. m_boxCtxMenuCBs[idx] = menuCB;
  1869. g_signal_connect(G_OBJECT(menuItem), "activate", G_CALLBACK(cb), &m_boxCtxMenuCBs[idx]);
  1870. return menuItem;
  1871. }
  1872. void CInterfacedScenario::scenarioDrawingAreaButtonPressedCB(GtkWidget* widget, GdkEventButton* event)
  1873. {
  1874. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "scenarioDrawingAreaButtonPressedCB\n";
  1875. if (this->isLocked()) { return; }
  1876. GtkWidget* tooltip = GTK_WIDGET(gtk_builder_get_object(m_guiBuilder, "tooltip"));
  1877. gtk_widget_hide(tooltip);
  1878. gtk_widget_grab_focus(widget);
  1879. m_buttonPressed |= ((event->type == GDK_BUTTON_PRESS) && (event->button == 1));
  1880. m_pressMouseX = event->x;
  1881. m_pressMouseY = event->y;
  1882. size_t objIdx = pickInterfacedObject(int(m_pressMouseX), int(m_pressMouseY));
  1883. m_currentObject = m_interfacedObjects[objIdx];
  1884. if (event->button == 1)
  1885. {
  1886. if (event->type == GDK_BUTTON_PRESS) // Simple click
  1887. {
  1888. if (m_currentObject.m_ID == CIdentifier::undefined())
  1889. {
  1890. if (m_shiftPressed) { m_currentMode = Mode_MoveScenario; }
  1891. else
  1892. {
  1893. if (m_controlPressed) { m_currentMode = Mode_SelectionAdd; }
  1894. else { m_currentMode = Mode_Selection; }
  1895. }
  1896. }
  1897. else
  1898. {
  1899. if (m_currentObject.m_ConnectorType == Box_Input || m_currentObject.m_ConnectorType == Box_Output) { m_currentMode = Mode_Connect; }
  1900. else
  1901. {
  1902. m_currentMode = Mode_MoveSelection;
  1903. /*
  1904. if (m_controlPressed) { m_interfacedObjects[m_currentObject.m_id]=!m_interfacedObjects[m_currentObject.m_id]; }
  1905. else
  1906. {
  1907. m_currentObject.clear();
  1908. m_currentObject[m_oCurrentObject.m_id]=true;
  1909. }
  1910. */
  1911. }
  1912. }
  1913. }
  1914. else if (event->type == GDK_2BUTTON_PRESS) // Double click
  1915. {
  1916. if (m_currentObject.m_ID != CIdentifier::undefined())
  1917. {
  1918. m_currentMode = Mode_EditSettings;
  1919. m_shiftPressed = false;
  1920. m_controlPressed = false;
  1921. m_altPressed = false;
  1922. m_aPressed = false;
  1923. m_wPressed = false;
  1924. if (m_currentObject.m_ConnectorType == Box_Input || m_currentObject.m_ConnectorType == Box_Output)
  1925. {
  1926. Kernel::IBox* box = m_Scenario.getBoxDetails(m_currentObject.m_ID);
  1927. if (box)
  1928. {
  1929. if ((m_currentObject.m_ConnectorType == Box_Input && box->hasAttribute(OV_AttributeId_Box_FlagCanModifyInput))
  1930. || (m_currentObject.m_ConnectorType == Box_Output && box->hasAttribute(OV_AttributeId_Box_FlagCanModifyOutput)))
  1931. {
  1932. CConnectorEditor editor(m_kernelCtx, *box, m_currentObject.m_ConnectorType, m_currentObject.m_ConnectorIdx,
  1933. m_currentObject.m_ConnectorType == Box_Input ? "Edit Input" : "Edit Output", m_guiFilename.c_str());
  1934. if (editor.run()) { this->snapshotCB(); }
  1935. }
  1936. }
  1937. }
  1938. else
  1939. {
  1940. if (m_Scenario.isBox(m_currentObject.m_ID))
  1941. {
  1942. Kernel::IBox* box = m_Scenario.getBoxDetails(m_currentObject.m_ID);
  1943. if (box)
  1944. {
  1945. CBoxConfigurationDialog dialog(m_kernelCtx, *box, m_guiFilename.c_str(), m_guiSettingsFilename.c_str(), false);
  1946. if (dialog.run()) { this->snapshotCB(); }
  1947. }
  1948. }
  1949. if (m_Scenario.isComment(m_currentObject.m_ID))
  1950. {
  1951. Kernel::IComment* comment = m_Scenario.getCommentDetails(m_currentObject.m_ID);
  1952. if (comment)
  1953. {
  1954. CCommentEditorDialog dialog(m_kernelCtx, *comment, m_guiFilename.c_str());
  1955. if (dialog.run()) { this->snapshotCB(); }
  1956. }
  1957. }
  1958. }
  1959. }
  1960. }
  1961. }
  1962. else if (event->button == 3) // right click
  1963. {
  1964. if (event->type == GDK_BUTTON_PRESS)
  1965. {
  1966. const auto unused = size_t(-1);
  1967. GtkMenu* menu = GTK_MENU(gtk_menu_new());
  1968. m_boxCtxMenuCBs.clear();
  1969. // -------------- SELECTION -----------
  1970. if (this->hasSelection()) { addNewImageMenuItemWithCB(menu, GTK_STOCK_CUT, "cut", context_menu_cb, nullptr, EContextMenu::SelectionCut, unused); }
  1971. if (this->hasSelection())
  1972. {
  1973. addNewImageMenuItemWithCB(menu, GTK_STOCK_COPY, "copy", context_menu_cb, nullptr, EContextMenu::SelectionCopy, unused);
  1974. }
  1975. if ((m_Application.m_ClipboardScenario->getNextBoxIdentifier(CIdentifier::undefined()) != CIdentifier::undefined())
  1976. || (m_Application.m_ClipboardScenario->getNextCommentIdentifier(CIdentifier::undefined()) != CIdentifier::undefined()))
  1977. {
  1978. addNewImageMenuItemWithCB(menu, GTK_STOCK_PASTE, "paste", context_menu_cb, nullptr, EContextMenu::SelectionPaste, unused);
  1979. }
  1980. if (this->hasSelection())
  1981. {
  1982. addNewImageMenuItemWithCB(menu, GTK_STOCK_DELETE, "delete", context_menu_cb, nullptr, EContextMenu::SelectionDelete, unused);
  1983. }
  1984. if (m_currentObject.m_ID != CIdentifier::undefined() && m_Scenario.isBox(m_currentObject.m_ID))
  1985. {
  1986. Kernel::IBox* box = m_Scenario.getBoxDetails(m_currentObject.m_ID);
  1987. if (box)
  1988. {
  1989. if (!m_boxCtxMenuCBs.empty()) { gtk_menu_add_separator_menu_item(menu); }
  1990. bool toBeUpdated = box->hasAttribute(OV_AttributeId_Box_ToBeUpdated);
  1991. bool pendingDeprecatedInterfacors = box->hasAttribute(OV_AttributeId_Box_PendingDeprecatedInterfacors);
  1992. // -------------- INPUTS --------------
  1993. bool canAddInput = box->hasAttribute(OV_AttributeId_Box_FlagCanAddInput);
  1994. bool canModifyInput = box->hasAttribute(OV_AttributeId_Box_FlagCanModifyInput);
  1995. bool canConnectScenarioInput = (box->getInputCount() > 0 && m_Scenario.getInputCount() > 0);
  1996. if (!pendingDeprecatedInterfacors && !toBeUpdated && (canAddInput || canModifyInput || canConnectScenarioInput))
  1997. {
  1998. size_t nFixedInput = 0;
  1999. sscanf(box->getAttributeValue(OV_AttributeId_Box_InitialInputCount).toASCIIString(), "%d", &nFixedInput);
  2000. GtkMenu* menuInput = GTK_MENU(gtk_menu_new());
  2001. GtkImageMenuItem* input = gtk_menu_add_new_image_menu_item(menu, GTK_STOCK_PROPERTIES, "inputs");
  2002. for (size_t i = 0; i < box->getInputCount(); ++i)
  2003. {
  2004. CString name;
  2005. CIdentifier typeID, id;
  2006. box->getInputName(i, name);
  2007. box->getInputType(i, typeID);
  2008. id = box->getIdentifier();
  2009. const std::string str = std::to_string(i + 1) + " : " + name.toASCIIString();
  2010. GtkImageMenuItem* menuItem = gtk_menu_add_new_image_menu_item(menuInput, GTK_STOCK_PROPERTIES, str.c_str());
  2011. GtkMenu* menuAction = GTK_MENU(gtk_menu_new());
  2012. if (canConnectScenarioInput)
  2013. {
  2014. for (size_t j = 0; j < m_Scenario.getInputCount(); ++j)
  2015. {
  2016. CString scenarioInputName;
  2017. CIdentifier boxID, inputTypeID;
  2018. auto idx = size_t(-1);
  2019. m_Scenario.getInputName(j, scenarioInputName);
  2020. m_Scenario.getInputType(j, inputTypeID);
  2021. m_Scenario.getScenarioInputLink(j, boxID, idx);
  2022. const std::string str2 = std::to_string(j + 1) + " : " + scenarioInputName.toASCIIString();
  2023. if (boxID == id && idx == i)
  2024. {
  2025. addNewImageMenuItemWithCBGeneric(menuAction, GTK_STOCK_DISCONNECT, ("disconnect from " + str2).c_str(), context_menu_cb,
  2026. box, EContextMenu::BoxDisconnectScenarioInput, i, j);
  2027. }
  2028. else
  2029. {
  2030. if (m_kernelCtx.getTypeManager().isDerivedFromStream(inputTypeID, typeID))
  2031. {
  2032. addNewImageMenuItemWithCBGeneric(menuAction, GTK_STOCK_CONNECT, ("connect to " + str2).c_str(), context_menu_cb,
  2033. box, EContextMenu::BoxConnectScenarioInput, i, j);
  2034. }
  2035. }
  2036. }
  2037. }
  2038. if (canModifyInput)
  2039. {
  2040. addNewImageMenuItemWithCB(menuAction, GTK_STOCK_EDIT, "configure...", context_menu_cb, box, EContextMenu::BoxEditInput, i);
  2041. }
  2042. if (canAddInput && nFixedInput <= i)
  2043. {
  2044. addNewImageMenuItemWithCB(menuAction, GTK_STOCK_REMOVE, "delete", context_menu_cb, box, EContextMenu::BoxRemoveInput, i);
  2045. }
  2046. if (gtk_container_get_children_count(GTK_CONTAINER(menuAction)) > 0)
  2047. {
  2048. gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuItem), GTK_WIDGET(menuAction));
  2049. }
  2050. else { gtk_widget_set_sensitive(GTK_WIDGET(menuItem), false); }
  2051. }
  2052. gtk_menu_add_separator_menu_item(menuInput);
  2053. if (canAddInput)
  2054. {
  2055. addNewImageMenuItemWithCB(menuInput, GTK_STOCK_ADD, "new...", context_menu_cb, box, EContextMenu::BoxAddInput, unused);
  2056. }
  2057. gtk_menu_item_set_submenu(GTK_MENU_ITEM(input), GTK_WIDGET(menuInput));
  2058. }
  2059. // -------------- OUTPUTS --------------
  2060. bool canAddOutput = box->hasAttribute(OV_AttributeId_Box_FlagCanAddOutput);
  2061. bool canModifyOutput = box->hasAttribute(OV_AttributeId_Box_FlagCanModifyOutput);
  2062. bool canConnectScenarioOutput = (box->getOutputCount() > 0 && m_Scenario.getOutputCount() > 0);
  2063. if (!pendingDeprecatedInterfacors && !toBeUpdated && (canAddOutput || canModifyOutput || canConnectScenarioOutput))
  2064. {
  2065. size_t nFixedOutput = 0;
  2066. sscanf(box->getAttributeValue(OV_AttributeId_Box_InitialOutputCount).toASCIIString(), "%d", &nFixedOutput);
  2067. GtkImageMenuItem* itemOutput = gtk_menu_add_new_image_menu_item(menu, GTK_STOCK_PROPERTIES, "outputs");
  2068. GtkMenu* menuOutput = GTK_MENU(gtk_menu_new());
  2069. for (size_t i = 0; i < box->getOutputCount(); ++i)
  2070. {
  2071. CString name;
  2072. CIdentifier typeID, id;
  2073. box->getOutputName(i, name);
  2074. box->getOutputType(i, typeID);
  2075. id = box->getIdentifier();
  2076. const std::string str = std::to_string(i + 1) + " : " + name.toASCIIString();
  2077. GtkImageMenuItem* menuItem = gtk_menu_add_new_image_menu_item(menuOutput, GTK_STOCK_PROPERTIES, str.c_str());
  2078. GtkMenu* menuAction = GTK_MENU(gtk_menu_new());
  2079. if (canConnectScenarioOutput)
  2080. {
  2081. for (size_t j = 0; j < m_Scenario.getOutputCount(); ++j)
  2082. {
  2083. CString scenarioOutputName;
  2084. CIdentifier boxID, outputTypeID;
  2085. auto idx = size_t(-1);
  2086. m_Scenario.getOutputName(j, scenarioOutputName);
  2087. m_Scenario.getOutputType(j, outputTypeID);
  2088. m_Scenario.getScenarioOutputLink(j, boxID, idx);
  2089. const std::string str2 = std::to_string(j + 1) + " : " + scenarioOutputName.toASCIIString();
  2090. if (boxID == id && idx == i)
  2091. {
  2092. addNewImageMenuItemWithCBGeneric(menuAction, GTK_STOCK_DISCONNECT, ("disconnect from " + str2).c_str(),
  2093. context_menu_cb, box, EContextMenu::BoxDisconnectScenarioOutput, i, j);
  2094. }
  2095. else if (m_kernelCtx.getTypeManager().isDerivedFromStream(typeID, outputTypeID))
  2096. {
  2097. addNewImageMenuItemWithCBGeneric(menuAction, GTK_STOCK_CONNECT, ("connect to " + str2).c_str(),
  2098. context_menu_cb, box, EContextMenu::BoxConnectScenarioOutput, i, j);
  2099. }
  2100. }
  2101. }
  2102. if (canModifyOutput)
  2103. {
  2104. addNewImageMenuItemWithCB(menuAction, GTK_STOCK_EDIT, "configure...", context_menu_cb, box, EContextMenu::BoxEditOutput, i);
  2105. }
  2106. if (canAddOutput && nFixedOutput <= i)
  2107. {
  2108. addNewImageMenuItemWithCB(menuAction, GTK_STOCK_REMOVE, "delete", context_menu_cb, box, EContextMenu::BoxRemoveOutput, i);
  2109. }
  2110. if (gtk_container_get_children_count(GTK_CONTAINER(menuAction)) > 0)
  2111. {
  2112. gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuItem), GTK_WIDGET(menuAction));
  2113. }
  2114. else { gtk_widget_set_sensitive(GTK_WIDGET(menuItem), false); }
  2115. }
  2116. gtk_menu_add_separator_menu_item(menuOutput);
  2117. if (canAddOutput)
  2118. {
  2119. addNewImageMenuItemWithCB(menuOutput, GTK_STOCK_ADD, "new...", context_menu_cb, box, EContextMenu::BoxAddOutput, unused);
  2120. }
  2121. gtk_menu_item_set_submenu(GTK_MENU_ITEM(itemOutput), GTK_WIDGET(menuOutput));
  2122. }
  2123. // -------------- SETTINGS --------------
  2124. bool canAddSetting = box->hasAttribute(OV_AttributeId_Box_FlagCanAddSetting);
  2125. bool canModifySetting = box->hasAttribute(OV_AttributeId_Box_FlagCanModifySetting);
  2126. if (!pendingDeprecatedInterfacors && !toBeUpdated && (canAddSetting || canModifySetting))
  2127. {
  2128. size_t nFixedSetting = 0;
  2129. sscanf(box->getAttributeValue(OV_AttributeId_Box_InitialSettingCount).toASCIIString(), "%d", &nFixedSetting);
  2130. GtkImageMenuItem* itemSetting = gtk_menu_add_new_image_menu_item(menu, GTK_STOCK_PROPERTIES, "modify settings");
  2131. GtkMenu* menuSetting = GTK_MENU(gtk_menu_new());
  2132. for (size_t i = 0; i < box->getSettingCount(); ++i)
  2133. {
  2134. CString name;
  2135. CIdentifier typeID;
  2136. box->getSettingName(i, name);
  2137. box->getSettingType(i, typeID);
  2138. const std::string str = std::to_string(i + 1) + " : " + name.toASCIIString();
  2139. GtkImageMenuItem* menuItem = gtk_menu_add_new_image_menu_item(menuSetting, GTK_STOCK_PROPERTIES, str.c_str());
  2140. if (canModifySetting || nFixedSetting <= i)
  2141. {
  2142. GtkMenu* menuAction = GTK_MENU(gtk_menu_new());
  2143. if (canModifySetting)
  2144. {
  2145. addNewImageMenuItemWithCB(menuAction, GTK_STOCK_EDIT, "configure...", context_menu_cb, box,
  2146. EContextMenu::BoxEditSetting, i);
  2147. }
  2148. if (canAddSetting && nFixedSetting <= i)
  2149. {
  2150. addNewImageMenuItemWithCB(menuAction, GTK_STOCK_REMOVE, "delete", context_menu_cb, box, EContextMenu::BoxRemoveSetting, i);
  2151. }
  2152. gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuItem), GTK_WIDGET(menuAction));
  2153. }
  2154. else { gtk_widget_set_sensitive(GTK_WIDGET(menuItem), false); }
  2155. }
  2156. gtk_menu_add_separator_menu_item(menuSetting);
  2157. if (canAddSetting)
  2158. {
  2159. addNewImageMenuItemWithCB(menuSetting, GTK_STOCK_ADD, "new...", context_menu_cb, box, EContextMenu::BoxAddSetting, unused);
  2160. }
  2161. gtk_menu_item_set_submenu(GTK_MENU_ITEM(itemSetting), GTK_WIDGET(menuSetting));
  2162. }
  2163. // -------------- ABOUT / RENAME --------------
  2164. if (!m_boxCtxMenuCBs.empty()) { gtk_menu_add_separator_menu_item(menu); }
  2165. if (box->hasAttribute(OV_AttributeId_Box_ToBeUpdated))
  2166. {
  2167. auto updateMenuItem = addNewImageMenuItemWithCB(menu, GTK_STOCK_REFRESH, "update box", context_menu_cb, box,
  2168. EContextMenu::BoxUpdate, unused);
  2169. if (box->hasAttribute(OV_AttributeId_Box_FlagNeedsManualUpdate)
  2170. || box->hasAttribute(OV_AttributeId_Box_FlagCanAddInput)
  2171. || box->hasAttribute(OV_AttributeId_Box_FlagCanAddOutput)
  2172. || box->hasAttribute(OV_AttributeId_Box_FlagCanAddSetting)
  2173. || box->hasAttribute(OV_AttributeId_Box_FlagCanModifyInput)
  2174. || box->hasAttribute(OV_AttributeId_Box_FlagCanModifyOutput)
  2175. || box->hasAttribute(OV_AttributeId_Box_FlagCanModifySetting))
  2176. {
  2177. gtk_widget_set_sensitive(GTK_WIDGET(updateMenuItem), FALSE);
  2178. gtk_widget_set_tooltip_text(GTK_WIDGET(updateMenuItem), "Box must be manually updated due to its complexity.");
  2179. }
  2180. }
  2181. if (box->hasAttribute(OV_AttributeId_Box_PendingDeprecatedInterfacors))
  2182. {
  2183. addNewImageMenuItemWithCB(menu, GTK_STOCK_REFRESH, "remove deprecated I/O/S", context_menu_cb, box,
  2184. EContextMenu::BoxRemoveDeprecatedInterfacors, unused);
  2185. }
  2186. addNewImageMenuItemWithCB(menu, GTK_STOCK_EDIT, "rename box...", context_menu_cb, box, EContextMenu::BoxRename, unused);
  2187. if (box->getSettingCount() != 0)
  2188. {
  2189. addNewImageMenuItemWithCB(menu, GTK_STOCK_PREFERENCES, "configure box...", context_menu_cb, box, EContextMenu::BoxConfigure, unused);
  2190. }
  2191. // Add this option only if the user has the authorization to open a metabox
  2192. if (box->getAlgorithmClassIdentifier() == OVP_ClassId_BoxAlgorithm_Metabox)
  2193. {
  2194. CIdentifier id;
  2195. id.fromString(box->getAttributeValue(OVP_AttributeId_Metabox_ID));
  2196. std::string path(m_kernelCtx.getMetaboxManager().getMetaboxFilePath(id).toASCIIString());
  2197. std::string ext = boost::filesystem::extension(path);
  2198. bool canImportFile = false;
  2199. CString fileExt;
  2200. while ((fileExt = m_kernelCtx.getScenarioManager().getNextScenarioImporter(OVD_ScenarioImportContext_OpenScenario, fileExt))
  2201. != CString(""))
  2202. {
  2203. if (ext == fileExt.toASCIIString())
  2204. {
  2205. canImportFile = true;
  2206. break;
  2207. }
  2208. }
  2209. if (canImportFile)
  2210. {
  2211. addNewImageMenuItemWithCB(menu, GTK_STOCK_PREFERENCES, "open this meta box in editor", context_menu_cb, box,
  2212. EContextMenu::BoxEditMetabox, unused);
  2213. }
  2214. }
  2215. addNewImageMenuItemWithCB(menu, GTK_STOCK_CONNECT, "enable box", context_menu_cb, box, EContextMenu::BoxEnable, unused);
  2216. addNewImageMenuItemWithCB(menu, GTK_STOCK_DISCONNECT, "disable box", context_menu_cb, box, EContextMenu::BoxDisable, unused);
  2217. addNewImageMenuItemWithCB(menu, GTK_STOCK_CUT, "delete box", context_menu_cb, box, EContextMenu::BoxDelete, unused);
  2218. addNewImageMenuItemWithCB(menu, GTK_STOCK_HELP, "box documentation...", context_menu_cb, box, EContextMenu::BoxDocumentation, unused);
  2219. addNewImageMenuItemWithCB(menu, GTK_STOCK_ABOUT, "about box...", context_menu_cb, box, EContextMenu::BoxAbout, unused);
  2220. }
  2221. }
  2222. gtk_menu_add_separator_menu_item(menu);
  2223. addNewImageMenuItemWithCB(menu, GTK_STOCK_EDIT, "add comment to scenario...", context_menu_cb, nullptr, EContextMenu::ScenarioAddComment, unused);
  2224. addNewImageMenuItemWithCB(menu, GTK_STOCK_ABOUT, "about scenario...", context_menu_cb, nullptr, EContextMenu::ScenarioAbout, unused);
  2225. // -------------- RUN --------------
  2226. gtk_widget_show_all(GTK_WIDGET(menu));
  2227. gtk_menu_popup(menu, nullptr, nullptr, nullptr, nullptr, 3, event->time);
  2228. if (m_boxCtxMenuCBs.empty()) { gtk_menu_popdown(menu); }
  2229. }
  2230. }
  2231. this->redraw();
  2232. }
  2233. void CInterfacedScenario::scenarioDrawingAreaButtonReleasedCB(GtkWidget* /*widget*/, GdkEventButton* event)
  2234. {
  2235. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "scenarioDrawingAreaButtonReleasedCB\n";
  2236. if (this->isLocked()) { return; }
  2237. m_buttonPressed &= !((event->type == GDK_BUTTON_RELEASE) && (event->button == 1));
  2238. m_releaseMouseX = event->x;
  2239. m_releaseMouseY = event->y;
  2240. if (m_currentMode != Mode_None)
  2241. {
  2242. const int startX = int(std::min(m_pressMouseX, m_currentMouseX));
  2243. const int startY = int(std::min(m_pressMouseY, m_currentMouseY));
  2244. const int sizeX = int(std::max(m_pressMouseX - m_currentMouseX, m_currentMouseX - m_pressMouseX));
  2245. const int sizeY = int(std::max(m_pressMouseY - m_currentMouseY, m_currentMouseY - m_pressMouseY));
  2246. if (m_currentMode == Mode_Selection || m_currentMode == Mode_SelectionAdd)
  2247. {
  2248. if (m_currentMode == Mode_Selection) { m_SelectedObjects.clear(); }
  2249. pickInterfacedObject(startX, startY, sizeX, sizeY);
  2250. }
  2251. if (m_currentMode == Mode_Connect)
  2252. {
  2253. bool isActuallyConnecting = false;
  2254. const bool connectionIsMessage = false;
  2255. const size_t interfacedObjectId = pickInterfacedObject(int(m_releaseMouseX), int(m_releaseMouseY));
  2256. const CInterfacedObject currentObject = m_interfacedObjects[interfacedObjectId];
  2257. CInterfacedObject srcObject;
  2258. CInterfacedObject dstObject;
  2259. if (currentObject.m_ConnectorType == Box_Output && m_currentObject.m_ConnectorType == Box_Input)
  2260. {
  2261. srcObject = currentObject;
  2262. dstObject = m_currentObject;
  2263. isActuallyConnecting = true;
  2264. }
  2265. if (currentObject.m_ConnectorType == Box_Input && m_currentObject.m_ConnectorType == Box_Output)
  2266. {
  2267. srcObject = m_currentObject;
  2268. dstObject = currentObject;
  2269. isActuallyConnecting = true;
  2270. }
  2271. //
  2272. if (isActuallyConnecting)
  2273. {
  2274. CIdentifier srcTypeID;
  2275. CIdentifier dstTypeID;
  2276. const Kernel::IBox* srcBox = m_Scenario.getBoxDetails(srcObject.m_ID);
  2277. const Kernel::IBox* dstBox = m_Scenario.getBoxDetails(dstObject.m_ID);
  2278. if (srcBox && dstBox)
  2279. {
  2280. srcBox->getOutputType(srcObject.m_ConnectorIdx, srcTypeID);
  2281. dstBox->getInputType(dstObject.m_ConnectorIdx, dstTypeID);
  2282. bool hasDeprecatedInput = false;
  2283. srcBox->getInterfacorDeprecatedStatus(Kernel::Output, srcObject.m_ConnectorIdx, hasDeprecatedInput);
  2284. bool hasDeprecatedOutput = false;
  2285. dstBox->getInterfacorDeprecatedStatus(Kernel::Input, dstObject.m_ConnectorIdx, hasDeprecatedOutput);
  2286. if ((m_kernelCtx.getTypeManager().isDerivedFromStream(srcTypeID, dstTypeID)
  2287. || m_kernelCtx.getConfigurationManager().expandAsBoolean("${Designer_AllowUpCastConnection}", false)) && (!connectionIsMessage))
  2288. {
  2289. if (!hasDeprecatedInput && !hasDeprecatedOutput)
  2290. {
  2291. CIdentifier id;
  2292. m_Scenario.connect(id, srcObject.m_ID, srcObject.m_ConnectorIdx, dstObject.m_ID, dstObject.m_ConnectorIdx,
  2293. CIdentifier::undefined());
  2294. this->snapshotCB();
  2295. }
  2296. else { m_kernelCtx.getLogManager() << Kernel::LogLevel_Warning << "Cannot connect to/from deprecated I/O\n"; }
  2297. }
  2298. else { m_kernelCtx.getLogManager() << Kernel::LogLevel_Warning << "Invalid connection\n"; }
  2299. }
  2300. }
  2301. }
  2302. if (m_currentMode == Mode_MoveSelection)
  2303. {
  2304. if (sizeX == 0 && sizeY == 0)
  2305. {
  2306. if (m_controlPressed)
  2307. {
  2308. if (m_SelectedObjects.count(m_currentObject.m_ID)) { m_SelectedObjects.erase(m_currentObject.m_ID); }
  2309. else { m_SelectedObjects.insert(m_currentObject.m_ID); }
  2310. }
  2311. else
  2312. {
  2313. m_SelectedObjects.clear();
  2314. m_SelectedObjects.insert(m_currentObject.m_ID);
  2315. }
  2316. }
  2317. else
  2318. {
  2319. for (const auto& id : m_SelectedObjects)
  2320. {
  2321. if (m_Scenario.isBox(id))
  2322. {
  2323. CBoxProxy proxy(m_kernelCtx, m_Scenario, id);
  2324. proxy.setCenter(int((proxy.getXCenter() + 8) & 0xfffffff0), int((proxy.getYCenter() + 8) & 0xfffffff0));
  2325. }
  2326. if (m_Scenario.isComment(id))
  2327. {
  2328. CCommentProxy proxy(m_kernelCtx, m_Scenario, id);
  2329. proxy.setCenter(int((proxy.getXCenter() + 8) & 0xfffffff0), int((proxy.getYCenter() + 8) & 0xfffffff0));
  2330. }
  2331. }
  2332. this->snapshotCB();
  2333. }
  2334. }
  2335. this->redraw();
  2336. }
  2337. m_currentMode = Mode_None;
  2338. }
  2339. void CInterfacedScenario::scenarioDrawingAreaKeyPressEventCB(GtkWidget* /*widget*/, GdkEventKey* event)
  2340. {
  2341. m_shiftPressed |= (event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R);
  2342. m_controlPressed |= (event->keyval == GDK_Control_L || event->keyval == GDK_Control_R);
  2343. m_altPressed |= (event->keyval == GDK_Alt_L || event->keyval == GDK_Alt_R);
  2344. m_aPressed |= (event->keyval == GDK_a || event->keyval == GDK_A);
  2345. m_wPressed |= (event->keyval == GDK_w || event->keyval == GDK_W);
  2346. // m_kernelCtx.getLogManager() << Kernel::LogLevel_Info << "Key pressed " << (size_t) event->keyval << "\n";
  2347. /*
  2348. if((event->keyval==GDK_Z || event->keyval==GDK_z) && m_controlPressed) { this->undoCB(); }
  2349. if((event->keyval==GDK_Y || event->keyval==GDK_y) && m_controlPressed) { this->redoCB(); }
  2350. */
  2351. // CTRL+A = select all
  2352. if (m_aPressed && m_controlPressed && !m_shiftPressed && !m_altPressed)
  2353. {
  2354. CIdentifier id;
  2355. while ((id = m_Scenario.getNextBoxIdentifier(id)) != CIdentifier::undefined()) { m_SelectedObjects.insert(id); }
  2356. while ((id = m_Scenario.getNextLinkIdentifier(id)) != CIdentifier::undefined()) { m_SelectedObjects.insert(id); }
  2357. while ((id = m_Scenario.getNextCommentIdentifier(id)) != CIdentifier::undefined()) { m_SelectedObjects.insert(id); }
  2358. this->redraw();
  2359. }
  2360. //CTRL+W : close current scenario
  2361. if (m_wPressed && m_controlPressed && !m_shiftPressed && !m_altPressed)
  2362. {
  2363. m_Application.closeScenarioCB(this);
  2364. return;
  2365. }
  2366. if ((event->keyval == GDK_C || event->keyval == GDK_c) && m_currentMode == Mode_None)
  2367. {
  2368. gint x = 0;
  2369. gint y = 0;
  2370. gdk_window_get_pointer(GTK_WIDGET(m_scenarioDrawingArea)->window, &x, &y, nullptr);
  2371. this->addCommentCB(x, y);
  2372. }
  2373. if (event->keyval == GDK_F12 && m_shiftPressed)
  2374. {
  2375. CIdentifier id;
  2376. while ((id = m_Scenario.getNextBoxIdentifier(id)) != CIdentifier::undefined())
  2377. {
  2378. Kernel::IBox* box = m_Scenario.getBoxDetails(id);
  2379. CIdentifier algorithmID = box->getAlgorithmClassIdentifier();
  2380. CIdentifier hashValue = m_kernelCtx.getPluginManager().getPluginObjectHashValue(algorithmID);
  2381. if (box->hasAttribute(OV_AttributeId_Box_InitialPrototypeHashValue))
  2382. {
  2383. box->setAttributeValue(OV_AttributeId_Box_InitialPrototypeHashValue, hashValue.toString());
  2384. }
  2385. else { box->addAttribute(OV_AttributeId_Box_InitialPrototypeHashValue, hashValue.toString()); }
  2386. }
  2387. this->redraw();
  2388. this->snapshotCB();
  2389. }
  2390. // F1 : browse documentation
  2391. if (event->keyval == GDK_F1)
  2392. {
  2393. bool hasDoc = false;
  2394. for (const auto& objectId : m_SelectedObjects)
  2395. {
  2396. if (m_Scenario.isBox(objectId))
  2397. {
  2398. browseBoxDocumentation(objectId);
  2399. hasDoc = true;
  2400. }
  2401. }
  2402. if (!hasDoc)
  2403. {
  2404. const CString fullUrl = m_Scenario.getAttributeValue(OV_AttributeId_Scenario_DocumentationPage);
  2405. if (fullUrl != CString(""))
  2406. {
  2407. browseURL(fullUrl, m_kernelCtx.getConfigurationManager().expand("${Designer_WebBrowserCommand}"),
  2408. m_kernelCtx.getConfigurationManager().expand("${Designer_WebBrowserCommandPostfix}"));
  2409. }
  2410. else { m_kernelCtx.getLogManager() << Kernel::LogLevel_Info << "The scenario does not define a documentation page.\n"; }
  2411. }
  2412. }
  2413. // F2 : rename all selected box(es)
  2414. if (event->keyval == GDK_F2) { contextMenuBoxRenameAllCB(); }
  2415. // F8 : toggle enable/disable on all selected box(es)
  2416. if (event->keyval == GDK_F3)
  2417. {
  2418. contextMenuBoxToggleEnableAllCB();
  2419. this->redraw();
  2420. }
  2421. //The shortcuts respect the order in the toolbar
  2422. // F7 :play/pause
  2423. if (event->keyval == GDK_F7)
  2424. {
  2425. if (m_Application.getCurrentInterfacedScenario()->m_PlayerStatus == Kernel::EPlayerStatus::Play) { m_Application.pauseScenarioCB(); }
  2426. else { m_Application.playScenarioCB(); }
  2427. }
  2428. // F6 : step
  2429. if (event->keyval == GDK_F6) { m_Application.nextScenarioCB(); }
  2430. // F8 :fastforward
  2431. if (event->keyval == GDK_F8) { m_Application.forwardScenarioCB(); }
  2432. // F5 : stop
  2433. if (event->keyval == GDK_F5) { m_Application.stopScenarioCB(); }
  2434. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "scenarioDrawingAreaKeyPressEventCB (" << (m_shiftPressed ? "true" : "false") << "|"
  2435. << (m_controlPressed ? "true" : "false") << "|" << (m_altPressed ? "true" : "false") << "|" << (m_aPressed ? "true" : "false") << "|"
  2436. << (m_wPressed ? "true" : "false") << "|" << ")\n";
  2437. if (this->isLocked()) { return; }
  2438. #if defined TARGET_OS_Windows || defined TARGET_OS_Linux
  2439. if (event->keyval == GDK_Delete || event->keyval == GDK_KP_Delete)
  2440. #elif defined TARGET_OS_MacOS
  2441. if (event->keyval == GDK_BackSpace)
  2442. #endif
  2443. {
  2444. this->deleteSelection();
  2445. }
  2446. }
  2447. void CInterfacedScenario::scenarioDrawingAreaKeyReleaseEventCB(GtkWidget* /*widget*/, GdkEventKey* event)
  2448. {
  2449. m_shiftPressed &= !(event->keyval == GDK_Shift_L || event->keyval == GDK_Shift_R);
  2450. m_controlPressed &= !(event->keyval == GDK_Control_L || event->keyval == GDK_Control_R);
  2451. m_altPressed &= !(event->keyval == GDK_Alt_L || event->keyval == GDK_Alt_R);
  2452. m_aPressed &= !(event->keyval == GDK_A || event->keyval == GDK_a);
  2453. m_wPressed &= !(event->keyval == GDK_W || event->keyval == GDK_w);
  2454. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug
  2455. << "scenarioDrawingAreaKeyReleaseEventCB ("
  2456. << (m_shiftPressed ? "true" : "false") << "|"
  2457. << (m_controlPressed ? "true" : "false") << "|"
  2458. << (m_altPressed ? "true" : "false") << "|"
  2459. << (m_aPressed ? "true" : "false") << "|"
  2460. << (m_wPressed ? "true" : "false") << "|"
  2461. << ")\n";
  2462. //if (this->isLocked()) { return; }
  2463. // ...
  2464. }
  2465. void CInterfacedScenario::copySelection()
  2466. {
  2467. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "copySelection\n";
  2468. // Prepares copy
  2469. std::map<CIdentifier, CIdentifier> mapping;
  2470. m_Application.m_ClipboardScenario->clear();
  2471. // Copies boxes to clipboard
  2472. for (auto& objectId : m_SelectedObjects)
  2473. {
  2474. if (m_Scenario.isBox(objectId))
  2475. {
  2476. CIdentifier id;
  2477. const Kernel::IBox* box = m_Scenario.getBoxDetails(objectId);
  2478. m_Application.m_ClipboardScenario->addBox(id, *box, objectId);
  2479. mapping[objectId] = id;
  2480. }
  2481. }
  2482. // Copies comments to clipboard
  2483. for (auto& objectId : m_SelectedObjects)
  2484. {
  2485. if (m_Scenario.isComment(objectId))
  2486. {
  2487. CIdentifier id;
  2488. const Kernel::IComment* comment = m_Scenario.getCommentDetails(objectId);
  2489. m_Application.m_ClipboardScenario->addComment(id, *comment, objectId);
  2490. mapping[objectId] = id;
  2491. }
  2492. }
  2493. // Copies links to clipboard
  2494. for (auto& objectId : m_SelectedObjects)
  2495. {
  2496. if (m_Scenario.isLink(objectId))
  2497. {
  2498. CIdentifier id;
  2499. const Kernel::ILink* link = m_Scenario.getLinkDetails(objectId);
  2500. // Connect link only if the source and target boxes are copied
  2501. if (mapping.find(link->getSourceBoxIdentifier()) != mapping.end()
  2502. && mapping.find(link->getTargetBoxIdentifier()) != mapping.end())
  2503. {
  2504. m_Application.m_ClipboardScenario->connect(id, mapping[link->getSourceBoxIdentifier()], link->getSourceBoxOutputIndex(),
  2505. mapping[link->getTargetBoxIdentifier()], link->getTargetBoxInputIndex(), link->getIdentifier());
  2506. }
  2507. }
  2508. }
  2509. }
  2510. void CInterfacedScenario::cutSelection()
  2511. {
  2512. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "cutSelection\n";
  2513. this->copySelection();
  2514. this->deleteSelection();
  2515. }
  2516. void CInterfacedScenario::pasteSelection()
  2517. {
  2518. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "pasteSelection\n";
  2519. // Prepares paste
  2520. CIdentifier id;
  2521. std::map<CIdentifier, CIdentifier> mapping;
  2522. // int centerX = 0, centerY = 0;
  2523. int mostTLCopiedBoxCenterX = 1 << 15; // most top most left
  2524. int mostTLCopiedBoxCenterY = 1 << 15; // most top most left
  2525. // std::cout << "Mouse position : " << m_currentMouseX << "/" << m_currentMouseY << std::endl;
  2526. // Pastes boxes from clipboard
  2527. while ((id = m_Application.m_ClipboardScenario->getNextBoxIdentifier(id)) != CIdentifier::undefined())
  2528. {
  2529. CIdentifier newID;
  2530. Kernel::IBox* box = m_Application.m_ClipboardScenario->getBoxDetails(id);
  2531. m_Scenario.addBox(newID, *box, id);
  2532. mapping[id] = newID;
  2533. // Updates visualization manager
  2534. CIdentifier boxAlgorithmID = box->getAlgorithmClassIdentifier();
  2535. const Plugins::IPluginObjectDesc* pod = m_kernelCtx.getPluginManager().getPluginObjectDescCreating(boxAlgorithmID);
  2536. // If a visualization box was dropped, add it in window manager
  2537. if (pod && pod->hasFunctionality(Plugins::EPluginFunctionality::Visualization))
  2538. {
  2539. // Let window manager know about new box
  2540. if (m_DesignerVisualization) { m_DesignerVisualization->onVisualizationBoxAdded(m_Scenario.getBoxDetails(newID)); }
  2541. }
  2542. CBoxProxy proxy(m_kernelCtx, m_Scenario, newID);
  2543. // get the position of the topmost-leftmost box (always position on an actual box so when user pastes he sees something)
  2544. if (proxy.getXCenter() < mostTLCopiedBoxCenterX && proxy.getXCenter() < mostTLCopiedBoxCenterY)
  2545. {
  2546. mostTLCopiedBoxCenterX = proxy.getXCenter();
  2547. mostTLCopiedBoxCenterY = proxy.getYCenter();
  2548. }
  2549. }
  2550. // Pastes comments from clipboard
  2551. while ((id = m_Application.m_ClipboardScenario->getNextCommentIdentifier(id)) != CIdentifier::undefined())
  2552. {
  2553. CIdentifier newID;
  2554. Kernel::IComment* comment = m_Application.m_ClipboardScenario->getCommentDetails(id);
  2555. m_Scenario.addComment(newID, *comment, id);
  2556. mapping[id] = newID;
  2557. CCommentProxy proxy(m_kernelCtx, m_Scenario, newID);
  2558. if (proxy.getXCenter() < mostTLCopiedBoxCenterX && proxy.getYCenter() < mostTLCopiedBoxCenterY)
  2559. {
  2560. mostTLCopiedBoxCenterX = proxy.getXCenter();
  2561. mostTLCopiedBoxCenterY = proxy.getYCenter();
  2562. }
  2563. }
  2564. // Pastes links from clipboard
  2565. while ((id = m_Application.m_ClipboardScenario->getNextLinkIdentifier(id)) != CIdentifier::undefined())
  2566. {
  2567. CIdentifier newID;
  2568. Kernel::ILink* link = m_Application.m_ClipboardScenario->getLinkDetails(id);
  2569. m_Scenario.connect(newID, mapping[link->getSourceBoxIdentifier()], link->getSourceBoxOutputIndex(), mapping[link->getTargetBoxIdentifier()],
  2570. link->getTargetBoxInputIndex(), link->getIdentifier());
  2571. }
  2572. // Makes pasted stuff the default selection
  2573. // Moves boxes under cursor
  2574. // Moves comments under cursor
  2575. if (m_Application.m_ClipboardScenario->getNextBoxIdentifier(CIdentifier::undefined()) != CIdentifier::undefined()
  2576. || m_Application.m_ClipboardScenario->getNextCommentIdentifier(CIdentifier::undefined()) != CIdentifier::undefined())
  2577. {
  2578. m_SelectedObjects.clear();
  2579. for (auto& it : mapping)
  2580. {
  2581. m_SelectedObjects.insert(it.second);
  2582. if (m_Scenario.isBox(it.second))
  2583. {
  2584. // Moves boxes under cursor
  2585. CBoxProxy proxy(m_kernelCtx, m_Scenario, it.second);
  2586. proxy.setCenter(int(proxy.getXCenter() + m_currentMouseX) - mostTLCopiedBoxCenterX - m_viewOffsetX,
  2587. int(proxy.getYCenter() + m_currentMouseY) - mostTLCopiedBoxCenterY - m_viewOffsetY);
  2588. // Ok, why 32 would you ask, just because it is fine
  2589. // Aligns boxes on grid
  2590. proxy.setCenter(int((proxy.getXCenter() + 8) & 0xfffffff0L), int((proxy.getYCenter() + 8) & 0xfffffff0L));
  2591. }
  2592. if (m_Scenario.isComment(it.second))
  2593. {
  2594. // Moves commentes under cursor
  2595. CCommentProxy proxy(m_kernelCtx, m_Scenario, it.second);
  2596. proxy.setCenter(int(proxy.getXCenter() + m_currentMouseX) - mostTLCopiedBoxCenterX - m_viewOffsetX,
  2597. int(proxy.getYCenter() + m_currentMouseY) - mostTLCopiedBoxCenterY - m_viewOffsetY);
  2598. // Ok, why 32 would you ask, just because it is fine
  2599. // Aligns commentes on grid
  2600. proxy.setCenter(int((proxy.getXCenter() + 8) & 0xfffffff0L), int((proxy.getYCenter() + 8) & 0xfffffff0L));
  2601. }
  2602. }
  2603. }
  2604. this->redraw();
  2605. this->snapshotCB();
  2606. }
  2607. void CInterfacedScenario::deleteSelection()
  2608. {
  2609. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "deleteSelection\n";
  2610. for (auto& id : m_SelectedObjects)
  2611. {
  2612. if (m_Scenario.isBox(id)) { this->deleteBox(id); }
  2613. if (m_Scenario.isComment(id))
  2614. {
  2615. // removes comment from scenario
  2616. m_Scenario.removeComment(id);
  2617. }
  2618. if (m_Scenario.isLink(id))
  2619. {
  2620. // removes link from scenario
  2621. m_Scenario.disconnect(id);
  2622. }
  2623. }
  2624. m_SelectedObjects.clear();
  2625. this->redraw();
  2626. this->snapshotCB();
  2627. }
  2628. void CInterfacedScenario::deleteBox(const CIdentifier& boxID)
  2629. {
  2630. // removes visualization box from window manager
  2631. if (m_DesignerVisualization) { m_DesignerVisualization->onVisualizationBoxRemoved(boxID); }
  2632. // removes box from scenario
  2633. m_Scenario.removeBox(boxID);
  2634. }
  2635. void CInterfacedScenario::contextMenuBoxUpdateCB(Kernel::IBox& box)
  2636. {
  2637. m_Scenario.updateBox(box.getIdentifier());
  2638. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuBoxUpdateCB\n";
  2639. this->snapshotCB();
  2640. }
  2641. void CInterfacedScenario::contextMenuBoxRemoveDeprecatedInterfacorsCB(Kernel::IBox& box)
  2642. {
  2643. m_Scenario.removeDeprecatedInterfacorsFromBox(box.getIdentifier());
  2644. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuBoxRemoveDeprecatedInterfacorsCB\n";
  2645. this->snapshotCB();
  2646. }
  2647. void CInterfacedScenario::contextMenuBoxRenameCB(Kernel::IBox& box)
  2648. {
  2649. const Plugins::IPluginObjectDesc* pod = m_kernelCtx.getPluginManager().getPluginObjectDescCreating(box.getAlgorithmClassIdentifier());
  2650. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuBoxRenameCB\n";
  2651. if (box.getAlgorithmClassIdentifier() == OVP_ClassId_BoxAlgorithm_Metabox)
  2652. {
  2653. CIdentifier id;
  2654. id.fromString(box.getAttributeValue(OVP_AttributeId_Metabox_ID));
  2655. pod = m_kernelCtx.getMetaboxManager().getMetaboxObjectDesc(id);
  2656. }
  2657. CRenameDialog rename(m_kernelCtx, box.getName(), pod ? pod->getName() : box.getName(), m_guiFilename.c_str());
  2658. if (rename.run())
  2659. {
  2660. box.setName(rename.getResult());
  2661. //check whether it is a visualization box
  2662. const CIdentifier id = box.getAlgorithmClassIdentifier();
  2663. const Plugins::IPluginObjectDesc* desc = m_kernelCtx.getPluginManager().getPluginObjectDescCreating(id);
  2664. //if a visualization box was renamed, tell window manager about it
  2665. if (desc && desc->hasFunctionality(Plugins::EPluginFunctionality::Visualization))
  2666. {
  2667. if (m_DesignerVisualization) { m_DesignerVisualization->onVisualizationBoxRenamed(box.getIdentifier()); }
  2668. }
  2669. this->snapshotCB();
  2670. }
  2671. }
  2672. void CInterfacedScenario::contextMenuBoxRenameAllCB()
  2673. {
  2674. //we find all selected boxes
  2675. std::map<CIdentifier, CIdentifier> selectedBoxes; // map(object,class)
  2676. for (auto& id : m_SelectedObjects) { if (m_Scenario.isBox(id)) { selectedBoxes[id] = m_Scenario.getBoxDetails(id)->getAlgorithmClassIdentifier(); } }
  2677. if (!selectedBoxes.empty())
  2678. {
  2679. bool dialogOk = true;
  2680. bool firstBox = true;
  2681. CString newName = "";
  2682. for (auto it = selectedBoxes.begin(); it != selectedBoxes.end() && dialogOk; ++it)
  2683. {
  2684. if (it->second != CIdentifier::undefined())
  2685. {
  2686. if (m_kernelCtx.getPluginManager().canCreatePluginObject(it->second) || it->second == OVP_ClassId_BoxAlgorithm_Metabox)
  2687. {
  2688. Kernel::IBox* box = m_Scenario.getBoxDetails(it->first);
  2689. if (firstBox)
  2690. {
  2691. firstBox = false;
  2692. const Plugins::IPluginObjectDesc* pod = m_kernelCtx.getPluginManager().getPluginObjectDescCreating(box->getAlgorithmClassIdentifier());
  2693. if (box->getAlgorithmClassIdentifier() == OVP_ClassId_BoxAlgorithm_Metabox)
  2694. {
  2695. CIdentifier metaboxId;
  2696. metaboxId.fromString(box->getAttributeValue(OVP_AttributeId_Metabox_ID));
  2697. pod = m_kernelCtx.getMetaboxManager().getMetaboxObjectDesc(metaboxId);
  2698. }
  2699. CRenameDialog rename(m_kernelCtx, box->getName(), pod ? pod->getName() : box->getName(), m_guiFilename.c_str());
  2700. if (rename.run()) { newName = rename.getResult(); }
  2701. else
  2702. {
  2703. // no rename at all.
  2704. dialogOk = false;
  2705. }
  2706. }
  2707. if (dialogOk)
  2708. {
  2709. box->setName(newName);
  2710. //check whether it is a visualization box
  2711. CIdentifier id = box->getAlgorithmClassIdentifier();
  2712. const Plugins::IPluginObjectDesc* pod = m_kernelCtx.getPluginManager().getPluginObjectDescCreating(id);
  2713. //if a visualization box was renamed, tell window manager about it
  2714. if (pod && pod->hasFunctionality(Plugins::EPluginFunctionality::Visualization))
  2715. {
  2716. if (m_DesignerVisualization) { m_DesignerVisualization->onVisualizationBoxRenamed(box->getIdentifier()); }
  2717. }
  2718. }
  2719. }
  2720. }
  2721. }
  2722. if (dialogOk) { this->snapshotCB(); }
  2723. }
  2724. }
  2725. void CInterfacedScenario::contextMenuBoxToggleEnableAllCB()
  2726. {
  2727. //we find all selected boxes
  2728. for (const auto& objectId : m_SelectedObjects)
  2729. {
  2730. if (m_Scenario.isBox(objectId))
  2731. {
  2732. TAttributeHandler handler(*m_Scenario.getBoxDetails(objectId));
  2733. if (handler.hasAttribute(OV_AttributeId_Box_Disabled)) { handler.removeAttribute(OV_AttributeId_Box_Disabled); }
  2734. else { handler.addAttribute(OV_AttributeId_Box_Disabled, 1); }
  2735. }
  2736. }
  2737. this->snapshotCB();
  2738. }
  2739. void CInterfacedScenario::contextMenuBoxEnableAllCB()
  2740. {
  2741. //we find all selected boxes
  2742. for (const auto& objectId : m_SelectedObjects)
  2743. {
  2744. if (m_Scenario.isBox(objectId))
  2745. {
  2746. TAttributeHandler handler(*m_Scenario.getBoxDetails(objectId));
  2747. if (handler.hasAttribute(OV_AttributeId_Box_Disabled)) { handler.removeAttribute(OV_AttributeId_Box_Disabled); }
  2748. }
  2749. }
  2750. this->snapshotCB();
  2751. }
  2752. void CInterfacedScenario::contextMenuBoxDisableAllCB()
  2753. {
  2754. //we find all selected boxes
  2755. for (const auto& objectId : m_SelectedObjects)
  2756. {
  2757. if (m_Scenario.isBox(objectId))
  2758. {
  2759. TAttributeHandler handler(*m_Scenario.getBoxDetails(objectId));
  2760. if (!handler.hasAttribute(OV_AttributeId_Box_Disabled)) { handler.addAttribute(OV_AttributeId_Box_Disabled, 1); }
  2761. }
  2762. }
  2763. this->snapshotCB();
  2764. }
  2765. void CInterfacedScenario::contextMenuBoxAddInputCB(Kernel::IBox& box)
  2766. {
  2767. if (box.hasAttribute(OV_AttributeId_Box_PendingDeprecatedInterfacors))
  2768. {
  2769. gtk_dialog_run(GTK_DIALOG(m_errorPendingDeprecatedInterfacorsDialog));
  2770. return;
  2771. }
  2772. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuBoxAddInputCB\n";
  2773. box.addInput("New input", OV_TypeId_EBMLStream, m_Scenario.getUnusedInputIdentifier());
  2774. if (box.hasAttribute(OV_AttributeId_Box_FlagCanModifyInput))
  2775. {
  2776. CConnectorEditor editor(m_kernelCtx, box, Box_Input, box.getInputCount() - 1, "Add Input", m_guiFilename.c_str());
  2777. if (editor.run()) { this->snapshotCB(); }
  2778. else { box.removeInput(box.getInputCount() - 1); }
  2779. }
  2780. else { this->snapshotCB(); }
  2781. }
  2782. void CInterfacedScenario::contextMenuBoxEditInputCB(Kernel::IBox& box, const size_t index)
  2783. {
  2784. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuBoxEditInputCB\n";
  2785. CConnectorEditor editor(m_kernelCtx, box, Box_Input, index, "Edit Input", m_guiFilename.c_str());
  2786. if (editor.run()) { this->snapshotCB(); }
  2787. }
  2788. void CInterfacedScenario::contextMenuBoxRemoveInputCB(Kernel::IBox& box, const size_t index)
  2789. {
  2790. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuBoxRemoveInputCB\n";
  2791. box.removeInput(index);
  2792. this->snapshotCB();
  2793. }
  2794. void CInterfacedScenario::contextMenuBoxAddOutputCB(Kernel::IBox& box)
  2795. {
  2796. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuBoxAddOutputCB\n";
  2797. box.addOutput("New output", OV_TypeId_EBMLStream, m_Scenario.getUnusedOutputIdentifier());
  2798. if (box.hasAttribute(OV_AttributeId_Box_FlagCanModifyOutput))
  2799. {
  2800. CConnectorEditor editor(m_kernelCtx, box, Box_Output, box.getOutputCount() - 1, "Add Output", m_guiFilename.c_str());
  2801. if (editor.run()) { this->snapshotCB(); }
  2802. else { box.removeOutput(box.getOutputCount() - 1); }
  2803. }
  2804. else { this->snapshotCB(); }
  2805. }
  2806. void CInterfacedScenario::contextMenuBoxEditOutputCB(Kernel::IBox& box, const size_t index)
  2807. {
  2808. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuBoxEditOutputCB\n";
  2809. CConnectorEditor editor(m_kernelCtx, box, Box_Output, index, "Edit Output", m_guiFilename.c_str());
  2810. if (editor.run()) { this->snapshotCB(); }
  2811. }
  2812. void CInterfacedScenario::contextMenuBoxRemoveOutputCB(Kernel::IBox& box, const size_t index)
  2813. {
  2814. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuBoxRemoveOutputCB\n";
  2815. box.removeOutput(index);
  2816. this->snapshotCB();
  2817. }
  2818. void CInterfacedScenario::contextMenuBoxConnectScenarioInputCB(Kernel::IBox& box, const size_t boxInputIdx, const size_t scenarioInputIdx)
  2819. {
  2820. // m_kernelCtx.getLogManager() << Kernel::LogLevel_Info << "contextMenuBoxConnectScenarioInputCB : box = " << box.getIdentifier().str() << " box input = " << boxInputIdx << " , scenario input = " << scenarioInputIdx << "\n";
  2821. m_Scenario.setScenarioInputLink(scenarioInputIdx, box.getIdentifier(), boxInputIdx);
  2822. this->snapshotCB();
  2823. }
  2824. void CInterfacedScenario::contextMenuBoxConnectScenarioOutputCB(Kernel::IBox& box, const size_t boxOutputIdx, const size_t scenarioOutputIdx)
  2825. {
  2826. // m_kernelCtx.getLogManager() << Kernel::LogLevel_Info << "contextMenuBoxConnectScenarioOutputCB : box = " << box.getIdentifier().str() << " box Output = " << boxOutputIdx << " , scenario Output = " << scenarioOutputIdx << "\n";
  2827. m_Scenario.setScenarioOutputLink(scenarioOutputIdx, box.getIdentifier(), boxOutputIdx);
  2828. this->snapshotCB();
  2829. }
  2830. // Note: In current implementation only the scenarioInputIdx is necessary as it can only be connected to one input
  2831. // but to keep things simpler we give it all the info
  2832. void CInterfacedScenario::contextMenuBoxDisconnectScenarioInputCB(Kernel::IBox& box, const size_t boxInputIdx, const size_t scenarioInputIdx)
  2833. {
  2834. m_Scenario.removeScenarioInputLink(scenarioInputIdx, box.getIdentifier(), boxInputIdx);
  2835. this->snapshotCB();
  2836. }
  2837. // Note: In current implementation only the scenarioOutputIdx is necessary as it can only be connected to one output
  2838. // but to keep things simpler we give it all the info
  2839. void CInterfacedScenario::contextMenuBoxDisconnectScenarioOutputCB(Kernel::IBox& box, const size_t boxOutputIdx, const size_t scenarioOutputIdx)
  2840. {
  2841. m_Scenario.removeScenarioOutputLink(scenarioOutputIdx, box.getIdentifier(), boxOutputIdx);
  2842. this->snapshotCB();
  2843. }
  2844. void CInterfacedScenario::contextMenuBoxAddSettingCB(Kernel::IBox& box)
  2845. {
  2846. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuBoxAddSettingCB\n";
  2847. // Store setting count in case the custom "onSettingAdded" of the box adds more than one setting
  2848. const size_t nOldSettings = box.getSettingCount();
  2849. box.addSetting("New setting", CIdentifier::undefined(), "", size_t(-1), false, m_Scenario.getUnusedSettingIdentifier(CIdentifier::undefined()));
  2850. const size_t nNewSettings = box.getSettingCount();
  2851. // Check that at least one setting was added
  2852. if (nNewSettings > nOldSettings && box.hasAttribute(OV_AttributeId_Box_FlagCanModifySetting))
  2853. {
  2854. CSettingEditorDialog dialog(m_kernelCtx, box, nOldSettings, "Add Setting", m_guiFilename.c_str(), m_guiSettingsFilename.c_str());
  2855. if (dialog.run()) { this->snapshotCB(); }
  2856. else { for (size_t i = nOldSettings; i < nNewSettings; ++i) { box.removeSetting(i); } }
  2857. }
  2858. else
  2859. {
  2860. if (nNewSettings > nOldSettings) { this->snapshotCB(); }
  2861. else
  2862. {
  2863. m_kernelCtx.getLogManager() << Kernel::LogLevel_Error << "No setting could be added to the box.\n";
  2864. return;
  2865. }
  2866. }
  2867. // Add an information message to inform the user about the new settings
  2868. m_kernelCtx.getLogManager() << Kernel::LogLevel_Info << "[" << nNewSettings - nOldSettings
  2869. << "] new setting(s) was(were) added to the box [" << box.getName().toASCIIString() << "]: ";
  2870. for (size_t i = nOldSettings; i < nNewSettings; ++i)
  2871. {
  2872. CString name;
  2873. box.getSettingName(i, name);
  2874. m_kernelCtx.getLogManager() << "[" << name << "] ";
  2875. }
  2876. m_kernelCtx.getLogManager() << "\n";
  2877. // After adding setting, open configuration so that the user can see the effects.
  2878. CBoxConfigurationDialog dialog(m_kernelCtx, box, m_guiFilename.c_str(), m_guiSettingsFilename.c_str());
  2879. dialog.run();
  2880. }
  2881. void CInterfacedScenario::contextMenuBoxEditSettingCB(Kernel::IBox& box, const size_t index)
  2882. {
  2883. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuBoxEditSettingCB\n";
  2884. CSettingEditorDialog dialog(m_kernelCtx, box, index, "Edit Setting", m_guiFilename.c_str(), m_guiSettingsFilename.c_str());
  2885. if (dialog.run()) { this->snapshotCB(); }
  2886. }
  2887. void CInterfacedScenario::contextMenuBoxRemoveSettingCB(Kernel::IBox& box, const size_t index)
  2888. {
  2889. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuBoxRemoveSettingCB\n";
  2890. const size_t nOldSettings = box.getSettingCount();
  2891. if (box.removeSetting(index))
  2892. {
  2893. const size_t nNewSettings = box.getSettingCount();
  2894. this->snapshotCB();
  2895. m_kernelCtx.getLogManager() << Kernel::LogLevel_Info << "[" << nOldSettings - nNewSettings
  2896. << "] setting(s) was(were) removed from box [" << box.getName() << "] \n";
  2897. }
  2898. else
  2899. {
  2900. m_kernelCtx.getLogManager() << Kernel::LogLevel_Error << "The setting with index [" << index
  2901. << "] could not be removed from box [" << box.getName() << "] \n";
  2902. }
  2903. }
  2904. void CInterfacedScenario::contextMenuBoxConfigureCB(Kernel::IBox& box)
  2905. {
  2906. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuBoxConfigureCB\n";
  2907. CBoxConfigurationDialog dialog(m_kernelCtx, box, m_guiFilename.c_str(), m_guiSettingsFilename.c_str());
  2908. dialog.run();
  2909. this->snapshotCB();
  2910. }
  2911. void CInterfacedScenario::contextMenuBoxAboutCB(Kernel::IBox& box) const
  2912. {
  2913. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuBoxAboutCB\n";
  2914. if (box.getAlgorithmClassIdentifier() != OVP_ClassId_BoxAlgorithm_Metabox)
  2915. {
  2916. CAboutPluginDialog dialog(m_kernelCtx, box.getAlgorithmClassIdentifier(), m_guiFilename.c_str());
  2917. dialog.run();
  2918. }
  2919. else
  2920. {
  2921. CIdentifier id;
  2922. id.fromString(box.getAttributeValue(OVP_AttributeId_Metabox_ID));
  2923. CAboutPluginDialog dialog(m_kernelCtx, m_kernelCtx.getMetaboxManager().getMetaboxObjectDesc(id), m_guiFilename.c_str());
  2924. dialog.run();
  2925. }
  2926. }
  2927. void CInterfacedScenario::contextMenuBoxEditMetaboxCB(Kernel::IBox& box) const
  2928. {
  2929. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuBoxEditMetaboxCB\n";
  2930. CIdentifier id;
  2931. id.fromString(box.getAttributeValue(OVP_AttributeId_Metabox_ID));
  2932. const CString path(m_kernelCtx.getMetaboxManager().getMetaboxFilePath(id));
  2933. m_Application.openScenario(path.toASCIIString());
  2934. }
  2935. bool CInterfacedScenario::browseURL(const CString& url, const CString& browserPrefix, const CString& browserPostfix) const
  2936. {
  2937. m_kernelCtx.getLogManager() << Kernel::LogLevel_Trace << "Requesting web browser on URL " << url << "\n";
  2938. const CString cmd = browserPrefix + CString(" \"") + url + CString("\"") + browserPostfix;
  2939. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "Launching [" << cmd << "]\n";
  2940. const int result = system(cmd.toASCIIString());
  2941. if (result < 0)
  2942. {
  2943. OV_WARNING("Could not launch command [" << cmd << "]\n", m_kernelCtx.getLogManager());
  2944. return false;
  2945. }
  2946. return true;
  2947. }
  2948. bool CInterfacedScenario::browseBoxDocumentation(const CIdentifier& boxID) const
  2949. {
  2950. const CIdentifier algorithmClassID = m_Scenario.getBoxDetails(boxID)->getAlgorithmClassIdentifier();
  2951. // Do not show documentation for non-metaboxes or boxes that can not be created
  2952. if (!(boxID != CIdentifier::undefined() && (m_kernelCtx.getPluginManager().canCreatePluginObject(algorithmClassID) ||
  2953. m_Scenario.getBoxDetails(boxID)->getAlgorithmClassIdentifier() == OVP_ClassId_BoxAlgorithm_Metabox)))
  2954. {
  2955. m_kernelCtx.getLogManager() << Kernel::LogLevel_Warning << "Box with id " << boxID << " can not create a pluging object\n";
  2956. return false;
  2957. }
  2958. const CString defaultURLBase = m_kernelCtx.getConfigurationManager().expand("${Designer_HelpBrowserURLBase}");
  2959. CString urlBase = defaultURLBase;
  2960. CString browser = m_kernelCtx.getConfigurationManager().expand("${Designer_HelpBrowserCommand}");
  2961. CString browserPostfix = m_kernelCtx.getConfigurationManager().expand("${Designer_HelpBrowserCommandPostfix}");
  2962. CString boxName;
  2963. CString html = "Doc_BoxAlgorithm_";
  2964. if (m_Scenario.getBoxDetails(boxID)->getAlgorithmClassIdentifier() == OVP_ClassId_BoxAlgorithm_Metabox)
  2965. {
  2966. CIdentifier id;
  2967. id.fromString(m_Scenario.getBoxDetails(boxID)->getAttributeValue(OVP_AttributeId_Metabox_ID));
  2968. boxName = m_kernelCtx.getMetaboxManager().getMetaboxObjectDesc(id)->getName();
  2969. }
  2970. else
  2971. {
  2972. const Plugins::IPluginObjectDesc* pod = m_kernelCtx.getPluginManager().getPluginObjectDescCreating(algorithmClassID);
  2973. boxName = pod->getName();
  2974. }
  2975. // The documentation files do not have spaces in their name, so we remove them
  2976. html = html + CString(getBoxAlgorithmURL(boxName.toASCIIString()).c_str());
  2977. if (m_Scenario.getBoxDetails(boxID)->hasAttribute(OV_AttributeId_Box_DocumentationURLBase))
  2978. {
  2979. urlBase = m_kernelCtx.getConfigurationManager().expand(m_Scenario.getBoxDetails(boxID)->getAttributeValue(OV_AttributeId_Box_DocumentationURLBase));
  2980. }
  2981. html = html + ".html";
  2982. if (m_Scenario.getBoxDetails(boxID)->hasAttribute(OV_AttributeId_Box_DocumentationCommand))
  2983. {
  2984. browser = m_kernelCtx.getConfigurationManager().expand(m_Scenario.getBoxDetails(boxID)->getAttributeValue(OV_AttributeId_Box_DocumentationCommand));
  2985. browserPostfix = "";
  2986. }
  2987. CString fullUrl = urlBase + CString("/") + html;
  2988. if (m_Scenario.getBoxDetails(boxID)->hasAttribute(OV_AttributeId_Box_DocumentationURL))
  2989. {
  2990. fullUrl = m_kernelCtx.getConfigurationManager().expand(m_Scenario.getBoxDetails(boxID)->getAttributeValue(OV_AttributeId_Box_DocumentationURL));
  2991. }
  2992. return browseURL(fullUrl, browser, browserPostfix);
  2993. }
  2994. void CInterfacedScenario::contextMenuBoxDocumentationCB(Kernel::IBox& box) const
  2995. {
  2996. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuBoxDocumentationCB\n";
  2997. const CIdentifier id = box.getIdentifier();
  2998. browseBoxDocumentation(id);
  2999. }
  3000. void CInterfacedScenario::contextMenuBoxEnableCB(Kernel::IBox& box)
  3001. {
  3002. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuBoxEnableCB\n";
  3003. TAttributeHandler handler(box);
  3004. handler.removeAttribute(OV_AttributeId_Box_Disabled);
  3005. this->snapshotCB();
  3006. }
  3007. void CInterfacedScenario::contextMenuBoxDisableCB(Kernel::IBox& box)
  3008. {
  3009. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuBoxDisableCB\n";
  3010. TAttributeHandler handler(box);
  3011. if (!handler.hasAttribute(OV_AttributeId_Box_Disabled)) { handler.addAttribute(OV_AttributeId_Box_Disabled, 1); }
  3012. else { handler.setAttributeValue(OV_AttributeId_Box_Disabled, 1); }
  3013. this->snapshotCB();
  3014. }
  3015. void CInterfacedScenario::contextMenuScenarioAddCommentCB()
  3016. {
  3017. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuScenarioAddCommentCB\n";
  3018. this->addCommentCB();
  3019. }
  3020. void CInterfacedScenario::contextMenuScenarioAboutCB()
  3021. {
  3022. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "contextMenuScenarioAboutCB\n";
  3023. CAboutScenarioDialog dialog(m_kernelCtx, m_Scenario, m_guiFilename.c_str());
  3024. dialog.run();
  3025. this->snapshotCB();
  3026. }
  3027. void CInterfacedScenario::toggleDesignerVisualization()
  3028. {
  3029. m_designerVisualizationToggled = !m_designerVisualizationToggled;
  3030. if (m_DesignerVisualization)
  3031. {
  3032. if (m_designerVisualizationToggled) { m_DesignerVisualization->show(); }
  3033. else { m_DesignerVisualization->hide(); }
  3034. }
  3035. }
  3036. void CInterfacedScenario::showCurrentVisualization() const
  3037. {
  3038. if (isLocked()) { if (m_playerVisualization != nullptr) { m_playerVisualization->showTopLevelWindows(); } }
  3039. else { if (m_DesignerVisualization != nullptr) { m_DesignerVisualization->show(); } }
  3040. }
  3041. void CInterfacedScenario::hideCurrentVisualization() const
  3042. {
  3043. if (isLocked()) { if (m_playerVisualization != nullptr) { m_playerVisualization->hideTopLevelWindows(); } }
  3044. else { if (m_DesignerVisualization != nullptr) { m_DesignerVisualization->hide(); } }
  3045. }
  3046. void CInterfacedScenario::createPlayerVisualization(VisualizationToolkit::IVisualizationTree* tree)
  3047. {
  3048. //hide window manager
  3049. if (m_DesignerVisualization) { m_DesignerVisualization->hide(); }
  3050. if (m_playerVisualization == nullptr)
  3051. {
  3052. if (tree) { m_playerVisualization = new CPlayerVisualization(m_kernelCtx, *tree, *this); }
  3053. else { m_playerVisualization = new CPlayerVisualization(m_kernelCtx, *m_Tree, *this); }
  3054. //we go here when we press start
  3055. //we have to set the modUI here
  3056. //first, find the concerned boxes
  3057. Kernel::IScenario& runtimeScenario = m_Player->getRuntimeScenarioManager().getScenario(m_Player->getRuntimeScenarioIdentifier());
  3058. CIdentifier id;
  3059. while ((id = runtimeScenario.getNextBoxIdentifier(id)) != CIdentifier::undefined())
  3060. {
  3061. Kernel::IBox* box = runtimeScenario.getBoxDetails(id);
  3062. if (box->hasModifiableSettings())//if the box has modUI
  3063. {
  3064. //create a BoxConfigurationDialog in mode true
  3065. auto* dialog = new CBoxConfigurationDialog(m_kernelCtx, *box, m_guiFilename.c_str(), m_guiSettingsFilename.c_str(), true);
  3066. //store it
  3067. m_boxConfigDialogs.push_back(dialog);
  3068. }
  3069. }
  3070. }
  3071. //initialize and show windows
  3072. m_playerVisualization->init();
  3073. }
  3074. void CInterfacedScenario::releasePlayerVisualization()
  3075. {
  3076. if (m_playerVisualization != nullptr)
  3077. {
  3078. delete m_playerVisualization;
  3079. m_playerVisualization = nullptr;
  3080. }
  3081. //reload designer visualization
  3082. if (m_DesignerVisualization)
  3083. {
  3084. m_DesignerVisualization->load();
  3085. //show it if it was toggled on
  3086. if (m_designerVisualizationToggled) { m_DesignerVisualization->show(); }
  3087. }
  3088. }
  3089. void CInterfacedScenario::stopAndReleasePlayer()
  3090. {
  3091. m_kernelCtx.getErrorManager().releaseErrors();
  3092. m_Player->stop();
  3093. m_PlayerStatus = m_Player->getStatus();
  3094. // removes idle function
  3095. g_idle_remove_by_data(this);
  3096. if (!m_Player->uninitialize()) { m_kernelCtx.getLogManager() << Kernel::LogLevel_Error << "Failed to uninitialize the player" << "\n"; }
  3097. for (auto elem : m_boxConfigDialogs)
  3098. {
  3099. elem->restoreState();
  3100. delete elem;
  3101. }
  3102. m_boxConfigDialogs.clear();
  3103. if (!m_kernelCtx.getPlayerManager().releasePlayer(m_PlayerID))
  3104. {
  3105. m_kernelCtx.getLogManager() << Kernel::LogLevel_Error << "Failed to release the player" << "\n";
  3106. }
  3107. m_PlayerID = CIdentifier::undefined();
  3108. m_Player = nullptr;
  3109. // destroy player windows
  3110. releasePlayerVisualization();
  3111. // redraws scenario
  3112. redraw();
  3113. }
  3114. //give the PlayerVisualisation the matching between the GtkWidget created by the CBoxConfigurationDialog and the Box CIdentifier
  3115. bool CInterfacedScenario::setModifiableSettingsWidgets()
  3116. {
  3117. for (auto& elem : m_boxConfigDialogs) { m_playerVisualization->setWidget(elem->getBoxID(), elem->getWidget()); }
  3118. return true;
  3119. }
  3120. bool CInterfacedScenario::centerOnBox(const CIdentifier& identifier)
  3121. {
  3122. //m_kernelCtx.getLogManager() << Kernel::LogLevel_Fatal << "CInterfacedScenario::centerOnBox" << "\n";
  3123. bool res = false;
  3124. if (m_Scenario.isBox(identifier))
  3125. {
  3126. //m_kernelCtx.getLogManager() << Kernel::LogLevel_Fatal << "CInterfacedScenario::centerOnBox is box" << "\n";
  3127. Kernel::IBox* box = m_Scenario.getBoxDetails(identifier);
  3128. //clear previous selection
  3129. m_SelectedObjects.clear();
  3130. //to select the box
  3131. m_SelectedObjects.insert(identifier);
  3132. // m_bScenarioModified=true;
  3133. redraw();
  3134. //CBoxProxy proxy(m_kernelCtx, *box);
  3135. const CBoxProxy proxy(m_kernelCtx, m_Scenario, box->getIdentifier());
  3136. const double marginX = 5.0 * m_currentScale;
  3137. const double merginY = 5.0 * m_currentScale;
  3138. const int sizeX = int(round(proxy.getWidth(GTK_WIDGET(m_scenarioDrawingArea)) + marginX * 2.0));
  3139. const int sizeY = int(round(proxy.getHeight(GTK_WIDGET(m_scenarioDrawingArea)) + merginY * 2.0));
  3140. const double centerX = proxy.getXCenter() * m_currentScale;
  3141. const double centerY = proxy.getYCenter() * m_currentScale;
  3142. int x, y;
  3143. //get the parameters of the current adjustement
  3144. GtkAdjustment* oldAdjustmentH = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(m_notebookPageContent));
  3145. GtkAdjustment* oldAdjustmentV = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(m_notebookPageContent));
  3146. gdouble upper, lower, step, page, pagesize, value;
  3147. g_object_get(oldAdjustmentH, "upper", &upper, "lower", &lower, "step-increment", &step, "page-increment", &page, "page-size", &pagesize, "value",
  3148. &value, nullptr);
  3149. //create a new adjustement with the correct value since we can not change the upper bound of the old adjustement
  3150. auto* adjustment = reinterpret_cast<GtkAdjustment*>(gtk_adjustment_new(value, lower, upper, step, page, pagesize));
  3151. if (centerX + m_viewOffsetX < upper / 2) { x = int(round(centerX - 2 * sizeX)) + m_viewOffsetX; }
  3152. else { x = int(round(centerX + 2 * sizeX - pagesize)) + m_viewOffsetX; }
  3153. gtk_adjustment_set_value(adjustment, x);
  3154. gtk_scrolled_window_set_hadjustment(GTK_SCROLLED_WINDOW(m_notebookPageContent), adjustment);
  3155. g_object_get(oldAdjustmentV, "upper", &upper, "lower", &lower, "step-increment", &step, "page-increment", &page, "page-size", &pagesize, "value",
  3156. &value, nullptr);
  3157. adjustment = reinterpret_cast<GtkAdjustment*>(gtk_adjustment_new(value, lower, upper, step, page, pagesize));
  3158. if (centerY - m_viewOffsetY < upper / 2) { y = int(round(centerY - 2 * sizeY) + m_viewOffsetY); }
  3159. else { y = int(round(centerY + 2 * sizeY - pagesize)) + m_viewOffsetY; }
  3160. gtk_adjustment_set_value(adjustment, y);
  3161. gtk_scrolled_window_set_vadjustment(GTK_SCROLLED_WINDOW(m_notebookPageContent), adjustment);
  3162. res = true;
  3163. }
  3164. return res;
  3165. }
  3166. void CInterfacedScenario::setScale(const double scale)
  3167. {
  3168. m_currentScale = std::max(scale, 0.1);
  3169. PangoContext* ctx = gtk_widget_get_pango_context(GTK_WIDGET(m_scenarioDrawingArea));
  3170. PangoFontDescription* desc = pango_context_get_font_description(ctx);
  3171. // not done in constructor because the font size is changed elsewhere after that withour our knowledge
  3172. if (m_normalFontSize == 0) { m_normalFontSize = pango_font_description_get_size(desc); }
  3173. pango_font_description_set_size(desc, gint(round(m_normalFontSize * m_currentScale)));
  3174. //m_scenarioModified = true;
  3175. redraw();
  3176. }
  3177. } // namespace Designer
  3178. } // namespace OpenViBE