guiEditCtrl.cc 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2013 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "console/console.h"
  23. #include "console/consoleTypes.h"
  24. #include "graphics/dgl.h"
  25. #include "sim/simBase.h"
  26. #include "gui/guiCanvas.h"
  27. #include "gui/editor/guiEditCtrl.h"
  28. #include "platform/event.h"
  29. #include "io/fileStream.h"
  30. #include "gui/containers/guiScrollCtrl.h"
  31. IMPLEMENT_CONOBJECT(GuiEditCtrl);
  32. GuiEditCtrl::GuiEditCtrl() : mCurrentAddSet(NULL), mEditorRoot(NULL), mGridSnap(10, 10), mDragBeginPoint(-1, -1)
  33. {
  34. VECTOR_SET_ASSOCIATION(mSelectedControls);
  35. VECTOR_SET_ASSOCIATION(mDragBeginPoints);
  36. mActive = true;
  37. mDragBeginPoints.clear();
  38. mSelectedControls.clear();
  39. mUseGridSnap = true;
  40. mMouseLockedEditCtrl = nullptr;
  41. mDefaultCursor = NULL;
  42. mLeftRightCursor = NULL;
  43. mUpDownCursor = NULL;
  44. mNWSECursor = NULL;
  45. mNESWCursor = NULL;
  46. mMoveCursor = NULL;
  47. }
  48. bool GuiEditCtrl::onAdd()
  49. {
  50. if (!Parent::onAdd())
  51. return false;
  52. mTrash.registerObject();
  53. mSelectedSet.registerObject();
  54. mUndoManager.registerObject();
  55. return true;
  56. }
  57. void GuiEditCtrl::onRemove()
  58. {
  59. Parent::onRemove();
  60. mTrash.unregisterObject();
  61. mSelectedSet.unregisterObject();
  62. mUndoManager.unregisterObject();
  63. }
  64. ConsoleMethod(GuiEditCtrl, setRoot, void, 3, 3, "(GuiControl root) Sets the given control as root\n"
  65. "@return No return value.")
  66. {
  67. GuiControl* ctrl;
  68. if (!Sim::findObject(argv[2], ctrl))
  69. return;
  70. object->setRoot(ctrl);
  71. }
  72. ConsoleMethod(GuiEditCtrl, addNewCtrl, void, 3, 3, "(GuiControl ctrl) Adds the given control to the control list\n"
  73. "@return No return value.")
  74. {
  75. GuiControl* ctrl;
  76. if (!Sim::findObject(argv[2], ctrl))
  77. return;
  78. object->addNewControl(ctrl);
  79. }
  80. ConsoleMethod(GuiEditCtrl, addSelection, void, 3, 3, "(ctrlID) Adds the selected control.\n"
  81. "@return No return value.")
  82. {
  83. S32 id = dAtoi(argv[2]);
  84. object->addSelection(id);
  85. }
  86. ConsoleMethod(GuiEditCtrl, removeSelection, void, 3, 3, "(ctrlID) Removes the selected control from list.\n"
  87. "@return No return value.")
  88. {
  89. S32 id = dAtoi(argv[2]);
  90. object->removeSelection(id);
  91. }
  92. ConsoleMethod(GuiEditCtrl, clearSelection, void, 2, 2, "() Clear selected controls list.\n"
  93. "@return No return value.")
  94. {
  95. object->clearSelection();
  96. }
  97. ConsoleMethod(GuiEditCtrl, select, void, 3, 3, "(GuiControl ctrl) Finds and selects given object\n"
  98. "@return No return value.")
  99. {
  100. GuiControl* ctrl;
  101. if (!Sim::findObject(argv[2], ctrl))
  102. return;
  103. object->setSelection(ctrl);
  104. }
  105. ConsoleMethod(GuiEditCtrl, setCurrentAddSet, void, 3, 3, "(GuiControl ctrl) Set the current control set in which controls are added.\n"
  106. "@param ctrl The addset\n"
  107. "@return No return value.")
  108. {
  109. GuiControl* addSet;
  110. if (!Sim::findObject(argv[2], addSet))
  111. {
  112. Con::printf("%s(): Invalid control: %s", argv[0], argv[2]);
  113. return;
  114. }
  115. object->setCurrentAddSet(addSet);
  116. }
  117. ConsoleMethod(GuiEditCtrl, getCurrentAddSet, S32, 2, 2, "()\n @return Returns the set to which new controls will be added")
  118. {
  119. const GuiControl* add = object->getCurrentAddSet();
  120. return add ? add->getId() : 0;
  121. }
  122. ConsoleMethod(GuiEditCtrl, toggle, void, 2, 2, "() Toggle activation.\n"
  123. "@return No return value.")
  124. {
  125. object->setEditMode(!object->mActive);
  126. }
  127. ConsoleMethod(GuiEditCtrl, justify, void, 3, 3, "(int mode) Sets justification mode of selection\n"
  128. "@return No return value.")
  129. {
  130. object->justifySelection((GuiEditCtrl::Justification)dAtoi(argv[2]));
  131. }
  132. ConsoleMethod(GuiEditCtrl, bringToFront, void, 2, 2, "() Brings control to front\n"
  133. "@return No return value.")
  134. {
  135. object->bringToFront();
  136. }
  137. ConsoleMethod(GuiEditCtrl, pushToBack, void, 2, 2, "() Sends control to back\n"
  138. "@return No return value.")
  139. {
  140. object->pushToBack();
  141. }
  142. ConsoleMethod(GuiEditCtrl, deleteSelection, void, 2, 2, "Delete the selected text.\n"
  143. "@return No return value.")
  144. {
  145. object->deleteSelection();
  146. }
  147. ConsoleMethod(GuiEditCtrl, moveSelection, void, 4, 4, "(int deltax, int deltay) Moves selection to given (relative to current position) point\n"
  148. "@param deltax,deltay The change in coordinates.\n"
  149. "@return No return value.")
  150. {
  151. if (object->hasSnapToGrid())
  152. {
  153. object->moveAndSnapSelection(Point2I(dAtoi(argv[2]), dAtoi(argv[3])));
  154. }
  155. else
  156. {
  157. object->moveSelection(Point2I(dAtoi(argv[2]), dAtoi(argv[3])));
  158. }
  159. }
  160. ConsoleMethod(GuiEditCtrl, saveSelection, void, 3, 3, "(string fileName) Saves the current selection to given filename\n"
  161. "@return No return value.")
  162. {
  163. object->saveSelection(argv[2]);
  164. }
  165. ConsoleMethod(GuiEditCtrl, loadSelection, void, 3, 3, "(string fileName) Loads from given filename\n"
  166. "@return No return value.")
  167. {
  168. object->loadSelection(argv[2]);
  169. }
  170. ConsoleMethod(GuiEditCtrl, selectAll, void, 2, 2, "() Selects all controls\n"
  171. "@return No return value.")
  172. {
  173. object->selectAll();
  174. }
  175. ConsoleMethod(GuiEditCtrl, getSelected, S32, 2, 2, "() Gets the GUI control(s) the editor is currently selecting\n"
  176. "@return Returns the ID of the control.")
  177. {
  178. return object->getSelectedSet().getId();
  179. }
  180. ConsoleMethod(GuiEditCtrl, getTrash, S32, 2, 2, "() Gets the GUI controls(s) that are currently in the trash.\n"
  181. "@return Returns the ID of the control")
  182. {
  183. return object->getTrash().getId();
  184. }
  185. ConsoleMethod(GuiEditCtrl, getUndoManager, S32, 2, 2, "() Gets the Gui Editor's UndoManager object\n"
  186. "@return Returns the ID of the object.")
  187. {
  188. return object->getUndoManager().getId();
  189. }
  190. bool GuiEditCtrl::onWake()
  191. {
  192. if (!Parent::onWake())
  193. return false;
  194. // Set GUI Controls to DesignTime mode
  195. GuiControl::smDesignTime = true;
  196. GuiControl::smEditorHandle = this;
  197. setEditMode(true);
  198. return true;
  199. }
  200. void GuiEditCtrl::onSleep()
  201. {
  202. // Set GUI Controls to run time mode
  203. GuiControl::smDesignTime = false;
  204. GuiControl::smEditorHandle = NULL;
  205. Parent::onSleep();
  206. }
  207. void GuiEditCtrl::setRoot(GuiControl* root)
  208. {
  209. mEditorRoot = root;
  210. if (root != NULL) root->mIsContainer = true;
  211. mCurrentAddSet = mEditorRoot;
  212. Con::executef(this, 1, "onClearSelected");
  213. mSelectedControls.clear();
  214. }
  215. enum GuiEditConstants {
  216. GUI_BLACK = 0,
  217. GUI_WHITE = 255,
  218. NUT_SIZE = 4
  219. };
  220. // Sizing Cursors
  221. bool GuiEditCtrl::initCursors()
  222. {
  223. if (mMoveCursor == NULL || mUpDownCursor == NULL || mLeftRightCursor == NULL || mDefaultCursor == NULL || mNWSECursor == NULL || mNESWCursor == NULL)
  224. {
  225. SimObject* obj;
  226. obj = Sim::findObject("MoveCursor");
  227. mMoveCursor = dynamic_cast<GuiCursor*>(obj);
  228. obj = Sim::findObject("UpDownCursor");
  229. mUpDownCursor = dynamic_cast<GuiCursor*>(obj);
  230. obj = Sim::findObject("LeftRightCursor");
  231. mLeftRightCursor = dynamic_cast<GuiCursor*>(obj);
  232. obj = Sim::findObject("DefaultCursor");
  233. mDefaultCursor = dynamic_cast<GuiCursor*>(obj);
  234. obj = Sim::findObject("NESWCursor");
  235. mNESWCursor = dynamic_cast<GuiCursor*>(obj);
  236. obj = Sim::findObject("NWSECursor");
  237. mNWSECursor = dynamic_cast<GuiCursor*>(obj);
  238. obj = Sim::findObject("MoveCursor");
  239. mMoveCursor = dynamic_cast<GuiCursor*>(obj);
  240. return(mMoveCursor != NULL && mUpDownCursor != NULL && mLeftRightCursor != NULL && mDefaultCursor != NULL && mNWSECursor != NULL && mNESWCursor != NULL && mMoveCursor != NULL);
  241. }
  242. else
  243. return(true);
  244. }
  245. void GuiEditCtrl::setEditMode(bool value)
  246. {
  247. mActive = value;
  248. Con::executef(this, 1, "onClearSelected");
  249. mSelectedControls.clear();
  250. if (mActive && mAwake)
  251. mCurrentAddSet = mEditorRoot;
  252. }
  253. void GuiEditCtrl::setCurrentAddSet(GuiControl* ctrl, bool clearSelection)
  254. {
  255. if (ctrl != mCurrentAddSet)
  256. {
  257. if (clearSelection)
  258. {
  259. Con::executef(this, 1, "onClearSelected");
  260. mSelectedControls.clear();
  261. }
  262. mCurrentAddSet = ctrl;
  263. }
  264. }
  265. const GuiControl* GuiEditCtrl::getCurrentAddSet() const
  266. {
  267. return mCurrentAddSet ? mCurrentAddSet : mEditorRoot;
  268. }
  269. void GuiEditCtrl::clearSelection(void)
  270. {
  271. mSelectedControls.clear();
  272. if (isMethod("onClearSelected"))
  273. {
  274. Con::executef(this, 1, "onClearSelected");
  275. }
  276. }
  277. void GuiEditCtrl::setSelection(GuiControl* ctrl)
  278. {
  279. //sanity check
  280. if (!ctrl)
  281. return;
  282. if (mEditorRoot == ctrl)
  283. {
  284. mCurrentAddSet = ctrl;
  285. Con::executef(this, 1, "onClearSelected");
  286. mSelectedControls.clear();
  287. }
  288. else
  289. {
  290. // otherwise, we hit a new control...
  291. GuiControl* newAddSet = ctrl->getParent();
  292. //see if we should clear the old selection set
  293. if (newAddSet != mCurrentAddSet) {
  294. Con::executef(this, 1, "onClearSelected");
  295. mSelectedControls.clear();
  296. }
  297. //set the selection
  298. mCurrentAddSet = newAddSet;
  299. mSelectedControls.push_back(ctrl);
  300. Con::executef(this, 2, "onAddSelected", Con::getIntArg(ctrl->getId()));
  301. }
  302. }
  303. void GuiEditCtrl::addNewControl(GuiControl* ctrl)
  304. {
  305. if (!mCurrentAddSet)
  306. mCurrentAddSet = mEditorRoot;
  307. mCurrentAddSet->addObject(ctrl);
  308. Con::executef(this, 1, "onClearSelected");
  309. mSelectedControls.clear();
  310. //if (!(ctrl->isLocked())) {
  311. mSelectedControls.push_back(ctrl);
  312. Con::executef(this, 2, "onAddSelected", Con::getIntArg(ctrl->getId()));
  313. //}
  314. // undo
  315. Con::executef(this, 2, "onAddNewCtrl", Con::getIntArg(ctrl->getId()));
  316. }
  317. void GuiEditCtrl::drawNut(const Point2I& nut, ColorI& outlineColor, ColorI& nutColor)
  318. {
  319. RectI r(nut.x - NUT_SIZE, nut.y - NUT_SIZE, 2 * NUT_SIZE, 2 * NUT_SIZE);
  320. r.inset(1, 1);
  321. dglDrawRectFill(r, nutColor);
  322. r.inset(-1, -1);
  323. dglDrawRect(r, outlineColor);
  324. }
  325. static inline bool inNut(const Point2I& pt, S32 x, S32 y)
  326. {
  327. S32 dx = pt.x - x;
  328. S32 dy = pt.y - y;
  329. return dx <= NUT_SIZE && dx >= -NUT_SIZE && dy <= NUT_SIZE && dy >= -NUT_SIZE;
  330. }
  331. S32 GuiEditCtrl::getSizingHitKnobs(const Point2I& pt, const RectI& box)
  332. {
  333. S32 lx = box.point.x, rx = box.point.x + box.extent.x - 1;
  334. S32 cx = (lx + rx) >> 1;
  335. S32 ty = box.point.y, by = box.point.y + box.extent.y - 1;
  336. S32 cy = (ty + by) >> 1;
  337. // adjust nuts, so they dont straddle the controls
  338. lx -= NUT_SIZE;
  339. ty -= NUT_SIZE;
  340. rx += NUT_SIZE;
  341. by += NUT_SIZE;
  342. if (inNut(pt, lx, ty))
  343. return sizingLeft | sizingTop;
  344. if (inNut(pt, cx, ty))
  345. return sizingTop;
  346. if (inNut(pt, rx, ty))
  347. return sizingRight | sizingTop;
  348. if (inNut(pt, lx, by))
  349. return sizingLeft | sizingBottom;
  350. if (inNut(pt, cx, by))
  351. return sizingBottom;
  352. if (inNut(pt, rx, by))
  353. return sizingRight | sizingBottom;
  354. if (inNut(pt, lx, cy))
  355. return sizingLeft;
  356. if (inNut(pt, rx, cy))
  357. return sizingRight;
  358. return sizingNone;
  359. }
  360. void GuiEditCtrl::drawControlDecoration(GuiControl* ctrl, RectI& box, ColorI& outlineColor, ColorI& nutColor)
  361. {
  362. S32 lx = box.point.x, rx = box.point.x + box.extent.x - 1;
  363. S32 cx = (lx + rx) >> 1;
  364. S32 ty = box.point.y, by = box.point.y + box.extent.y - 1;
  365. S32 cy = (ty + by) >> 1;
  366. ColorI fillColor = mProfile->getFillColor(DisabledState);
  367. ColorI weakColor(fillColor.red, fillColor.green, fillColor.blue, 120);
  368. ColorI strongColor(fillColor.red, fillColor.green, fillColor.blue, 200);
  369. ColorI strongestColor(fillColor.red, fillColor.green, fillColor.blue, 255);
  370. drawTargetLines(lx, ty, weakColor, by, rx, strongColor);
  371. if (ctrl->isHidden())
  372. {
  373. box.inset(-1, -1);
  374. drawDashedLine(10, lx, ty, rx, ty, strongestColor);
  375. drawDashedLine(10, lx, ty, lx, by, strongestColor);
  376. drawDashedLine(10, rx, ty, rx, by, strongestColor);
  377. drawDashedLine(10, lx, by, rx, by, strongestColor);
  378. if (ctrl->isLocked())
  379. {
  380. box.inset(-1, -1);
  381. dglDrawRect(box, outlineColor);
  382. }
  383. }
  384. else if (ctrl->isLocked())
  385. {
  386. box.inset(-1, -1);
  387. dglDrawRect(box, strongestColor);
  388. box.inset(-1,-1);
  389. dglDrawRect(box, outlineColor);
  390. }
  391. else
  392. {
  393. drawNuts(lx, ty, rx, by, outlineColor, nutColor, cy, cx);
  394. }
  395. }
  396. void GuiEditCtrl::drawDashedLine(const F32& dashLength, const S32& x1, const S32& y1, const S32& x2, const S32& y2, ColorI& color)
  397. {
  398. F32 dx = x2 - x1;
  399. F32 dy = y2 - y1;
  400. F32 lineLength = mSqrt(dx * dx + dy * dy);
  401. S32 numSegments = mCeil(lineLength / dashLength);
  402. F32 unitX = dx / lineLength;
  403. F32 unitY = dy / lineLength;
  404. for (int i = 0; i < numSegments; i++)
  405. {
  406. F32 startX = x1 + (i * dashLength * unitX);
  407. F32 startY = y1 + (i * dashLength * unitY);
  408. F32 remainingLength = lineLength - (i * dashLength);
  409. F32 currentDashLength = getMin((dashLength), remainingLength);
  410. F32 endX = startX + (currentDashLength * unitX);
  411. F32 endY = startY + (currentDashLength * unitY);
  412. if (i % 2 == 0) {
  413. dglDrawLine(startX, startY, endX, endY, color);
  414. }
  415. }
  416. }
  417. void GuiEditCtrl::drawTargetLines(const S32& lx, const S32& ty, ColorI& weakColor, const S32& by, const S32& rx, ColorI& strongColor)
  418. {
  419. if (lx > 0 && ty > 0)
  420. {
  421. dglDrawLine(0, ty, lx, ty, weakColor);
  422. dglDrawLine(lx, 0, lx, ty, weakColor);
  423. }
  424. if (lx > 0 && by > 0)
  425. dglDrawLine(0, by, lx, by, weakColor);
  426. if (rx > 0 && ty > 0)
  427. dglDrawLine(rx, 0, rx, ty, weakColor);
  428. Point2I extent = localToGlobalCoord(mBounds.extent);
  429. if (lx < extent.x && by < extent.y)
  430. dglDrawLine(lx, by, lx, extent.y, strongColor);
  431. if (rx < extent.x && by < extent.y)
  432. {
  433. dglDrawLine(rx, by, rx, extent.y, strongColor);
  434. dglDrawLine(rx, by, extent.x, by, strongColor);
  435. }
  436. if (rx < extent.x && ty < extent.y)
  437. dglDrawLine(rx, ty, extent.x, ty, strongColor);
  438. }
  439. void GuiEditCtrl::drawNuts(S32& lx, S32& ty, S32& rx, S32& by, ColorI& outlineColor, ColorI& nutColor, const S32& cy, const S32& cx)
  440. {
  441. // adjust nuts, so they dont straddle the controls
  442. lx -= NUT_SIZE;
  443. ty -= NUT_SIZE;
  444. rx += NUT_SIZE;
  445. by += NUT_SIZE;
  446. drawNut(Point2I(lx, ty), outlineColor, nutColor);
  447. drawNut(Point2I(lx, cy), outlineColor, nutColor);
  448. drawNut(Point2I(lx, by), outlineColor, nutColor);
  449. drawNut(Point2I(rx, ty), outlineColor, nutColor);
  450. drawNut(Point2I(rx, cy), outlineColor, nutColor);
  451. drawNut(Point2I(rx, by), outlineColor, nutColor);
  452. drawNut(Point2I(cx, ty), outlineColor, nutColor);
  453. drawNut(Point2I(cx, by), outlineColor, nutColor);
  454. }
  455. void GuiEditCtrl::getDragRect(RectI& box)
  456. {
  457. box.point.x = getMin(mLastMousePos.x, mSelectionAnchor.x);
  458. box.extent.x = getMax(mLastMousePos.x, mSelectionAnchor.x) - box.point.x + 1;
  459. box.point.y = getMin(mLastMousePos.y, mSelectionAnchor.y);
  460. box.extent.y = getMax(mLastMousePos.y, mSelectionAnchor.y) - box.point.y + 1;
  461. }
  462. void GuiEditCtrl::onPreRender()
  463. {
  464. setUpdate();
  465. }
  466. void GuiEditCtrl::onRender(Point2I offset, const RectI& updateRect)
  467. {
  468. Point2I ctOffset;
  469. Point2I cext;
  470. bool keyFocused = isFirstResponder();
  471. if (mActive)
  472. {
  473. if (mCurrentAddSet)
  474. {
  475. // draw a white frame inset around the current add set.
  476. cext = mCurrentAddSet->getExtent();
  477. ctOffset = mCurrentAddSet->localToGlobalCoord(Point2I(0, 0));
  478. RectI box(ctOffset.x, ctOffset.y, cext.x, cext.y);
  479. ColorI fillColor = mProfile->getFillColor(DisabledState);
  480. bool isRoot = (getCurrentAddSet() == mEditorRoot);
  481. S32 d = isRoot ? 1 : -1;
  482. if (!isRoot)
  483. {
  484. box.inset(d, d);
  485. }
  486. dglDrawRect(box, ColorI(fillColor.red, fillColor.green, fillColor.blue, 200));
  487. box.inset(d, d);
  488. dglDrawRect(box, ColorI(fillColor.red, fillColor.green, fillColor.blue, 180));
  489. box.inset(d, d);
  490. dglDrawRect(box, ColorI(fillColor.red, fillColor.green, fillColor.blue, 160));
  491. box.inset(d, d);
  492. dglDrawRect(box, ColorI(fillColor.red, fillColor.green, fillColor.blue, 140));
  493. box.inset(d, d);
  494. dglDrawRect(box, ColorI(fillColor.red, fillColor.green, fillColor.blue, 120));
  495. }
  496. Vector<GuiControl*>::iterator i;
  497. bool multisel = mSelectedControls.size() > 1;
  498. for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
  499. {
  500. GuiControl* ctrl = (*i);
  501. cext = ctrl->getExtent();
  502. ctOffset = ctrl->localToGlobalCoord(Point2I(0, 0));
  503. RectI box(ctOffset.x, ctOffset.y, cext.x, cext.y);
  504. auto border = mProfile->getTopBorder();
  505. ColorI nutColor = multisel ? mProfile->getFillColor(HighlightState) : mProfile->getFillColor(NormalState);
  506. ColorI outlineColor = multisel ? border->getBorderColor(HighlightState) : border->getBorderColor(NormalState);
  507. if (keyFocused)
  508. {
  509. nutColor = mProfile->getFillColor(SelectedState);
  510. outlineColor = border->getBorderColor(SelectedState);
  511. }
  512. drawControlDecoration(ctrl, box, outlineColor, nutColor);
  513. }
  514. if (mMouseDownMode == DragSelecting)
  515. {
  516. RectI b;
  517. getDragRect(b);
  518. b.point += offset;
  519. ColorI fillColor = mProfile->getFillColor(DisabledState);
  520. dglDrawRect(b, fillColor);
  521. }
  522. }
  523. RectI contentRect = RectI(offset, mBounds.extent);
  524. renderChildControls(offset, contentRect, updateRect);
  525. if (mActive && mCurrentAddSet && mUseGridSnap &&
  526. (mMouseDownMode == MovingSelection || mMouseDownMode == SizingSelection))
  527. {
  528. Point2I cext = mCurrentAddSet->getExtent();
  529. Point2I coff = mCurrentAddSet->localToGlobalCoord(Point2I(0, 0));
  530. // create point-dots
  531. Point2I snap = mGridSnap;
  532. if (snap.x < 6)
  533. snap *= 2;
  534. if (snap.x < 6)
  535. snap *= 2;
  536. U32 maxdot = (cext.x / snap.x) * (cext.y / snap.y);
  537. Point2F* dots = new Point2F[maxdot];
  538. U32 ndot = 0;
  539. for (U32 ix = (U32)snap.x; ix < (U32)cext.x; ix += snap.x)
  540. {
  541. for (U32 iy = snap.y; iy < (U32)cext.y; iy += snap.y)
  542. {
  543. dots[ndot].x = (F32)(ix + coff.x);
  544. dots[ndot++].y = (F32)(iy + coff.y);
  545. }
  546. }
  547. AssertFatal(ndot <= maxdot, "dot overflow");
  548. // draw the points.
  549. ColorI fillColor = mProfile->getFillColor(DisabledState);
  550. glEnableClientState(GL_VERTEX_ARRAY);
  551. glEnable(GL_BLEND);
  552. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  553. glVertexPointer(2, GL_FLOAT, 0, dots);
  554. glColor4ub(fillColor.red, fillColor.green, fillColor.blue, 200);
  555. glDrawArrays(GL_POINTS, 0, ndot);
  556. glDisableClientState(GL_VERTEX_ARRAY);
  557. glDisable(GL_BLEND);
  558. delete[] dots;
  559. }
  560. }
  561. bool GuiEditCtrl::selectionContains(GuiControl* ctrl)
  562. {
  563. Vector<GuiControl*>::iterator i;
  564. for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
  565. if (ctrl == *i) return true;
  566. return false;
  567. }
  568. void GuiEditCtrl::onRightMouseDown(const GuiEvent& event)
  569. {
  570. if (!mActive || !mEditorRoot)
  571. {
  572. Parent::onRightMouseDown(event);
  573. return;
  574. }
  575. setFirstResponder();
  576. //search for the control hit in any layer below the edit layer
  577. GuiControl* hitCtrl = mEditorRoot->findHitControl(globalToLocalCoord(event.mousePoint), mLayer - 1, true, true);
  578. if (hitCtrl != mCurrentAddSet)
  579. {
  580. Con::executef(this, 1, "onClearSelected");
  581. mSelectedControls.clear();
  582. mCurrentAddSet = hitCtrl;
  583. }
  584. // select the parent if we right-click on the current add set
  585. else if (mCurrentAddSet != mEditorRoot)
  586. {
  587. mCurrentAddSet = hitCtrl->getParent();
  588. select(hitCtrl);
  589. }
  590. //Design time mouse events
  591. GuiEvent designEvent = event;
  592. designEvent.mousePoint = mLastMousePos;
  593. Point2I localOffset = localToGlobalCoord(Point2I(0, 0));
  594. hitCtrl->onRightMouseDownEditor(designEvent, localOffset);
  595. }
  596. void GuiEditCtrl::select(GuiControl* ctrl)
  597. {
  598. Con::executef(this, 1, "onClearSelected");
  599. mSelectedControls.clear();
  600. if (ctrl != mEditorRoot) {
  601. //if (!(ctrl->isLocked())) {
  602. mSelectedControls.push_back(ctrl);
  603. Con::executef(this, 2, "onAddSelected", Con::getIntArg(ctrl->getId()));
  604. //}
  605. }
  606. else
  607. mCurrentAddSet = mEditorRoot;
  608. }
  609. void GuiEditCtrl::getCursor(GuiCursor*& cursor, bool& showCursor, const GuiEvent& lastGuiEvent)
  610. {
  611. showCursor = true;
  612. Point2I ctOffset;
  613. Point2I cext;
  614. GuiControl* ctrl;
  615. Point2I mousePos = globalToLocalCoord(lastGuiEvent.mousePoint);
  616. // first see if we hit a sizing knob on the currently selected control...
  617. if (mSelectedControls.size() == 1 && initCursors() == true)
  618. {
  619. ctrl = mSelectedControls.first();
  620. cext = ctrl->getExtent();
  621. ctOffset = globalToLocalCoord(ctrl->localToGlobalCoord(Point2I(0, 0)));
  622. RectI box(ctOffset.x, ctOffset.y, cext.x, cext.y);
  623. GuiEditCtrl::sizingModes sizeMode = (GuiEditCtrl::sizingModes)getSizingHitKnobs(mousePos, box);
  624. if (mMouseDownMode == SizingSelection)
  625. {
  626. if ((mSizingMode == (sizingBottom | sizingRight)) || (mSizingMode == (sizingTop | sizingLeft)))
  627. cursor = mNWSECursor;
  628. else if ((mSizingMode == (sizingBottom | sizingLeft)) || (mSizingMode == (sizingTop | sizingRight)))
  629. cursor = mNESWCursor;
  630. else if (mSizingMode == sizingLeft || mSizingMode == sizingRight)
  631. cursor = mLeftRightCursor;
  632. else if (mSizingMode == sizingTop || mSizingMode == sizingBottom)
  633. cursor = mUpDownCursor;
  634. else
  635. cursor = NULL;
  636. }
  637. else
  638. {
  639. // Check for current mouse position after checking for actual sizing mode
  640. if ((sizeMode == (sizingBottom | sizingRight)) ||
  641. (sizeMode == (sizingTop | sizingLeft)))
  642. cursor = mNWSECursor;
  643. else if ((sizeMode == (sizingBottom | sizingLeft)) ||
  644. (sizeMode == (sizingTop | sizingRight)))
  645. cursor = mNESWCursor;
  646. else if (sizeMode == sizingLeft || sizeMode == sizingRight)
  647. cursor = mLeftRightCursor;
  648. else if (sizeMode == sizingTop || sizeMode == sizingBottom)
  649. cursor = mUpDownCursor;
  650. else
  651. cursor = NULL;
  652. }
  653. }
  654. if (mMouseDownMode == MovingSelection && cursor == NULL)
  655. cursor = mMoveCursor;
  656. }
  657. void GuiEditCtrl::onTouchDown(const GuiEvent& event)
  658. {
  659. if (!mActive)
  660. {
  661. Parent::onTouchDown(event);
  662. return;
  663. }
  664. if (!mEditorRoot)
  665. return;
  666. setFirstResponder();
  667. //lock the mouse
  668. mouseLock();
  669. Point2I ctOffset;
  670. Point2I cext;
  671. GuiControl* ctrl;
  672. mLastMousePos = globalToLocalCoord(event.mousePoint);
  673. // first see if we hit a sizing knob on the currently selected control...
  674. if (mSelectedControls.size() == 1)
  675. {
  676. ctrl = mSelectedControls.first();
  677. cext = ctrl->getExtent();
  678. ctOffset = globalToLocalCoord(ctrl->localToGlobalCoord(Point2I(0, 0)));
  679. RectI box(ctOffset.x, ctOffset.y, cext.x, cext.y);
  680. if ((mSizingMode = (GuiEditCtrl::sizingModes)getSizingHitKnobs(mLastMousePos, box)) != 0)
  681. {
  682. mMouseDownMode = SizingSelection;
  683. // undo
  684. Con::executef(this, 2, "onPreEdit", Con::getIntArg(getSelectedSet().getId()));
  685. return;
  686. }
  687. }
  688. if (!mCurrentAddSet)
  689. mCurrentAddSet = mEditorRoot;
  690. bool handledEvent = false;
  691. Point2I editorOffset = localToGlobalCoord(Point2I(0, 0));
  692. if (mMouseLockedEditCtrl)
  693. {
  694. handledEvent = mMouseLockedEditCtrl->onMouseDownEditor(event, editorOffset);
  695. }
  696. else
  697. {
  698. //find the control we clicked
  699. ctrl = mEditorRoot->findHitControl(mLastMousePos, mCurrentAddSet->mLayer, true, true);
  700. handledEvent = ctrl->onMouseDownEditor(event, editorOffset);
  701. }
  702. if (handledEvent)
  703. {
  704. // The Control handled the event and requested the edit ctrl
  705. // *NOT* act on it. The dude abides.
  706. return;
  707. }
  708. else if (selectionContains(ctrl))
  709. {
  710. //if we're holding shift, de-select the clicked ctrl
  711. if (event.modifier & SI_SHIFT)
  712. {
  713. Vector<GuiControl*>::iterator i;
  714. for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
  715. {
  716. if (*i == ctrl)
  717. {
  718. Con::executef(this, 2, "onRemoveSelected", Con::getIntArg(ctrl->getId()));
  719. mSelectedControls.erase(i);
  720. break;
  721. }
  722. }
  723. //set the mode
  724. mMouseDownMode = Selecting;
  725. }
  726. else //else we hit a ctrl we've already selected, so set the mode to moving
  727. {
  728. // For calculating mouse delta
  729. mDragBeginPoint = event.mousePoint;
  730. // Allocate enough space for our selected controls
  731. mDragBeginPoints.reserve(mSelectedControls.size());
  732. // For snapping to origin
  733. Vector<GuiControl*>::iterator i;
  734. for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
  735. mDragBeginPoints.push_back((*i)->mBounds.point);
  736. // Set Mouse Mode
  737. mMouseDownMode = MovingSelection;
  738. // undo
  739. Con::executef(this, 2, "onPreEdit", Con::getIntArg(getSelectedSet().getId()));
  740. }
  741. }
  742. else //else we clicked on an unselected control
  743. {
  744. //if we clicked in the current add set
  745. if (ctrl == mCurrentAddSet)
  746. {
  747. // start dragging a rectangle
  748. // if the shift is not down, nuke prior selection
  749. if (!(event.modifier & SI_SHIFT)) {
  750. Con::executef(this, 1, "onClearSelected");
  751. mSelectedControls.clear();
  752. }
  753. mSelectionAnchor = mLastMousePos;
  754. mMouseDownMode = DragSelecting;
  755. }
  756. else
  757. {
  758. //find the new add set
  759. GuiControl* newAddSet = ctrl->getParent();
  760. //if we're holding shift and the ctrl is in the same add set
  761. if (event.modifier & SI_SHIFT && newAddSet == mCurrentAddSet)
  762. {
  763. mSelectedControls.push_back(ctrl);
  764. Con::executef(this, 2, "onAddSelected", Con::getIntArg(ctrl->getId()));
  765. mMouseDownMode = Selecting;
  766. }
  767. else if (ctrl != mEditorRoot)
  768. {
  769. //find and set the new add set
  770. mCurrentAddSet = newAddSet;
  771. //clear and set the selected controls
  772. Con::executef(this, 1, "onClearSelected");
  773. mSelectedControls.clear();
  774. mSelectedControls.push_back(ctrl);
  775. Con::executef(this, 2, "onAddSelected", Con::getIntArg(ctrl->getId()));
  776. mMouseDownMode = Selecting;
  777. }
  778. else
  779. mMouseDownMode = Selecting;
  780. }
  781. }
  782. }
  783. void GuiEditCtrl::addSelection(S32 id)
  784. {
  785. GuiControl* ctrl;
  786. if (Sim::findObject(id, ctrl))
  787. mSelectedControls.push_back(ctrl);
  788. }
  789. void GuiEditCtrl::removeSelection(S32 id)
  790. {
  791. GuiControl* ctrl;
  792. if (Sim::findObject(id, ctrl)) {
  793. Vector<GuiControl*>::iterator i;
  794. for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
  795. {
  796. if (*i == ctrl)
  797. {
  798. mSelectedControls.erase(i);
  799. break;
  800. }
  801. }
  802. }
  803. }
  804. void GuiEditCtrl::onTouchUp(const GuiEvent& event)
  805. {
  806. if (!mActive || !mEditorRoot || !mCurrentAddSet)
  807. {
  808. Parent::onTouchUp(event);
  809. return;
  810. }
  811. //find the control we clicked
  812. GuiControl* ctrl = mEditorRoot->findHitControl(mLastMousePos, mCurrentAddSet->mLayer, true, true);
  813. Point2I localOffset = localToGlobalCoord(Point2I(0, 0));
  814. bool handledEvent = false;
  815. if (mMouseLockedEditCtrl)
  816. {
  817. handledEvent = mMouseLockedEditCtrl->onMouseUpEditor(event, localOffset);
  818. }
  819. else
  820. {
  821. handledEvent = ctrl->onMouseUpEditor(event, localOffset);
  822. }
  823. if (handledEvent == true)
  824. {
  825. // The Control handled the event and requested the edit ctrl
  826. // *NOT* act on it. The dude abides.
  827. return;
  828. }
  829. //unlock the mouse
  830. mouseUnlock();
  831. editMouseUnlock();
  832. // Reset Drag Axis Alignment Information
  833. mDragBeginPoint.set(-1, -1);
  834. mDragBeginPoints.clear();
  835. mLastMousePos = globalToLocalCoord(event.mousePoint);
  836. if (mMouseDownMode == DragSelecting)
  837. {
  838. RectI b;
  839. getDragRect(b);
  840. GuiControl::iterator i;
  841. for (i = mCurrentAddSet->begin(); i != mCurrentAddSet->end(); i++)
  842. {
  843. GuiControl* ctrl = dynamic_cast<GuiControl*>(*i);
  844. Point2I upperL = globalToLocalCoord(ctrl->localToGlobalCoord(Point2I(0, 0)));
  845. Point2I lowerR = upperL + ctrl->mBounds.extent - Point2I(1, 1);
  846. if (b.pointInRect(upperL) && b.pointInRect(lowerR) && !selectionContains(ctrl)) {
  847. mSelectedControls.push_back(ctrl);
  848. Con::executef(this, 2, "onAddSelected", Con::getIntArg(ctrl->getId()));
  849. }
  850. }
  851. }
  852. if (isMethod("onEdit") && mSelectedControls.size() > 0)
  853. {
  854. Con::executef(this, 2, "onEdit", Con::getIntArg(mSelectedControls[0]->getId()));
  855. }
  856. // deliver post edit event if we've been editing
  857. // note: paxorr: this may need to be moved earlier, if the selection has changed.
  858. // undo
  859. if (mMouseDownMode == SizingSelection || mMouseDownMode == MovingSelection)
  860. Con::executef(this, 2, "onPostEdit", Con::getIntArg(getSelectedSet().getId()));
  861. //reset the mouse mode
  862. setFirstResponder();
  863. mMouseDownMode = Selecting;
  864. }
  865. void GuiEditCtrl::onTouchDragged(const GuiEvent& event)
  866. {
  867. if (!mActive || !mEditorRoot || !mCurrentAddSet)
  868. {
  869. Parent::onTouchDragged(event);
  870. return;
  871. }
  872. if (!mCurrentAddSet)
  873. mCurrentAddSet = mEditorRoot;
  874. Point2I mousePoint = globalToLocalCoord(event.mousePoint);
  875. Point2I localOffset = localToGlobalCoord(Point2I(0, 0));
  876. bool handledEvent = false;
  877. if (mMouseLockedEditCtrl)
  878. {
  879. handledEvent = mMouseLockedEditCtrl->onMouseDraggedEditor(event, localOffset);
  880. }
  881. else
  882. {
  883. GuiControl* ctrl = mEditorRoot->findHitControl(mousePoint, mCurrentAddSet->mLayer, true, true);
  884. handledEvent = ctrl->onMouseDraggedEditor(event, localOffset);
  885. }
  886. if (handledEvent)
  887. {
  888. // The Control handled the event and requested the edit ctrl
  889. // *NOT* act on it. The dude abides.
  890. return;
  891. }
  892. if (mMouseDownMode == SizingSelection)
  893. {
  894. if (mGridSnap.x)
  895. mousePoint.x -= mousePoint.x % mGridSnap.x;
  896. if (mGridSnap.y)
  897. mousePoint.y -= mousePoint.y % mGridSnap.y;
  898. GuiControl* ctrl = mSelectedControls.first();
  899. // can't resize a locked control
  900. if (ctrl && ctrl->isLocked())
  901. return;
  902. Point2I ctrlPoint = mCurrentAddSet->globalToLocalCoord(event.mousePoint);
  903. if (mGridSnap.x)
  904. ctrlPoint.x -= ctrlPoint.x % mGridSnap.x;
  905. if (mGridSnap.y)
  906. ctrlPoint.y -= ctrlPoint.y % mGridSnap.y;
  907. Point2I newPosition = ctrl->getPosition();
  908. Point2I newExtent = ctrl->getExtent();
  909. Point2I minExtent = ctrl->getMinExtent();
  910. if (mSizingMode & sizingLeft)
  911. {
  912. newPosition.x = ctrlPoint.x;
  913. newExtent.x = ctrl->mBounds.extent.x + ctrl->mBounds.point.x - ctrlPoint.x;
  914. if (newExtent.x < minExtent.x)
  915. {
  916. newPosition.x -= minExtent.x - newExtent.x;
  917. newExtent.x = minExtent.x;
  918. }
  919. }
  920. else if (mSizingMode & sizingRight)
  921. {
  922. newExtent.x = ctrlPoint.x - ctrl->mBounds.point.x;
  923. if (mGridSnap.x)
  924. newExtent.x -= newExtent.x % mGridSnap.x;
  925. if (newExtent.x < minExtent.x)
  926. newExtent.x = minExtent.x;
  927. }
  928. if (mSizingMode & sizingTop)
  929. {
  930. newPosition.y = ctrlPoint.y;
  931. newExtent.y = ctrl->mBounds.extent.y + ctrl->mBounds.point.y - ctrlPoint.y;
  932. if (newExtent.y < minExtent.y)
  933. {
  934. newPosition.y -= minExtent.y - newExtent.y;
  935. newExtent.y = minExtent.y;
  936. }
  937. }
  938. else if (mSizingMode & sizingBottom)
  939. {
  940. newExtent.y = ctrlPoint.y - ctrl->mBounds.point.y;
  941. if (newExtent.y < minExtent.y)
  942. newExtent.y = minExtent.y;
  943. }
  944. if (mGridSnap.x)
  945. {
  946. newPosition.x -= newPosition.x % mGridSnap.x;
  947. newExtent.x -= newExtent.x % mGridSnap.x;
  948. }
  949. if (mGridSnap.y)
  950. {
  951. newPosition.y -= newPosition.y % mGridSnap.y;
  952. newExtent.y -= newExtent.y % mGridSnap.y;
  953. }
  954. ctrl->resize(newPosition, newExtent);
  955. }
  956. else if (mMouseDownMode == MovingSelection && mSelectedControls.size())
  957. {
  958. Vector<GuiControl*>::iterator i = mSelectedControls.begin();
  959. //Point2I minPos = (*i)->mBounds.point;
  960. Point2I minPos(S32_MAX, S32_MAX);
  961. for (; i != mSelectedControls.end(); i++)
  962. {
  963. // skip locked controls
  964. if ((*i)->isLocked())
  965. continue;
  966. if ((*i)->mBounds.point.x < minPos.x)
  967. minPos.x = (*i)->mBounds.point.x;
  968. if ((*i)->mBounds.point.y < minPos.y)
  969. minPos.y = (*i)->mBounds.point.y;
  970. }
  971. Point2I delta = mousePoint - mLastMousePos;
  972. delta += minPos; // find new minPos;
  973. if (mGridSnap.x)
  974. delta.x -= delta.x % mGridSnap.x;
  975. if (mGridSnap.y)
  976. delta.y -= delta.y % mGridSnap.y;
  977. delta -= minPos;
  978. // Do we want to align this drag to the X and Y axes within a certain threshold?
  979. if (event.modifier & SI_SHIFT)
  980. {
  981. Point2I dragTotalDelta = event.mousePoint - mDragBeginPoint;
  982. if (dragTotalDelta.y < 10 && dragTotalDelta.y > -10)
  983. {
  984. for (S32 i = 0; i < mSelectedControls.size(); i++)
  985. {
  986. // skip locked controls
  987. if (mSelectedControls[i]->isLocked())
  988. continue;
  989. Point2I snapBackPoint(mSelectedControls[i]->mBounds.point.x, mDragBeginPoints[i].y);
  990. // This is kind of nasty but we need to snap back if we're not at origin point with selection - JDD
  991. if (mSelectedControls[i]->mBounds.point.y != mDragBeginPoints[i].y)
  992. mSelectedControls[i]->resize(snapBackPoint, mSelectedControls[i]->mBounds.extent);
  993. }
  994. delta.y = 0;
  995. }
  996. if (dragTotalDelta.x < 10 && dragTotalDelta.x > -10)
  997. {
  998. for (S32 i = 0; i < mSelectedControls.size(); i++)
  999. {
  1000. // skip locked controls
  1001. if (mSelectedControls[i]->isLocked())
  1002. continue;
  1003. Point2I snapBackPoint(mDragBeginPoints[i].x, mSelectedControls[i]->mBounds.point.y);
  1004. // This is kind of nasty but we need to snap back if we're not at origin point with selection - JDD
  1005. if (mSelectedControls[i]->mBounds.point.x != mDragBeginPoints[i].x)
  1006. mSelectedControls[i]->resize(snapBackPoint, mSelectedControls[i]->mBounds.extent);
  1007. }
  1008. delta.x = 0;
  1009. }
  1010. }
  1011. moveSelection(delta);
  1012. // find the current control under the mouse but not in the selected set.
  1013. GuiControl* inCtrl = mEditorRoot->findHitControl(mousePoint, mCurrentAddSet->mLayer, true, false);
  1014. // find the nearest control up the heirarchy from the control the mouse is in
  1015. // that is flagged as a container.
  1016. while (!inCtrl->mIsContainer)
  1017. inCtrl = inCtrl->getParent();
  1018. // if the control under the mouse is not our parent, move the selected controls
  1019. // into the new parent.
  1020. if (mSelectedControls[0]->getParent() != inCtrl && inCtrl->mIsContainer)
  1021. {
  1022. moveSelectionToCtrl(inCtrl);
  1023. setCurrentAddSet(inCtrl, false);
  1024. }
  1025. mLastMousePos += delta;
  1026. }
  1027. else
  1028. {
  1029. mLastMousePos = mousePoint;
  1030. }
  1031. if (isMethod("onEdit") && mSelectedControls.size() > 0)
  1032. {
  1033. Con::executef(this, 2, "onEdit", Con::getIntArg(mSelectedControls[0]->getId()));
  1034. }
  1035. }
  1036. void GuiEditCtrl::moveSelectionToCtrl(GuiControl* newParent)
  1037. {
  1038. for (int i = 0; i < mSelectedControls.size(); i++)
  1039. {
  1040. GuiControl* ctrl = mSelectedControls[i];
  1041. if (ctrl->getParent() == newParent)
  1042. continue;
  1043. // skip locked controls
  1044. if (ctrl->isLocked())
  1045. continue;
  1046. Point2I globalpos = ctrl->localToGlobalCoord(Point2I(0, 0));
  1047. newParent->addObject(ctrl);
  1048. Point2I newpos = ctrl->globalToLocalCoord(globalpos) + ctrl->mBounds.point;
  1049. ctrl->mBounds.set(newpos, ctrl->mBounds.extent);
  1050. }
  1051. Con::executef(this, 2, "onSelectionParentChange", Con::getIntArg(newParent->getId()));
  1052. }
  1053. static Point2I snapPoint(Point2I point, Point2I delta, Point2I gridSnap)
  1054. {
  1055. S32 snap;
  1056. if (gridSnap.x && delta.x)
  1057. {
  1058. snap = point.x % gridSnap.x;
  1059. point.x -= snap;
  1060. if (delta.x > 0 && snap != 0)
  1061. point.x += gridSnap.x;
  1062. }
  1063. if (gridSnap.y && delta.y)
  1064. {
  1065. snap = point.y % gridSnap.y;
  1066. point.y -= snap;
  1067. if (delta.y > 0 && snap != 0)
  1068. point.y += gridSnap.y;
  1069. }
  1070. return point;
  1071. }
  1072. //-----------------
  1073. void GuiEditCtrl::moveAndSnapSelection(const Point2I& delta)
  1074. {
  1075. // move / nudge gets a special callback so that multiple small moves can be
  1076. // coalesced into one large undo action.
  1077. // undo
  1078. Con::executef(this, 2, "onPreSelectionNudged", Con::getIntArg(getSelectedSet().getId()));
  1079. Vector<GuiControl*>::iterator i;
  1080. Point2I newPos;
  1081. for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
  1082. {
  1083. newPos = (*i)->mBounds.point + delta;
  1084. newPos = snapPoint(newPos, delta, mGridSnap);
  1085. (*i)->resize(newPos, (*i)->mBounds.extent);
  1086. }
  1087. // undo
  1088. Con::executef(this, 2, "onPostSelectionNudged", Con::getIntArg(getSelectedSet().getId()));
  1089. // allow script to update the inspector
  1090. if (mSelectedControls.size() == 1)
  1091. Con::executef(this, 2, "onSelectionMoved", Con::getIntArg(mSelectedControls[0]->getId()));
  1092. }
  1093. void GuiEditCtrl::moveSelection(const Point2I& delta)
  1094. {
  1095. // move / nudge gets a special callback so that multiple small moves can be
  1096. // coalesced into one large undo action.
  1097. // undo
  1098. Con::executef(this, 2, "onPreSelectionNudged", Con::getIntArg(getSelectedSet().getId()));
  1099. Vector<GuiControl*>::iterator i;
  1100. for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
  1101. {
  1102. // skip locked controls
  1103. if ((*i)->isLocked())
  1104. continue;
  1105. (*i)->resize((*i)->mBounds.point + delta, (*i)->mBounds.extent);
  1106. }
  1107. // undo
  1108. Con::executef(this, 2, "onPostSelectionNudged", Con::getIntArg(getSelectedSet().getId()));
  1109. // allow script to update the inspector
  1110. if (mSelectedControls.size() == 1)
  1111. Con::executef(this, 2, "onSelectionMoved", Con::getIntArg(mSelectedControls[0]->getId()));
  1112. }
  1113. void GuiEditCtrl::justifySelection(Justification j)
  1114. {
  1115. S32 minX, maxX;
  1116. S32 minY, maxY;
  1117. S32 extentX, extentY;
  1118. if (mSelectedControls.size() < 2)
  1119. return;
  1120. Vector<GuiControl*>::iterator i = mSelectedControls.begin();
  1121. minX = (*i)->mBounds.point.x;
  1122. maxX = minX + (*i)->mBounds.extent.x;
  1123. minY = (*i)->mBounds.point.y;
  1124. maxY = minY + (*i)->mBounds.extent.y;
  1125. extentX = (*i)->mBounds.extent.x;
  1126. extentY = (*i)->mBounds.extent.y;
  1127. i++;
  1128. for (; i != mSelectedControls.end(); i++)
  1129. {
  1130. minX = getMin(minX, (*i)->mBounds.point.x);
  1131. maxX = getMax(maxX, (*i)->mBounds.point.x + (*i)->mBounds.extent.x);
  1132. minY = getMin(minY, (*i)->mBounds.point.y);
  1133. maxY = getMax(maxY, (*i)->mBounds.point.y + (*i)->mBounds.extent.y);
  1134. extentX += (*i)->mBounds.extent.x;
  1135. extentY += (*i)->mBounds.extent.y;
  1136. }
  1137. S32 deltaX = maxX - minX;
  1138. S32 deltaY = maxY - minY;
  1139. switch (j)
  1140. {
  1141. case JUSTIFY_LEFT:
  1142. for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
  1143. (*i)->resize(Point2I(minX, (*i)->mBounds.point.y), (*i)->mBounds.extent);
  1144. break;
  1145. case JUSTIFY_TOP:
  1146. for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
  1147. (*i)->resize(Point2I((*i)->mBounds.point.x, minY), (*i)->mBounds.extent);
  1148. break;
  1149. case JUSTIFY_RIGHT:
  1150. for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
  1151. (*i)->resize(Point2I(maxX - (*i)->mBounds.extent.x + 1, (*i)->mBounds.point.y), (*i)->mBounds.extent);
  1152. break;
  1153. case JUSTIFY_BOTTOM:
  1154. for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
  1155. (*i)->resize(Point2I((*i)->mBounds.point.x, maxY - (*i)->mBounds.extent.y + 1), (*i)->mBounds.extent);
  1156. break;
  1157. case JUSTIFY_CENTER:
  1158. for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
  1159. (*i)->resize(Point2I(minX + ((deltaX - (*i)->mBounds.extent.x) >> 1), (*i)->mBounds.point.y),
  1160. (*i)->mBounds.extent);
  1161. break;
  1162. case SPACING_VERTICAL:
  1163. {
  1164. Vector<GuiControl*> sortedList;
  1165. Vector<GuiControl*>::iterator k;
  1166. for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
  1167. {
  1168. for (k = sortedList.begin(); k != sortedList.end(); k++)
  1169. {
  1170. if ((*i)->mBounds.point.y < (*k)->mBounds.point.y)
  1171. break;
  1172. }
  1173. sortedList.insert(k, *i);
  1174. }
  1175. S32 space = (deltaY - extentY) / (mSelectedControls.size() - 1);
  1176. S32 curY = minY;
  1177. for (k = sortedList.begin(); k != sortedList.end(); k++)
  1178. {
  1179. (*k)->resize(Point2I((*k)->mBounds.point.x, curY), (*k)->mBounds.extent);
  1180. curY += (*k)->mBounds.extent.y + space;
  1181. }
  1182. }
  1183. break;
  1184. case SPACING_HORIZONTAL:
  1185. {
  1186. Vector<GuiControl*> sortedList;
  1187. Vector<GuiControl*>::iterator k;
  1188. for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
  1189. {
  1190. for (k = sortedList.begin(); k != sortedList.end(); k++)
  1191. {
  1192. if ((*i)->mBounds.point.x < (*k)->mBounds.point.x)
  1193. break;
  1194. }
  1195. sortedList.insert(k, *i);
  1196. }
  1197. S32 space = (deltaX - extentX) / (mSelectedControls.size() - 1);
  1198. S32 curX = minX;
  1199. for (k = sortedList.begin(); k != sortedList.end(); k++)
  1200. {
  1201. (*k)->resize(Point2I(curX, (*k)->mBounds.point.y), (*k)->mBounds.extent);
  1202. curX += (*k)->mBounds.extent.x + space;
  1203. }
  1204. }
  1205. break;
  1206. }
  1207. }
  1208. void GuiEditCtrl::deleteSelection(void)
  1209. {
  1210. // undo
  1211. Con::executef(this, 2, "onTrashSelection", Con::getIntArg(getSelectedSet().getId()));
  1212. Vector<GuiControl*>::iterator i;
  1213. for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
  1214. {
  1215. mTrash.addObject(*i);
  1216. }
  1217. mSelectedControls.clear();
  1218. }
  1219. void GuiEditCtrl::loadSelection(const char* filename)
  1220. {
  1221. if (!mCurrentAddSet)
  1222. mCurrentAddSet = mEditorRoot;
  1223. Con::executef(2, "exec", filename);
  1224. SimSet* set;
  1225. if (!Sim::findObject("guiClipboard", set))
  1226. return;
  1227. if (set->size())
  1228. {
  1229. Con::executef(this, 1, "onClearSelected");
  1230. mSelectedControls.clear();
  1231. for (U32 i = 0; i < (U32)set->size(); i++)
  1232. {
  1233. GuiControl* ctrl = dynamic_cast<GuiControl*>((*set)[i]);
  1234. if (ctrl)
  1235. {
  1236. mCurrentAddSet->addObject(ctrl);
  1237. mSelectedControls.push_back(ctrl);
  1238. Con::executef(this, 2, "onAddSelected", Con::getIntArg(ctrl->getId()));
  1239. }
  1240. }
  1241. // Undo
  1242. Con::executef(this, 2, "onAddNewCtrlSet", Con::getIntArg(getSelectedSet().getId()));
  1243. }
  1244. set->deleteObject();
  1245. }
  1246. void GuiEditCtrl::saveSelection(const char* filename)
  1247. {
  1248. // if there are no selected objects, then don't save
  1249. if (mSelectedControls.size() == 0)
  1250. return;
  1251. FileStream stream;
  1252. if (!ResourceManager->openFileForWrite(stream, filename))
  1253. return;
  1254. SimSet* clipboardSet = new SimSet;
  1255. clipboardSet->registerObject();
  1256. Sim::getRootGroup()->addObject(clipboardSet, "guiClipboard");
  1257. Vector<GuiControl*>::iterator i;
  1258. for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
  1259. clipboardSet->addObject(*i);
  1260. clipboardSet->write(stream, 0);
  1261. clipboardSet->deleteObject();
  1262. }
  1263. void GuiEditCtrl::selectAll()
  1264. {
  1265. if (!mCurrentAddSet)
  1266. return;
  1267. mSelectedControls.clear();
  1268. if (isMethod("onClearSelected"))
  1269. {
  1270. Con::executef(this, 1, "onClearSelected");
  1271. }
  1272. for (GuiControl::iterator i = mCurrentAddSet->begin(); i != mCurrentAddSet->end(); i++)
  1273. {
  1274. GuiControl* ctrl = dynamic_cast<GuiControl*>(*i);
  1275. if (ctrl)
  1276. {
  1277. mSelectedControls.push_back(ctrl);
  1278. if (isMethod("onAddSelected"))
  1279. {
  1280. Con::executef(this, 2, "onAddSelected", Con::getIntArg(ctrl->getId()));
  1281. }
  1282. }
  1283. }
  1284. }
  1285. void GuiEditCtrl::bringToFront()
  1286. {
  1287. // undo
  1288. if (mSelectedControls.size() != 1)
  1289. return;
  1290. GuiControl* ctrl = *(mSelectedControls.begin());
  1291. mCurrentAddSet->pushObjectToBack(ctrl);
  1292. }
  1293. void GuiEditCtrl::pushToBack()
  1294. {
  1295. // undo
  1296. if (mSelectedControls.size() != 1)
  1297. return;
  1298. GuiControl* ctrl = *(mSelectedControls.begin());
  1299. mCurrentAddSet->bringObjectToFront(ctrl);
  1300. }
  1301. bool GuiEditCtrl::onKeyDown(const GuiEvent& event)
  1302. {
  1303. if (!mActive)
  1304. return Parent::onKeyDown(event);
  1305. if (!(event.modifier & SI_CTRL) && !(event.modifier & SI_SHIFT))
  1306. {
  1307. switch (event.keyCode)
  1308. {
  1309. case KEY_BACKSPACE:
  1310. case KEY_DELETE:
  1311. deleteSelection();
  1312. Con::executef(this, 1, "onDelete");
  1313. return true;
  1314. }
  1315. }
  1316. else if (event.modifier & SI_SHIFT && !(event.modifier & SI_CTRL))
  1317. {
  1318. //holding shift while using arrow keys toggles the grid snap
  1319. switch (event.keyCode)
  1320. {
  1321. case KEY_LEFT:
  1322. moveSelection(Point2I(mUseGridSnap ? -1 : -mGridSnap.x, 0));
  1323. return true;
  1324. case KEY_RIGHT:
  1325. moveSelection(Point2I(mUseGridSnap ? 1 : mGridSnap.x, 0));
  1326. return true;
  1327. case KEY_UP:
  1328. moveSelection(Point2I(0, mUseGridSnap ? -1 : -mGridSnap.y));
  1329. return true;
  1330. case KEY_DOWN:
  1331. moveSelection(Point2I(0, mUseGridSnap ? 1 : mGridSnap.y));
  1332. return true;
  1333. }
  1334. }
  1335. return false;
  1336. }
  1337. ConsoleMethod(GuiEditCtrl, setSnapToGrid, void, 3, 3, "(gridsize) Set the size of the snap-to grid.\n"
  1338. "@return No return value.")
  1339. {
  1340. U32 gridsize = dAtoi(argv[2]);
  1341. object->setSnapToGrid(gridsize);
  1342. }
  1343. ConsoleMethod(GuiEditCtrl, getGridSize, S32, 2, 2, "() Returns the grid size even if the grid is off.\n"
  1344. "@return A single int as the grid size.")
  1345. {
  1346. U32 gridSize = object->getGridSize();
  1347. return gridSize;
  1348. }
  1349. void GuiEditCtrl::setSnapToGrid(U32 gridsize)
  1350. {
  1351. if (gridsize == 0)
  1352. {
  1353. mUseGridSnap = false;
  1354. }
  1355. else
  1356. {
  1357. mGridSnap.set(gridsize, gridsize);
  1358. mUseGridSnap = true;
  1359. }
  1360. }
  1361. void GuiEditCtrl::controlInspectPreApply(GuiControl* object)
  1362. {
  1363. // undo
  1364. Con::executef(this, 2, "onControlInspectPreApply", Con::getIntArg(object->getId()));
  1365. }
  1366. void GuiEditCtrl::controlInspectPostApply(GuiControl* object)
  1367. {
  1368. // undo
  1369. Con::executef(this, 2, "onControlInspectPostApply", Con::getIntArg(object->getId()));
  1370. }
  1371. void GuiEditCtrl::updateSelectedSet()
  1372. {
  1373. mSelectedSet.clear();
  1374. Vector<GuiControl*>::iterator i;
  1375. for (i = mSelectedControls.begin(); i != mSelectedControls.end(); i++)
  1376. {
  1377. mSelectedSet.addObject(*i);
  1378. }
  1379. }
  1380. // -----------------------------------------------------------------------------
  1381. // GuiEditor Ruler
  1382. // -----------------------------------------------------------------------------
  1383. class GuiEditorRuler : public GuiControl {
  1384. StringTableEntry refCtrl;
  1385. typedef GuiControl Parent;
  1386. public:
  1387. void onPreRender()
  1388. {
  1389. setUpdate();
  1390. }
  1391. void onRender(Point2I offset, const RectI& updateRect)
  1392. {
  1393. dglDrawRectFill(updateRect, ColorF(1, 1, 1, 1));
  1394. GuiScrollCtrl* ref;
  1395. SimObject* o = Sim::findObject(refCtrl);
  1396. //Sim::findObject(refCtrl, &ref);
  1397. ref = dynamic_cast<GuiScrollCtrl*>(o);
  1398. Point2I choffset(0, 0);
  1399. //if(ref)
  1400. // choffset = ref->getChildPos();
  1401. if (mBounds.extent.x > mBounds.extent.y)
  1402. {
  1403. // it's horizontal.
  1404. for (U32 i = 0; i < (U32)mBounds.extent.x; i++)
  1405. {
  1406. S32 x = offset.x + i;
  1407. S32 pos = i - choffset.x;
  1408. if (!(pos % 10))
  1409. {
  1410. S32 start = 6;
  1411. if (!(pos % 20))
  1412. start = 4;
  1413. if (!(pos % 100))
  1414. start = 1;
  1415. dglDrawLine(x, offset.y + start, x, offset.y + 10, ColorF(0, 0, 0, 1));
  1416. }
  1417. }
  1418. }
  1419. else
  1420. {
  1421. // it's vertical.
  1422. for (U32 i = 0; i < (U32)mBounds.extent.y; i++)
  1423. {
  1424. S32 y = offset.y + i;
  1425. S32 pos = i - choffset.y;
  1426. if (!(pos % 10))
  1427. {
  1428. S32 start = 6;
  1429. if (!(pos % 20))
  1430. start = 4;
  1431. if (!(pos % 100))
  1432. start = 1;
  1433. dglDrawLine(offset.x + start, y, offset.x + 10, y, ColorF(0, 0, 0, 1));
  1434. }
  1435. }
  1436. }
  1437. }
  1438. static void initPersistFields()
  1439. {
  1440. Parent::initPersistFields();
  1441. addField("refCtrl", TypeString, Offset(refCtrl, GuiEditorRuler));
  1442. }
  1443. DECLARE_CONOBJECT(GuiEditorRuler);
  1444. };
  1445. IMPLEMENT_CONOBJECT(GuiEditorRuler);