gcsx_tilesetpaint.cpp 94 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419
  1. /* GCSx
  2. ** TILESETPAINT.CPP
  3. **
  4. ** Tileset editing- actual paint/edit window
  5. */
  6. /*****************************************************************************
  7. ** Copyright (C) 2003-2006 Janson
  8. **
  9. ** This program is free software; you can redistribute it and/or modify
  10. ** it under the terms of the GNU General Public License as published by
  11. ** the Free Software Foundation; either version 2 of the License, or
  12. ** (at your option) any later version.
  13. **
  14. ** This program is distributed in the hope that it will be useful,
  15. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ** GNU General Public License for more details.
  18. **
  19. ** You should have received a copy of the GNU General Public License
  20. ** along with this program; if not, write to the Free Software
  21. ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
  22. *****************************************************************************/
  23. #include "all.h"
  24. // Tile edit window- toolbar
  25. const ToolSelect::ToolIconStruct TilePaintTools::toolIcons[TOOLSELECT_NUM_TOOLS] = {
  26. { TOOLS_PEN, 0, 0, "Draw (pixel by pixel)" },
  27. { TOOLS_LINE, 20, 0, "Straight line" },
  28. { TOOLS_DROPPER, 40, 40, "Dropper (grab/select color)" },
  29. { TOOLS_FILL, 80, 0, "Flood fill" },
  30. { TOOLS_RECT, 80, 60, "Rectangle (outline)" },
  31. { TOOLS_RECTFILL, 40, 0, "Rectangle (filled)" },
  32. { TOOLS_ELLIPSE, 40, 80, "Ellipse (outline)" },
  33. { TOOLS_ELLIPSEFILL, 60, 0, "Ellipse (filled)" },
  34. { TOOLS_SELECT, 0, 20, "Select area (rectangle)" },
  35. { TOOLS_SELECTELLIPSE, 20, 20, "Select area (ellipse)" },
  36. { TOOLS_WAND, 40, 20, "Magic wand (select area by color)" },
  37. };
  38. const ToolSelect::ToolIconStruct TilePaintTools::dotsIcons[TOOLSELECT_NUM_DOTS] = {
  39. { VIEW_DOTS_NONE, 20, 100, "Don't show dots" },
  40. { VIEW_DOTS_TRANS, 40, 100, "Show dots on transparent pixels" },
  41. { VIEW_DOTS_ALPHA, 60, 100, "Show alpha as dots" },
  42. };
  43. Window::WindowSort TilePaintTools::windowSort() const { start_func
  44. return WINDOWSORT_ONTOP;
  45. }
  46. void TilePaintTools::adjustForTool() { start_func
  47. WCheckBox* check = dynamic_cast<WCheckBox*>(findWidget(ID_ANTIALIAS));
  48. if ((*toolPtr == TOOLS_WAND) || (*toolPtr == TOOLS_FILL)) {
  49. check->enable();
  50. check->changeText("Contiguous");
  51. check->setToolTip("Contiguous will only fill or select a single, connected area");
  52. findWidget(ID_TOLERANCELABEL)->enable();
  53. findWidget(ID_TOLERANCE_S)->enable();
  54. findWidget(ID_TOLERANCE_N)->enable();
  55. }
  56. else {
  57. check->changeText("Smooth");
  58. check->setToolTip("Draw smooth lines and circles (antialiased)");
  59. if ((*toolPtr == TOOLS_LINE) || (*toolPtr == TOOLS_ELLIPSE) || (*toolPtr == TOOLS_ELLIPSEFILL)) check->enable();
  60. else check->disable();
  61. findWidget(ID_TOLERANCELABEL)->disable();
  62. findWidget(ID_TOLERANCE_S)->disable();
  63. findWidget(ID_TOLERANCE_N)->disable();
  64. }
  65. }
  66. int TilePaintTools::event(int hasFocus, const SDL_Event* event) { start_func
  67. if (event->type == SDL_COMMAND) {
  68. // Simple cases
  69. switch (event->user.code) {
  70. case VIEW_GRID:
  71. *gridPtr = *gridPtr ? 0 : 1;
  72. findWidget(ID_GRID)->load();
  73. paintArea->refresh();
  74. config->write(TILEPAINT_GRID, *gridPtr);
  75. return 1;
  76. case TOOLS_BLENDUP:
  77. case TOOLS_BLENDDOWN:
  78. if (event->user.code == TOOLS_BLENDUP) {
  79. if (((*alphaBlendPtr) += 10) > 100) *alphaBlendPtr = 100;
  80. }
  81. else {
  82. if (((*alphaBlendPtr) -= 10) < 0) *alphaBlendPtr = 0;
  83. }
  84. findWidget(ID_ALPHA_S)->load();
  85. findWidget(ID_ALPHA_N)->load();
  86. paintArea->refresh();
  87. return 1;
  88. case TOOLS_TOLERANCEUP:
  89. case TOOLS_TOLERANCEDOWN:
  90. if (event->user.code == TOOLS_TOLERANCEUP) {
  91. if (((*tolerancePtr) += 10) > 100) *tolerancePtr = 100;
  92. }
  93. else {
  94. if (((*tolerancePtr) -= 10) < 0) *tolerancePtr = 0;
  95. }
  96. findWidget(ID_TOLERANCE_S)->load();
  97. findWidget(ID_TOLERANCE_N)->load();
  98. config->write(TILEPAINT_TOLERANCE, *tolerancePtr);
  99. return 1;
  100. case TOOLS_ANTIALIAS:
  101. case TOOLS_CONTIGUOUS:
  102. *antiAliasPtr = *antiAliasPtr ? 0 : 1;
  103. findWidget(ID_ANTIALIAS)->load();
  104. config->write(TILEPAINT_ANTIALIAS, *antiAliasPtr);
  105. return 1;
  106. }
  107. // Choose tool or dots
  108. int newTool = 0;
  109. int newDots = 0;
  110. WButton* toolButton = dynamic_cast<WButton*>(findWidget(ID_TOOL));
  111. WButton* dotsButton = dynamic_cast<WButton*>(findWidget(ID_DOTS));
  112. int x, y;
  113. if (event->user.code == TOOLS_CHOOSE) {
  114. newTool = toolSelect->run(toolButton->getScreenX(), toolButton->getScreenY() + toolButton->getHeight());
  115. }
  116. else if (toolSelect->isTool(event->user.code)) {
  117. newTool = event->user.code;
  118. paintArea->startToolIfMove();
  119. }
  120. else if (event->user.code == VIEW_DOTS_CHOOSE) {
  121. newDots = dotsSelect->run(dotsButton->getScreenX(), dotsButton->getScreenY() + dotsButton->getHeight());
  122. }
  123. else if (dotsSelect->isTool(event->user.code)) {
  124. newDots = event->user.code;
  125. }
  126. if (newTool) {
  127. if (toolSelect->getToolIcon(newTool, x, y)) {
  128. toolButton->changeIcon(x, y);
  129. *toolPtr = newTool;
  130. adjustForTool();
  131. return 1;
  132. }
  133. }
  134. if (newDots) {
  135. if (dotsSelect->getToolIcon(newDots, x, y)) {
  136. dotsButton->changeIcon(x, y);
  137. *dotsPtr = newDots;
  138. paintArea->refresh();
  139. config->write(TILEPAINT_DOTS, *dotsPtr);
  140. return 1;
  141. }
  142. }
  143. if (event->user.code == TOOLS_CHOOSE) return 1;
  144. if (event->user.code == VIEW_DOTS_CHOOSE) return 1;
  145. }
  146. return Dialog::event(hasFocus, event);
  147. }
  148. void TilePaintTools::childModified(Window* modified) { start_func
  149. Dialog::childModified(modified);
  150. Widget* widget = dynamic_cast<Widget*>(modified);
  151. if (widget) {
  152. // If certain settings modified, tile area needs to refresh
  153. if ((widget->getId() == ID_GRID) || (widget->getId() == ID_ALPHA_S) || (widget->getId() == ID_ALPHA_N)) {
  154. paintArea->refresh();
  155. }
  156. // Save config settings
  157. config->write(TILEPAINT_ANTIALIAS, *antiAliasPtr);
  158. config->write(TILEPAINT_TOLERANCE, *tolerancePtr);
  159. config->write(TILEPAINT_GRID, *gridPtr);
  160. }
  161. }
  162. void TilePaintTools::tileRedraw() { start_func
  163. assert(findWidget(ID_PREVIEW));
  164. // (doesn't throw exceptions)
  165. findWidget(ID_PREVIEW)->load();
  166. }
  167. int TilePaintTools::wantsToBeDeleted() const { start_func
  168. return 1;
  169. }
  170. FrameWindow* TilePaintTools::createWindowed() { start_func
  171. // Prevent duplication
  172. if (myFrame) {
  173. return myFrame;
  174. }
  175. // We remember the frame pointer even though it'll delete itself
  176. myFrame = new FrameWindow("Tools", FrameWindow::RESIZING_SNAP, FrameWindow::FRAMETYPE_DIALOG, this, FrameWindow::TITLEBAR_TOOL, 0);
  177. return myFrame;
  178. }
  179. TilePaintTools::TilePaintTools(TilePaint* tPaintArea, SDL_Surface** tileSource, int* tool, int* alphaBlend, int* antiAlias, int* tolerance, int* grid, int* dots) : Dialog(blankString, 1, 0, 1) { start_func
  180. myFrame = NULL;
  181. paintArea = tPaintArea;
  182. toolPtr = tool;
  183. antiAliasPtr = antiAlias;
  184. alphaBlendPtr = alphaBlend;
  185. tolerancePtr = tolerance;
  186. gridPtr = grid;
  187. dotsPtr = dots;
  188. Widget* w = NULL;
  189. WNumberBox* n = NULL;
  190. toolSelect = new ToolSelect(getIconSurface(), TOOLSELECT_NUM_TOOLS, toolIcons);
  191. dotsSelect = new ToolSelect(getIconSurface(), TOOLSELECT_NUM_DOTS, dotsIcons);
  192. int x = 0;
  193. int y = 0;
  194. toolSelect->getToolIcon(*toolPtr, x, y);
  195. w = new WButton(ID_TOOL, getIconSurface(), x, y, 20, 20, Dialog::BUTTON_NOTHING, TOOLS_CHOOSE);
  196. w->setToolTip("Change tool");
  197. w->addTo(this);
  198. w = new WCheckBox(ID_ANTIALIAS, "Smooth", antiAlias, 1);
  199. w->setToolTip("Draw smooth lines and circles (antialiased)");
  200. w->addTo(this);
  201. arrangeRow();
  202. w = new WStatic(ID_ALPHALABEL, "Blend %:");
  203. w->addTo(this);
  204. w = n = new WNumberBox(ID_ALPHA_N, alphaBlend, 0, 100);
  205. w->setToolTip("Blend controls translucency of tools and pasting");
  206. w->addTo(this);
  207. w = new WSlider(ID_ALPHA_S, 0, 100, alphaBlend);
  208. w->setToolTip("Blend controls translucency of tools and pasting");
  209. dynamic_cast<WSlider*>(w)->linkTo(n);
  210. w->addTo(this);
  211. w = new WStatic(ID_TOLERANCELABEL, "Tolerance:");
  212. w->addTo(this);
  213. w = n = new WNumberBox(ID_TOLERANCE_N, tolerance, 0, 100);
  214. w->setToolTip("Higher tolerance will affect a wider range of color");
  215. w->addTo(this);
  216. w = new WSlider(ID_TOLERANCE_S, 0, 100, tolerance);
  217. w->setToolTip("Higher tolerance will affect a wider range of color");
  218. dynamic_cast<WSlider*>(w)->linkTo(n);
  219. w->addTo(this);
  220. makePretty(3);
  221. w = new WButton(ID_COPY, getIconSurface(), 60, 20, 20, 20, Dialog::BUTTON_NOTHING, EDIT_COPY);
  222. w->setToolTip("Copy");
  223. w->addTo(this);
  224. w = new WButton(ID_PASTE, getIconSurface(), 80, 20, 20, 20, Dialog::BUTTON_NOTHING, EDIT_PASTE);
  225. w->setToolTip("Paste (as new selection)");
  226. w->addTo(this);
  227. w = new WCheckBox(ID_GRID, getIconSurface(), 0, 100, 20, 20, grid, 1);
  228. w->setToolTip("Show or hide grid");
  229. w->addTo(this);
  230. arrangeRow();
  231. w = new WButton(ID_ZOOMIN, getIconSurface(), 0, 80, 20, 20, Dialog::BUTTON_NOTHING, VIEW_ZOOMIN);
  232. w->setToolTip("Zoom in");
  233. w->addTo(this);
  234. w = new WButton(ID_ZOOMOUT, getIconSurface(), 20, 80, 20, 20, Dialog::BUTTON_NOTHING, VIEW_ZOOMOUT);
  235. w->setToolTip("Zoom out");
  236. w->addTo(this);
  237. w = new WButton(ID_PREV, getIconSurface(), 60, 80, 20, 20, Dialog::BUTTON_NOTHING, VIEW_PREV);
  238. w->setToolTip("Previous image");
  239. w->addTo(this);
  240. w = new WButton(ID_NEXT, getIconSurface(), 80, 80, 20, 20, Dialog::BUTTON_NOTHING, VIEW_NEXT);
  241. w->setToolTip("Next image");
  242. w->addTo(this);
  243. dotsSelect->getToolIcon(*dotsPtr, x, y);
  244. w = new WButton(ID_DOTS, getIconSurface(), x, y, 20, 20, Dialog::BUTTON_NOTHING, VIEW_DOTS_CHOOSE);
  245. w->setToolTip("Control display of alpha dots");
  246. w->addTo(this);
  247. arrangeRow();
  248. // Request initial height based on tile, although this will inevitably be resized
  249. int previewHeight = 96;
  250. if (*tileSource) previewHeight = (*tileSource)->h * 3;
  251. w = new WPreview(ID_PREVIEW, tileSource, 166, previewHeight, 1, 0);
  252. w->setToolTip("Preview");
  253. w->addTo(this);
  254. arrangeRow();
  255. // Our height if preview is 0 pixels high
  256. minHeight = height - previewHeight;
  257. adjustForTool();
  258. runAsPanel();
  259. }
  260. TilePaintTools::~TilePaintTools() { start_func
  261. delete toolSelect;
  262. delete dotsSelect;
  263. }
  264. void TilePaintTools::resize(int newWidth, int newHeight, int newViewWidth, int newViewHeight, int fromParent) { start_func
  265. if (newViewHeight == -1) newViewHeight = viewHeight;
  266. if (newViewHeight >= 0) {
  267. // Resize tile preview to fit
  268. WPreview* widget = dynamic_cast<WPreview*>(findWidget(ID_PREVIEW));
  269. if (widget) {
  270. int previewHeight = newViewHeight - minHeight;
  271. if (previewHeight > 0) {
  272. widget->changeSize(166, previewHeight);
  273. // Doesn't throw exceptions
  274. widget->load();
  275. newHeight = newViewHeight;
  276. }
  277. else {
  278. widget->changeSize(166, 0);
  279. // Doesn't throw exceptions
  280. widget->load();
  281. newHeight = minHeight;
  282. }
  283. }
  284. }
  285. Window::resize(newWidth, newHeight, newViewWidth, newViewHeight, fromParent);
  286. }
  287. // Tile edit window- Main
  288. TilePaint::TilePaint(TileSetEdit* myTileset, int myTile, int myIsColl) throw_File : Window() { start_func
  289. assert(myTileset);
  290. assert(myTile > 0);
  291. assert(myTile <= (myIsColl ? myTileset->getCollisionCount() : myTileset->getCount()));
  292. cursorX = cursorY = virtualCursorX = virtualCursorY = 0;
  293. hideCursor = 0;
  294. haveFocus = partialFocus = 0;
  295. toolActive = startToolOnMove = 0;
  296. dirtyRange.w = 0;
  297. selectionRect.w = 0;
  298. selectionMode = SELECTION_OUTLINE;
  299. selectionXOffs = 0;
  300. selectionYOffs = 0;
  301. tileset = myTileset;
  302. world = tileset->getWorldEdit();
  303. isColl = myIsColl;
  304. // Intentionally doesn't remember last tool selected
  305. tool = TOOLS_PEN;
  306. // Intentionally doesn't remember last alpha blend
  307. alphaBlend = 100;
  308. // Recall last settings
  309. antiAlias = config->readNum(TILEPAINT_ANTIALIAS) ? 1 : 0;
  310. tolerance = config->readNum(TILEPAINT_TOLERANCE);
  311. if (tolerance < 0) tolerance = 0;
  312. if (tolerance > 100) tolerance = 100;
  313. if (isColl) {
  314. antiAlias = 0;
  315. tolerance = 100;
  316. }
  317. enableGrid = config->readNum(TILEPAINT_GRID) ? 1 : 0;
  318. drawDots = config->readNum(TILEPAINT_DOTS);
  319. if ((drawDots != VIEW_DOTS_TRANS) && (drawDots != VIEW_DOTS_ALPHA)) drawDots = VIEW_DOTS_NONE;
  320. pixelSize = config->readNum(TILEPAINT_ZOOM);
  321. if (pixelSize >= MAX_ZOOMGRID) gridSize = enableGrid ? 1 : 0;
  322. else gridSize = 0;
  323. myFrame = NULL;
  324. colorbar = NULL;
  325. tools = NULL;
  326. tile = NULL;
  327. alphaWorkspace = NULL;
  328. selection = NULL;
  329. backupSelection = NULL;
  330. tileset->markLock(); // Exception point
  331. FrameWindow* toolsFrame = NULL;
  332. FrameWindow* colorbarFrame = NULL;
  333. currentTile = myTile; // So changetile isn't called
  334. reloadTileStats();
  335. changeTile(myTile);
  336. colorbar = new ColorSelect(&colors, tileset->getDefaultTransparent());
  337. tools = new TilePaintTools(this, &tile, &tool, &alphaBlend, &antiAlias, &tolerance, &enableGrid, &drawDots);
  338. colorbarFrame = colorbar->createWindowed();
  339. toolsFrame = tools->createWindowed();
  340. myFrame = new FrameWindow(blankString, FrameWindow::RESIZING_NORMAL, FrameWindow::FRAMETYPE_BEVEL_BK, this);
  341. myFrame->setAutoCenter(1);
  342. updateTitlebar();
  343. myFrame->addToolPanel(colorbarFrame, FrameWindow::CLIENT_RIGHT);
  344. myFrame->addToolPanel(toolsFrame, FrameWindow::CLIENT_RIGHT);
  345. myFrame->show(FrameWindow::SHOW_CASCADE, FrameWindow::SHOW_CASCADE, FrameWindow::SHOW_CURRMIN, FrameWindow::SHOW_CURRMIN);
  346. // Disabled/etc status on various tools
  347. if (pixelSize == 1) tools->findWidget(TilePaintTools::ID_ZOOMOUT)->disable();
  348. if (pixelSize >= MAX_ZOOM) tools->findWidget(TilePaintTools::ID_ZOOMIN)->disable();
  349. tools->findWidget(TilePaintTools::ID_COPY)->disable();
  350. }
  351. TilePaint::~TilePaint() { start_func
  352. if (tile) SDL_FreeSurface(tile);
  353. if (alphaWorkspace) SDL_FreeSurface(alphaWorkspace);
  354. if (selection) SDL_FreeSurface(selection);
  355. if (backupSelection) SDL_FreeSurface(backupSelection);
  356. if (tileset) tileset->markUnlock();
  357. }
  358. void TilePaint::setDirtyRect(const Rect& rect, int selectionFeather) { start_func
  359. // Clip
  360. Rect bound = { 0, 0, tileWidth, tileHeight };
  361. if (intersectRects(bound, rect)) {
  362. // Scale to pixel size
  363. bound.x *= pixelSize;
  364. bound.y *= pixelSize;
  365. bound.w = bound.w * pixelSize + 1;
  366. bound.h = bound.h * pixelSize + 1;
  367. // Feather for selection modification?
  368. if (selectionFeather) {
  369. if (bound.x > 0) {
  370. --bound.x;
  371. ++bound.w;
  372. }
  373. if (bound.y > 0) {
  374. --bound.y;
  375. ++bound.h;
  376. }
  377. // We don't bother checking if our w/h goes over the limit-
  378. // this will get clipped; but negative x/y is too odd to allow, above
  379. ++bound.w;
  380. ++bound.h;
  381. }
  382. // Add rectangle into dirty range
  383. boundRects(dirtyRange, bound);
  384. setDirty();
  385. }
  386. }
  387. void TilePaint::setDirtyGlyphWidth(int x1, int x2) { start_func
  388. // Create a rectangle
  389. if (x1 > x2) swap(x1, x2);
  390. Rect glyph = { x1 * pixelSize - GLYPH_SIZE_POINTER_HEIGHT, 0, (x2 - x1) * pixelSize + 1 + GLYPH_SIZE_POINTER_HEIGHT * 2, height };
  391. boundRects(dirtyRange, glyph);
  392. setDirty();
  393. }
  394. void TilePaint::setDirtyBox(int x1, int y1, int x2, int y2, int selectionFeather) { start_func
  395. // Create a rectangle
  396. // Add rectangle into dirty range
  397. setDirtyRect(createRect(x1, y1, x2, y2), selectionFeather);
  398. }
  399. void TilePaint::changeTile(int tileNum) { start_func
  400. assert(tile);
  401. assert(tileset);
  402. assert(tileNum > 0);
  403. assert(tileNum <= (isColl ? tileset->getCollisionCount() : tileset->getCount()));
  404. currentTile = tileNum;
  405. refreshTile();
  406. if (tools) tools->tileRedraw();
  407. setDirty(1);
  408. updateTitlebar();
  409. }
  410. void TilePaint::refreshTile() { start_func
  411. assert(tile);
  412. assert(tileset);
  413. assert(currentTile > 0);
  414. assert(currentTile <= (isColl ? tileset->getCollisionCount() : tileset->getCount()));
  415. if (isColl) tileset->loadColl(currentTile, tile);
  416. else tileset->loadTile(currentTile, tile);
  417. if (isFont) glyphWidth = tileset->getGlyphWidth(currentTile);
  418. }
  419. void TilePaint::applyTile() { start_func
  420. assert(tile);
  421. assert(tileset);
  422. assert(currentTile > 0);
  423. assert(currentTile <= (isColl ? tileset->getCollisionCount() : tileset->getCount()));
  424. if (isColl) tileset->saveTile(currentTile, tile, this);
  425. else tileset->saveTile(currentTile, tile, this);
  426. tools->tileRedraw();
  427. }
  428. void TilePaint::applyGlyphWidth() { start_func
  429. assert(currentTile > 0);
  430. // (can't be a collision map)
  431. assert(currentTile <= tileset->getCount());
  432. tileset->setGlyphWidth(currentTile, glyphWidth, this);
  433. }
  434. void TilePaint::setGlyphWidth(int gX) { start_func
  435. if (glyphWidth != gX) {
  436. if (gX < 0) gX = 0;
  437. if (gX > tileWidth) gX = tileWidth;
  438. setDirtyGlyphWidth(gX, glyphWidth);
  439. glyphWidth = gX;
  440. }
  441. }
  442. void TilePaint::changePixelSizing(int newPixelSize, int newGridSize) { start_func
  443. assert(newPixelSize > 0);
  444. assert(newGridSize >= 0);
  445. assert(newPixelSize > newGridSize);
  446. pixelSize = newPixelSize;
  447. gridSize = newGridSize;
  448. config->write(TILEPAINT_ZOOM, pixelSize);
  449. resize(tileWidth * pixelSize + 1, tileHeight * pixelSize + 1 + (isFont ? GLYPH_SIZE_POINTER_HEIGHT + GLYPH_SIZE_POINTER_MARGIN : 0));
  450. if (myFrame) myFrame->requestViewSize(width, height);
  451. }
  452. void TilePaint::reloadTileStats() { start_func
  453. int oldWidth = tileWidth;
  454. int oldHeight = tileHeight;
  455. if (isColl) {
  456. numTiles = tileset->getCollisionCount();
  457. // Even if fonts can have maps, we don't want glyphs on maps!
  458. isFont = 0;
  459. }
  460. else {
  461. numTiles = tileset->getCount();
  462. isFont = tileset->getIsFont();
  463. }
  464. tileWidth = tileset->getWidth();
  465. tileHeight = tileset->getHeight();
  466. // Close if no tiles left
  467. if (numTiles == 0) {
  468. closeWindow();
  469. return;
  470. }
  471. // If numtiles > 0, make sure current tile still exists
  472. else if (numTiles < currentTile) {
  473. changeTile(numTiles);
  474. }
  475. else {
  476. // Update titlebar (changetile would've done so, above)
  477. updateTitlebar();
  478. }
  479. // Ensure within bounds
  480. moveCursor(cursorX, cursorY);
  481. // Reallocate surfaces
  482. SDL_Surface* newTile = NULL;
  483. newTile = createSurface32(tileWidth, tileHeight);
  484. if (tile) {
  485. SDL_FreeSurface(tile);
  486. }
  487. tile = newTile;
  488. newTile = NULL;
  489. newTile = createSurface32(tileWidth, tileHeight);
  490. if (alphaWorkspace) {
  491. SDL_FreeSurface(alphaWorkspace);
  492. }
  493. alphaWorkspace = newTile;
  494. newTile = NULL;
  495. SDL_SetAlpha(alphaWorkspace, SDL_SRCALPHA, 255);
  496. // Selection must be filled/copied over
  497. newTile = createSurface32(tileWidth, tileHeight);
  498. sge_FilledRect(newTile, 0, 0, tileWidth, tileHeight, mapColor32(0, 0, 0, 0));
  499. if (selection) {
  500. blit(0, 0, selection, 0, 0, newTile, min(oldWidth, tileWidth), min(oldHeight, tileHeight));
  501. SDL_FreeSurface(selection);
  502. // Clip selection rect also
  503. Rect newSize = { 0, 0, tileWidth, tileHeight };
  504. intersectRects(selectionRect, newSize);
  505. }
  506. selection = newTile;
  507. newTile = NULL;
  508. newTile = createSurface32(tileWidth, tileHeight);
  509. sge_FilledRect(newTile, 0, 0, tileWidth, tileHeight, mapColor32(0, 0, 0, 0));
  510. if (backupSelection) {
  511. blit(0, 0, backupSelection, 0, 0, newTile, min(oldWidth, tileWidth), min(oldHeight, tileHeight));
  512. SDL_FreeSurface(backupSelection);
  513. }
  514. backupSelection = newTile;
  515. newTile = NULL;
  516. resize(tileWidth * pixelSize + 1, tileHeight * pixelSize + 1 + (isFont ? GLYPH_SIZE_POINTER_HEIGHT + GLYPH_SIZE_POINTER_MARGIN : 0));
  517. if (myFrame) {
  518. myFrame->setScroll(pixelSize, pixelSize);
  519. myFrame->requestViewSize(width, height);
  520. }
  521. }
  522. void TilePaint::updateTitlebar() { start_func
  523. if (myFrame) {
  524. if (isColl) myFrame->setTitle(formatString("%s : %s : Collision Map %d / %d", world->getTitle().c_str(), tileset->getName().c_str(), currentTile, numTiles));
  525. else if (isFont) myFrame->setTitle(formatString("%s : %s : Glyph %d / %d (%c)", world->getTitle().c_str(), tileset->getName().c_str(), currentTile, numTiles, currentTile + 31));
  526. else myFrame->setTitle(formatString("%s : %s : Image %d / %d", world->getTitle().c_str(), tileset->getName().c_str(), currentTile, numTiles));
  527. }
  528. }
  529. int TilePaint::event(int hasFocus, const SDL_Event* event) { start_func
  530. int changed = 0;
  531. int clickX, clickY;
  532. ObjChange* obj;
  533. switch (event->type) {
  534. case SDL_CLOSE:
  535. // Merge any existing selection
  536. try {
  537. mergeSelection();
  538. }
  539. catch (UndoException& e) {
  540. }
  541. return 1;
  542. case SDL_SPECIAL:
  543. // Refresh selection rectangle?
  544. if ((event->user.code == SDL_IDLEPHASE) && (partialFocus)) {
  545. Rect rect = { selectionRect.x + selectionXOffs, selectionRect.y + selectionYOffs, selectionRect.w, selectionRect.h };
  546. setDirtyRect(rect);
  547. }
  548. // Refresh cursor?
  549. if (((event->user.code == SDL_IDLECURSOR) || (event->user.code == SDL_IDLEPHASE)) && (haveFocus)) {
  550. Rect rect = { cursorX, cursorY, 1, 1 };
  551. setDirtyRect(rect);
  552. }
  553. return 1;
  554. case SDL_COMMAND:
  555. switch (event->user.code) {
  556. case VIEW_NEXT:
  557. // Merge any existing selection
  558. try {
  559. mergeSelection();
  560. }
  561. catch (UndoException& e) {
  562. return 1;
  563. }
  564. cancelTool();
  565. if (currentTile < numTiles) changeTile(currentTile + 1);
  566. else changeTile(1);
  567. return 1;
  568. case VIEW_PREV:
  569. // Merge any existing selection
  570. try {
  571. mergeSelection();
  572. }
  573. catch (UndoException& e) {
  574. return 1;
  575. }
  576. cancelTool();
  577. if (currentTile > 1) changeTile(currentTile - 1);
  578. else changeTile(numTiles);
  579. return 1;
  580. case VIEW_ZOOMOUT:
  581. zoomOut();
  582. return 1;
  583. case VIEW_ZOOMIN:
  584. zoomIn();
  585. return 1;
  586. case EDIT_UNDO:
  587. // finish instead of cancel- this causes undo to undo current
  588. // tool if one is being used
  589. finishTool();
  590. // (let world handle from here)
  591. break;
  592. case EDIT_REDO:
  593. cancelTool();
  594. // (let world handle from here)
  595. break;
  596. case EDIT_COPY:
  597. copySelection();
  598. return 1;
  599. case EDIT_CUT:
  600. try {
  601. world->undo.preUndoBlock();
  602. copySelection();
  603. deleteSelection();
  604. clearSelection();
  605. world->undo.postUndoBlock();
  606. }
  607. catch (UndoException& e) {
  608. }
  609. return 1;
  610. case EDIT_DELETE:
  611. try {
  612. deleteSelection();
  613. }
  614. catch (UndoException& e) {
  615. }
  616. return 1;
  617. case EDIT_SELECTALL:
  618. try {
  619. world->undo.preUndoBlock();
  620. // Merge any floating
  621. mergeSelection();
  622. // Store undo for entire selection
  623. undoStoreSelect(0, 0, tileWidth, tileHeight);
  624. world->undo.postUndoBlock();
  625. // Select all
  626. sge_FilledRect(selection, 0, 0, tileWidth, tileHeight, mapColor32(0, 0, 0, 255));
  627. selectionRect.x = 0;
  628. selectionRect.y = 0;
  629. selectionRect.w = tileWidth;
  630. selectionRect.h = tileHeight;
  631. setDirty(1);
  632. tools->findWidget(TilePaintTools::ID_COPY)->enable();
  633. }
  634. catch (UndoException& e) {
  635. }
  636. return 1;
  637. case EDIT_DESELECTALL:
  638. cancelTool();
  639. try {
  640. doneSelection();
  641. }
  642. catch (UndoException& e) {
  643. }
  644. return 1;
  645. case EDIT_PASTE:
  646. try {
  647. pasteSelection();
  648. }
  649. catch (UndoException& e) {
  650. return 1;
  651. }
  652. // Make sure a selection tool is current
  653. if ((!toolActive) && (tool != TOOLS_SELECT) && (tool != TOOLS_SELECTELLIPSE) && (tool != TOOLS_WAND)) {
  654. desktop->broadcastEvent(SDL_COMMAND, TOOLS_SELECT);
  655. desktop->broadcastEvent(SDL_COMMAND, CMD_RELEASE);
  656. }
  657. return 1;
  658. case EDIT_SETGLYPHWIDTH:
  659. if (isFont) {
  660. try {
  661. undoStoreGlyph();
  662. }
  663. catch (UndoException& e) {
  664. return 1;
  665. }
  666. setGlyphWidth(cursorX + 1);
  667. applyGlyphWidth();
  668. return 1;
  669. }
  670. break;
  671. case CMD_RELEASE:
  672. finishTool(); // Clears startToolOnMove too; safe even if no tool
  673. moveCursor(cursorX, cursorY);
  674. return 1;
  675. case TOOLS_NEXTCOLOR:
  676. colorbar->colorSelection(ColorSelect::SELECTED_FG,
  677. colorbar->colorSelection(ColorSelect::SELECTED_FG) + 1);
  678. return 1;
  679. case TOOLS_PREVCOLOR:
  680. colorbar->colorSelection(ColorSelect::SELECTED_FG,
  681. colorbar->colorSelection(ColorSelect::SELECTED_FG) - 1);
  682. return 1;
  683. case TOOLS_EDITCOLOR:
  684. colorbar->editColor(colorbar->colorSelection(ColorSelect::SELECTED_FG));
  685. return 1;
  686. }
  687. if (world->commandEvent(event->user.code)) return 1;
  688. break;
  689. case SDL_OBJECTCHANGE:
  690. obj = (ObjChange*)event->user.data1;
  691. if ((event->user.code & OBJ_TILESET) && (obj->obj == tileset)) {
  692. if (event->user.code & OBJMOD_DEFTRANS) {
  693. colorbar->changeDefaultTransparent(tileset->getDefaultTransparent());
  694. }
  695. if (event->user.code & OBJMOD_DELETE) {
  696. tileset = NULL;
  697. closeWindow();
  698. }
  699. if (event->user.code & OBJMOD_NAME) {
  700. updateTitlebar();
  701. }
  702. if (event->user.code & (OBJMOD_WIDTH | OBJMOD_HEIGHT | OBJMOD_COUNT | OBJMOD_COUNTCOLL)) {
  703. reloadTileStats();
  704. if (numTiles) {
  705. cancelTool(); // After reloading stats, so refresh works
  706. // Reload tile, resizing may have been from an undo
  707. refreshTile();
  708. }
  709. }
  710. if (event->user.code & OBJMOD_GLYPHW) {
  711. if (obj->info1 == currentTile) {
  712. // Clear tool, tile changed from outside source
  713. cancelTool();
  714. refreshTile();
  715. tools->tileRedraw();
  716. setDirty(1);
  717. }
  718. }
  719. if (event->user.code & OBJMOD_TILE) {
  720. if ((!isColl) && (currentTile >= obj->info1) && (currentTile <= obj->info2)) {
  721. // Clear tool, tile changed from outside source
  722. cancelTool();
  723. refreshTile();
  724. tools->tileRedraw();
  725. setDirty(1);
  726. }
  727. }
  728. if (event->user.code & OBJMOD_COLL) {
  729. if ((isColl) && (currentTile >= obj->info1) && (currentTile <= obj->info2)) {
  730. // Clear tool, coll map changed from outside source
  731. cancelTool();
  732. refreshTile();
  733. tools->tileRedraw();
  734. setDirty(1);
  735. }
  736. }
  737. }
  738. if ((event->user.code & OBJ_WORLD) && (obj->obj == world)) {
  739. if (event->user.code & OBJMOD_DELETE) {
  740. tileset = NULL;
  741. closeWindow();
  742. }
  743. if (event->user.code & OBJMOD_NAME) {
  744. updateTitlebar();
  745. }
  746. }
  747. return 1;
  748. case SDL_MOUSEBUTTONDOWN:
  749. case SDL_MOUSEBUTTONDBL:
  750. if ((event->button.button == SDL_BUTTON_LEFT) || (event->button.button == SDL_BUTTON_RIGHT)) {
  751. hideCursor = 1;
  752. // Signed as it could go off the edge
  753. clickX = (Sint16)event->button.x / pixelSize;
  754. clickY = (Sint16)event->button.y / pixelSize;
  755. // Glyph width?
  756. if ((clickY >= tileHeight) && (isFont)) {
  757. startToolGlyphWidthDrag();
  758. }
  759. // If unmodified selection-tool left-click and within selection, this is a selection drag
  760. else if ((!toolActive) &&
  761. ((tool == TOOLS_SELECT) || (tool == TOOLS_SELECTELLIPSE) || (tool == TOOLS_WAND)) &&
  762. !(SDL_GetModState() & (KMOD_CTRL | KMOD_ALT)) &&
  763. (event->button.button == SDL_BUTTON_LEFT) &&
  764. (isInSelection(clickX, clickY))) {
  765. startToolOnMove = 0;
  766. moveCursor(clickX, clickY);
  767. startToolSelectionDrag();
  768. }
  769. else {
  770. moveCursor(clickX, clickY);
  771. startTool(event->button.button == SDL_BUTTON_LEFT ? ColorSelect::SELECTED_FG : ColorSelect::SELECTED_BK);
  772. }
  773. return 1;
  774. }
  775. else if (event->button.button == SDL_BUTTON_WHEELUP) {
  776. zoomIn();
  777. return 1;
  778. }
  779. else if (event->button.button == SDL_BUTTON_WHEELDOWN) {
  780. zoomOut();
  781. return 1;
  782. }
  783. break;
  784. case SDL_MOUSEBUTTONUP:
  785. finishTool();
  786. // Force cursor back into area, no more virtual
  787. moveCursor(cursorX, cursorY);
  788. return 1;
  789. case SDL_MOUSEMOTION:
  790. if ((event->motion.state & SDL_BUTTON_LMASK) || (event->motion.state & SDL_BUTTON_RMASK)) {
  791. hideCursor = 1;
  792. // Signed as it could go off the edge
  793. moveCursor(((Sint16)event->motion.x) / pixelSize, ((Sint16)event->motion.y) / pixelSize);
  794. }
  795. else {
  796. // Update mouse pointer
  797. mousePointer(((Sint16)event->motion.x) / pixelSize, ((Sint16)event->motion.y) / pixelSize);
  798. }
  799. return 1;
  800. case SDL_MOUSEFOCUS:
  801. if (event->user.code & 1) {
  802. hover = 1;
  803. mousePointer();
  804. }
  805. else {
  806. hover = 0;
  807. selectMouse(MOUSE_NORMAL);
  808. }
  809. return 1;
  810. case SDL_INPUTFOCUS:
  811. if (event->user.code & 1) {
  812. if (!haveFocus) {
  813. hideCursor = 0;
  814. haveFocus = partialFocus = 1;
  815. changed = 1;
  816. }
  817. }
  818. else if (event->user.code & 2) {
  819. if (!partialFocus) {
  820. partialFocus = 1;
  821. changed = 1;
  822. }
  823. }
  824. else {
  825. if (partialFocus) {
  826. partialFocus = 0;
  827. changed = 1;
  828. }
  829. }
  830. if (!(event->user.code & 1)) {
  831. if (haveFocus) {
  832. cancelTool();
  833. haveFocus = 0;
  834. changed = 1;
  835. }
  836. }
  837. if (changed) {
  838. // Refresh selection rectangle and cursor
  839. Rect rect = { selectionRect.x + selectionXOffs, selectionRect.y + selectionYOffs, selectionRect.w, selectionRect.h };
  840. Rect rect2 = { cursorX, cursorY, 1, 1 };
  841. setDirtyRect(rect);
  842. setDirtyRect(rect2);
  843. }
  844. return 1;
  845. case SDL_KEYUP:
  846. switch (event->key.keysym.sym) {
  847. case SDLK_LSHIFT:
  848. case SDLK_RSHIFT:
  849. case SDLK_SPACE:
  850. finishTool();
  851. // Force cursor back into area, no more virtual
  852. moveCursor(cursorX, cursorY);
  853. return 1;
  854. case SDLK_LALT:
  855. case SDLK_RALT:
  856. case SDLK_LCTRL:
  857. case SDLK_RCTRL:
  858. modifyTool();
  859. return 1;
  860. default:
  861. break;
  862. }
  863. break;
  864. case SDL_KEYDOWN:
  865. hideCursor = 0;
  866. // We can't stick modifiers in due to the numerous combinations that
  867. // could occur of ctrl/shift/alt in use of various tools
  868. switch (event->key.keysym.sym) {
  869. case SDLK_KP_ENTER:
  870. case SDLK_RETURN:
  871. // Merge any existing selection
  872. try {
  873. mergeSelection();
  874. }
  875. catch (UndoException& e) {
  876. }
  877. return 1;
  878. case SDLK_LALT:
  879. case SDLK_RALT:
  880. case SDLK_LCTRL:
  881. case SDLK_RCTRL:
  882. modifyTool();
  883. return 1;
  884. case SDLK_SPACE:
  885. startTool(ColorSelect::SELECTED_FG);
  886. return 1;
  887. case SDLK_LSHIFT:
  888. startToolOnMove = 1;
  889. return 1;
  890. // @TODO: This should probably end up as a configurable shortcut somehow
  891. case SDLK_RSHIFT:
  892. startTool(ColorSelect::SELECTED_BK);
  893. return 1;
  894. case SDLK_RIGHT:
  895. if (selectionMode != SELECTION_OUTLINE) {
  896. if (event->key.keysym.mod & KMOD_CTRL) moveSelection(tileWidth - selectionRect.w - selectionRect.x, selectionYOffs);
  897. else moveSelection(selectionXOffs + 1, selectionYOffs);
  898. }
  899. else {
  900. if (event->key.keysym.mod & KMOD_CTRL) moveCursor(tileWidth - 1, virtualCursorY);
  901. else moveCursor(virtualCursorX + 1, virtualCursorY);
  902. }
  903. return 1;
  904. case SDLK_END:
  905. if (selectionMode != SELECTION_OUTLINE) {
  906. if (event->key.keysym.mod & KMOD_CTRL) moveSelection(tileWidth - selectionRect.w - selectionRect.x, tileHeight - selectionRect.h - selectionRect.y);
  907. else moveSelection(tileWidth - selectionRect.w - selectionRect.x, selectionYOffs);
  908. }
  909. else {
  910. if (event->key.keysym.mod & KMOD_CTRL) moveCursor(tileWidth - 1, tileHeight - 1);
  911. else moveCursor(tileWidth - 1, virtualCursorY);
  912. }
  913. return 1;
  914. case SDLK_LEFT:
  915. if (selectionMode != SELECTION_OUTLINE) {
  916. if (event->key.keysym.mod & KMOD_CTRL) moveSelection(-selectionRect.x, selectionYOffs);
  917. else moveSelection(selectionXOffs - 1, selectionYOffs);
  918. }
  919. else {
  920. if (event->key.keysym.mod & KMOD_CTRL) moveCursor(0, virtualCursorY);
  921. else moveCursor(virtualCursorX - 1, virtualCursorY);
  922. }
  923. return 1;
  924. case SDLK_HOME:
  925. if (selectionMode != SELECTION_OUTLINE) {
  926. if (event->key.keysym.mod & KMOD_CTRL) moveSelection(-selectionRect.x, -selectionRect.y);
  927. else moveSelection(-selectionRect.x, selectionYOffs);
  928. }
  929. else {
  930. if (event->key.keysym.mod & KMOD_CTRL) moveCursor(0, 0);
  931. else moveCursor(0, virtualCursorY);
  932. }
  933. return 1;
  934. case SDLK_DOWN:
  935. if (selectionMode != SELECTION_OUTLINE) {
  936. if (event->key.keysym.mod & KMOD_CTRL) moveSelection(selectionXOffs, tileHeight - selectionRect.h - selectionRect.y);
  937. else moveSelection(selectionXOffs, selectionYOffs + 1);
  938. }
  939. else {
  940. if (event->key.keysym.mod & KMOD_CTRL) moveCursor(virtualCursorX, tileHeight - 1);
  941. else moveCursor(virtualCursorX, virtualCursorY + 1);
  942. }
  943. return 1;
  944. case SDLK_UP:
  945. if (selectionMode != SELECTION_OUTLINE) {
  946. if (event->key.keysym.mod & KMOD_CTRL) moveSelection(selectionXOffs, -selectionRect.y);
  947. else moveSelection(selectionXOffs, selectionYOffs - 1);
  948. }
  949. else {
  950. if (event->key.keysym.mod & KMOD_CTRL) moveCursor(virtualCursorX, 0);
  951. else moveCursor(virtualCursorX, virtualCursorY - 1);
  952. }
  953. return 1;
  954. case SDLK_PAGEDOWN:
  955. if (selectionMode != SELECTION_OUTLINE) {
  956. moveSelection(selectionXOffs, selectionYOffs + (viewHeight / pixelSize - 1));
  957. }
  958. else {
  959. moveCursor(virtualCursorX, virtualCursorY + (viewHeight / pixelSize - 1));
  960. }
  961. return 1;
  962. case SDLK_PAGEUP:
  963. if (selectionMode != SELECTION_OUTLINE) {
  964. moveSelection(selectionXOffs, selectionYOffs - (viewHeight / pixelSize - 1));
  965. }
  966. else {
  967. moveCursor(virtualCursorX, virtualCursorY - (viewHeight / pixelSize - 1));
  968. }
  969. return 1;
  970. default:
  971. break;
  972. }
  973. break;
  974. }
  975. return 0;
  976. }
  977. void TilePaint::zoomOut() { start_func
  978. if (pixelSize > MAX_ZOOMGRID) changePixelSizing(pixelSize - 1, enableGrid ? 1 : 0);
  979. else if (pixelSize > 1) changePixelSizing(pixelSize - 1, 0);
  980. if (pixelSize == 1) tools->findWidget(TilePaintTools::ID_ZOOMOUT)->disable();
  981. tools->findWidget(TilePaintTools::ID_ZOOMIN)->enable();
  982. }
  983. void TilePaint::zoomIn() { start_func
  984. if (pixelSize < MAX_ZOOMGRID - 1) changePixelSizing(pixelSize + 1, 0);
  985. else if (pixelSize < MAX_ZOOM) changePixelSizing(pixelSize + 1, enableGrid ? 1 : 0);
  986. if (pixelSize == MAX_ZOOM) tools->findWidget(TilePaintTools::ID_ZOOMIN)->disable();
  987. tools->findWidget(TilePaintTools::ID_ZOOMOUT)->enable();
  988. }
  989. void TilePaint::moveSelection(int newX, int newY) { start_func
  990. // No dragging of outlines allowed
  991. if (selectionMode == SELECTION_OUTLINE) return;
  992. if ((newX != selectionXOffs) || (newY != selectionYOffs)) {
  993. // Dirty old
  994. Rect rect = { selectionRect.x + selectionXOffs, selectionRect.y + selectionYOffs, selectionRect.w, selectionRect.h };
  995. setDirtyRect(rect);
  996. // Move (no limits to where it can move to)
  997. selectionXOffs = newX;
  998. selectionYOffs = newY;
  999. // Dirty new
  1000. rect.x = selectionRect.x + newX;
  1001. rect.y = selectionRect.y + newX;
  1002. setDirtyRect(rect);
  1003. }
  1004. }
  1005. void TilePaint::moveCursor(int newX, int newY) { start_func
  1006. int oldX = cursorX;
  1007. int oldY = cursorY;
  1008. int doTool = 0;
  1009. if (startToolOnMove) startTool(ColorSelect::SELECTED_FG);
  1010. // This alone doesn't make anything dirty; normally, this isn't used;
  1011. // anything that does use it, dirties; we don't track virtual cursor
  1012. // if no active tool
  1013. if (toolActive) {
  1014. if ((newX != virtualCursorX) || (virtualCursorY != newY)) {
  1015. virtualCursorX = newX;
  1016. virtualCursorY = newY;
  1017. // Tool
  1018. doTool = 1;
  1019. }
  1020. }
  1021. if (newX < 0) newX = 0;
  1022. if (newX >= tileWidth) newX = tileWidth - 1;
  1023. if (newY < 0) newY = 0;
  1024. if (newY >= tileHeight) newY = tileHeight - 1;
  1025. if ((newX != cursorX) || (newY != cursorY)) {
  1026. cursorX = newX;
  1027. cursorY = newY;
  1028. // Scroll?
  1029. if (myFrame) myFrame->scrollToView(cursorX * pixelSize,
  1030. cursorY * pixelSize,
  1031. pixelSize, pixelSize);
  1032. // Dirty
  1033. setDirtyBox(oldX, oldY, cursorX, cursorY);
  1034. }
  1035. if (doTool) dragTool();
  1036. // No active tool, we track virtual cursor, but to CLIPPED coordinates
  1037. else if (!toolActive) {
  1038. virtualCursorX = newX;
  1039. virtualCursorY = newY;
  1040. }
  1041. }
  1042. void TilePaint::startToolGlyphWidthDrag() { start_func
  1043. assert(tile);
  1044. if (toolActive) return;
  1045. toolActive = TOOLS_GLYPHWIDTHDRAG;
  1046. mousePointer();
  1047. dragTool(1);
  1048. }
  1049. void TilePaint::startToolSelectionDrag() { start_func
  1050. assert(tile);
  1051. startToolOnMove = 0;
  1052. if (toolActive) return;
  1053. toolActive = TOOLS_SELECTDRAG;
  1054. toolStartX = virtualCursorX;
  1055. toolStartY = virtualCursorY;
  1056. toolCtrl = SDL_GetModState() & KMOD_CTRL;
  1057. toolAlt = SDL_GetModState() & KMOD_ALT;
  1058. mousePointer();
  1059. dragTool(1);
  1060. }
  1061. void TilePaint::startTool(int fgbk) { start_func
  1062. assert(tile);
  1063. startToolOnMove = 0;
  1064. if (toolActive) return;
  1065. // Merge any existing selection
  1066. try {
  1067. mergeSelection();
  1068. }
  1069. catch (UndoException& e) {
  1070. return;
  1071. }
  1072. toolMinX = virtualCursorX;
  1073. toolMinY = virtualCursorY;
  1074. toolMaxX = virtualCursorX;
  1075. toolMaxY = virtualCursorY;
  1076. toolFgBk = fgbk;
  1077. toolActive = tool;
  1078. toolStartX = virtualCursorX;
  1079. toolStartY = virtualCursorY;
  1080. toolAlpha = alphaBlend;
  1081. toolAlias = antiAlias;
  1082. // Scale tolerance to 500, the max two colors can differ is 1019
  1083. toolTolerance = tolerance * 5;
  1084. toolCtrl = SDL_GetModState() & KMOD_CTRL;
  1085. toolAlt = SDL_GetModState() & KMOD_ALT;
  1086. if (toolFgBk == ColorSelect::SELECTED_FG) {
  1087. toolColor = mapColor32(colors.fg.r, colors.fg.g, colors.fg.b, colors.fg.a * toolAlpha / 100);
  1088. }
  1089. else {
  1090. toolColor = mapColor32(colors.bk.r, colors.bk.g, colors.bk.b, colors.bk.a * toolAlpha / 100);
  1091. }
  1092. mousePointer();
  1093. dragTool(1);
  1094. }
  1095. void TilePaint::modifyTool() { start_func
  1096. if (toolActive) {
  1097. toolCtrl = SDL_GetModState() & KMOD_CTRL;
  1098. toolAlt = SDL_GetModState() & KMOD_ALT;
  1099. // Selection tools need entire area dirtied because this may cause
  1100. // a deleted selection to reappear
  1101. if ((toolActive == TOOLS_SELECT) || (toolActive == TOOLS_SELECTELLIPSE) || (toolActive == TOOLS_WAND)) {
  1102. setDirty(1);
  1103. }
  1104. // Selection dragging changes selection type
  1105. if ((toolActive == TOOLS_SELECTDRAG) && (selectionMode != SELECTION_OUTLINE)) {
  1106. if ((toolCtrl) || (toolAlt)) selectionMode = SELECTION_OPAQUE;
  1107. else selectionMode = SELECTION_ALPHA;
  1108. }
  1109. dragTool();
  1110. }
  1111. // Always update mouse pointer even if no active tool
  1112. mousePointer();
  1113. }
  1114. void TilePaint::dragTool(int firstTime, int lastTime) { start_func
  1115. assert(tile);
  1116. // Selection tools merely clear, on right-click
  1117. if ((firstTime) && (toolFgBk == ColorSelect::SELECTED_BK) &&
  1118. ((toolActive == TOOLS_SELECT) ||
  1119. (toolActive == TOOLS_SELECTELLIPSE) ||
  1120. (toolActive == TOOLS_WAND))) {
  1121. try {
  1122. doneSelection();
  1123. }
  1124. catch (UndoException& e) {
  1125. }
  1126. toolActive = 0;
  1127. return;
  1128. }
  1129. if (toolActive) {
  1130. int rX, rY;
  1131. int slope1, slope2;
  1132. SDL_Surface* drawTo;
  1133. Rect rect;
  1134. Rect tileBound = { 0, 0, tileWidth, tileHeight };
  1135. Uint8* data;
  1136. int nonDraw = 0;
  1137. if ((toolActive == TOOLS_SELECT) || (toolActive == TOOLS_SELECTELLIPSE) ||
  1138. (toolActive == TOOLS_DROPPER) || (toolActive == TOOLS_WAND) ||
  1139. (toolActive == TOOLS_SELECTDRAG) || (toolActive == TOOLS_GLYPHWIDTHDRAG)) {
  1140. nonDraw = 1;
  1141. }
  1142. if ((tool != TOOLS_PEN) && (!nonDraw)) refreshTile();
  1143. if (((toolAlpha < 100) || ((selectionMode == SELECTION_OUTLINE) && (selectionRect.w))) && (!nonDraw)) {
  1144. // If we're drawing using alpha, start with transparency
  1145. if (toolAlpha < 100) drawRect(0, 0, alphaWorkspace->w, alphaWorkspace->h, mapColor32(0, 0, 0, 0), alphaWorkspace);
  1146. // ...otherwise start with current data
  1147. else blit(0, 0, tile, 0, 0, alphaWorkspace, alphaWorkspace->w, alphaWorkspace->h);
  1148. drawTo = alphaWorkspace;
  1149. }
  1150. else {
  1151. drawTo = tile;
  1152. }
  1153. // So that undo warning boxes don't cancel us!
  1154. int tool = toolActive;
  1155. if (lastTime) toolActive = 0;
  1156. try {
  1157. switch (tool) {
  1158. case TOOLS_GLYPHWIDTHDRAG:
  1159. if (lastTime) {
  1160. refreshTile();
  1161. undoStoreGlyph();
  1162. setGlyphWidth(virtualCursorX);
  1163. applyGlyphWidth();
  1164. }
  1165. else {
  1166. setGlyphWidth(virtualCursorX);
  1167. }
  1168. break;
  1169. case TOOLS_SELECTDRAG:
  1170. // Dirty current selection area
  1171. setDirtyBox(selectionRect.x + selectionXOffs, selectionRect.y + selectionYOffs,
  1172. selectionRect.x + selectionRect.w - 1 + selectionXOffs,
  1173. selectionRect.y + selectionRect.h - 1 + selectionYOffs);
  1174. // Float selection?
  1175. if (firstTime) {
  1176. floatSelection();
  1177. }
  1178. else {
  1179. // Move selection
  1180. selectionXOffs += virtualCursorX - toolLastX;
  1181. selectionYOffs += virtualCursorY - toolLastY;
  1182. // Dirty new area also
  1183. setDirtyBox(selectionRect.x + selectionXOffs, selectionRect.y + selectionYOffs,
  1184. selectionRect.x + selectionRect.w - 1 + selectionXOffs,
  1185. selectionRect.y + selectionRect.h - 1 + selectionYOffs);
  1186. }
  1187. break;
  1188. case TOOLS_WAND:
  1189. case TOOLS_SELECT:
  1190. case TOOLS_SELECTELLIPSE:
  1191. // Keep a backup copy of old selection
  1192. if (firstTime) {
  1193. blit(0, 0, selection, 0, 0, backupSelection, tileWidth, tileHeight);
  1194. backupSelectionRect = selectionRect;
  1195. }
  1196. // Refresh from backup if dragging with ctrl/alt
  1197. else if ((toolCtrl) || (toolAlt)) {
  1198. blit(0, 0, backupSelection, 0, 0, selection, tileWidth, tileHeight);
  1199. selectionRect = backupSelectionRect;
  1200. }
  1201. if (tool == TOOLS_WAND) {
  1202. // We get rectangle from previous time
  1203. if (!firstTime) {
  1204. rect.x = toolMinX;
  1205. rect.y = toolMinY;
  1206. rect.w = toolMaxX - toolMinX + 1;
  1207. rect.h = toolMaxY - toolMinY + 1;
  1208. // Mark previous rect dirty
  1209. setDirtyRect(rect, 1);
  1210. }
  1211. else {
  1212. rect.w = 0;
  1213. }
  1214. }
  1215. else if (tool == TOOLS_SELECT) {
  1216. rect = createRect(toolStartX, toolStartY, virtualCursorX, virtualCursorY);
  1217. }
  1218. else {
  1219. rX = abs(toolStartX - virtualCursorX);
  1220. rY = abs(toolStartY - virtualCursorY);
  1221. // Special case
  1222. if (rX < 1) rX = 1;
  1223. if (rY < 1) rY = 1;
  1224. rect = createRect(toolStartX - rX, toolStartY - rY, toolStartX + rX, toolStartY + rY);
  1225. }
  1226. // We can't have a selection rect that's bigger than the selection surface
  1227. intersectRects(rect, tileBound);
  1228. // Undo?
  1229. if (lastTime) {
  1230. // If we're clearing selection, we must do extra work here
  1231. if ((!toolCtrl) && (!toolAlt)) {
  1232. // Refresh from backup
  1233. blit(0, 0, backupSelection, 0, 0, selection, tileWidth, tileHeight);
  1234. selectionRect = backupSelectionRect;
  1235. // Add into rectangle the area we're selecting
  1236. boundRects(selectionRect, rect);
  1237. // Clear it AND store undo
  1238. clearSelection();
  1239. }
  1240. else {
  1241. // Just undo the area we're adding/deleting from
  1242. if (rect.w) undoStoreSelect(rect.x, rect.y, rect.w, rect.h);
  1243. }
  1244. }
  1245. // Clear selection if no ctrl/alt
  1246. else if ((!toolCtrl) && (!toolAlt)) clearSelection(0);
  1247. // Draw
  1248. if (tool == TOOLS_WAND) {
  1249. // Non-contiguous always works
  1250. if (!toolAlias) {
  1251. rect = floodFillNonContiguous32(tile, selection, virtualCursorX, virtualCursorY, mapColor32(0, 0, 0, toolAlt ? 0 : 255), toolTolerance);
  1252. }
  1253. else {
  1254. // Use alpha workspace as temp area, use 255/255/255 as filler color
  1255. drawRect(0, 0, alphaWorkspace->w, alphaWorkspace->h, mapColor32(255, 255, 255, 255), alphaWorkspace);
  1256. rect = floodFill32(tile, alphaWorkspace, virtualCursorX, virtualCursorY, mapColor32(0, 0, 0, toolAlt ? 0 : 255), toolTolerance);
  1257. SDL_SetAlpha(alphaWorkspace, 0, 255);
  1258. SDL_SetColorKey(alphaWorkspace, SDL_SRCCOLORKEY, mapColor32(255, 255, 255, 255));
  1259. blit(0, 0, alphaWorkspace, 0, 0, selection, tileWidth, tileHeight);
  1260. SDL_SetColorKey(alphaWorkspace, 0, 0);
  1261. SDL_SetAlpha(alphaWorkspace, SDL_SRCALPHA, 255);
  1262. }
  1263. // Remember min/max for undo next time
  1264. if (rect.w) {
  1265. toolMinX = rect.x;
  1266. toolMinY = rect.y;
  1267. toolMaxX = rect.x + rect.w - 1;
  1268. toolMaxY = rect.y + rect.h - 1;
  1269. }
  1270. else {
  1271. // Causes no undo area or undirty to occur next frame
  1272. toolMinX = toolMaxX = -1;
  1273. }
  1274. }
  1275. else if (tool == TOOLS_SELECT) {
  1276. sge_FilledRect(selection, toolStartX, toolStartY, virtualCursorX, virtualCursorY, mapColor32(0, 0, 0, toolAlt ? 0 : 255));
  1277. }
  1278. else {
  1279. sge_FilledEllipse(selection, toolStartX, toolStartY, rX, rY, mapColor32(0, 0, 0, toolAlt ? 0 : 255));
  1280. }
  1281. setDirtyRect(rect, 1);
  1282. // Add to overall selection bounding box
  1283. if (!toolAlt) boundRects(selectionRect, rect);
  1284. // ...or fix selection rectangle if removing stuff
  1285. else fixSelectionRect();
  1286. // Previous
  1287. if (!firstTime) {
  1288. if (tool == TOOLS_WAND) {
  1289. // (was dirtied above, at beginning)
  1290. }
  1291. else if (tool == TOOLS_SELECT) {
  1292. setDirtyBox(toolStartX, toolStartY, toolLastX, toolLastY, 1);
  1293. }
  1294. else {
  1295. rX = abs(toolStartX - toolLastX);
  1296. rY = abs(toolStartY - toolLastY);
  1297. if (rX < 1) rX = 1;
  1298. if (rY < 1) rY = 1;
  1299. setDirtyBox(toolStartX - rX, toolStartY - rY, toolStartX + rX, toolStartY + rY, 1);
  1300. }
  1301. }
  1302. // Allow copy now?
  1303. if (lastTime) {
  1304. if (selectionRect.w) tools->findWidget(TilePaintTools::ID_COPY)->enable();
  1305. else tools->findWidget(TilePaintTools::ID_COPY)->disable();
  1306. }
  1307. break;
  1308. case TOOLS_DROPPER:
  1309. data = ((Uint8*)tile->pixels) + cursorX * 4 + cursorY * tile->pitch;
  1310. colorbar->addColor(toolFgBk, data[0], data[1], data[2], data[3], !firstTime);
  1311. break;
  1312. case TOOLS_PEN:
  1313. // We don't want to draw the previous pixel twice; this is more important
  1314. // than the slight degradation in line quality, as lines are only used
  1315. // if the mouse moves "too fast"
  1316. if (!lastTime) {
  1317. if (toolStartX > virtualCursorX) --toolStartX;
  1318. else if (toolStartX < virtualCursorX) ++toolStartX;
  1319. if (toolStartY > virtualCursorY) --toolStartY;
  1320. else if (toolStartY < virtualCursorY) ++toolStartY;
  1321. sge_Line(drawTo, toolStartX, toolStartY, virtualCursorX, virtualCursorY, toolColor);
  1322. setDirtyBox(toolStartX, toolStartY, virtualCursorX, virtualCursorY);
  1323. }
  1324. // Track area covered for undo
  1325. if (virtualCursorX < toolMinX) toolMinX = virtualCursorX;
  1326. if (virtualCursorX > toolMaxX) toolMaxX = virtualCursorX;
  1327. if (virtualCursorY < toolMinY) toolMinY = virtualCursorY;
  1328. if (virtualCursorY > toolMaxY) toolMaxY = virtualCursorY;
  1329. if (toolStartX < toolMinX) toolMinX = toolStartX;
  1330. if (toolStartX > toolMaxX) toolMaxX = toolStartX;
  1331. if (toolStartY < toolMinY) toolMinY = toolStartY;
  1332. if (toolStartY > toolMaxY) toolMaxY = toolStartY;
  1333. toolStartX = virtualCursorX;
  1334. toolStartY = virtualCursorY;
  1335. if (lastTime) undoStoreBlitBox(toolMinX, toolMinY, toolMaxX, toolMaxY);
  1336. else if (tools) tools->tileRedraw();
  1337. break;
  1338. case TOOLS_FILL:
  1339. // Undo based on rectangle from last time
  1340. if (lastTime) undoStoreBlitBox(toolMinX, toolMinY, toolMaxX, toolMaxY);
  1341. // Dirty previous rectangle
  1342. if (!firstTime) setDirtyBox(toolMinX, toolMinY, toolMaxX, toolMaxY);
  1343. // Non-contiguous always works
  1344. if (!toolAlias) {
  1345. rect = floodFillNonContiguous32(tile, drawTo, virtualCursorX, virtualCursorY, toolColor, toolTolerance);
  1346. }
  1347. // Possible situations-
  1348. // Drawing to tile- works OK if 0 tolerance (fills directly on surface)
  1349. // Drawing to alpha 100%- works OK if 0 tolerance (fills directly on copy of surface)
  1350. // Drawing to alpha <100%- works OK always (0,0,0,0 doesn't fill, but would have no effect anyways)
  1351. // Situations that don't work OK- we must draw to a temp surface and colorkey-blit over
  1352. else if ((toolTolerance > 0) && ((drawTo == tile) || ((drawTo == alphaWorkspace) && (toolAlpha == 100)))) {
  1353. // Use bitwise complement for background, so there can't be a conflict
  1354. drawRect(0, 0, backupSelection->w, backupSelection->h, ~toolColor, backupSelection);
  1355. rect = floodFill32(tile, backupSelection, virtualCursorX, virtualCursorY, toolColor, toolTolerance);
  1356. SDL_SetColorKey(backupSelection, SDL_SRCCOLORKEY, ~toolColor);
  1357. blit(0, 0, backupSelection, 0, 0, drawTo, tileWidth, tileHeight);
  1358. SDL_SetColorKey(backupSelection, 0, 0);
  1359. }
  1360. else {
  1361. rect = floodFill32(tile, drawTo, virtualCursorX, virtualCursorY, toolColor, toolTolerance);
  1362. }
  1363. setDirtyRect(rect);
  1364. // Remember min/max for undo on last time
  1365. if (rect.w) {
  1366. toolMinX = rect.x;
  1367. toolMinY = rect.y;
  1368. toolMaxX = rect.x + rect.w - 1;
  1369. toolMaxY = rect.y + rect.h - 1;
  1370. }
  1371. else {
  1372. // Causes no undo area or undirty to occur next frame
  1373. toolMinX = toolMaxX = -1;
  1374. }
  1375. break;
  1376. case TOOLS_LINE:
  1377. case TOOLS_RECT:
  1378. case TOOLS_RECTFILL:
  1379. // @TODO: we changed this line, haven't tested it yet
  1380. if (lastTime) undoStoreBlitBox(toolStartX, toolStartY, virtualCursorX, virtualCursorY);
  1381. rX = virtualCursorX;
  1382. rY = virtualCursorY;
  1383. // Limit to square or straight line?
  1384. if (toolCtrl) {
  1385. if (tool == TOOLS_LINE) {
  1386. // Determine approximate slope of line
  1387. slope1 = abs(toolStartX - rX);
  1388. slope2 = abs(toolStartY - rY);
  1389. // (we only care, if both sizes are > 0)
  1390. if ((slope1) && (slope2)) {
  1391. if (slope1 > slope2) swap(slope1, slope2);
  1392. // slope1/slope2 will be a fraction between 0 (flat) and
  1393. // 1 (diagonal of 45deg multiple); cutoff point is 0.5
  1394. if (slope1 * 2 / slope2 >= 1) {
  1395. // Square
  1396. if (abs(toolStartX - rX) < abs(toolStartY - rY)) {
  1397. rY = toolStartY + abs(toolStartX - rX) * (toolStartY < rY ? 1 : -1);
  1398. }
  1399. else {
  1400. rX = toolStartX + abs(toolStartY - rY) * (toolStartX < rX ? 1 : -1);
  1401. }
  1402. }
  1403. else {
  1404. // Flat line
  1405. if (abs(toolStartX - rX) < abs(toolStartY - rY)) {
  1406. rX = toolStartX;
  1407. }
  1408. else {
  1409. rY = toolStartY;
  1410. }
  1411. }
  1412. }
  1413. }
  1414. else {
  1415. // Square
  1416. if (abs(toolStartX - rX) < abs(toolStartY - rY)) {
  1417. rY = toolStartY + abs(toolStartX - rX) * (toolStartY < rY ? 1 : -1);
  1418. }
  1419. else {
  1420. rX = toolStartX + abs(toolStartY - rY) * (toolStartX < rX ? 1 : -1);
  1421. }
  1422. }
  1423. }
  1424. if (tool == TOOLS_LINE) {
  1425. if (toolAlias) sge_AALine(drawTo, toolStartX, toolStartY, rX, rY, toolColor);
  1426. else sge_Line(drawTo, toolStartX, toolStartY, rX, rY, toolColor);
  1427. }
  1428. else if (tool == TOOLS_RECT) sge_Rect(drawTo, toolStartX, toolStartY, rX, rY, toolColor);
  1429. else sge_FilledRect(drawTo, toolStartX, toolStartY, rX, rY, toolColor);
  1430. setDirtyBox(toolStartX, toolStartY, rX, rY);
  1431. // (no need to limit based on toolCtrl, as a limited area will always
  1432. // be smaller than this area we use here)
  1433. if (!firstTime) setDirtyBox(toolStartX, toolStartY, toolLastX, toolLastY);
  1434. break;
  1435. case TOOLS_ELLIPSE:
  1436. case TOOLS_ELLIPSEFILL:
  1437. rX = abs(toolStartX - virtualCursorX);
  1438. rY = abs(toolStartY - virtualCursorY);
  1439. // Special case
  1440. if (rX < 1) rX = 1;
  1441. if (rY < 1) rY = 1;
  1442. // Circle?
  1443. if (toolCtrl) {
  1444. rX = min(rX, rY);
  1445. rY = rX;
  1446. }
  1447. if (lastTime) undoStoreBlitBox(toolStartX - rX, toolStartY - rY, toolStartX + rX, toolStartY + rY);
  1448. if (tool == TOOLS_ELLIPSE) {
  1449. if (toolAlias) sge_AAEllipse(drawTo, toolStartX, toolStartY, rX, rY, toolColor);
  1450. else sge_Ellipse(drawTo, toolStartX, toolStartY, rX, rY, toolColor);
  1451. }
  1452. else {
  1453. if (toolAlias) sge_AAFilledEllipse(drawTo, toolStartX, toolStartY, rX, rY, toolColor);
  1454. else sge_FilledEllipse(drawTo, toolStartX, toolStartY, rX, rY, toolColor);
  1455. }
  1456. setDirtyBox(toolStartX - rX, toolStartY - rY, toolStartX + rX, toolStartY + rY);
  1457. // Previous
  1458. if (!firstTime) {
  1459. rX = abs(toolStartX - toolLastX);
  1460. rY = abs(toolStartY - toolLastY);
  1461. if (rX < 1) rX = 1;
  1462. if (rY < 1) rY = 1;
  1463. // (no need to limit to a circle based on toolCtrl, this will always
  1464. // cover the minimum area needed)
  1465. setDirtyBox(toolStartX - rX, toolStartY - rY, toolStartX + rX, toolStartY + rY);
  1466. }
  1467. break;
  1468. }
  1469. }
  1470. catch (UndoException& e) {
  1471. toolActive = tool;
  1472. cancelTool();
  1473. return;
  1474. }
  1475. if (drawTo == alphaWorkspace) {
  1476. // Clip to selection, alpha blit, or both?
  1477. if ((selectionMode == SELECTION_OUTLINE) && (selectionRect.w)) {
  1478. if (toolAlpha < 100) maskAlphaBlit32(0, 0, alphaWorkspace, 0, 0, tile, 0, 0, selection, 100, tileWidth, tileHeight);
  1479. else maskBlit32(0, 0, alphaWorkspace, 0, 0, tile, 0, 0, selection, tileWidth, tileHeight);
  1480. }
  1481. else alphaBlit32(0, 0, alphaWorkspace, 0, 0, tile, tileWidth, tileHeight);
  1482. }
  1483. if ((lastTime) && (!nonDraw)) {
  1484. applyTile();
  1485. }
  1486. else if (!nonDraw) tools->tileRedraw();
  1487. toolLastX = virtualCursorX;
  1488. toolLastY = virtualCursorY;
  1489. }
  1490. }
  1491. void TilePaint::cancelTool() { start_func
  1492. assert(tile);
  1493. startToolOnMove = 0;
  1494. if (toolActive) {
  1495. int rX;
  1496. int rY;
  1497. switch (toolActive) {
  1498. case TOOLS_GLYPHWIDTHDRAG:
  1499. rX = glyphWidth;
  1500. refreshTile();
  1501. setDirtyGlyphWidth(rX, glyphWidth);
  1502. // The cursor may appear, and the above won't necessarily cover it
  1503. setDirtyBox(cursorX, cursorY, cursorX, cursorY);
  1504. break;
  1505. case TOOLS_SELECT:
  1506. case TOOLS_SELECTELLIPSE:
  1507. case TOOLS_WAND:
  1508. // Refresh from backup, dirty all
  1509. blit(0, 0, backupSelection, 0, 0, selection, tileWidth, tileHeight);
  1510. selectionRect = backupSelectionRect;
  1511. setDirty(1);
  1512. break;
  1513. case TOOLS_PEN:
  1514. case TOOLS_FILL:
  1515. refreshTile();
  1516. tools->tileRedraw();
  1517. setDirtyBox(toolMinX, toolMinY, toolMaxX, toolMaxY);
  1518. break;
  1519. case TOOLS_LINE:
  1520. case TOOLS_RECT:
  1521. case TOOLS_RECTFILL:
  1522. refreshTile();
  1523. tools->tileRedraw();
  1524. setDirtyBox(toolStartX, toolStartY, toolLastX, toolLastY);
  1525. break;
  1526. case TOOLS_ELLIPSE:
  1527. case TOOLS_ELLIPSEFILL:
  1528. refreshTile();
  1529. tools->tileRedraw();
  1530. rX = abs(toolStartX - toolLastX);
  1531. rY = abs(toolStartY - toolLastY);
  1532. if (rX < 1) rX = 1;
  1533. if (rY < 1) rY = 1;
  1534. setDirtyBox(toolStartX - rX, toolStartY - rY, toolStartX + rX, toolStartY + rY);
  1535. break;
  1536. }
  1537. mousePointer();
  1538. toolActive = 0;
  1539. }
  1540. }
  1541. void TilePaint::finishTool() { start_func
  1542. assert(tile);
  1543. startToolOnMove = 0;
  1544. if (toolActive) {
  1545. dragTool(0, 1);
  1546. mousePointer();
  1547. // toolActive = 0 taken care of by dragtool now
  1548. }
  1549. }
  1550. void TilePaint::display(SDL_Surface* destSurface, Rect& toDisplay, const Rect& clipArea, int xOffset, int yOffset) { start_func
  1551. assert(destSurface);
  1552. if (visible) {
  1553. // If dirty, redraw range or all
  1554. if (dirty) {
  1555. if (totalDirty) {
  1556. getRect(toDisplay);
  1557. toDisplay.x += xOffset;
  1558. toDisplay.y += yOffset;
  1559. }
  1560. else {
  1561. dirtyRange.x += x + xOffset;
  1562. dirtyRange.y += y + yOffset;
  1563. // Range must include requested update area as well
  1564. boundRects(toDisplay, dirtyRange);
  1565. }
  1566. dirty = totalDirty = 0;
  1567. dirtyRange.w = 0;
  1568. intersectRects(toDisplay, clipArea);
  1569. }
  1570. xOffset += x;
  1571. yOffset += y;
  1572. // Anything to draw?
  1573. if (toDisplay.w) {
  1574. SDL_SetClipRect(destSurface, &toDisplay);
  1575. SDL_FillRect(destSurface, &toDisplay, guiPacked[COLOR_BKFILL]);
  1576. // position of original pixel
  1577. int opX = max(0, (toDisplay.x - xOffset) / pixelSize);
  1578. int pY = max(0, (toDisplay.y - yOffset) / pixelSize);
  1579. // drawn position
  1580. int odX = opX * pixelSize + xOffset;
  1581. int dY = pY * pixelSize + yOffset;
  1582. // source material
  1583. int pitch = tile->pitch;
  1584. const Uint8* source = (Uint8*)tile->pixels + opX * 4 + pY * pitch;
  1585. int selPitch = selection->pitch;
  1586. const Uint8* selSrc = (Uint8*)selection->pixels + (opX - selectionXOffs) * 4 + (pY - selectionYOffs) * selPitch;
  1587. // limits
  1588. int lastY = toDisplay.y + toDisplay.h;
  1589. int lastX = toDisplay.x + toDisplay.w;
  1590. // Area we can legally check for selection (outside this area,
  1591. // pointer is not gaurunteed to point to anything)
  1592. int selX1 = -1;
  1593. int selX2 = -1;
  1594. int selY1 = -1;
  1595. int selY2 = -1;
  1596. if (selectionRect.w) {
  1597. selX1 = selectionRect.x + selectionXOffs;
  1598. selX2 = selectionRect.x + selectionRect.w - 1 + selectionXOffs;
  1599. selY1 = selectionRect.y + selectionYOffs;
  1600. selY2 = selectionRect.y + selectionRect.h - 1 + selectionYOffs;
  1601. }
  1602. // Temp storage for alpha selections
  1603. Uint8 selTemp[4];
  1604. // Draw right/bottom grid line first, because pixels may overwrite
  1605. // this with selection lines
  1606. // Right/bottom grid is always drawn even if grid is "off"
  1607. if (xOffset + tileWidth * pixelSize < lastX) {
  1608. drawVLine(yOffset, yOffset + tileHeight * pixelSize,
  1609. xOffset + tileWidth * pixelSize, guiPacked[COLOR_GRID], destSurface);
  1610. }
  1611. if (yOffset + tileHeight * pixelSize < lastY) {
  1612. drawHLine(xOffset, xOffset + tileWidth * pixelSize,
  1613. yOffset + tileHeight * pixelSize, guiPacked[COLOR_GRID], destSurface);
  1614. }
  1615. for (; (dY < lastY) && (pY < tileHeight); dY += pixelSize, ++pY) {
  1616. int pX = opX;
  1617. int dX = odX;
  1618. for (; (dX < lastX) && (pX < tileWidth); dX += pixelSize, ++pX, source += 4, selSrc += 4) {
  1619. const Uint8* data = source;
  1620. int drawT = gridSize;
  1621. int drawB = 0;
  1622. int drawL = gridSize;
  1623. int drawR = 0;
  1624. // Selection?
  1625. if ((pX >= selX1) && (pX <= selX2) && (pY >= selY1) && (pY <= selY2)) {
  1626. if (selSrc[3]) {
  1627. // Modify data?
  1628. if (selectionMode == SELECTION_OPAQUE) {
  1629. data = selSrc;
  1630. }
  1631. else if (selectionMode == SELECTION_ALPHA) {
  1632. int alpha = selSrc[3];
  1633. int blend = alphaBlend;
  1634. // Adjust alpha based on alpha option
  1635. alpha = alpha * blend / 100;
  1636. // Special cases
  1637. if ((alpha == 255) || ((source[3] == 0) && (blend == 100))) {
  1638. data = selSrc;
  1639. }
  1640. else {
  1641. data = selTemp;
  1642. // Scale RGB blend based on alpha of destination
  1643. int destAlpha = source[3];
  1644. int rgbScale = alpha;
  1645. if (destAlpha < alpha) rgbScale = alpha + (256 - alpha) * (alpha - source[3]) / 255;
  1646. selTemp[0] = (source[0] + ((selSrc[0] - source[0]) * rgbScale >> 8)) & 0xFF;
  1647. selTemp[1] = (source[1] + ((selSrc[1] - source[1]) * rgbScale >> 8)) & 0xFF;
  1648. selTemp[2] = (source[2] + ((selSrc[2] - source[2]) * rgbScale >> 8)) & 0xFF;
  1649. selTemp[3] = (source[3] + ((255 - source[3]) * alpha >> 8)) & 0xFF;
  1650. }
  1651. }
  1652. // Selection rectangle
  1653. if (partialFocus) {
  1654. if ((pX == selX1) || !(selSrc[-1])) drawL = 2;
  1655. if ((pX == selX2) || !(selSrc[7])) drawR = 2;
  1656. if ((pY == selY1) || !(selSrc[-selPitch + 3])) drawT = 2;
  1657. if ((pY == selY2) || !(selSrc[selPitch + 3])) drawB = 2;
  1658. }
  1659. }
  1660. }
  1661. // 0 alpha shows different
  1662. if (data[3] == 0) {
  1663. drawRect(dX, dY, pixelSize, pixelSize, guiPacked[COLOR_TRANSPARENT1], destSurface);
  1664. if ((drawDots == VIEW_DOTS_ALPHA) && (pixelSize - gridSize > 2)) {
  1665. drawRect(dX + 1 + gridSize, dY + 1 + gridSize, pixelSize - 2 - gridSize, pixelSize - 2 - gridSize,
  1666. SDL_MapRGB(destSurface->format, 0, 0, 0), destSurface);
  1667. }
  1668. else if ((drawDots == VIEW_DOTS_TRANS) && (pixelSize > 1)) {
  1669. drawPixel(dX + pixelSize/2, dY + pixelSize/2, guiPacked[COLOR_TRANSPARENT2], destSurface);
  1670. }
  1671. }
  1672. else if (data[3] == 255) {
  1673. drawRect(dX, dY, pixelSize, pixelSize,
  1674. SDL_MapRGB(destSurface->format, data[0], data[1], data[2]), destSurface);
  1675. if ((drawDots == VIEW_DOTS_ALPHA) && (pixelSize - gridSize > 2)) {
  1676. drawRect(dX + 1 + gridSize, dY + 1 + gridSize, pixelSize - 2 - gridSize, pixelSize - 2 - gridSize,
  1677. SDL_MapRGB(destSurface->format, 255, 255, 255), destSurface);
  1678. }
  1679. }
  1680. else {
  1681. drawRect(dX, dY, pixelSize, pixelSize,
  1682. SDL_MapRGB(destSurface->format, data[0] * data[3] >> 8, data[1] * data[3] >> 8, data[2] * data[3] >> 8), destSurface);
  1683. if ((drawDots == VIEW_DOTS_ALPHA) && (pixelSize - gridSize > 2)) {
  1684. drawRect(dX + 1 + gridSize, dY + 1 + gridSize, pixelSize - 2 - gridSize, pixelSize - 2 - gridSize,
  1685. SDL_MapRGB(destSurface->format, data[3], data[3], data[3]), destSurface);
  1686. }
  1687. }
  1688. // Grid assumes 1-thickness lines
  1689. if (drawT == 1) drawHLine(dX, dX + pixelSize - 1, dY, guiPacked[COLOR_GRID], destSurface);
  1690. else if (drawT == 2) drawAntsHLine(dX, dX + pixelSize - 1, dY, desktop->currentSelectionPhase(), destSurface);
  1691. if (drawL == 1) drawVLine(dY + 1, dY + pixelSize - 1, dX, guiPacked[COLOR_GRID], destSurface);
  1692. else if (drawL == 2) drawAntsVLine(dY, dY + pixelSize - 1, dX, desktop->currentSelectionPhase(), destSurface);
  1693. // (B/R can only be == 2 right now)
  1694. if (drawB) drawAntsHLine(dX, dX + pixelSize - 1, dY + pixelSize - 1, desktop->currentSelectionPhase(), destSurface);
  1695. if (drawR) drawAntsVLine(dY, dY + pixelSize - 1, dX + pixelSize - 1, desktop->currentSelectionPhase(), destSurface);
  1696. }
  1697. source += pitch - (pX - opX) * 4;
  1698. selSrc += selPitch - (pX - opX) * 4;
  1699. }
  1700. // Cursor
  1701. if ((haveFocus) && (!hideCursor)) {
  1702. int cX = xOffset + cursorX * pixelSize;
  1703. int cY = yOffset + cursorY * pixelSize;
  1704. if ((cX < toDisplay.x + toDisplay.w) && (cY < toDisplay.y + toDisplay.h) &&
  1705. (cX + pixelSize + gridSize > toDisplay.x) &&
  1706. (cY + pixelSize + gridSize > toDisplay.y)) {
  1707. drawSelectRect(cX, cY, pixelSize + gridSize, pixelSize + gridSize,
  1708. guiPacked[COLOR_TILECURSOR], destSurface, desktop->currentCursorAlpha());
  1709. }
  1710. }
  1711. // Glyph width
  1712. if (isFont) {
  1713. int atX = xOffset + glyphWidth * pixelSize;
  1714. int atY = yOffset + tileHeight * pixelSize;
  1715. drawVLine(yOffset, atY,
  1716. atX, guiPacked[COLOR_GLYPHWIDTH], destSurface);
  1717. drawLine(atX, atY + GLYPH_SIZE_POINTER_MARGIN, atX + GLYPH_SIZE_POINTER_HEIGHT,
  1718. atY + GLYPH_SIZE_POINTER_MARGIN + GLYPH_SIZE_POINTER_HEIGHT,
  1719. guiPacked[COLOR_GLYPHWIDTH], destSurface);
  1720. drawLine(atX, atY + GLYPH_SIZE_POINTER_MARGIN, atX - GLYPH_SIZE_POINTER_HEIGHT,
  1721. atY + GLYPH_SIZE_POINTER_MARGIN + GLYPH_SIZE_POINTER_HEIGHT,
  1722. guiPacked[COLOR_GLYPHWIDTH], destSurface);
  1723. drawHLine(atX - GLYPH_SIZE_POINTER_HEIGHT, atX + GLYPH_SIZE_POINTER_HEIGHT,
  1724. atY + GLYPH_SIZE_POINTER_MARGIN + GLYPH_SIZE_POINTER_HEIGHT,
  1725. guiPacked[COLOR_GLYPHWIDTH], destSurface);
  1726. }
  1727. }
  1728. }
  1729. }
  1730. void TilePaint::undoStoreBlitBox(int x1, int y1, int x2, int y2) throw_Undo { start_func
  1731. if (x1 > x2) swap(x1, x2);
  1732. if (y1 > y2) swap(y1, y2);
  1733. undoStoreBlit(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
  1734. }
  1735. void TilePaint::undoStoreBlit(int x, int y, int w, int h) throw_Undo { start_func
  1736. if (isColl) world->undo.storeUndoColl(tileset->getId(), currentTile, x, y, w, h, myFrame);
  1737. else world->undo.storeUndoTile(tileset->getId(), currentTile, x, y, w, h, myFrame);
  1738. }
  1739. void TilePaint::undoStoreGlyph() throw_Undo { start_func
  1740. world->undo.storeUndoTileGlyph(tileset->getId(), currentTile, glyphWidth, myFrame);
  1741. }
  1742. void TilePaint::undoStoreSelect(int x, int y, int w, int h) throw_Undo { start_func
  1743. world->undo.storeUndoPaintSelection(&selection, x, y, w, h, selectionXOffs, selectionYOffs, selectionMode, myFrame);
  1744. }
  1745. void TilePaint::clearSelection(int storeUndo) throw_Undo { start_func
  1746. assert(selection);
  1747. if (selectionRect.w) {
  1748. if (storeUndo) undoStoreSelect(selectionRect.x, selectionRect.y, selectionRect.w, selectionRect.h);
  1749. SDL_FillRect(selection, &selectionRect, mapColor32(0, 0, 0, 0));
  1750. // Add in offset so proper area is dirtied
  1751. selectionRect.x += selectionXOffs;
  1752. selectionRect.y += selectionYOffs;
  1753. // (don't need to set feather because we're clearing -entire- selection)
  1754. setDirtyRect(selectionRect);
  1755. selectionRect.w = 0;
  1756. selectionXOffs = selectionYOffs = 0;
  1757. selectionMode = SELECTION_OUTLINE;
  1758. tools->findWidget(TilePaintTools::ID_COPY)->disable();
  1759. // Was selection oversized? if so, correct this
  1760. if ((selection->w > tileWidth) || (selection->h > tileHeight)) {
  1761. // Ideally, if we free then alloc, this shouldn't fail
  1762. SDL_FreeSurface(selection);
  1763. selection = NULL;
  1764. selection = createSurface32(tileWidth, tileHeight);
  1765. sge_FilledRect(selection, 0, 0, tileWidth, tileHeight, mapColor32(0, 0, 0, 0));
  1766. }
  1767. }
  1768. }
  1769. void TilePaint::floatSelection() throw_Undo { start_func
  1770. if ((selectionMode == SELECTION_OUTLINE) && (selectionRect.w)) {
  1771. Uint32 bkColor = mapColor32(colors.bk.r, colors.bk.g, colors.bk.b, colors.bk.a);
  1772. world->undo.preUndoBlock();
  1773. undoStoreBlit(selectionRect.x, selectionRect.y, selectionRect.w, selectionRect.h);
  1774. undoStoreSelect(selectionRect.x, selectionRect.y, selectionRect.w, selectionRect.h);
  1775. world->undo.postUndoBlock();
  1776. maskBlit32(selectionRect.x, selectionRect.y, tile, selectionRect.x, selectionRect.y, selection, selectionRect.x, selectionRect.y, selection, selectionRect.w, selectionRect.h, 1, bkColor);
  1777. selectionMode = SELECTION_ALPHA;
  1778. setDirtyRect(selectionRect);
  1779. // (we fix the rect because we may have floated transparents, so the
  1780. // actual selection may have reduced in size)
  1781. fixSelectionRect();
  1782. applyTile();
  1783. // (even possible that there's no selection now)
  1784. if (!selectionRect.w) tools->findWidget(TilePaintTools::ID_COPY)->disable();
  1785. }
  1786. }
  1787. void TilePaint::doneSelection() throw_Undo { start_func
  1788. if (selectionMode == SELECTION_OUTLINE) clearSelection();
  1789. else mergeSelection();
  1790. }
  1791. void TilePaint::pasteSelection() throw_Undo { start_func
  1792. if (canConvertClipboard(CLIPBOARD_IMAGE)) {
  1793. world->undo.preUndoBlock();
  1794. doneSelection();
  1795. int w;
  1796. int h;
  1797. clipboardPasteImageSize(&w, &h);
  1798. // Do we need a larger selection surface?
  1799. if ((w > tileWidth) || (h > tileHeight)) {
  1800. // (if this fails, nothing's been affected yet)
  1801. SDL_Surface* newSel = createSurface32(max(tileWidth, w), max(tileHeight, h));
  1802. if (selection) SDL_FreeSurface(selection);
  1803. selection = newSel;
  1804. sge_FilledRect(selection, 0, 0, selection->w, selection->h, mapColor32(0, 0, 0, 0));
  1805. // (no need to undo this on exception, larger selection buffer doesn't harm us)
  1806. }
  1807. undoStoreSelect(0, 0, w, h);
  1808. clipboardPasteImage(selection, 0, 0);
  1809. world->undo.postUndoBlock();
  1810. selectionRect.x = 0;
  1811. selectionRect.y = 0;
  1812. selectionRect.w = w;
  1813. selectionRect.h = h;
  1814. selectionMode = SELECTION_ALPHA;
  1815. // Possible nothing was actually pasted
  1816. fixSelectionRect();
  1817. if (selectionRect.w) {
  1818. tools->findWidget(TilePaintTools::ID_COPY)->enable();
  1819. // @TODO: Move selection to ensure it's visible
  1820. }
  1821. // Dirty
  1822. setDirtyRect(selectionRect);
  1823. }
  1824. }
  1825. void TilePaint::copySelection() { start_func
  1826. if (selectionRect.w) {
  1827. if (selectionMode == SELECTION_OUTLINE) {
  1828. // We copy to backup selection so we don't affect our outline
  1829. sge_FilledRect(backupSelection, 0, 0, tileWidth, tileHeight, mapColor32(0, 0, 0, 0));
  1830. maskBlit32(selectionRect.x, selectionRect.y, tile, selectionRect.x, selectionRect.y, backupSelection, selectionRect.x, selectionRect.y, selection, selectionRect.w, selectionRect.h);
  1831. clipboardCopy(backupSelection, selectionRect.x, selectionRect.y, selectionRect.w, selectionRect.h);
  1832. }
  1833. else {
  1834. // We copy what's in the floating selection
  1835. clipboardCopy(selection, selectionRect.x, selectionRect.y, selectionRect.w, selectionRect.h);
  1836. }
  1837. }
  1838. }
  1839. void TilePaint::deleteSelection() throw_Undo { start_func
  1840. if ((selectionMode == SELECTION_OUTLINE) && (selectionRect.w)) {
  1841. // Delete where outline is
  1842. Uint32 bkColor = mapColor32(colors.bk.r, colors.bk.g, colors.bk.b, colors.bk.a);
  1843. undoStoreBlit(selectionRect.x, selectionRect.y, selectionRect.w, selectionRect.h);
  1844. maskBlit32(selectionRect.x, selectionRect.y, tile, selectionRect.x, selectionRect.y, tile, selectionRect.x, selectionRect.y, selection, selectionRect.w, selectionRect.h, 1, bkColor);
  1845. setDirtyRect(selectionRect);
  1846. applyTile();
  1847. }
  1848. else if (selectionRect.w) {
  1849. // Delete floating selection
  1850. clearSelection();
  1851. }
  1852. }
  1853. void TilePaint::mergeSelection() throw_Undo { start_func
  1854. // (no tileset = closing)
  1855. if (((selectionMode == SELECTION_OPAQUE) || (selectionMode == SELECTION_ALPHA)) && (tileset)) {
  1856. world->undo.preUndoBlock();
  1857. // Paste selection, with offset
  1858. if (selectionRect.w) {
  1859. undoStoreBlit(selectionRect.x + selectionXOffs, selectionRect.y + selectionYOffs, selectionRect.w, selectionRect.h);
  1860. if (selectionMode == SELECTION_OPAQUE) {
  1861. maskBlit32(selectionRect.x, selectionRect.y, selection, selectionRect.x + selectionXOffs, selectionRect.y + selectionYOffs, tile, selectionRect.x, selectionRect.y, selection, selectionRect.w, selectionRect.h);
  1862. }
  1863. else {
  1864. maskAlphaBlit32(selectionRect.x, selectionRect.y, selection, selectionRect.x + selectionXOffs, selectionRect.y + selectionYOffs, tile, selectionRect.x, selectionRect.y, selection, alphaBlend, selectionRect.w, selectionRect.h);
  1865. }
  1866. }
  1867. // Clear selection
  1868. clearSelection();
  1869. world->undo.postUndoBlock();
  1870. applyTile();
  1871. }
  1872. }
  1873. int TilePaint::isInSelection(int x, int y) { start_func
  1874. if (!selectionRect.w) return 0;
  1875. x -= selectionXOffs;
  1876. y -= selectionYOffs;
  1877. Uint8* source = (Uint8*)selection->pixels + y * selection->pitch + x * 4;
  1878. return source[3];
  1879. }
  1880. void TilePaint::fixSelectionRect() { start_func
  1881. assert(selection);
  1882. int minX = -1;
  1883. int maxX;
  1884. int minY;
  1885. int maxY;
  1886. Uint8* source = (Uint8*)selection->pixels;
  1887. int pitchOffs = selection->pitch - selection->w * 4;
  1888. // We use size of selection surface insetad of tile size, on purpose
  1889. for (int y = 0; y < selection->h; ++y) {
  1890. for (int x = 0; x < selection->w; ++x) {
  1891. if (source[3]) {
  1892. if (minX == -1) {
  1893. minX = maxX = x;
  1894. minY = maxY = y;
  1895. }
  1896. else {
  1897. if (x < minX) minX = x;
  1898. else if (x > maxX) maxX = x;
  1899. if (y < minY) minY = y;
  1900. else if (y > maxY) maxY = y;
  1901. }
  1902. }
  1903. source += 4;
  1904. }
  1905. source += pitchOffs;
  1906. }
  1907. if (minX == -1) {
  1908. selectionRect.w = 0;
  1909. }
  1910. else {
  1911. selectionRect = createRect(minX, minY, maxX, maxY);
  1912. }
  1913. }
  1914. void TilePaint::mousePointer(int mouseX, int mouseY) { start_func
  1915. lastMouseX = mouseX;
  1916. lastMouseY = mouseY;
  1917. mousePointer();
  1918. }
  1919. void TilePaint::mousePointer() { start_func
  1920. if (!hover) return;
  1921. if ((toolActive == TOOLS_SELECT) || (toolActive == TOOLS_SELECTELLIPSE) || (toolActive == TOOLS_WAND)) {
  1922. if (toolAlt) {
  1923. selectMouse(MOUSE_SUBTRACT);
  1924. return;
  1925. }
  1926. else if (toolCtrl) {
  1927. selectMouse(MOUSE_ADD);
  1928. return;
  1929. }
  1930. }
  1931. if (toolActive == TOOLS_GLYPHWIDTHDRAG) {
  1932. selectMouse(MOUSE_HORIZ);
  1933. return;
  1934. }
  1935. if (toolActive == TOOLS_SELECTDRAG) {
  1936. selectMouse(MOUSE_FOURDIRECTION);
  1937. return;
  1938. }
  1939. if ((!toolActive) && ((tool == TOOLS_SELECT) || (tool == TOOLS_SELECTELLIPSE) || (tool == TOOLS_WAND))) {
  1940. if (SDL_GetModState() & KMOD_ALT) {
  1941. selectMouse(MOUSE_SUBTRACT);
  1942. return;
  1943. }
  1944. if (SDL_GetModState() & KMOD_CTRL) {
  1945. selectMouse(MOUSE_ADD);
  1946. return;
  1947. }
  1948. if (isInSelection(lastMouseX, lastMouseY)) {
  1949. selectMouse(MOUSE_FOURDIRECTION);
  1950. return;
  1951. }
  1952. }
  1953. if ((!toolActive) && (lastMouseY >= tileHeight) && (isFont)) {
  1954. selectMouse(MOUSE_HORIZ);
  1955. return;
  1956. }
  1957. selectMouse(MOUSE_PIXEL);
  1958. }
  1959. Window::CommandSupport TilePaint::supportsCommand(int code) const { start_func
  1960. switch (code) {
  1961. // Options
  1962. case VIEW_DOTS_NONE:
  1963. case VIEW_DOTS_TRANS:
  1964. case VIEW_DOTS_ALPHA:
  1965. if (isColl) return Window::COMMAND_HIDE;
  1966. return (Window::CommandSupport)(((drawDots == code) ? Window::COMMAND_SELECTED : 0) | Window::COMMAND_RADIO | Window::COMMAND_ENABLE);
  1967. case VIEW_GRID:
  1968. return (Window::CommandSupport)((enableGrid ? Window::COMMAND_SELECTED : 0) | Window::COMMAND_CHECKBOX | Window::COMMAND_ENABLE);
  1969. case TOOLS_ANTIALIAS:
  1970. if (isColl) return Window::COMMAND_HIDE;
  1971. if ((tool == TOOLS_WAND) || (tool == TOOLS_FILL)) return Window::COMMAND_HIDE;
  1972. if ((tool == TOOLS_LINE) || (tool == TOOLS_ELLIPSE) || (tool == TOOLS_ELLIPSEFILL)) {
  1973. return (Window::CommandSupport)((antiAlias ? Window::COMMAND_SELECTED : 0) | Window::COMMAND_CHECKBOX | Window::COMMAND_ENABLE);
  1974. }
  1975. else return (Window::CommandSupport)((antiAlias ? Window::COMMAND_SELECTED : 0) | Window::COMMAND_CHECKBOX | Window::COMMAND_DISABLE);
  1976. case TOOLS_CONTIGUOUS:
  1977. if ((tool == TOOLS_WAND) || (tool == TOOLS_FILL)) {
  1978. return (Window::CommandSupport)((antiAlias ? Window::COMMAND_SELECTED : 0) | Window::COMMAND_CHECKBOX | Window::COMMAND_ENABLE);
  1979. }
  1980. else return Window::COMMAND_HIDE;
  1981. case TOOLS_DROPPER:
  1982. if (isColl) return Window::COMMAND_HIDE;
  1983. return (Window::CommandSupport)(((tool == code) ? Window::COMMAND_SELECTED : 0) | Window::COMMAND_RADIO | Window::COMMAND_ENABLE);
  1984. case TOOLS_PEN:
  1985. case TOOLS_LINE:
  1986. case TOOLS_RECT:
  1987. case TOOLS_RECTFILL:
  1988. case TOOLS_ELLIPSE:
  1989. case TOOLS_ELLIPSEFILL:
  1990. case TOOLS_SELECT:
  1991. case TOOLS_SELECTELLIPSE:
  1992. case TOOLS_FILL:
  1993. case TOOLS_WAND:
  1994. return (Window::CommandSupport)(((tool == code) ? Window::COMMAND_SELECTED : 0) | Window::COMMAND_RADIO | Window::COMMAND_ENABLE);
  1995. case VIEW_DOTS_CHOOSE:
  1996. case TOOLS_BLENDUP:
  1997. case TOOLS_BLENDDOWN:
  1998. case TOOLS_EDITCOLOR:
  1999. return isColl ? Window::COMMAND_HIDE : Window::COMMAND_ENABLE;
  2000. // These are always available
  2001. case TOOLS_CHOOSE:
  2002. case VIEW_PREV:
  2003. case VIEW_NEXT:
  2004. case EDIT_SELECTALL:
  2005. case TOOLS_SETTINGS:
  2006. case TOOLS_NEXTCOLOR:
  2007. case TOOLS_PREVCOLOR:
  2008. return Window::COMMAND_ENABLE;
  2009. case TOOLS_TOLERANCEUP:
  2010. case TOOLS_TOLERANCEDOWN:
  2011. if (isColl) return Window::COMMAND_HIDE;
  2012. if ((tool == TOOLS_WAND) || (tool == TOOLS_FILL)) return Window::COMMAND_ENABLE;
  2013. else return Window::COMMAND_DISABLE;
  2014. case EDIT_COPY:
  2015. case EDIT_CUT:
  2016. case EDIT_DELETE:
  2017. case EDIT_DESELECTALL:
  2018. if (selectionRect.w) return Window::COMMAND_ENABLE;
  2019. else return Window::COMMAND_DISABLE;
  2020. case EDIT_PASTE:
  2021. if (canConvertClipboard(CLIPBOARD_IMAGE)) return Window::COMMAND_ENABLE;
  2022. else return Window::COMMAND_DISABLE;
  2023. case EDIT_SETGLYPHWIDTH:
  2024. if (isFont) return Window::COMMAND_ENABLE;
  2025. return Window::COMMAND_HIDE;
  2026. case VIEW_ZOOMIN:
  2027. if (pixelSize >= MAX_ZOOM) return Window::COMMAND_DISABLE;
  2028. else return Window::COMMAND_ENABLE;
  2029. case VIEW_ZOOMOUT:
  2030. if (pixelSize == 1) return Window::COMMAND_DISABLE;
  2031. else return Window::COMMAND_ENABLE;
  2032. // These aren't done yet (remember some may not be appropriate for isColl)
  2033. case EDIT_FLIP:
  2034. case EDIT_MIRROR:
  2035. case EDIT_SIZEROTATE:
  2036. case EDIT_GRADIENT:
  2037. case EDIT_COLORIZE:
  2038. case EDIT_FILTER:
  2039. return Window::COMMAND_DISABLE;
  2040. }
  2041. return world->supportsCommand(code);
  2042. }
  2043. void TilePaint::startToolIfMove() { start_func
  2044. startToolOnMove = 1;
  2045. }
  2046. void TilePaint::refresh() { start_func
  2047. if (pixelSize >= MAX_ZOOMGRID) changePixelSizing(pixelSize, enableGrid ? 1 : 0);
  2048. else changePixelSizing(pixelSize, 0);
  2049. setDirty(1);
  2050. }
  2051. void TilePaint::undoNotify(int undoType, int undoItemId, int undoItemSubId, int uX, int uY, int uW, int uH) { start_func
  2052. if ((undoType == UndoBuffer::UNDO_TILE) ||
  2053. (undoType == UndoBuffer::UNDO_TILECOLL) ||
  2054. (undoType == UndoBuffer::UNDO_TILEGLYPH)) {
  2055. assert(tileset->getId() == undoItemId);
  2056. // Ensure proper tile is selected
  2057. changeTile(undoItemSubId);
  2058. // Scroll?
  2059. if ((myFrame) && (undoType != UndoBuffer::UNDO_TILEGLYPH)) {
  2060. myFrame->scrollToView(uX * pixelSize, uY * pixelSize,
  2061. uW * pixelSize, uH * pixelSize);
  2062. }
  2063. }
  2064. if (undoType == UndoBuffer::UNDO_PAINTSEL) {
  2065. // Scroll?
  2066. if (myFrame) myFrame->scrollToView(uX * pixelSize, uY * pixelSize,
  2067. uW * pixelSize, uH * pixelSize);
  2068. }
  2069. }
  2070. void TilePaint::swapSelectionParameters(int& xoffs, int& yoffs, int& mode) { start_func
  2071. swap(xoffs, selectionXOffs);
  2072. swap(yoffs, selectionYOffs);
  2073. swap(mode, selectionMode);
  2074. // Refresh
  2075. fixSelectionRect();
  2076. setDirty(1);
  2077. }
  2078. Window::WindowType TilePaint::windowType() const { start_func
  2079. return WINDOW_CLIENT;
  2080. }