gcsx_colorselect.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. /* GCSx
  2. ** COLORSELECT.CPP
  3. **
  4. ** Color selection toolbar
  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. Uint8 ColorSelect::defaultColors[NUM_COLORS][4] = {
  25. { 0, 0, 0, 0 },
  26. { 128, 128, 128, 255 }, { 192, 192, 192, 255 }, { 255, 255, 255, 255 },
  27. { 255, 0, 0, 255 }, { 255, 128, 0, 255 }, { 255, 255, 0, 255 },
  28. { 0, 255, 0, 255 }, { 0, 255, 255, 255 },
  29. { 0, 0, 255, 255 }, { 255, 0, 255, 255 }
  30. };
  31. int ColorSelect::defaultSelected[2] = { 3, 0 };
  32. const string ColorSelect::wtArrowUp("m");
  33. const string ColorSelect::wtArrowDown("n");
  34. int ColorSelect::arrowHeight = 0;
  35. int ColorSelect::arrowXOffset = 0;
  36. ColorSelect::ColorSelect(ColorStore* cStorage, int defaultTransparent, int allowBk, int alphaBitDepth, int myBitDepth) : Window() { start_func
  37. assert((myBitDepth <= 8) && (myBitDepth >= 3));
  38. myFrame = NULL;
  39. storage = cStorage;
  40. numColors = allowBk ? 2 : 1;
  41. bitDepth = myBitDepth;
  42. bitDepthAlpha = alphaBitDepth;
  43. maxComponent = (1 << myBitDepth) - 1;
  44. maxAlpha = (1 << alphaBitDepth) - 1;
  45. // Default colors
  46. memcpy(color, defaultColors, NUM_COLORS * 4);
  47. // First color is black or transparent black dep. on tileset mode
  48. color[0][3] = defaultTransparent ? 0 : 255;
  49. // Scale default colors to depth
  50. for (int pos = 0; pos < NUM_COLORS; ++pos) {
  51. for (int sub = 0; sub < 3; ++sub) {
  52. color[pos][sub] = scaleComponent(color[pos][sub], 255, maxComponent);
  53. }
  54. color[pos][3] = scaleComponent(color[pos][3], 255, maxAlpha);
  55. }
  56. selected[SELECTED_FG] = defaultSelected[SELECTED_FG];
  57. selectedFirst[SELECTED_FG] = defaultSelected[SELECTED_FG];
  58. selectedLast[SELECTED_FG] = defaultSelected[SELECTED_FG];
  59. if (numColors == 2) {
  60. selected[SELECTED_BK] = defaultSelected[SELECTED_BK];
  61. selectedFirst[SELECTED_BK] = defaultSelected[SELECTED_BK];
  62. selectedLast[SELECTED_BK] = defaultSelected[SELECTED_BK];
  63. }
  64. else {
  65. // Color 0 can't change, so put unused BK there
  66. selected[SELECTED_BK] = 0;
  67. selectedFirst[SELECTED_BK] = 0;
  68. selectedLast[SELECTED_BK] = 0;
  69. }
  70. // Marker sizing
  71. arrowHeight = fontHeight(FONT_WIDGET);
  72. arrowXOffset = (COLOR_SIZE - fontWidth(wtArrowUp, FONT_WIDGET)) / 2;
  73. // Fill storage
  74. apply();
  75. // Calculate size
  76. resize(NUM_COLORS * (COLOR_SIZE + COLOR_SEPARATION) - COLOR_SEPARATION,
  77. arrowHeight * numColors + COLOR_HEIGHT);
  78. }
  79. FrameWindow* ColorSelect::createWindowed() { start_func
  80. // Prevent duplication
  81. if (myFrame) {
  82. return myFrame;
  83. }
  84. // We remember the frame pointer even though it'll delete itself
  85. myFrame = new FrameWindow("Colors", FrameWindow::RESIZING_SNAP, FrameWindow::FRAMETYPE_DIALOG, this, FrameWindow::TITLEBAR_TOOL, 0);
  86. return myFrame;
  87. }
  88. void ColorSelect::changeDefaultTransparent(int defaultTransparent) { start_func
  89. color[0][3] = defaultTransparent ? 0 : 255;
  90. setDirty();
  91. apply();
  92. }
  93. int ColorSelect::addColor(int which, Uint8 r, Uint8 g, Uint8 b, Uint8 a, int putInCurrent) { start_func
  94. int slot = selected[which];
  95. // (never ever modify slot 0)
  96. if ((!putInCurrent) || (slot == 0)) {
  97. // If color exists in current palette, select it
  98. for (int pos = 0; pos < NUM_COLORS; ++pos) {
  99. if ((color[pos][0] == r) && (color[pos][1] == g) &&
  100. (color[pos][2] == b) && (color[pos][3] == a)) {
  101. return colorSelection(which, pos);
  102. }
  103. }
  104. // Put color in next available slot after current selection.
  105. // Skip slots selected by either fg or bk, and first slot.
  106. // If no available slots, stick in current slot.
  107. while (((slot >= selectedFirst[SELECTED_FG]) && (slot <= selectedLast[SELECTED_FG])) ||
  108. ((slot >= selectedFirst[SELECTED_BK]) && (slot <= selectedLast[SELECTED_BK])) ||
  109. (slot == 0)) {
  110. if (++slot >= NUM_COLORS) slot = 0;
  111. if (slot == selected[which]) break;
  112. }
  113. // (never ever modify slot 0)
  114. if (slot == 0) slot = 1;
  115. }
  116. color[slot][0] = r;
  117. color[slot][1] = g;
  118. color[slot][2] = b;
  119. color[slot][3] = a;
  120. setDirty();
  121. apply();
  122. return colorSelection(which, slot);
  123. }
  124. void ColorSelect::editColor(int pos) { start_func
  125. assert((pos >= 0) && (pos < NUM_COLORS));
  126. // Can't edit background color
  127. if (pos == 0) return;
  128. if (RGBSelect::create()->run(color[pos][0], color[pos][1], color[pos][2], color[pos][3], bitDepthAlpha, bitDepth)) {
  129. setDirty();
  130. apply();
  131. }
  132. }
  133. int ColorSelect::colorSelection(int which) const { start_func
  134. return selected[which];
  135. }
  136. int ColorSelect::colorSelection(int which, int newPos, int drag) { start_func
  137. // Wraparound
  138. if (newPos < 0) newPos = NUM_COLORS - 1;
  139. if (newPos >= NUM_COLORS) newPos = 0;
  140. // Drag?
  141. if (drag) {
  142. // If selection beginning matches insert point
  143. if (selectedFirst[which] == selected[which]) {
  144. // Drag beginning point
  145. selectedFirst[which] = selected[which] = newPos;
  146. }
  147. else {
  148. // Drag end point
  149. selectedLast[which] = selected[which] = newPos;
  150. }
  151. // Ensure in proper order
  152. if (selectedLast[which] < selectedFirst[which]) {
  153. swap(selectedFirst[which], selectedLast[which]);
  154. }
  155. }
  156. else {
  157. // Insertion point and selection
  158. selectedFirst[which] = selectedLast[which] = selected[which] = newPos;
  159. }
  160. // Also update default
  161. defaultSelected[which] = selected[which];
  162. // Dirty
  163. setDirty();
  164. apply();
  165. return selected[which];
  166. }
  167. int ColorSelect::event(int hasFocus, const SDL_Event* event) { start_func
  168. assert(event);
  169. assert(parent);
  170. int selColor;
  171. int drag;
  172. switch (event->type) {
  173. case SDL_MOUSEBUTTONDOWN:
  174. case SDL_MOUSEBUTTONDBL:
  175. selColor = event->button.x / (COLOR_SIZE + COLOR_SEPARATION);
  176. if (((event->button.button == SDL_BUTTON_LEFT) || (event->button.button == SDL_BUTTON_RIGHT)) &&
  177. (selColor >= 0) && (selColor < NUM_COLORS)) {
  178. colorSelection((event->button.button == SDL_BUTTON_LEFT) || (numColors == 1) ? SELECTED_FG : SELECTED_BK, selColor, SDL_GetModState() & KMOD_SHIFT);
  179. if (event->type == SDL_MOUSEBUTTONDBL) editColor(selColor);
  180. return 1;
  181. }
  182. break;
  183. case SDL_MOUSEMOTION:
  184. if ((event->motion.state & SDL_BUTTON_LMASK) || (event->motion.state & SDL_BUTTON_RMASK)) {
  185. selColor = ((Sint16)event->button.x) / (COLOR_SIZE + COLOR_SEPARATION);
  186. if ((selColor >= 0) && (selColor < NUM_COLORS) && (event->button.y < height)) {
  187. colorSelection((event->motion.state & SDL_BUTTON_LMASK) || (numColors == 1) ? SELECTED_FG : SELECTED_BK, selColor, 1);
  188. return 1;
  189. }
  190. }
  191. break;
  192. case SDL_COMMAND:
  193. switch (event->user.code) {
  194. case EDIT_GRADIENT:
  195. // Only perform and dirty if appropriate...
  196. if (selectedLast[SELECTED_FG] > selectedFirst[SELECTED_FG]) {
  197. int last = selectedLast[SELECTED_FG];
  198. int first = selectedFirst[SELECTED_FG];
  199. // Number of steps
  200. int count = last - first;
  201. for (int pos = first + 1; pos < last; ++pos) {
  202. for (int sub = 0; sub < 4; ++sub) {
  203. color[pos][sub] = color[first][sub] + (color[last][sub] - color[first][sub]) * (pos - first) / count;
  204. }
  205. }
  206. setDirty();
  207. apply();
  208. }
  209. // ... but we always use the command
  210. return 1;
  211. case EDIT_COPY:
  212. clipboardCopy(&color[selectedFirst[SELECTED_FG]][0], selectedLast[SELECTED_FG] - selectedFirst[SELECTED_FG] + 1);
  213. return 1;
  214. case EDIT_PASTE:
  215. if ((selectedFirst[SELECTED_FG] > 0) && (canConvertClipboard(CLIPBOARD_RGBA))) {
  216. if (selectedFirst[SELECTED_FG] == selectedLast[SELECTED_FG]) {
  217. // Paste as much as possible
  218. clipboardPasteColor(&color[selectedFirst[SELECTED_FG]][0], NUM_COLORS - selectedFirst[SELECTED_FG]);
  219. }
  220. else {
  221. // Fill selection
  222. int count = selectedLast[SELECTED_FG] - selectedFirst[SELECTED_FG] + 1;
  223. int offset = 0;
  224. while (count) {
  225. int did = clipboardPasteColor(&color[selectedFirst[SELECTED_FG] + offset][0], count);
  226. count -= did;
  227. offset += did;
  228. }
  229. }
  230. setDirty();
  231. apply();
  232. return 1;
  233. }
  234. break;
  235. }
  236. break;
  237. case SDL_KEYDOWN:
  238. selColor = (event->key.keysym.mod & KMOD_CTRL) && (numColors == 2) ? SELECTED_BK : SELECTED_FG;
  239. drag = (event->key.keysym.mod & KMOD_SHIFT) ? 1 : 0;
  240. switch (combineKey(event->key.keysym.sym, event->key.keysym.mod & ~(KMOD_SHIFT | KMOD_CTRL))) {
  241. case SDLK_RIGHT:
  242. case SDLK_DOWN:
  243. colorSelection(selColor, selected[selColor] + 1, drag);
  244. return 1;
  245. case SDLK_LEFT:
  246. case SDLK_UP:
  247. colorSelection(selColor, selected[selColor] - 1, drag);
  248. return 1;
  249. case SDLK_HOME:
  250. colorSelection(selColor, 0, drag);
  251. return 1;
  252. case SDLK_END:
  253. colorSelection(selColor, NUM_COLORS - 1, drag);
  254. return 1;
  255. case SDLK_KP_ENTER:
  256. case SDLK_RETURN:
  257. editColor(selected[selColor]);
  258. return 1;
  259. }
  260. }
  261. return 0;
  262. }
  263. void ColorSelect::apply() { start_func
  264. // Currently, our parent never knows the selected ranges, but those aren't used yet
  265. storage->fg.r = color[selected[SELECTED_FG]][0];
  266. storage->fg.g = color[selected[SELECTED_FG]][1];
  267. storage->fg.b = color[selected[SELECTED_FG]][2];
  268. storage->fg.a = color[selected[SELECTED_FG]][3];
  269. storage->bk.r = color[selected[SELECTED_BK]][0];
  270. storage->bk.g = color[selected[SELECTED_BK]][1];
  271. storage->bk.b = color[selected[SELECTED_BK]][2];
  272. storage->bk.a = color[selected[SELECTED_BK]][3];
  273. // Remember defaults
  274. memcpy(defaultColors, color, NUM_COLORS * 4);
  275. // Scale default colors to depth
  276. for (int pos = 0; pos < NUM_COLORS; ++pos) {
  277. assert(maxComponent);
  278. for (int sub = 0; sub < 3; ++sub) {
  279. defaultColors[pos][sub] = scaleComponent(defaultColors[pos][sub], maxComponent, 255);
  280. }
  281. if (maxAlpha) {
  282. defaultColors[pos][3] = scaleComponent(defaultColors[pos][3], maxAlpha, 255);
  283. }
  284. else {
  285. defaultColors[pos][3] = 255;
  286. }
  287. }
  288. defaultSelected[SELECTED_FG] = selected[SELECTED_FG];
  289. if (numColors == 2) defaultSelected[SELECTED_BK] = selected[SELECTED_BK];
  290. // Inform parent
  291. if (parent) parent->childModified(this);
  292. }
  293. void ColorSelect::display(SDL_Surface* destSurface, Rect& toDisplay, const Rect& clipArea, int xOffset, int yOffset) { start_func
  294. assert(destSurface);
  295. if (visible) {
  296. // If dirty, redraw all
  297. if (dirty) {
  298. getRect(toDisplay);
  299. toDisplay.x += xOffset;
  300. toDisplay.y += yOffset;
  301. dirty = 0;
  302. intersectRects(toDisplay, clipArea);
  303. }
  304. // Anything to draw?
  305. if (toDisplay.w) {
  306. SDL_SetClipRect(destSurface, &toDisplay);
  307. xOffset += x;
  308. yOffset += y;
  309. // This widget only redraws colors that are part of the dirty area
  310. SDL_FillRect(destSurface, &toDisplay, guiPacked[COLOR_FILL]);
  311. // Selection markers
  312. if (selectedFirst[SELECTED_FG] != selectedLast[SELECTED_FG]) {
  313. int xBox = xOffset + (COLOR_SIZE + COLOR_SEPARATION) * selectedFirst[SELECTED_FG];
  314. int wBox = (COLOR_SIZE + COLOR_SEPARATION) * (selectedLast[SELECTED_FG] - selectedFirst[SELECTED_FG] + 1) - COLOR_SEPARATION;
  315. drawGuiBox(xBox, yOffset, wBox, arrowHeight, 1, destSurface);
  316. drawGradient(xBox + 1, yOffset + 1, wBox - 2, arrowHeight - 2, guiRGB[COLOR_SELECTION1], guiRGB[COLOR_SELECTION2], destSurface);
  317. }
  318. if ((numColors == 2) && (selectedFirst[SELECTED_BK] != selectedLast[SELECTED_BK])) {
  319. int xBox = xOffset + (COLOR_SIZE + COLOR_SEPARATION) * selectedFirst[SELECTED_BK];
  320. int wBox = (COLOR_SIZE + COLOR_SEPARATION) * (selectedLast[SELECTED_BK] - selectedFirst[SELECTED_BK] + 1) - COLOR_SEPARATION;
  321. drawGuiBox(xBox, arrowHeight + COLOR_HEIGHT + yOffset, wBox, arrowHeight, 1, destSurface);
  322. drawGradient(xBox + 1, arrowHeight + COLOR_HEIGHT + yOffset + 1, wBox - 2, arrowHeight - 2, guiRGB[COLOR_SELECTION1], guiRGB[COLOR_SELECTION2], destSurface);
  323. }
  324. int last = (toDisplay.x + toDisplay.w - xOffset) / (COLOR_SIZE + COLOR_SEPARATION);
  325. for (int pos = (toDisplay.x - xOffset) / (COLOR_SIZE + COLOR_SEPARATION); pos <= last; ++pos) {
  326. int xPos = xOffset + (COLOR_SIZE + COLOR_SEPARATION) * pos;
  327. // 0 alpha shows different
  328. if ((color[pos][3] == 0) && (maxAlpha)) {
  329. drawRect(xPos + COLOR_BEVEL, arrowHeight + yOffset + COLOR_BEVEL, COLOR_SIZE - COLOR_BEVEL * 2, COLOR_HEIGHT - COLOR_BEVEL * 2,
  330. guiPacked[COLOR_TRANSPARENT1], destSurface);
  331. drawLine(xPos + COLOR_BEVEL, arrowHeight + yOffset + COLOR_BEVEL, xPos + COLOR_SIZE - COLOR_BEVEL - 1, arrowHeight + yOffset + COLOR_HEIGHT - COLOR_BEVEL - 1,
  332. guiPacked[COLOR_TRANSPARENT2], destSurface);
  333. drawLine(xPos + COLOR_BEVEL, arrowHeight + yOffset + COLOR_HEIGHT - COLOR_BEVEL - 1, xPos + COLOR_SIZE - COLOR_BEVEL - 1, arrowHeight + yOffset + COLOR_BEVEL,
  334. guiPacked[COLOR_TRANSPARENT2], destSurface);
  335. }
  336. else {
  337. assert(maxComponent);
  338. drawRect(xPos + COLOR_BEVEL, arrowHeight + yOffset + COLOR_BEVEL, COLOR_SIZE - COLOR_BEVEL * 2, COLOR_HEIGHT - COLOR_BEVEL * 2,
  339. SDL_MapRGB(destSurface->format,
  340. scaleComponent(color[pos][0], maxComponent, 255),
  341. scaleComponent(color[pos][1], maxComponent, 255),
  342. scaleComponent(color[pos][2], maxComponent, 255)), destSurface);
  343. // < 255 alpha shows shaded part on lower half
  344. if ((color[pos][3] < maxAlpha) && (maxAlpha)) {
  345. drawRect(xPos + COLOR_BEVEL, arrowHeight + yOffset + COLOR_HEIGHT / 2, COLOR_SIZE - COLOR_BEVEL * 2, COLOR_HEIGHT - COLOR_HEIGHT / 2 - COLOR_BEVEL,
  346. SDL_MapRGB(destSurface->format,
  347. scaleComponent(color[pos][0] * color[pos][3] >> bitDepthAlpha, maxComponent, 255),
  348. scaleComponent(color[pos][1] * color[pos][3] >> bitDepthAlpha, maxComponent, 255),
  349. scaleComponent(color[pos][2] * color[pos][3] >> bitDepthAlpha, maxComponent, 255)),
  350. destSurface);
  351. }
  352. }
  353. drawGuiBoxInvert(xPos, arrowHeight + yOffset,
  354. COLOR_SIZE, COLOR_HEIGHT, COLOR_BEVEL, destSurface);
  355. // Selected?
  356. if (pos == selected[SELECTED_FG]) {
  357. drawText(wtArrowDown, guiRGB[selectedFirst[SELECTED_FG] == selectedLast[SELECTED_FG] ? COLOR_TEXT : COLOR_TEXTBOX], xPos + arrowXOffset, yOffset, destSurface, FONT_WIDGET);
  358. }
  359. if ((numColors == 2) && (pos == selected[SELECTED_BK])) {
  360. drawText(wtArrowUp, guiRGB[selectedFirst[SELECTED_BK] == selectedLast[SELECTED_BK] ? COLOR_TEXT : COLOR_TEXTBOX], xPos + arrowXOffset, arrowHeight + COLOR_HEIGHT + yOffset, destSurface, FONT_WIDGET);
  361. }
  362. }
  363. }
  364. }
  365. }
  366. const char* ColorSelect::tooltip(int xPos, int yPos) const { start_func
  367. // @TODO: more specific tool tips?
  368. return "Select color; double-click to edit";
  369. }
  370. void ColorSelect::alphaDepth(int newAlphaDepth) { start_func
  371. bitDepthAlpha = newAlphaDepth;
  372. int oldMaxAlpha = maxAlpha;
  373. maxAlpha = (1 << bitDepthAlpha) - 1;
  374. // Scale to new depth
  375. for (int pos = 0; pos < NUM_COLORS; ++pos) {
  376. if ((oldMaxAlpha) && (maxAlpha)) {
  377. color[pos][3] = scaleComponent(color[pos][3], oldMaxAlpha, maxAlpha);
  378. }
  379. else {
  380. color[pos][3] = maxAlpha;
  381. }
  382. }
  383. setDirty();
  384. apply();
  385. }
  386. Window::CommandSupport ColorSelect::supportsCommand(int code) const { start_func
  387. switch (code) {
  388. case EDIT_GRADIENT:
  389. if (selectedFirst[SELECTED_FG] < selectedLast[SELECTED_FG] - 1) return Window::COMMAND_ENABLE;
  390. return Window::COMMAND_DISABLE;
  391. case EDIT_COPY:
  392. return Window::COMMAND_ENABLE;
  393. case EDIT_PASTE:
  394. if (selected[SELECTED_FG] == 0) return Window::COMMAND_DISABLE;
  395. if (canConvertClipboard(CLIPBOARD_RGBA)) return Window::COMMAND_ENABLE;
  396. return Window::COMMAND_DISABLE;
  397. }
  398. return Window::COMMAND_HIDE;
  399. }