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.

ovdCDesignerVisualization.cpp 66KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752
  1. #include "ovd_base.h"
  2. #include "ovdCDesignerVisualization.h"
  3. #include "ovdCInterfacedScenario.h"
  4. #include "ovdCInputDialog.h"
  5. #include <gdk/gdkkeysyms.h>
  6. #include <cstring>
  7. namespace OpenViBE {
  8. namespace Designer {
  9. using EVTColumn = VisualizationToolkit::EVisualizationTreeColumn;
  10. using EVTNode = VisualizationToolkit::EVisualizationTreeNode;
  11. using EVTWidget = VisualizationToolkit::EVisualizationWidget;
  12. using IVTWidget = VisualizationToolkit::IVisualizationWidget;
  13. static const GtkTargetEntry TARGETS[] = { { static_cast<gchar*>("STRING"), 0, 0 }, { static_cast<gchar*>("text/plain"), 0, 0 } };
  14. /**
  15. * \brief Display an error dialog
  16. * \param[in] text text to display in the dialog
  17. * \param[in] secondaryText additional text to display in the dialog
  18. */
  19. void displayErrorDialog(const char* text, const char* secondaryText)
  20. {
  21. GtkWidget* dialog = gtk_message_dialog_new(nullptr, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "%s", text);
  22. gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s", secondaryText);
  23. gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
  24. gtk_dialog_run(GTK_DIALOG(dialog));
  25. gtk_widget_destroy(dialog);
  26. }
  27. /**
  28. * \brief Helper function retrieving a child in a table from its attach indices
  29. * \param table Table parent to the child to be retrieved
  30. * \param leftAttach Left attach index
  31. * \param rightAttach Right attach index
  32. * \param topAttach Top attach index
  33. * \param bottomAttach Bottom attach index
  34. * \return Pointer to table child if one was found, nullptr otherwise
  35. */
  36. GtkTableChild* getTableChild(GtkTable* table, const int leftAttach, const int rightAttach, const int topAttach, const int bottomAttach)
  37. {
  38. GList* list = table->children;
  39. do
  40. {
  41. GtkTableChild* pTC = static_cast<GtkTableChild*>(list->data);
  42. if (pTC->left_attach == leftAttach && pTC->right_attach == rightAttach &&
  43. pTC->top_attach == topAttach && pTC->bottom_attach == bottomAttach) { return pTC; }
  44. list = list->next;
  45. } while (list);
  46. return nullptr;
  47. }
  48. /**
  49. * \brief Display a yes/no question dialog
  50. * \param[in] pText text to display in the dialog
  51. * \param[in] pSecondaryText additional text to display in the dialog
  52. * \return identifier of the button pressed
  53. */
  54. /*
  55. gint displayQuestionDialog(const char* pText, const char* pSecondaryText)
  56. {
  57. ::GtkWidget* dialog = gtk_message_dialog_new(nullptr, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, pText);
  58. gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), pSecondaryText);
  59. gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
  60. gint ret = gtk_dialog_run(GTK_DIALOG(dialog));
  61. gtk_widget_destroy(dialog);
  62. return ret;
  63. }*/
  64. //Menus
  65. //-----
  66. static char* cNewWin = const_cast<char*>("/New window");
  67. static char* cNewTab = const_cast<char*>("/New tab");
  68. static char* cRename = const_cast<char*>("/Rename");
  69. static char* cRemove = const_cast<char*>("/Remove");
  70. static char* cStockI = const_cast<char*>("<StockItem>");
  71. static char* cBlank = const_cast<char*>("");
  72. static GtkItemFactoryEntry unaffectedItems[] = {
  73. { cNewWin, cBlank, GtkItemFactoryCallback(CDesignerVisualization::askNewVisualizationWindowCB), 1, cStockI, GTK_STOCK_DND_MULTIPLE }
  74. };
  75. static GtkItemFactoryEntry visualizationWindowItems[] = {
  76. { cNewTab, cBlank, GtkItemFactoryCallback(CDesignerVisualization::askNewVisualizationPanelCB), 1, cStockI, GTK_STOCK_DND },
  77. { cRename, cBlank, GtkItemFactoryCallback(CDesignerVisualization::askRenameVisualizationWindowCB), 1, cStockI, GTK_STOCK_BOLD },
  78. { cRemove, cBlank, GtkItemFactoryCallback(CDesignerVisualization::removeVisualizationWindowCB), 1, cStockI, GTK_STOCK_DELETE }
  79. };
  80. static GtkItemFactoryEntry visualizationPanelItems[] = {
  81. { cRename, cBlank, GtkItemFactoryCallback(CDesignerVisualization::askRenameVisualizationPanelCB), 1, cStockI, GTK_STOCK_BOLD },
  82. { cRemove, cBlank, GtkItemFactoryCallback(CDesignerVisualization::removeVisualizationPanelCB), 1, cStockI, GTK_STOCK_DELETE }
  83. };
  84. static GtkItemFactoryEntry visualizationBoxItems[] = {
  85. { cRemove, cBlank, GtkItemFactoryCallback(CDesignerVisualization::removeVisualizationWidgetCB), 1, cStockI, GTK_STOCK_DELETE }
  86. };
  87. static GtkItemFactoryEntry undefinedWidgetItems[] = {
  88. { cRemove, cBlank, GtkItemFactoryCallback(CDesignerVisualization::removeVisualizationWidgetCB), 1, cStockI, GTK_STOCK_DELETE }
  89. };
  90. static GtkItemFactoryEntry splitWidgetItems[] = {
  91. { cRemove, cBlank, GtkItemFactoryCallback(CDesignerVisualization::removeVisualizationWidgetCB), 1, cStockI, GTK_STOCK_DELETE }
  92. };
  93. static const gint N_UNAFFECTED_ITEMS = sizeof(unaffectedItems) / sizeof(unaffectedItems[0]);
  94. static const gint N_VISUALIZATION_WINDOW_ITEMS = sizeof(visualizationWindowItems) / sizeof(visualizationWindowItems[0]);
  95. static const gint N_VISUALIZATION_PANEL_ITEMS = sizeof(visualizationPanelItems) / sizeof(visualizationPanelItems[0]);
  96. static const gint N_VISUALIZATION_BOX_ITEMS = sizeof(visualizationBoxItems) / sizeof(visualizationBoxItems[0]);
  97. static const gint N_UNDEFINED_WIDGET_ITEMS = sizeof(undefinedWidgetItems) / sizeof(undefinedWidgetItems[0]);
  98. static const gint N_SPLIT_WIDGET_ITEMS = sizeof(splitWidgetItems) / sizeof(splitWidgetItems[0]);
  99. CDesignerVisualization::~CDesignerVisualization()
  100. {
  101. g_signal_handlers_disconnect_by_func(G_OBJECT(m_dialog), G_CALLBACK2(configureEventCB), this);
  102. #ifdef HANDLE_MIN_MAX_EVENTS
  103. g_signal_handlers_disconnect_by_func(G_OBJECT(m_dialog), G_CALLBACK2(window_state_event_cb), this);
  104. #endif
  105. gtk_widget_destroy(m_dialog);
  106. m_tree.setTreeViewCB(nullptr);
  107. }
  108. void CDesignerVisualization::init(const std::string& guiFile)
  109. {
  110. m_guiFile = guiFile;
  111. //create tree view
  112. //----------------
  113. //register towards tree store
  114. m_tree.setTreeViewCB(this);
  115. m_treeView = m_tree.createTreeViewWithModel();
  116. GtkTreeViewColumn* treeViewColumnName = gtk_tree_view_column_new();
  117. GtkCellRenderer* cellRendererIcon = gtk_cell_renderer_pixbuf_new();
  118. GtkCellRenderer* cellRendererName = gtk_cell_renderer_text_new();
  119. gtk_tree_view_column_set_title(treeViewColumnName, "Windows for current scenario");
  120. gtk_tree_view_column_pack_start(treeViewColumnName, cellRendererIcon, FALSE);
  121. gtk_tree_view_column_pack_start(treeViewColumnName, cellRendererName, TRUE);
  122. gtk_tree_view_column_set_attributes(treeViewColumnName, cellRendererIcon, "stock-id", EVTColumn::StringStockIcon, nullptr);
  123. gtk_tree_view_column_set_attributes(treeViewColumnName, cellRendererName, "text", EVTColumn::StringName, nullptr);
  124. //gtk_tree_view_column_set_sizing(treeViewColumnName, GTK_TREE_VIEW_COLUMN_FIXED);
  125. gtk_tree_view_column_set_expand(treeViewColumnName, TRUE/*FALSE*/);
  126. gtk_tree_view_column_set_resizable(treeViewColumnName, TRUE);
  127. gtk_tree_view_column_set_min_width(treeViewColumnName, 64);
  128. gtk_tree_view_append_column(m_treeView, treeViewColumnName);
  129. GtkTreeViewColumn* desc = gtk_tree_view_column_new();
  130. gtk_tree_view_append_column(m_treeView, desc);
  131. gtk_tree_view_column_set_visible(desc, 0);
  132. gtk_tree_view_columns_autosize(GTK_TREE_VIEW(m_treeView));
  133. //allow tree items to be dragged
  134. gtk_drag_source_set(GTK_WIDGET(m_treeView), GDK_BUTTON1_MASK, TARGETS, sizeof(TARGETS) / sizeof(GtkTargetEntry), GDK_ACTION_COPY);
  135. //require notifications upon tree item dragging, mouse button release, active item change
  136. g_signal_connect(G_OBJECT(m_treeView), "drag_data_get", G_CALLBACK(dragDataGetFromTreeCB), this);
  137. g_signal_connect(G_OBJECT(m_treeView), "button-release-event", G_CALLBACK(buttonReleaseCB), this);
  138. g_signal_connect(G_OBJECT(m_treeView), "cursor-changed", G_CALLBACK(cursorChangedCB), this);
  139. GTK_WIDGET_SET_FLAGS(GTK_WIDGET(m_treeView), GDK_KEY_PRESS_MASK);
  140. g_signal_connect(G_OBJECT(m_treeView), "key-press-event", G_CALLBACK(widgetKeyPressEventCB), this);
  141. //create main dialog
  142. //------------------
  143. m_dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  144. //retrieve default window size
  145. const size_t treeViewWidth = 200;
  146. m_previewWindowW = size_t(m_kernelCtx.getConfigurationManager().expandAsUInteger("${Designer_UnaffectedVisualizationWindowWidth}", 400));
  147. m_previewWindowH = size_t(m_kernelCtx.getConfigurationManager().expandAsUInteger("${Designer_UnaffectedVisualizationWindowHeight}", 400));
  148. CIdentifier windowID;
  149. //if at least one window was created, retrieve its dimensions
  150. if (m_tree.getNextVisualizationWidgetIdentifier(windowID, EVTWidget::Window))
  151. {
  152. IVTWidget* window = m_tree.getVisualizationWidget(windowID);
  153. m_previewWindowW = window->getWidth();
  154. m_previewWindowH = window->getHeight();
  155. /* Change the way window sizes are stored in the widget
  156. TAttributeHandler handler(*window);
  157. m_previewWindowW = handler.getAttributeValue<int>(OVD_AttributeId_VisualizationWindow_Width);
  158. m_previewWindowH = handler.getAttributeValue<int>(OVD_AttributeId_VisualizationWindow_Height);
  159. */
  160. }
  161. gtk_window_set_default_size(GTK_WINDOW(m_dialog), gint(treeViewWidth + m_previewWindowW), gint(m_previewWindowH));
  162. //set window title
  163. gtk_window_set_title(GTK_WINDOW(m_dialog), " Window Manager");
  164. // gtk_window_set_transient_for(GTK_WINDOW(m_dialog), GTK_WINDOW(m_scenario.m_application.m_MainWindow));
  165. gtk_signal_connect(GTK_OBJECT(m_dialog), "configure_event", G_CALLBACK(configureEventCB), this);
  166. #ifdef HANDLE_MIN_MAX_EVENTS
  167. gtk_signal_connect(GTK_OBJECT(m_dialog), "window_state_event", G_CALLBACK(window_state_event_cb), this);
  168. #endif
  169. g_signal_connect(G_OBJECT(m_dialog), "delete-event", G_CALLBACK(deleteEventCB), this);
  170. //main pane : tree view to the left, widgets table to the right
  171. m_pane = gtk_hpaned_new();
  172. gtk_container_add(GTK_CONTAINER(m_dialog), GTK_WIDGET(m_pane));
  173. // Add a scrollview to above the treeview
  174. GtkWidget* scrolledWindow = gtk_scrolled_window_new(nullptr, nullptr);
  175. gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledWindow), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
  176. gtk_container_add(GTK_CONTAINER(scrolledWindow), GTK_WIDGET(m_treeView));
  177. //add tree view to pane
  178. gtk_paned_add1(GTK_PANED(m_pane), GTK_WIDGET(scrolledWindow));
  179. //set initial divider position
  180. gtk_paned_set_position(GTK_PANED(m_pane), gint(treeViewWidth));
  181. //create popup menus
  182. //------------------
  183. m_unaffectedItemFactory = gtk_item_factory_new(GTK_TYPE_MENU, "<unaffected_main>", nullptr);
  184. gtk_item_factory_create_items(m_unaffectedItemFactory, N_UNAFFECTED_ITEMS, unaffectedItems, this);
  185. m_visualizationWindowItemFactory = gtk_item_factory_new(GTK_TYPE_MENU, "<visualization_window_main>", nullptr);
  186. gtk_item_factory_create_items(m_visualizationWindowItemFactory, N_VISUALIZATION_WINDOW_ITEMS, visualizationWindowItems, this);
  187. m_visualizationPanelItemFactory = gtk_item_factory_new(GTK_TYPE_MENU, "<visualization_panel_main>", nullptr);
  188. gtk_item_factory_create_items(m_visualizationPanelItemFactory, N_VISUALIZATION_PANEL_ITEMS, visualizationPanelItems, this);
  189. m_visualizationBoxItemFactory = gtk_item_factory_new(GTK_TYPE_MENU, "<visualization_box_main>", nullptr);
  190. gtk_item_factory_create_items(m_visualizationBoxItemFactory, N_VISUALIZATION_BOX_ITEMS, visualizationBoxItems, this);
  191. m_undefinedItemFactory = gtk_item_factory_new(GTK_TYPE_MENU, "<undefined_widget_main>", nullptr);
  192. gtk_item_factory_create_items(m_undefinedItemFactory, N_UNDEFINED_WIDGET_ITEMS, undefinedWidgetItems, this);
  193. m_splitItemFactory = gtk_item_factory_new(GTK_TYPE_MENU, "<split_widget_main>", nullptr);
  194. gtk_item_factory_create_items(m_splitItemFactory, N_SPLIT_WIDGET_ITEMS, splitWidgetItems, this);
  195. }
  196. void CDesignerVisualization::load()
  197. {
  198. m_tree.setTreeViewCB(this);
  199. m_tree.reloadTree();
  200. //if at least one window was created, retrieve its dimensions
  201. CIdentifier id;
  202. if (m_tree.getNextVisualizationWidgetIdentifier(id, EVTWidget::Window))
  203. {
  204. IVTWidget* window = m_tree.getVisualizationWidget(id);
  205. m_previewWindowW = window->getWidth();
  206. m_previewWindowH = window->getHeight();
  207. }
  208. const size_t width = gtk_paned_get_position(GTK_PANED(m_pane));
  209. gtk_widget_set_size_request(GTK_WIDGET(m_dialog), gint(width + m_previewWindowW), gint(m_previewWindowH));
  210. gtk_tree_view_expand_all(m_treeView);
  211. setActiveVisualization(m_activeVisualizationWindowName, m_activeVisualizationPanelName);
  212. }
  213. void CDesignerVisualization::show() const
  214. {
  215. // since gtk is asynchronous for the expose event, the m_previewWindowVisible flag is turned on in the corresponding callback
  216. //m_previewWindowVisible = true;
  217. gtk_widget_show_all(static_cast<GtkWidget*>(m_dialog));
  218. }
  219. void CDesignerVisualization::hide()
  220. {
  221. m_previewWindowVisible = false;
  222. gtk_widget_hide_all(static_cast<GtkWidget*>(m_dialog));
  223. }
  224. void CDesignerVisualization::setDeleteEventCB(visualization_delete_event_cb_t cb, gpointer data)
  225. {
  226. m_deleteEventCB = cb;
  227. m_deleteEventUserData = data;
  228. }
  229. void CDesignerVisualization::onVisualizationBoxAdded(const Kernel::IBox* box)
  230. {
  231. CIdentifier widgetID;
  232. m_tree.addVisualizationWidget(widgetID, box->getName(), EVTWidget::Box, CIdentifier::undefined(), 0, box->getIdentifier(), 0, CIdentifier::undefined());
  233. m_tree.reloadTree();
  234. //refresh view
  235. GtkTreeIter iter;
  236. m_tree.findChildNodeFromRoot(&iter, widgetID);
  237. refreshActiveVisualization(m_tree.getTreePath(&iter));
  238. }
  239. void CDesignerVisualization::onVisualizationBoxRemoved(const CIdentifier& boxID)
  240. {
  241. IVTWidget* widget = m_tree.getVisualizationWidgetFromBoxIdentifier(boxID);
  242. if (widget != nullptr)
  243. {
  244. //unaffected widget : delete it
  245. if (widget->getParentIdentifier() == CIdentifier::undefined()) { m_tree.destroyHierarchy(widget->getIdentifier()); }
  246. else { destroyVisualizationWidget(widget->getIdentifier()); } //simplify tree
  247. m_tree.reloadTree();
  248. //refresh view
  249. refreshActiveVisualization(nullptr);
  250. }
  251. }
  252. void CDesignerVisualization::onVisualizationBoxRenamed(const CIdentifier& boxID)
  253. {
  254. //retrieve visualization widget
  255. IVTWidget* widget = m_tree.getVisualizationWidgetFromBoxIdentifier(boxID);
  256. if (widget != nullptr)
  257. {
  258. //retrieve box name
  259. const Kernel::IBox* box = m_scenario.m_Scenario.getBoxDetails(boxID);
  260. if (box != nullptr)
  261. {
  262. widget->setName(box->getName()); //set new visualization widget name
  263. m_tree.reloadTree(); //reload tree
  264. refreshActiveVisualization(nullptr); //refresh view
  265. }
  266. }
  267. }
  268. void CDesignerVisualization::createTreeWidget(IVTWidget* widget)
  269. {
  270. if (widget->getType() == EVTWidget::HorizontalSplit || widget->getType() == EVTWidget::VerticalSplit)
  271. {
  272. /* TODO_JL: Find a way to store divider position and max divider position
  273. TAttributeHandler handler(*widget);
  274. handler.addAttribute(OVD_AttributeId_EVisualizationWidget::DividerPosition, 1);
  275. handler.addAttribute(OVD_AttributeId_EVisualizationWidget::MaxDividerPosition, 2);
  276. */
  277. }
  278. }
  279. //need width request of 0 to avoid graphical bugs (label/icon overlapping other widgets) when shrinking buttons
  280. static const gint labelWidthRequest = 0;
  281. static const gint iconWidthRequest = 0;
  282. //need expand and fill flags to TRUE to see 0-size-requesting widgets
  283. static const gboolean labelExpand = TRUE;
  284. static const gboolean labelFill = TRUE;
  285. static const gboolean iconExpand = TRUE;
  286. static const gboolean iconFill = TRUE;
  287. GtkWidget* CDesignerVisualization::loadTreeWidget(IVTWidget* widget)
  288. {
  289. GtkWidget* treeWidget = nullptr;
  290. //create widget
  291. //-------------
  292. if (widget->getType() == EVTWidget::Panel)
  293. {
  294. //retrieve panel index
  295. IVTWidget* window = m_tree.getVisualizationWidget(widget->getParentIdentifier());
  296. if (window != nullptr)
  297. {
  298. size_t idx;
  299. window->getChildIndex(widget->getIdentifier(), idx);
  300. //create notebook if this is the first panel
  301. if (idx == 0) { treeWidget = gtk_notebook_new(); }
  302. else //otherwise retrieve it from first panel
  303. {
  304. CIdentifier firstPanelID;
  305. window->getChildIdentifier(0, firstPanelID);
  306. GtkTreeIter firstPanelIter;
  307. m_tree.findChildNodeFromRoot(&firstPanelIter, firstPanelID);
  308. void* notebookWidget = nullptr;
  309. m_tree.getPointerValueFromTreeIter(&firstPanelIter, notebookWidget, EVTColumn::PointerWidget);
  310. treeWidget = static_cast<GtkWidget*>(notebookWidget);
  311. }
  312. }
  313. }
  314. else if (widget->getType() == EVTWidget::VerticalSplit || widget->getType() == EVTWidget::HorizontalSplit
  315. || widget->getType() == EVTWidget::Undefined || widget->getType() == EVTWidget::Box)
  316. {
  317. //tree widget = table containing event boxes + visualization widget in the center
  318. treeWidget = GTK_WIDGET(newWidgetsTable());
  319. GtkWidget* currentWidget = getVisualizationWidget(treeWidget);
  320. if (currentWidget != nullptr) { gtk_container_remove(GTK_CONTAINER(treeWidget), currentWidget); }
  321. if (widget->getType() == EVTWidget::VerticalSplit || widget->getType() == EVTWidget::HorizontalSplit)
  322. {
  323. if (gtk_widget_get_parent(treeWidget) != nullptr) { gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(treeWidget)), treeWidget); }
  324. //create a paned and insert it in table
  325. GtkWidget* paned = (widget->getType() == EVTWidget::HorizontalSplit) ? gtk_hpaned_new() : gtk_vpaned_new();
  326. gtk_table_attach(GTK_TABLE(treeWidget), paned, 1, 2, 1, 2, GtkAttachOptions(GTK_EXPAND | GTK_SHRINK | GTK_FILL),
  327. GtkAttachOptions(GTK_EXPAND | GTK_SHRINK | GTK_FILL), 0, 0);
  328. }
  329. else //undefined or visualization box : visualization widget is a GtkButton (left : icon, right : label)
  330. {
  331. if (gtk_widget_get_parent(treeWidget) != nullptr) { gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(treeWidget)), treeWidget); }
  332. //create a button and insert it in table
  333. GtkWidget* button = gtk_button_new();
  334. gtk_widget_set_size_request(button, 0, 0);
  335. gtk_table_attach(GTK_TABLE(treeWidget), button, 1, 2, 1, 2, GtkAttachOptions(GTK_EXPAND | GTK_SHRINK | GTK_FILL),
  336. GtkAttachOptions(GTK_EXPAND | GTK_SHRINK | GTK_FILL), 0, 0);
  337. //box inserted in button
  338. GtkBox* box = GTK_BOX(gtk_vbox_new(FALSE, 0));
  339. gtk_widget_set_size_request(GTK_WIDGET(box), 0, 0);
  340. //icon - actual icon will be loaded in endLoadTreeWidget
  341. GtkWidget* icon = gtk_image_new_from_stock(getTreeWidgetIcon(EVTNode::Undefined), GTK_ICON_SIZE_BUTTON);
  342. if (iconWidthRequest == 0) { gtk_widget_set_size_request(icon, 0, 0); }
  343. gtk_box_pack_start(box, icon, iconExpand, iconFill, 0);
  344. //label
  345. GtkWidget* label = gtk_label_new(static_cast<const char*>(widget->getName()));
  346. if (labelWidthRequest == 0) { gtk_widget_set_size_request(label, 0, 0); }
  347. gtk_box_pack_start(box, label, labelExpand, labelFill, 0);
  348. //add box to button
  349. gtk_container_add(GTK_CONTAINER(button), GTK_WIDGET(box));
  350. //set up button as drag destination
  351. gtk_drag_dest_set(button, GTK_DEST_DEFAULT_ALL, TARGETS, sizeof(TARGETS) / sizeof(GtkTargetEntry), GDK_ACTION_COPY);
  352. g_signal_connect(G_OBJECT(button), "drag_data_received", G_CALLBACK(dragDataReceivedInWidgetCB), this);
  353. //set up button as drag source as well
  354. gtk_drag_source_set(button, GDK_BUTTON1_MASK, TARGETS, sizeof(TARGETS) / sizeof(GtkTargetEntry), GDK_ACTION_COPY);
  355. g_signal_connect(G_OBJECT(button), "drag_data_get", G_CALLBACK(dragDataGetFromWidgetCB), this);
  356. //ask for notification of some events
  357. if (widget->getType() == EVTWidget::Box)
  358. {
  359. GTK_WIDGET_SET_FLAGS(button, GDK_KEY_PRESS_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
  360. g_signal_connect(G_OBJECT(button), "key-press-event", G_CALLBACK(widgetKeyPressEventCB), this);
  361. g_signal_connect(G_OBJECT(button), "enter-notify-event", G_CALLBACK(widgetEnterNotifyEventCB), this);
  362. g_signal_connect(G_OBJECT(button), "leave-notify-event", G_CALLBACK(widgetLeaveNotifyEventCB), this);
  363. }
  364. }
  365. //parent widget to its parent, if any
  366. //-----------------------------------
  367. IVTWidget* parentWidget = m_tree.getVisualizationWidget(widget->getParentIdentifier());
  368. if (parentWidget != nullptr) //visualization boxes may be unparented
  369. {
  370. GtkTreeIter parentIter;
  371. m_tree.findChildNodeFromRoot(&parentIter, parentWidget->getIdentifier());
  372. if (parentWidget->getType() == EVTWidget::Panel)
  373. {
  374. //parent widget to notebook as a new page
  375. void* notebook = nullptr;
  376. m_tree.getPointerValueFromTreeIter(&parentIter, notebook, EVTColumn::PointerWidget);
  377. char* panelName = nullptr;
  378. m_tree.getStringValueFromTreeIter(&parentIter, panelName, EVTColumn::StringName);
  379. gtk_notebook_append_page(GTK_NOTEBOOK(notebook), treeWidget, gtk_label_new(panelName));
  380. }
  381. else if (parentWidget->getType() == EVTWidget::VerticalSplit || parentWidget->getType() == EVTWidget::HorizontalSplit)
  382. {
  383. //insert widget in parent paned
  384. void* parentTreeWidget = nullptr;
  385. m_tree.getPointerValueFromTreeIter(&parentIter, parentTreeWidget, EVTColumn::PointerWidget);
  386. if (parentTreeWidget != nullptr && GTK_IS_WIDGET(parentTreeWidget))
  387. {
  388. GtkWidget* pParentWidget = getVisualizationWidget(GTK_WIDGET(parentTreeWidget));
  389. if (pParentWidget != nullptr && GTK_IS_PANED(pParentWidget))
  390. {
  391. size_t idx;
  392. if (parentWidget->getChildIndex(widget->getIdentifier(), idx))
  393. {
  394. if (idx == 0) { gtk_paned_pack1(GTK_PANED(pParentWidget), treeWidget, TRUE, TRUE); }
  395. else { gtk_paned_pack2(GTK_PANED(pParentWidget), treeWidget, TRUE, TRUE); }
  396. }
  397. }
  398. }
  399. }
  400. }
  401. }
  402. //resize widgets once they are allocated : this is the case when they are shown on an expose event
  403. //FIXME : perform resizing only once (when it is done as many times as there are widgets in the tree here)
  404. if (treeWidget != nullptr) { gtk_signal_connect(GTK_OBJECT(getVisualizationWidget(treeWidget)), "expose-event", G_CALLBACK(widgetExposeEventCB), this); }
  405. return treeWidget;
  406. }
  407. void CDesignerVisualization::endLoadTreeWidget(IVTWidget* widget)
  408. {
  409. //retrieve tree widget
  410. GtkTreeIter it;
  411. m_tree.findChildNodeFromRoot(&it, widget->getIdentifier());
  412. void* treeWidget = nullptr;
  413. m_tree.getPointerValueFromTreeIter(&it, treeWidget, EVTColumn::PointerWidget);
  414. //get actual visualization widget
  415. GtkWidget* vizWidget = getVisualizationWidget(static_cast<GtkWidget*>(treeWidget));
  416. if (widget->getType() == EVTWidget::Panel)
  417. {
  418. //reposition paned widget handles
  419. resizeCB(nullptr);
  420. }
  421. else if (widget->getType() == EVTWidget::Undefined || widget->getType() == EVTWidget::Box)
  422. {
  423. if (GTK_IS_BUTTON(vizWidget) != FALSE)
  424. {
  425. //replace dummy icon with correct one
  426. //-----------------------------------
  427. //retrieve icon name from tree
  428. char* iconString = nullptr;
  429. m_tree.getStringValueFromTreeIter(&it, iconString, EVTColumn::StringStockIcon);
  430. //retrieve hbox
  431. GList* buttonChildren = gtk_container_get_children(GTK_CONTAINER(vizWidget));
  432. GtkContainer* box = GTK_CONTAINER(buttonChildren->data);
  433. //remove first widget
  434. GList* boxChildren = gtk_container_get_children(box);
  435. gtk_container_remove(box, GTK_WIDGET(boxChildren->data));
  436. //create new icon
  437. GtkWidget* icon = gtk_image_new_from_stock(iconString, GTK_ICON_SIZE_BUTTON);
  438. if (iconWidthRequest == 0) { gtk_widget_set_size_request(icon, 0, 0); }
  439. gtk_box_pack_start(GTK_BOX(box), icon, iconExpand, iconFill, 0);
  440. //insert it in first position
  441. gtk_box_reorder_child(GTK_BOX(box), icon, 0);
  442. }
  443. }
  444. }
  445. GtkWidget* CDesignerVisualization::getTreeWidget(GtkWidget* widget) { return gtk_widget_get_parent(widget); }
  446. GtkWidget* CDesignerVisualization::getVisualizationWidget(GtkWidget* widget)
  447. {
  448. if (GTK_IS_TABLE(widget)) { return getTableChild(GTK_TABLE(widget), 1, 2, 1, 2)->widget; }
  449. return widget;
  450. }
  451. const char* CDesignerVisualization::getTreeWidgetIcon(const EVTNode type)
  452. {
  453. switch (type)
  454. {
  455. case EVTNode::Unaffected: return GTK_STOCK_DIALOG_QUESTION;
  456. case EVTNode::Undefined: return GTK_STOCK_CANCEL;
  457. case EVTNode::VisualizationWindow: return GTK_STOCK_DND_MULTIPLE;
  458. case EVTNode::VisualizationPanel: return GTK_STOCK_DND;
  459. case EVTNode::VisualizationBox: return GTK_STOCK_EXECUTE; //default (actual icon name may be retrieved from box descriptor)
  460. case EVTNode::HorizontalSplit:
  461. case EVTNode::VerticalSplit: return GTK_STOCK_ADD;
  462. case EVTNode::VPU:
  463. default: return "";
  464. }
  465. }
  466. gboolean CDesignerVisualization::deleteEventCB(GtkWidget* /*widget*/, GdkEvent* /*event*/, gpointer data)
  467. {
  468. return static_cast<CDesignerVisualization*>(data)->deleteEvent() ? TRUE : FALSE;
  469. }
  470. bool CDesignerVisualization::deleteEvent() const
  471. {
  472. if (m_deleteEventCB != nullptr)
  473. {
  474. m_deleteEventCB(m_deleteEventUserData);
  475. return true;
  476. }
  477. return false;
  478. }
  479. #ifdef HANDLE_MIN_MAX_EVENTS
  480. gboolean CDesignerVisualization::window_state_event_cb(GtkWidget * widget, GdkEventWindowState * event, gpointer data)
  481. {
  482. //refresh widgets if window was maximized or minimized
  483. if (event->changed_mask& GDK_WINDOW_STATE_MAXIMIZED ||
  484. event->changed_mask& GDK_WINDOW_STATE_ICONIFIED)
  485. {
  486. //widgets haven't been reallocated yet, perform resizing only when this happens
  487. //gtk_signal_connect(GTK_OBJECT(gtk_paned_get_child2(GTK_PANED(m_pane))), "size-allocate", G_CALLBACK(widget_size_allocate_cb), this);
  488. gtk_signal_connect(GTK_OBJECT(gtk_paned_get_child2(GTK_PANED(m_pane))), "expose-event", G_CALLBACK(widget_expose_cb), this);
  489. }
  490. return FALSE;
  491. }
  492. #endif
  493. //event generated whenever window size changes, including when it is first created
  494. gboolean CDesignerVisualization::configureEventCB(GtkWidget* /*widget*/, GdkEventConfigure* /*event*/, gpointer data)
  495. {
  496. static_cast<CDesignerVisualization*>(data)->m_previewWindowVisible = true;
  497. static_cast<CDesignerVisualization*>(data)->resizeCB(nullptr);
  498. return FALSE;
  499. }
  500. gboolean CDesignerVisualization::widgetExposeEventCB(GtkWidget* widget, GdkEventExpose* /*event*/, gpointer data)
  501. {
  502. static_cast<CDesignerVisualization*>(data)->m_previewWindowVisible = true;
  503. g_signal_handlers_disconnect_by_func(G_OBJECT(widget), G_CALLBACK2(CDesignerVisualization::widgetExposeEventCB), data);
  504. static_cast<CDesignerVisualization*>(data)->resizeCB(nullptr);
  505. return FALSE;
  506. }
  507. void CDesignerVisualization::resizeCB(IVTWidget* widget)
  508. {
  509. if (widget == nullptr)
  510. {
  511. //assign current window size to each window
  512. GtkWidget* notebook = gtk_paned_get_child2(GTK_PANED(m_pane));
  513. if (notebook != nullptr)
  514. {
  515. CIdentifier id = CIdentifier::undefined();
  516. //retrieve current preview window size, if window is visible
  517. if (m_previewWindowVisible)
  518. {
  519. notebook = gtk_paned_get_child2(GTK_PANED(m_pane));
  520. if (notebook != nullptr)
  521. {
  522. //update preview window dims
  523. m_previewWindowW = notebook->allocation.width;
  524. m_previewWindowH = notebook->allocation.height;
  525. }
  526. }
  527. while (m_tree.getNextVisualizationWidgetIdentifier(id, EVTWidget::Window))
  528. {
  529. IVTWidget* window = m_tree.getVisualizationWidget(id);
  530. //store new dimensions
  531. window->setWidth(m_previewWindowW);
  532. window->setHeight(m_previewWindowH);
  533. }
  534. }
  535. // else { return;} //?
  536. //retrieve active visualization panel
  537. GtkTreeIter windowIt;
  538. if (!m_tree.findChildNodeFromRoot(&windowIt, m_activeVisualizationWindowName, EVTNode::VisualizationWindow)) { return; }
  539. GtkTreeIter panelIt = windowIt;
  540. if (!m_tree.findChildNodeFromParent(&panelIt, m_activeVisualizationPanelName, EVTNode::VisualizationPanel)) { return; }
  541. CIdentifier panelID;
  542. if (!m_tree.getIdentifierFromTreeIter(&panelIt, panelID, EVTColumn::StringIdentifier)) { return; }
  543. IVTWidget* panel = m_tree.getVisualizationWidget(panelID);
  544. //resize visualization panel hierarchy
  545. if (panel != nullptr)
  546. {
  547. CIdentifier childID;
  548. panel->getChildIdentifier(0, childID);
  549. IVTWidget* childWidget = m_tree.getVisualizationWidget(childID);
  550. if (childWidget != nullptr) { resizeCB(childWidget); }
  551. }
  552. }
  553. else if (widget->getType() == EVTWidget::VerticalSplit || widget->getType() == EVTWidget::HorizontalSplit)
  554. {
  555. GtkTreeIter it;
  556. if (m_tree.findChildNodeFromRoot(&it, widget->getIdentifier()) == TRUE)
  557. {
  558. //retrieve paned widget
  559. void* treeWidget = nullptr;
  560. m_tree.getPointerValueFromTreeIter(&it, treeWidget, EVTColumn::PointerWidget);
  561. GtkWidget* paned = getVisualizationWidget(GTK_WIDGET(treeWidget));
  562. enablePanedSignals(paned, false);
  563. //retrieve paned attributes
  564. const int handlePos = widget->getDividerPosition();
  565. const int maxHandlePos = widget->getMaxDividerPosition();
  566. if (handlePos == std::numeric_limits<int>::min() || maxHandlePos == std::numeric_limits<int>::min())
  567. {
  568. // these variables hadn't been initialized meaningfully before. @fixme what is the correct place to init them?
  569. notifyPositionPaned(paned); // for now, this inits them as a side effect
  570. }
  571. if (maxHandlePos > 0)
  572. {
  573. //retrieve current maximum handle position
  574. const int pos = GTK_IS_VPANED(paned) ? GTK_PANED(paned)->container.widget.allocation.height
  575. : GTK_PANED(paned)->container.widget.allocation.width;
  576. //set new paned handle position
  577. gtk_paned_set_position(GTK_PANED(paned), handlePos * pos / maxHandlePos);
  578. }
  579. enablePanedSignals(paned, true);
  580. //go down child 1
  581. CIdentifier childID;
  582. widget->getChildIdentifier(0, childID);
  583. IVTWidget* childWidget = m_tree.getVisualizationWidget(childID);
  584. if (childWidget != nullptr) { resizeCB(childWidget); }
  585. //go down child 2
  586. widget->getChildIdentifier(1, childID);
  587. childWidget = m_tree.getVisualizationWidget(childID);
  588. if (childWidget != nullptr) { resizeCB(childWidget); }
  589. }
  590. }
  591. }
  592. void CDesignerVisualization::notebookPageSwitchCB(GtkNotebook* notebook, GtkNotebookPage* /*page*/, const guint pagenum, gpointer data)
  593. {
  594. static_cast<CDesignerVisualization*>(data)->notebookPageSelectedCB(notebook, pagenum);
  595. }
  596. gboolean CDesignerVisualization::notifyPositionPanedCB(GtkWidget* widget, GParamSpec* /*spec*/, gpointer data)
  597. {
  598. static_cast<CDesignerVisualization*>(data)->notifyPositionPaned(widget);
  599. return TRUE;
  600. }
  601. //--------------------------
  602. //Event box table management
  603. //--------------------------
  604. void CDesignerVisualization::setupNewEventBoxTable(GtkBuilder* xml)
  605. {
  606. //set up event boxes as drag targets
  607. gtk_drag_dest_set(
  608. GTK_WIDGET(gtk_builder_get_object(xml, "window_manager_eventbox-eventbox2")), GTK_DEST_DEFAULT_ALL, TARGETS, sizeof(TARGETS) / sizeof(GtkTargetEntry),
  609. GDK_ACTION_COPY);
  610. gtk_drag_dest_set(
  611. GTK_WIDGET(gtk_builder_get_object(xml, "window_manager_eventbox-eventbox4")), GTK_DEST_DEFAULT_ALL, TARGETS, sizeof(TARGETS) / sizeof(GtkTargetEntry),
  612. GDK_ACTION_COPY);
  613. gtk_drag_dest_set(
  614. GTK_WIDGET(gtk_builder_get_object(xml, "window_manager_eventbox-eventbox6")), GTK_DEST_DEFAULT_ALL, TARGETS, sizeof(TARGETS) / sizeof(GtkTargetEntry),
  615. GDK_ACTION_COPY);
  616. gtk_drag_dest_set(
  617. GTK_WIDGET(gtk_builder_get_object(xml, "window_manager_eventbox-eventbox8")), GTK_DEST_DEFAULT_ALL, TARGETS, sizeof(TARGETS) / sizeof(GtkTargetEntry),
  618. GDK_ACTION_COPY);
  619. //set up event boxes callbacks for drag data received events
  620. char buf[256];
  621. sprintf(buf, "%p %s", this, "top");
  622. m_topEventBoxData = buf;
  623. g_signal_connect(G_OBJECT(gtk_builder_get_object(xml, "window_manager_eventbox-eventbox2")), "drag_data_received", G_CALLBACK(dataReceivedInEventBoxCB),
  624. gpointer(m_topEventBoxData.c_str()));
  625. sprintf(buf, "%p %s", this, "left");
  626. m_leftEventBoxData = buf;
  627. g_signal_connect(G_OBJECT(gtk_builder_get_object(xml, "window_manager_eventbox-eventbox4")), "drag_data_received", G_CALLBACK(dataReceivedInEventBoxCB),
  628. gpointer(m_leftEventBoxData.c_str()));
  629. sprintf(buf, "%p %s", this, "right");
  630. m_rightEventBoxData = buf;
  631. g_signal_connect(G_OBJECT(gtk_builder_get_object(xml, "window_manager_eventbox-eventbox6")), "drag_data_received", G_CALLBACK(dataReceivedInEventBoxCB),
  632. gpointer(m_rightEventBoxData.c_str()));
  633. sprintf(buf, "%p %s", this, "bottom");
  634. m_bottomEventBoxData = buf;
  635. g_signal_connect(G_OBJECT(gtk_builder_get_object(xml, "window_manager_eventbox-eventbox8")), "drag_data_received", G_CALLBACK(dataReceivedInEventBoxCB),
  636. gpointer(m_bottomEventBoxData.c_str()));
  637. }
  638. void CDesignerVisualization::refreshActiveVisualization(GtkTreePath* selectedItemPath)
  639. {
  640. //show tree
  641. gtk_tree_view_expand_all(m_treeView);
  642. //select item
  643. if (selectedItemPath != nullptr) { gtk_tree_view_set_cursor(m_treeView, selectedItemPath, nullptr, false); }
  644. else //select previous visualization tab again (or another tab if it doesn't exist anymore)
  645. {
  646. setActiveVisualization(m_activeVisualizationWindowName, m_activeVisualizationPanelName);
  647. }
  648. }
  649. void CDesignerVisualization::setActiveVisualization(const char* activeWindow, const char* activePanel)
  650. {
  651. //clear active window/panel names
  652. m_activeVisualizationWindowName = "";
  653. m_activeVisualizationPanelName = "";
  654. //retrieve active window
  655. GtkTreeIter windowIter;
  656. if (m_tree.findChildNodeFromRoot(&windowIter, activeWindow, EVTNode::VisualizationWindow)) { m_activeVisualizationWindowName = CString(activeWindow); }
  657. else
  658. {
  659. //pick first window if previously active window doesn't exist anymore
  660. CIdentifier id = CIdentifier::undefined();
  661. if (m_tree.getNextVisualizationWidgetIdentifier(id, EVTWidget::Window))
  662. {
  663. m_activeVisualizationWindowName = m_tree.getVisualizationWidget(id)->getName();
  664. m_tree.findChildNodeFromRoot(&windowIter, m_activeVisualizationWindowName.toASCIIString(), EVTNode::VisualizationWindow);
  665. }
  666. else //no windows left
  667. {
  668. if (gtk_paned_get_child2(GTK_PANED(m_pane)) != nullptr) { gtk_container_remove(GTK_CONTAINER(m_pane), gtk_paned_get_child2(GTK_PANED(m_pane))); }
  669. return;
  670. }
  671. }
  672. //retrieve active panel
  673. GtkTreeIter panelIter = windowIter;
  674. if (m_tree.findChildNodeFromParent(&panelIter, activePanel, EVTNode::VisualizationPanel)) { m_activeVisualizationPanelName = CString(activePanel); }
  675. else //couldn't find panel : select first one
  676. {
  677. CIdentifier windowID;
  678. m_tree.getIdentifierFromTreeIter(&windowIter, windowID, EVTColumn::StringIdentifier);
  679. IVTWidget* window = m_tree.getVisualizationWidget(windowID);
  680. CIdentifier panelID;
  681. if (window->getChildIdentifier(0, panelID))
  682. {
  683. panelIter = windowIter;
  684. m_tree.findChildNodeFromParent(&panelIter, panelID);
  685. char* str = nullptr;
  686. m_tree.getStringValueFromTreeIter(&panelIter, str, EVTColumn::StringName);
  687. m_activeVisualizationPanelName = str;
  688. }
  689. else //no panel in window
  690. {
  691. GtkWidget* currentNotebook = gtk_paned_get_child2(GTK_PANED(m_pane));
  692. if (currentNotebook != nullptr)
  693. {
  694. gtk_object_ref(GTK_OBJECT(currentNotebook));
  695. gtk_container_remove(GTK_CONTAINER(m_pane), currentNotebook);
  696. }
  697. return;
  698. }
  699. }
  700. //retrieve notebook and set it visible
  701. void* notebook = nullptr;
  702. m_tree.getPointerValueFromTreeIter(&panelIter, notebook, EVTColumn::PointerWidget);
  703. GtkWidget* widget = gtk_paned_get_child2(GTK_PANED(m_pane));
  704. if (widget != GTK_WIDGET(notebook))
  705. {
  706. if (widget != nullptr)
  707. {
  708. //FIXME : don't ref previous notebook if parent window doesn't exist anymore
  709. gtk_object_ref(GTK_OBJECT(widget));
  710. gtk_container_remove(GTK_CONTAINER(m_pane), widget);
  711. }
  712. gtk_paned_add2(GTK_PANED(m_pane), GTK_WIDGET(notebook));
  713. //gtk_object_unref(currentNotebook);
  714. }
  715. //disable switch page notifications
  716. enableNotebookSignals(GTK_WIDGET(notebook), false);
  717. //set active panel visible
  718. int i;
  719. for (i = 0; i < gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook)); ++i)
  720. {
  721. if (strcmp(gtk_notebook_get_tab_label_text(GTK_NOTEBOOK(notebook), gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), i)),
  722. m_activeVisualizationPanelName) == 0)
  723. {
  724. gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), i);
  725. break;
  726. }
  727. }
  728. //if active page couldn't be found
  729. if (i == gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook)))
  730. {
  731. //error!
  732. //pick first page if it exists
  733. if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook)) > 0)
  734. {
  735. m_activeVisualizationPanelName = gtk_notebook_get_tab_label_text(GTK_NOTEBOOK(notebook), gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), 0));
  736. gtk_notebook_set_current_page(GTK_NOTEBOOK(notebook), 0);
  737. }
  738. else //error : no pages in notebook, clear panel name
  739. {
  740. m_activeVisualizationPanelName = "";
  741. }
  742. }
  743. //enable switch page notifications
  744. enableNotebookSignals(GTK_WIDGET(notebook), true);
  745. //refresh display
  746. gtk_widget_show_all(m_pane);
  747. }
  748. //creates a new widgets table and sets it as current
  749. GtkTable* CDesignerVisualization::newWidgetsTable()
  750. {
  751. //@FIXME is the memory ever freed? Valgrind is suspicious about this. It seems that a builder is allocated, but only a member of builder is returned as GtkTable*.
  752. GtkBuilder* pGtkBuilderTable = gtk_builder_new(); // glade_xml_new(m_guiFile.c_str(), "window_manager_eventbox-table", nullptr);
  753. gtk_builder_add_from_file(pGtkBuilderTable, m_guiFile.c_str(), nullptr);
  754. gtk_builder_connect_signals(pGtkBuilderTable, nullptr);
  755. //set up event boxes
  756. setupNewEventBoxTable(pGtkBuilderTable);
  757. GtkTable* table = GTK_TABLE(gtk_builder_get_object(pGtkBuilderTable, "window_manager_eventbox-table"));
  758. //clear central button label
  759. GtkTableChild* tc = getTableChild(table, 1, 2, 1, 2);
  760. GtkButton* button = GTK_BUTTON(tc->widget);
  761. gtk_button_set_label(button, "");
  762. //set it up as drag destination
  763. gtk_drag_dest_set(GTK_WIDGET(button), GTK_DEST_DEFAULT_ALL, TARGETS, sizeof(TARGETS) / sizeof(GtkTargetEntry), GDK_ACTION_COPY);
  764. g_signal_connect(G_OBJECT(button), "drag_data_received", G_CALLBACK(dragDataReceivedInWidgetCB), this);
  765. //set it up as drag source as well
  766. gtk_drag_source_set(GTK_WIDGET(button), GDK_BUTTON1_MASK, TARGETS, sizeof(TARGETS) / sizeof(GtkTargetEntry), GDK_ACTION_COPY);
  767. g_signal_connect(G_OBJECT(button), "drag_data_get", G_CALLBACK(dragDataGetFromWidgetCB), this);
  768. return table;
  769. }
  770. void CDesignerVisualization::askNewVisualizationWindow()
  771. {
  772. //show dialog
  773. CInputDialog id(m_guiFile.c_str(), &CDesignerVisualization::newVisualizationWindowCB, this, "New window", "Please enter name of new window : ");
  774. id.run();
  775. }
  776. bool CDesignerVisualization::newVisualizationWindow(const char* label)
  777. {
  778. //ensure name is unique
  779. IVTWidget* window;
  780. CIdentifier windowID = CIdentifier::undefined();
  781. while (m_tree.getNextVisualizationWidgetIdentifier(windowID, EVTWidget::Window))
  782. {
  783. window = m_tree.getVisualizationWidget(windowID);
  784. if (strcmp(window->getName().toASCIIString(), label) == 0)
  785. {
  786. displayErrorDialog("Window creation failed !", "An existing window already uses this name. Please choose another name.");
  787. return false;
  788. }
  789. }
  790. //proceed with window creation
  791. //m_visualizationTree.addVisualizationWindow(windowID, CString(label));
  792. m_tree.addVisualizationWidget(windowID, CString(label), EVTWidget::Window, CIdentifier::undefined(), 0, CIdentifier::undefined(), 0,
  793. CIdentifier::undefined());
  794. window = m_tree.getVisualizationWidget(windowID);
  795. //add attributes
  796. window->setWidth(1);
  797. window->setHeight(1);
  798. //create default visualization panel as well
  799. CIdentifier childID;
  800. const CString childName = "Default tab";
  801. m_tree.addVisualizationWidget(childID, childName, EVTWidget::Panel, windowID, 0, CIdentifier::undefined(), 1, CIdentifier::undefined());
  802. m_tree.reloadTree();
  803. //refresh view
  804. GtkTreeIter childIter;
  805. m_tree.findChildNodeFromRoot(&childIter, childID);
  806. refreshActiveVisualization(m_tree.getTreePath(&childIter));
  807. return true;
  808. }
  809. void CDesignerVisualization::askRenameVisualizationWindow()
  810. {
  811. //show dialog
  812. CInputDialog id(m_guiFile.c_str(), &CDesignerVisualization::renameVisualizationWindowCB, this, "Rename window", "Please enter new name of window : ");
  813. id.run();
  814. }
  815. bool CDesignerVisualization::renameVisualizationWindow(const char* label)
  816. {
  817. //retrieve visualization window
  818. GtkTreeIter iter;
  819. if (!m_tree.findChildNodeFromRoot(&iter, m_activeVisualizationWindowName, EVTNode::VisualizationWindow))
  820. {
  821. displayErrorDialog("Window renaming failed !", "Couldn't retrieve window.");
  822. return false;
  823. }
  824. CIdentifier windowID;
  825. m_tree.getIdentifierFromTreeIter(&iter, windowID, EVTColumn::StringIdentifier);
  826. IVTWidget* window = m_tree.getVisualizationWidget(windowID);
  827. if (window == nullptr)
  828. {
  829. displayErrorDialog("Window renaming failed !", "Couldn't retrieve window.");
  830. return false;
  831. }
  832. //if trying to set identical name, return
  833. const CString newName = label;
  834. if (window->getName() == newName) { return true; }
  835. //ensure name is unique
  836. CIdentifier id = CIdentifier::undefined();
  837. while (m_tree.getNextVisualizationWidgetIdentifier(id, EVTWidget::Window))
  838. {
  839. //name already in use : warn user
  840. if (m_tree.getVisualizationWidget(id)->getName() == newName)
  841. {
  842. displayErrorDialog("Window renaming failed !", "An existing window already uses this name. Please choose another name.");
  843. return false;
  844. }
  845. }
  846. //change its name
  847. window->setName(newName);
  848. m_tree.reloadTree();
  849. //refresh view
  850. m_tree.findChildNodeFromRoot(&iter, windowID);
  851. refreshActiveVisualization(m_tree.getTreePath(&iter));
  852. return true;
  853. }
  854. bool CDesignerVisualization::removeVisualizationWindow()
  855. {
  856. //retrieve visualization window
  857. CIdentifier windowID = CIdentifier::undefined();
  858. while (m_tree.getNextVisualizationWidgetIdentifier(windowID, EVTWidget::Window))
  859. {
  860. if (m_tree.getVisualizationWidget(windowID)->getName() == m_activeVisualizationWindowName) { break; }
  861. }
  862. //return if window was not found
  863. if (windowID == CIdentifier::undefined())
  864. {
  865. displayErrorDialog("Window removal failed !", "Couldn't retrieve window.");
  866. return false;
  867. }
  868. //destroy hierarchy but only unaffect visualization boxes
  869. m_tree.destroyHierarchy(windowID, false);
  870. m_tree.reloadTree();
  871. //refresh view
  872. refreshActiveVisualization(nullptr);
  873. return true;
  874. }
  875. void CDesignerVisualization::askNewVisualizationPanel()
  876. {
  877. //show dialog
  878. CInputDialog id(m_guiFile.c_str(), &CDesignerVisualization::newVisualizationPanelCB, this, "New tab", "Please enter name of new tab : ");
  879. id.run();
  880. }
  881. bool CDesignerVisualization::newVisualizationPanel(const char* label)
  882. {
  883. //retrieve visualization window
  884. IVTWidget* window = nullptr;
  885. CIdentifier windowID = CIdentifier::undefined();
  886. while (m_tree.getNextVisualizationWidgetIdentifier(windowID, EVTWidget::Window))
  887. {
  888. window = m_tree.getVisualizationWidget(windowID);
  889. if (window->getName() == m_activeVisualizationWindowName) { break; }
  890. }
  891. //return if parent window was not found
  892. if (windowID == CIdentifier::undefined() || window == nullptr)
  893. {
  894. displayErrorDialog("Tab creation failed !", "Couldn't retrieve parent window.");
  895. return false;
  896. }
  897. CIdentifier childID;
  898. const CString newName = label;
  899. //ensure visualization panel name is unique in this window
  900. for (size_t i = 0; i < window->getNbChildren(); ++i)
  901. {
  902. window->getChildIdentifier(i, childID);
  903. if (m_tree.getVisualizationWidget(childID)->getName() == newName)
  904. {
  905. displayErrorDialog("Tab creation failed !", "An existing tab already uses this name. Please choose another name.");
  906. return false;
  907. }
  908. }
  909. //proceed with panel creation
  910. m_tree.addVisualizationWidget(childID, newName, EVTWidget::Panel, windowID, window->getNbChildren(), CIdentifier::undefined(), 1,
  911. CIdentifier::undefined());
  912. m_tree.reloadTree();
  913. //refresh view
  914. GtkTreeIter iter;
  915. m_tree.findChildNodeFromRoot(&iter, childID);
  916. refreshActiveVisualization(m_tree.getTreePath(&iter));
  917. return true;
  918. }
  919. void CDesignerVisualization::askRenameVisualizationPanel()
  920. {
  921. //show dialog
  922. CInputDialog id(m_guiFile.c_str(), &CDesignerVisualization::renameVisualizationPanelCB, this, "Rename tab", "Please enter new name of tab : ");
  923. id.run();
  924. }
  925. bool CDesignerVisualization::renameVisualizationPanel(const char* label)
  926. {
  927. //retrieve visualization window
  928. GtkTreeIter iter;
  929. if (!m_tree.findChildNodeFromRoot(&iter, m_activeVisualizationWindowName.toASCIIString(), EVTNode::VisualizationWindow)
  930. )
  931. {
  932. displayErrorDialog("Tab renaming failed !", "Couldn't retrieve parent window.");
  933. return false;
  934. }
  935. CIdentifier windowID;
  936. m_tree.getIdentifierFromTreeIter(&iter, windowID, EVTColumn::StringIdentifier);
  937. IVTWidget* window = m_tree.getVisualizationWidget(windowID);
  938. if (window == nullptr)
  939. {
  940. displayErrorDialog("Tab renaming failed !", "Couldn't retrieve parent window.");
  941. return false;
  942. }
  943. //retrieve visualization panel
  944. if (!m_tree.findChildNodeFromParent(&iter, m_activeVisualizationPanelName.toASCIIString(), EVTNode::VisualizationPanel))
  945. {
  946. displayErrorDialog("Tab renaming failed !", "Couldn't retrieve tab.");
  947. return false;
  948. }
  949. CIdentifier panelID;
  950. m_tree.getIdentifierFromTreeIter(&iter, panelID, EVTColumn::StringIdentifier);
  951. IVTWidget* widget = m_tree.getVisualizationWidget(panelID);
  952. if (widget == nullptr)
  953. {
  954. displayErrorDialog("tab renaming failed !", "Couldn't retrieve tab.");
  955. return false;
  956. }
  957. //if trying to set identical name, return
  958. const CString newName = label;
  959. if (widget->getName() == newName) { return true; }
  960. //ensure visualization panel name is unique in this window
  961. CIdentifier childID;
  962. for (size_t i = 0; i < window->getNbChildren(); ++i)
  963. {
  964. window->getChildIdentifier(i, childID);
  965. if (m_tree.getVisualizationWidget(childID)->getName() == newName)
  966. {
  967. displayErrorDialog("Tab renaming failed !", "An existing tab already uses this name. Please choose another name.");
  968. return false;
  969. }
  970. }
  971. widget->setName(newName);
  972. m_tree.reloadTree();
  973. //refresh view
  974. m_tree.findChildNodeFromRoot(&iter, panelID);
  975. refreshActiveVisualization(m_tree.getTreePath(&iter));
  976. return true;
  977. }
  978. bool CDesignerVisualization::removeVisualizationPanel()
  979. {
  980. //retrieve visualization window
  981. GtkTreeIter iter;
  982. m_tree.findChildNodeFromRoot(&iter, m_activeVisualizationWindowName.toASCIIString(), EVTNode::VisualizationWindow);
  983. //retrieve visualization panel
  984. m_tree.findChildNodeFromParent(&iter, m_activeVisualizationPanelName.toASCIIString(), EVTNode::VisualizationPanel);
  985. CIdentifier panelID;
  986. m_tree.getIdentifierFromTreeIter(&iter, panelID, EVTColumn::StringIdentifier);
  987. //destroy hierarchy but only unaffect visualization boxes (as opposed to destroying them)
  988. if (!m_tree.destroyHierarchy(m_tree.getVisualizationWidget(panelID)->getIdentifier(), false))
  989. {
  990. displayErrorDialog("Tab removal failed !", "An error occurred while destroying widget hierarchy.");
  991. return false;
  992. }
  993. m_tree.reloadTree();
  994. //refresh view
  995. refreshActiveVisualization(nullptr);
  996. return true;
  997. }
  998. bool CDesignerVisualization::removeVisualizationWidget()
  999. {
  1000. //retrieve widget
  1001. GtkTreeIter iter;
  1002. if (!m_tree.getTreeSelection(m_treeView, &iter)) { return false; }
  1003. CIdentifier id;
  1004. m_tree.getIdentifierFromTreeIter(&iter, id, EVTColumn::StringIdentifier);
  1005. return removeVisualizationWidget(id);
  1006. }
  1007. //TODO : move this to CVisualizationTree?
  1008. bool CDesignerVisualization::removeVisualizationWidget(const CIdentifier& identifier)
  1009. {
  1010. IVTWidget* widget = m_tree.getVisualizationWidget(identifier);
  1011. if (widget == nullptr) { return false; }
  1012. IVTWidget* parentWidget = m_tree.getVisualizationWidget(widget->getParentIdentifier());
  1013. //unparent or destroy widget
  1014. size_t idx;
  1015. m_tree.unparentVisualizationWidget(identifier, idx);
  1016. if (widget->getType() != EVTWidget::Box) { m_tree.destroyHierarchy(identifier, false); }
  1017. //reparent other child widget, if any
  1018. if (parentWidget->getType() != EVTWidget::Panel)
  1019. {
  1020. //retrieve parent's other widget
  1021. CIdentifier otherWidgetID;
  1022. parentWidget->getChildIdentifier(1 - idx, otherWidgetID);
  1023. //unparent parent
  1024. size_t parentIdx;
  1025. const CIdentifier parentID = parentWidget->getParentIdentifier();
  1026. m_tree.unparentVisualizationWidget(parentWidget->getIdentifier(), parentIdx);
  1027. //reparent other widget to its grandparent
  1028. m_tree.unparentVisualizationWidget(otherWidgetID, idx);
  1029. m_tree.parentVisualizationWidget(otherWidgetID, parentID, parentIdx);
  1030. //destroy parent
  1031. m_tree.destroyHierarchy(parentWidget->getIdentifier(), false);
  1032. }
  1033. m_tree.reloadTree();
  1034. //refresh view
  1035. refreshActiveVisualization(nullptr);
  1036. return true;
  1037. }
  1038. bool CDesignerVisualization::destroyVisualizationWidget(const CIdentifier& identifier)
  1039. {
  1040. const bool b = removeVisualizationWidget(identifier);
  1041. m_tree.destroyHierarchy(identifier, true);
  1042. return b;
  1043. }
  1044. //CALLBACKS
  1045. //---------
  1046. void CDesignerVisualization::notebookPageSelectedCB(GtkNotebook* notebook, const guint pagenum)
  1047. {
  1048. GtkTreeIter iter;
  1049. m_tree.findChildNodeFromRoot(&iter, static_cast<void*>(notebook));
  1050. CIdentifier id;
  1051. m_tree.getIdentifierFromTreeIter(&iter, id, EVTColumn::StringIdentifier);
  1052. IVTWidget* widget = m_tree.getVisualizationWidget(id);
  1053. if (widget != nullptr)
  1054. {
  1055. IVTWidget* window = m_tree.getVisualizationWidget(widget->getParentIdentifier());
  1056. if (window != nullptr)
  1057. {
  1058. window->getChildIdentifier(pagenum, id);
  1059. if (m_tree.findChildNodeFromRoot(&iter, id)) { refreshActiveVisualization(m_tree.getTreePath(&iter)); }
  1060. }
  1061. }
  1062. }
  1063. void CDesignerVisualization::enableNotebookSignals(GtkWidget* notebook, const bool b)
  1064. {
  1065. if (b) { g_signal_connect(G_OBJECT(notebook), "switch-page", G_CALLBACK(notebookPageSwitchCB), this); }
  1066. else { g_signal_handlers_disconnect_by_func(G_OBJECT(notebook), G_CALLBACK2(notebookPageSwitchCB), this); }
  1067. }
  1068. void CDesignerVisualization::notifyPositionPaned(GtkWidget* widget)
  1069. {
  1070. GtkPaned* paned = GTK_PANED(widget);
  1071. //return if handle pos was changed because parent window was resized
  1072. const int pos = gtk_paned_get_position(paned);
  1073. const int maxPos = GTK_IS_VPANED(paned) ? paned->container.widget.allocation.height : paned->container.widget.allocation.width;
  1074. const int handleThickness = GTK_IS_VPANED(paned) ? paned->handle_pos.height : paned->handle_pos.width;
  1075. if (pos + handleThickness == maxPos) { return; }
  1076. //look for widget in tree
  1077. GtkWidget* treeWidget = getTreeWidget(widget);
  1078. GtkTreeIter iter;
  1079. if (m_tree.findChildNodeFromRoot(&iter, treeWidget))
  1080. {
  1081. CIdentifier id;
  1082. m_tree.getIdentifierFromTreeIter(&iter, id, EVTColumn::StringIdentifier);
  1083. //store new position and max position
  1084. auto* visualizationWidget = m_tree.getVisualizationWidget(id);
  1085. visualizationWidget->setDividerPosition(pos);
  1086. visualizationWidget->setMaxDividerPosition(maxPos);
  1087. }
  1088. }
  1089. void CDesignerVisualization::enablePanedSignals(GtkWidget* paned, const bool b)
  1090. {
  1091. if (b) { g_signal_connect(G_OBJECT(paned), "notify::position", G_CALLBACK(notifyPositionPanedCB), this); }
  1092. else { g_signal_handlers_disconnect_by_func(G_OBJECT(paned), G_CALLBACK2(notifyPositionPanedCB), this); }
  1093. }
  1094. void CDesignerVisualization::askNewVisualizationWindowCB(gpointer data, guint /*callback_action*/, GtkWidget* /*widget*/)
  1095. {
  1096. static_cast<CDesignerVisualization*>(data)->askNewVisualizationWindow();
  1097. }
  1098. void CDesignerVisualization::newVisualizationWindowCB(GtkWidget* /*widget*/, gpointer data)
  1099. {
  1100. CInputDialog* inputDialog = static_cast<CInputDialog*>(data);
  1101. if (inputDialog->getUserData() != nullptr)
  1102. {
  1103. static_cast<CDesignerVisualization*>(inputDialog->getUserData())->newVisualizationWindow(inputDialog->getEntry());
  1104. }
  1105. }
  1106. void CDesignerVisualization::askRenameVisualizationWindowCB(gpointer data, guint /*callback_action*/, GtkWidget* /*widget*/)
  1107. {
  1108. static_cast<CDesignerVisualization*>(data)->askRenameVisualizationWindow();
  1109. }
  1110. void CDesignerVisualization::renameVisualizationWindowCB(GtkWidget* /*widget*/, gpointer data)
  1111. {
  1112. CInputDialog* inputDialog = static_cast<CInputDialog*>(data);
  1113. if (inputDialog->getUserData() != nullptr)
  1114. {
  1115. static_cast<CDesignerVisualization*>(inputDialog->getUserData())->renameVisualizationWindow(inputDialog->getEntry());
  1116. }
  1117. }
  1118. void CDesignerVisualization::removeVisualizationWindowCB(gpointer data, guint /*action*/, GtkWidget* /*widget*/)
  1119. {
  1120. static_cast<CDesignerVisualization*>(data)->removeVisualizationWindow();
  1121. }
  1122. void CDesignerVisualization::askNewVisualizationPanelCB(gpointer data, guint /*action*/, GtkWidget* /*widget*/)
  1123. {
  1124. static_cast<CDesignerVisualization*>(data)->askNewVisualizationPanel();
  1125. }
  1126. void CDesignerVisualization::newVisualizationPanelCB(GtkWidget* /*widget*/, gpointer data)
  1127. {
  1128. auto* inputDialog = static_cast<CInputDialog*>(data);
  1129. if (inputDialog->getUserData() != nullptr)
  1130. {
  1131. static_cast<CDesignerVisualization*>(inputDialog->getUserData())->newVisualizationPanel(inputDialog->getEntry());
  1132. }
  1133. }
  1134. void CDesignerVisualization::askRenameVisualizationPanelCB(gpointer data, guint /*action*/, GtkWidget* /*widget*/)
  1135. {
  1136. static_cast<CDesignerVisualization*>(data)->askRenameVisualizationPanel();
  1137. }
  1138. void CDesignerVisualization::renameVisualizationPanelCB(GtkWidget* /*widget*/, gpointer data)
  1139. {
  1140. auto* inputDialog = static_cast<CInputDialog*>(data);
  1141. if (inputDialog->getUserData() != nullptr)
  1142. {
  1143. static_cast<CDesignerVisualization*>(inputDialog->getUserData())->renameVisualizationPanel(inputDialog->getEntry());
  1144. }
  1145. }
  1146. void CDesignerVisualization::removeVisualizationPanelCB(gpointer data, guint /*action*/, GtkWidget* /*widget*/)
  1147. {
  1148. static_cast<CDesignerVisualization*>(data)->removeVisualizationPanel();
  1149. }
  1150. void CDesignerVisualization::removeVisualizationWidgetCB(gpointer data, guint /*action*/, GtkWidget* /*widget*/)
  1151. {
  1152. static_cast<CDesignerVisualization*>(data)->removeVisualizationWidget();
  1153. }
  1154. void CDesignerVisualization::widgetKeyPressEventCB(GtkWidget* widget, GdkEventKey* event, gpointer data)
  1155. {
  1156. static_cast<CDesignerVisualization*>(data)->widgetKeyPressEvent(widget, event);
  1157. }
  1158. void CDesignerVisualization::widgetKeyPressEvent(GtkWidget* /*widget*/, GdkEventKey* event)
  1159. {
  1160. //remove widget
  1161. if (event->keyval == GDK_Delete || event->keyval == GDK_KP_Delete)
  1162. {
  1163. if (m_highlightedWidget != nullptr)
  1164. {
  1165. GtkTreeIter iter;
  1166. if (m_tree.findChildNodeFromRoot(&iter, getTreeWidget(m_highlightedWidget)))
  1167. {
  1168. CIdentifier id;
  1169. m_tree.getIdentifierFromTreeIter(&iter, id, EVTColumn::StringIdentifier);
  1170. removeVisualizationWidget(id);
  1171. }
  1172. }
  1173. }
  1174. }
  1175. gboolean CDesignerVisualization::widgetEnterNotifyEventCB(GtkWidget* widget, GdkEventCrossing* event, gpointer data)
  1176. {
  1177. static_cast<CDesignerVisualization*>(data)->widgetEnterNotifyEvent(widget, event);
  1178. return FALSE;
  1179. }
  1180. void CDesignerVisualization::widgetEnterNotifyEvent(GtkWidget* widget, GdkEventCrossing* /*event*/) { m_highlightedWidget = widget; }
  1181. gboolean CDesignerVisualization::widgetLeaveNotifyEventCB(GtkWidget* widget, GdkEventCrossing* event, gpointer data)
  1182. {
  1183. static_cast<CDesignerVisualization*>(data)->widgetLeaveNotifyEvent(widget, event);
  1184. return FALSE;
  1185. }
  1186. void CDesignerVisualization::widgetLeaveNotifyEvent(GtkWidget* /*widget*/, GdkEventCrossing* /*event*/) { m_highlightedWidget = nullptr; }
  1187. gboolean CDesignerVisualization::buttonReleaseCB(GtkWidget* widget, GdkEventButton* event, gpointer data)
  1188. {
  1189. static_cast<CDesignerVisualization*>(data)->buttonRelease(widget, event);
  1190. return FALSE;
  1191. }
  1192. void CDesignerVisualization::buttonRelease(GtkWidget* widget, GdkEventButton* event) const
  1193. {
  1194. if (GTK_IS_TREE_VIEW(widget))
  1195. {
  1196. if (event->button == 3) //right button
  1197. {
  1198. if (event->type != GDK_BUTTON_PRESS)
  1199. {
  1200. GtkTreeIter it;
  1201. if (!m_tree.getTreeSelection(m_treeView, &it)) { return; }
  1202. const EVTNode type = EVTNode(m_tree.getULongValueFromTreeIter(&it, EVTColumn::ULongNodeType));
  1203. if (type == EVTNode::Unaffected)
  1204. {
  1205. gtk_menu_popup(GTK_MENU(gtk_item_factory_get_widget(m_unaffectedItemFactory, "<unaffected_main>")),
  1206. nullptr, nullptr, nullptr, nullptr, event->button, event->time);
  1207. }
  1208. else if (type == EVTNode::VisualizationWindow)
  1209. {
  1210. gtk_menu_popup(GTK_MENU(gtk_item_factory_get_widget(m_visualizationWindowItemFactory, "<visualization_window_main>")),
  1211. nullptr, nullptr, nullptr, nullptr, event->button, event->time);
  1212. }
  1213. else if (type == EVTNode::VisualizationPanel)
  1214. {
  1215. gtk_menu_popup(GTK_MENU(gtk_item_factory_get_widget(m_visualizationPanelItemFactory, "<visualization_panel_main>")),
  1216. nullptr, nullptr, nullptr, nullptr, event->button, event->time);
  1217. }
  1218. else if (type == EVTNode::HorizontalSplit || type == EVTNode::VerticalSplit)
  1219. {
  1220. gtk_menu_popup(GTK_MENU(gtk_item_factory_get_widget(m_splitItemFactory, "<split_widget_main>")),
  1221. nullptr, nullptr, nullptr, nullptr, event->button, event->time);
  1222. }
  1223. else if (type == EVTNode::VisualizationBox)
  1224. {
  1225. //ensure visualization box is parented to a tab
  1226. if (m_tree.findParentNode(&it, EVTNode::VisualizationPanel))
  1227. {
  1228. gtk_menu_popup(GTK_MENU(gtk_item_factory_get_widget(m_visualizationBoxItemFactory, "<visualization_box_main>")),
  1229. nullptr, nullptr, nullptr, nullptr, event->button, event->time);
  1230. }
  1231. }
  1232. else if (type == EVTNode::Undefined)
  1233. {
  1234. //ensure empty plugin is not parented to a panel (because an empty widget is always present in an empty panel)
  1235. CIdentifier id;
  1236. m_tree.getIdentifierFromTreeIter(&it, id, EVTColumn::StringIdentifier);
  1237. IVTWidget* visuWidget = m_tree.getVisualizationWidget(id);
  1238. if (visuWidget != nullptr)
  1239. {
  1240. IVTWidget* parentVisuWidget = m_tree.getVisualizationWidget(visuWidget->getParentIdentifier());
  1241. if (parentVisuWidget != nullptr)
  1242. {
  1243. if (parentVisuWidget->getType() != EVTWidget::Panel)
  1244. {
  1245. gtk_menu_popup(GTK_MENU(gtk_item_factory_get_widget(m_undefinedItemFactory, "<undefined_widget_main>")),
  1246. nullptr, nullptr, nullptr, nullptr, event->button, event->time);
  1247. }
  1248. }
  1249. }
  1250. }
  1251. }
  1252. }
  1253. }
  1254. }
  1255. void CDesignerVisualization::cursorChangedCB(GtkTreeView* treeView, gpointer data) { static_cast<CDesignerVisualization*>(data)->cursorChanged(treeView); }
  1256. void CDesignerVisualization::cursorChanged(GtkTreeView* treeView)
  1257. {
  1258. //retrieve selection
  1259. GtkTreeIter selectionIt;
  1260. if (!m_tree.getTreeSelection(treeView, &selectionIt)) { return; }
  1261. //save active item
  1262. if (m_tree.getULongValueFromTreeIter(&selectionIt, EVTColumn::ULongNodeType) == size_t(EVTNode::VisualizationBox))
  1263. {
  1264. m_tree.getIdentifierFromTreeIter(&selectionIt, m_activeVisualizationBoxID, EVTColumn::StringIdentifier);
  1265. }
  1266. GtkTreeIter panelIt = selectionIt;
  1267. //if selection lies in a visualization panel subtree, display this subtree
  1268. if (m_tree.findParentNode(&panelIt, EVTNode::VisualizationPanel))
  1269. {
  1270. //get visualization panel name
  1271. char* panelName = nullptr;
  1272. m_tree.getStringValueFromTreeIter(&panelIt, panelName, EVTColumn::StringName);
  1273. //retrieve visualization window that contains selection
  1274. GtkTreeIter windowIt = panelIt;
  1275. if (m_tree.findParentNode(&windowIt, EVTNode::VisualizationWindow))
  1276. {
  1277. //get its name
  1278. char* windowName = nullptr;
  1279. m_tree.getStringValueFromTreeIter(&windowIt, windowName, EVTColumn::StringName);
  1280. //set active visualization
  1281. setActiveVisualization(windowName, panelName);
  1282. }
  1283. }
  1284. else
  1285. {
  1286. GtkTreeIter windowIt = selectionIt;
  1287. //if selection is a visualization window, display it
  1288. if (m_tree.findParentNode(&windowIt, EVTNode::VisualizationWindow))
  1289. {
  1290. //retrieve visualization window
  1291. CIdentifier windowID;
  1292. m_tree.getIdentifierFromTreeIter(&windowIt, windowID, EVTColumn::StringIdentifier);
  1293. IVTWidget* window = m_tree.getVisualizationWidget(windowID);
  1294. //if window has at least one panel
  1295. if (window->getNbChildren() > 0)
  1296. {
  1297. //retrieve first panel
  1298. CIdentifier panelID;
  1299. window->getChildIdentifier(0, panelID);
  1300. m_tree.findChildNodeFromParent(&panelIt, panelID);
  1301. //retrieve notebook
  1302. void* notebook = nullptr;
  1303. m_tree.getPointerValueFromTreeIter(&panelIt, notebook, EVTColumn::PointerWidget);
  1304. //get label of its active tab
  1305. GtkWidget* pageLabel = gtk_notebook_get_tab_label(
  1306. GTK_NOTEBOOK(notebook), gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook))));
  1307. //set active visualization
  1308. if (pageLabel != nullptr) { setActiveVisualization(window->getName().toASCIIString(), gtk_label_get_text(GTK_LABEL(pageLabel))); }
  1309. else { setActiveVisualization(window->getName().toASCIIString(), nullptr); }
  1310. }
  1311. else //window has no panels
  1312. {
  1313. setActiveVisualization(window->getName().toASCIIString(), nullptr);
  1314. }
  1315. }
  1316. else
  1317. {
  1318. //refresh active visualization (GtkWidgets may have changed if tree was reloaded)
  1319. setActiveVisualization(m_activeVisualizationWindowName, m_activeVisualizationPanelName);
  1320. }
  1321. }
  1322. }
  1323. void CDesignerVisualization::dragDataGetFromTreeCB(GtkWidget* srcWidget, GdkDragContext* /*dc*/, GtkSelectionData* selection,
  1324. guint /*info*/, guint /*time*/, gpointer /*data*/)
  1325. {
  1326. char str[1024];
  1327. sprintf(str, "%p", srcWidget);
  1328. gtk_selection_data_set_text(selection, str, gint(strlen(str)));
  1329. }
  1330. void CDesignerVisualization::dragDataGetFromWidgetCB(GtkWidget* srcWidget, GdkDragContext* /*dc*/, GtkSelectionData* selection,
  1331. guint /*info*/, guint /*time*/, gpointer /*data*/)
  1332. {
  1333. char str[1024];
  1334. sprintf(str, "%p", srcWidget);
  1335. gtk_selection_data_set_text(selection, str, gint(strlen(str)));
  1336. }
  1337. void CDesignerVisualization::dragDataReceivedInWidgetCB(GtkWidget* dstWidget, GdkDragContext* /*dc*/, gint /*x*/, gint /*y*/, GtkSelectionData* selection,
  1338. guint /*info*/, guint /*time*/, gpointer data)
  1339. {
  1340. static_cast<CDesignerVisualization*>(data)->dragDataReceivedInWidget(dstWidget, selection);
  1341. }
  1342. void CDesignerVisualization::dragDataReceivedInWidget(GtkWidget* dstWidget, GtkSelectionData* selection)
  1343. {
  1344. void* srcWidget = nullptr;
  1345. sscanf(reinterpret_cast<const char*>(gtk_selection_data_get_text(selection)), "%p", &srcWidget);
  1346. GtkTreeIter srcIter;
  1347. //retrieve source widget iterator
  1348. if (GTK_IS_TREE_VIEW(srcWidget))
  1349. {
  1350. //ensure dragged widget is a visualization box
  1351. if (!m_tree.findChildNodeFromRoot(&srcIter, m_activeVisualizationBoxID))
  1352. {
  1353. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "dragDataReceivedInWidget couldn't retrieve iterator of active visualization box!\n";
  1354. return;
  1355. }
  1356. }
  1357. else if (GTK_IS_BUTTON(srcWidget))
  1358. {
  1359. if (srcWidget == dstWidget) { return; }
  1360. if (!m_tree.findChildNodeFromRoot(&srcIter, getTreeWidget(GTK_WIDGET(srcWidget))))
  1361. {
  1362. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "dragDataReceivedInWidget couldn't retrieve iterator of dragged button!\n";
  1363. return;
  1364. }
  1365. }
  1366. else { return; }
  1367. //retrieve src widget identifier and src visualization widget
  1368. CIdentifier srcID;
  1369. m_tree.getIdentifierFromTreeIter(&srcIter, srcID, EVTColumn::StringIdentifier);
  1370. IVTWidget* srcVisualizationWidget = m_tree.getVisualizationWidget(srcID);
  1371. if (srcVisualizationWidget == nullptr)
  1372. {
  1373. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "dragDataReceivedInWidget couldn't retrieve source visualization widget!\n";
  1374. return;
  1375. }
  1376. //retrieve dest widget type
  1377. GtkTreeIter dstIter;
  1378. if (!m_tree.findChildNodeFromRoot(&dstIter, getTreeWidget(dstWidget)))
  1379. {
  1380. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "dragDataReceivedInWidget couldn't retrieve iterator of destination widget!\n";
  1381. return;
  1382. }
  1383. //if src widget is unaffected or if dest widget is a visualization box, perform the drop operation directly
  1384. if (srcVisualizationWidget->getParentIdentifier() == CIdentifier::undefined()
  1385. || m_tree.getULongValueFromTreeIter(&dstIter, EVTColumn::ULongNodeType) == size_t(EVTNode::VisualizationBox))
  1386. {
  1387. m_tree.dragDataReceivedInWidgetCB(srcID, dstWidget);
  1388. }
  1389. else //dest widget is a dummy : unaffect src widget and simplify the tree before performing the drop operation
  1390. {
  1391. //save dest widget identifier
  1392. CIdentifier dstID;
  1393. m_tree.getIdentifierFromTreeIter(&dstIter, dstID, EVTColumn::StringIdentifier);
  1394. //unaffect src widget, so that tree is simplified
  1395. if (!removeVisualizationWidget(srcID))
  1396. {
  1397. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug << "dragDataReceivedInWidget couldn't remove source widget from its parent!\n";
  1398. return;
  1399. }
  1400. //then drop it
  1401. if (!m_tree.findChildNodeFromRoot(&dstIter, dstID))
  1402. {
  1403. m_kernelCtx.getLogManager() << Kernel::LogLevel_Debug
  1404. << "dragDataReceivedInWidget couldn't retrieve iterator of dummy destination widget to delete!\n";
  1405. return;
  1406. }
  1407. void* newDstTreeWidget = nullptr;
  1408. m_tree.getPointerValueFromTreeIter(&dstIter, newDstTreeWidget, EVTColumn::PointerWidget);
  1409. m_tree.dragDataReceivedInWidgetCB(srcID, getVisualizationWidget(GTK_WIDGET(newDstTreeWidget)));
  1410. }
  1411. //refresh view
  1412. GtkTreeIter draggedIter;
  1413. m_tree.findChildNodeFromRoot(&draggedIter, srcID);
  1414. refreshActiveVisualization(m_tree.getTreePath(&draggedIter));
  1415. }
  1416. void CDesignerVisualization::dataReceivedInEventBoxCB(GtkWidget* dstWidget, GdkDragContext* /*dc*/, gint /*x*/, gint /*y*/, GtkSelectionData* selection,
  1417. guint /*info*/, guint /*time*/, gpointer data)
  1418. {
  1419. char buf[1024];
  1420. void* visualization = nullptr;
  1421. sscanf(static_cast<const char*>(data), "%p %s", &visualization, buf);
  1422. VisualizationToolkit::EDragLocation location;
  1423. if (strcmp(buf, "left") == 0) { location = VisualizationToolkit::EDragLocation::Left; }
  1424. else if (strcmp(buf, "right") == 0) { location = VisualizationToolkit::EDragLocation::Right; }
  1425. else if (strcmp(buf, "top") == 0) { location = VisualizationToolkit::EDragLocation::Top; }
  1426. else { location = VisualizationToolkit::EDragLocation::Bottom; }
  1427. static_cast<CDesignerVisualization*>(visualization)->dragDataReceivedInEventBox(dstWidget, selection, location);
  1428. }
  1429. void CDesignerVisualization::dragDataReceivedInEventBox(GtkWidget* dstWidget, GtkSelectionData* selection, const VisualizationToolkit::EDragLocation location)
  1430. {
  1431. void* srcWidget = nullptr;
  1432. sscanf(reinterpret_cast<const char*>(gtk_selection_data_get_text(selection)), "%p", &srcWidget);
  1433. GtkTreeIter srcIter;
  1434. //get iterator to src widget
  1435. if (GTK_IS_TREE_VIEW(srcWidget))
  1436. {
  1437. if (!m_tree.findChildNodeFromRoot(&srcIter, m_activeVisualizationBoxID)) { return; }
  1438. //get actual src widget (item being dropped) and ensure it isn't being dropped in its own table
  1439. m_tree.getPointerValueFromTreeIter(&srcIter, srcWidget, EVTColumn::PointerWidget);
  1440. if (srcWidget == gtk_widget_get_parent(dstWidget)) { return; }
  1441. }
  1442. else if (GTK_IS_BUTTON(srcWidget))
  1443. {
  1444. //ensure src widget isn't being dropped in its own table
  1445. if (gtk_widget_get_parent(GTK_WIDGET(srcWidget)) == gtk_widget_get_parent(dstWidget)) { return; }
  1446. m_tree.findChildNodeFromRoot(&srcIter, getTreeWidget(GTK_WIDGET(srcWidget)));
  1447. }
  1448. else { return; }
  1449. //ensure src widget is a visualization box
  1450. if (m_tree.getULongValueFromTreeIter(&srcIter, EVTColumn::ULongNodeType) != size_t(EVTNode::VisualizationBox)) { return; }
  1451. //retrieve src widget identifier
  1452. CIdentifier srcID;
  1453. m_tree.getIdentifierFromTreeIter(&srcIter, srcID, EVTColumn::StringIdentifier);
  1454. //if widget is unaffected, just drag n drop it
  1455. GtkTreeIter unaffectedIter = srcIter;
  1456. if (m_tree.findParentNode(&unaffectedIter, EVTNode::Unaffected)) { m_tree.dragDataReceivedOutsideWidgetCB(srcID, dstWidget, location); }
  1457. else
  1458. {
  1459. //save dest widget identifier
  1460. GtkTreeIter dstIter;
  1461. m_tree.findChildNodeFromRoot(&dstIter, getTreeWidget(dstWidget));
  1462. CIdentifier dstID;
  1463. m_tree.getIdentifierFromTreeIter(&dstIter, dstID, EVTColumn::StringIdentifier);
  1464. //if dest widget is src widget's parent (paned widget), drop src widget in corresponding event box of parent's other child
  1465. //(otherwise, DND will fail due to parent's removal during tree simplification process)
  1466. IVTWidget* srcVisualizationWidget = m_tree.getVisualizationWidget(srcID);
  1467. if (srcVisualizationWidget->getParentIdentifier() == dstID)
  1468. {
  1469. IVTWidget* srcParentWidget = m_tree.getVisualizationWidget(srcVisualizationWidget->getParentIdentifier());
  1470. srcParentWidget->getChildIdentifier(0, dstID);
  1471. if (srcID == dstID) { srcParentWidget->getChildIdentifier(1, dstID); }
  1472. }
  1473. //unaffect src widget, so that tree is simplified
  1474. removeVisualizationWidget(srcID);
  1475. //then drop it
  1476. m_tree.findChildNodeFromRoot(&dstIter, dstID);
  1477. void* newDstTreeWidget = nullptr;
  1478. m_tree.getPointerValueFromTreeIter(&dstIter, newDstTreeWidget, EVTColumn::PointerWidget);
  1479. m_tree.dragDataReceivedOutsideWidgetCB(srcID, getVisualizationWidget(GTK_WIDGET(newDstTreeWidget)), location);
  1480. }
  1481. //refresh view
  1482. GtkTreeIter draggedIter;
  1483. m_tree.findChildNodeFromRoot(&draggedIter, srcID);
  1484. refreshActiveVisualization(m_tree.getTreePath(&draggedIter));
  1485. }
  1486. } // namespace Designer
  1487. } // namespace OpenViBE