guiEditCtrl.cc 49 KB

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