123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475 |
- /* GCSx
- ** COLORSELECT.CPP
- **
- ** Color selection toolbar
- */
- /*****************************************************************************
- ** Copyright (C) 2003-2006 Janson
- **
- ** This program is free software; you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation; either version 2 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program; if not, write to the Free Software
- ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
- *****************************************************************************/
- #include "all.h"
- Uint8 ColorSelect::defaultColors[NUM_COLORS][4] = {
- { 0, 0, 0, 0 },
- { 128, 128, 128, 255 }, { 192, 192, 192, 255 }, { 255, 255, 255, 255 },
- { 255, 0, 0, 255 }, { 255, 128, 0, 255 }, { 255, 255, 0, 255 },
- { 0, 255, 0, 255 }, { 0, 255, 255, 255 },
- { 0, 0, 255, 255 }, { 255, 0, 255, 255 }
- };
- int ColorSelect::defaultSelected[2] = { 3, 0 };
- const string ColorSelect::wtArrowUp("m");
- const string ColorSelect::wtArrowDown("n");
- int ColorSelect::arrowHeight = 0;
- int ColorSelect::arrowXOffset = 0;
- ColorSelect::ColorSelect(ColorStore* cStorage, int defaultTransparent, int allowBk, int alphaBitDepth, int myBitDepth) : Window() { start_func
- assert((myBitDepth <= 8) && (myBitDepth >= 3));
- myFrame = NULL;
- storage = cStorage;
- numColors = allowBk ? 2 : 1;
- bitDepth = myBitDepth;
- bitDepthAlpha = alphaBitDepth;
- maxComponent = (1 << myBitDepth) - 1;
- maxAlpha = (1 << alphaBitDepth) - 1;
- // Default colors
- memcpy(color, defaultColors, NUM_COLORS * 4);
- // First color is black or transparent black dep. on tileset mode
- color[0][3] = defaultTransparent ? 0 : 255;
- // Scale default colors to depth
- for (int pos = 0; pos < NUM_COLORS; ++pos) {
- for (int sub = 0; sub < 3; ++sub) {
- color[pos][sub] = scaleComponent(color[pos][sub], 255, maxComponent);
- }
- color[pos][3] = scaleComponent(color[pos][3], 255, maxAlpha);
- }
- selected[SELECTED_FG] = defaultSelected[SELECTED_FG];
- selectedFirst[SELECTED_FG] = defaultSelected[SELECTED_FG];
- selectedLast[SELECTED_FG] = defaultSelected[SELECTED_FG];
- if (numColors == 2) {
- selected[SELECTED_BK] = defaultSelected[SELECTED_BK];
- selectedFirst[SELECTED_BK] = defaultSelected[SELECTED_BK];
- selectedLast[SELECTED_BK] = defaultSelected[SELECTED_BK];
- }
- else {
- // Color 0 can't change, so put unused BK there
- selected[SELECTED_BK] = 0;
- selectedFirst[SELECTED_BK] = 0;
- selectedLast[SELECTED_BK] = 0;
- }
- // Marker sizing
- arrowHeight = fontHeight(FONT_WIDGET);
- arrowXOffset = (COLOR_SIZE - fontWidth(wtArrowUp, FONT_WIDGET)) / 2;
-
- // Fill storage
- apply();
- // Calculate size
- resize(NUM_COLORS * (COLOR_SIZE + COLOR_SEPARATION) - COLOR_SEPARATION,
- arrowHeight * numColors + COLOR_HEIGHT);
- }
- FrameWindow* ColorSelect::createWindowed() { start_func
- // Prevent duplication
- if (myFrame) {
- return myFrame;
- }
- // We remember the frame pointer even though it'll delete itself
- myFrame = new FrameWindow("Colors", FrameWindow::RESIZING_SNAP, FrameWindow::FRAMETYPE_DIALOG, this, FrameWindow::TITLEBAR_TOOL, 0);
- return myFrame;
- }
- void ColorSelect::changeDefaultTransparent(int defaultTransparent) { start_func
- color[0][3] = defaultTransparent ? 0 : 255;
- setDirty();
- apply();
- }
- int ColorSelect::addColor(int which, Uint8 r, Uint8 g, Uint8 b, Uint8 a, int putInCurrent) { start_func
- int slot = selected[which];
- // (never ever modify slot 0)
- if ((!putInCurrent) || (slot == 0)) {
- // If color exists in current palette, select it
- for (int pos = 0; pos < NUM_COLORS; ++pos) {
- if ((color[pos][0] == r) && (color[pos][1] == g) &&
- (color[pos][2] == b) && (color[pos][3] == a)) {
- return colorSelection(which, pos);
- }
- }
- // Put color in next available slot after current selection.
- // Skip slots selected by either fg or bk, and first slot.
- // If no available slots, stick in current slot.
- while (((slot >= selectedFirst[SELECTED_FG]) && (slot <= selectedLast[SELECTED_FG])) ||
- ((slot >= selectedFirst[SELECTED_BK]) && (slot <= selectedLast[SELECTED_BK])) ||
- (slot == 0)) {
- if (++slot >= NUM_COLORS) slot = 0;
- if (slot == selected[which]) break;
- }
- // (never ever modify slot 0)
- if (slot == 0) slot = 1;
- }
- color[slot][0] = r;
- color[slot][1] = g;
- color[slot][2] = b;
- color[slot][3] = a;
- setDirty();
- apply();
- return colorSelection(which, slot);
- }
- void ColorSelect::editColor(int pos) { start_func
- assert((pos >= 0) && (pos < NUM_COLORS));
- // Can't edit background color
- if (pos == 0) return;
- if (RGBSelect::create()->run(color[pos][0], color[pos][1], color[pos][2], color[pos][3], bitDepthAlpha, bitDepth)) {
- setDirty();
- apply();
- }
- }
- int ColorSelect::colorSelection(int which) const { start_func
- return selected[which];
- }
- int ColorSelect::colorSelection(int which, int newPos, int drag) { start_func
- // Wraparound
- if (newPos < 0) newPos = NUM_COLORS - 1;
- if (newPos >= NUM_COLORS) newPos = 0;
- // Drag?
- if (drag) {
- // If selection beginning matches insert point
- if (selectedFirst[which] == selected[which]) {
- // Drag beginning point
- selectedFirst[which] = selected[which] = newPos;
- }
- else {
- // Drag end point
- selectedLast[which] = selected[which] = newPos;
- }
- // Ensure in proper order
- if (selectedLast[which] < selectedFirst[which]) {
- swap(selectedFirst[which], selectedLast[which]);
- }
- }
- else {
- // Insertion point and selection
- selectedFirst[which] = selectedLast[which] = selected[which] = newPos;
- }
- // Also update default
- defaultSelected[which] = selected[which];
- // Dirty
- setDirty();
- apply();
- return selected[which];
- }
- int ColorSelect::event(int hasFocus, const SDL_Event* event) { start_func
- assert(event);
- assert(parent);
- int selColor;
- int drag;
- switch (event->type) {
- case SDL_MOUSEBUTTONDOWN:
- case SDL_MOUSEBUTTONDBL:
- selColor = event->button.x / (COLOR_SIZE + COLOR_SEPARATION);
- if (((event->button.button == SDL_BUTTON_LEFT) || (event->button.button == SDL_BUTTON_RIGHT)) &&
- (selColor >= 0) && (selColor < NUM_COLORS)) {
- colorSelection((event->button.button == SDL_BUTTON_LEFT) || (numColors == 1) ? SELECTED_FG : SELECTED_BK, selColor, SDL_GetModState() & KMOD_SHIFT);
- if (event->type == SDL_MOUSEBUTTONDBL) editColor(selColor);
- return 1;
- }
- break;
- case SDL_MOUSEMOTION:
- if ((event->motion.state & SDL_BUTTON_LMASK) || (event->motion.state & SDL_BUTTON_RMASK)) {
- selColor = ((Sint16)event->button.x) / (COLOR_SIZE + COLOR_SEPARATION);
- if ((selColor >= 0) && (selColor < NUM_COLORS) && (event->button.y < height)) {
- colorSelection((event->motion.state & SDL_BUTTON_LMASK) || (numColors == 1) ? SELECTED_FG : SELECTED_BK, selColor, 1);
- return 1;
- }
- }
- break;
- case SDL_COMMAND:
- switch (event->user.code) {
- case EDIT_GRADIENT:
- // Only perform and dirty if appropriate...
- if (selectedLast[SELECTED_FG] > selectedFirst[SELECTED_FG]) {
- int last = selectedLast[SELECTED_FG];
- int first = selectedFirst[SELECTED_FG];
- // Number of steps
- int count = last - first;
- for (int pos = first + 1; pos < last; ++pos) {
- for (int sub = 0; sub < 4; ++sub) {
- color[pos][sub] = color[first][sub] + (color[last][sub] - color[first][sub]) * (pos - first) / count;
- }
- }
- setDirty();
- apply();
- }
- // ... but we always use the command
- return 1;
- case EDIT_COPY:
- clipboardCopy(&color[selectedFirst[SELECTED_FG]][0], selectedLast[SELECTED_FG] - selectedFirst[SELECTED_FG] + 1);
- return 1;
- case EDIT_PASTE:
- if ((selectedFirst[SELECTED_FG] > 0) && (canConvertClipboard(CLIPBOARD_RGBA))) {
- if (selectedFirst[SELECTED_FG] == selectedLast[SELECTED_FG]) {
- // Paste as much as possible
- clipboardPasteColor(&color[selectedFirst[SELECTED_FG]][0], NUM_COLORS - selectedFirst[SELECTED_FG]);
- }
- else {
- // Fill selection
- int count = selectedLast[SELECTED_FG] - selectedFirst[SELECTED_FG] + 1;
- int offset = 0;
- while (count) {
- int did = clipboardPasteColor(&color[selectedFirst[SELECTED_FG] + offset][0], count);
- count -= did;
- offset += did;
- }
- }
- setDirty();
- apply();
- return 1;
- }
- break;
- }
- break;
- case SDL_KEYDOWN:
- selColor = (event->key.keysym.mod & KMOD_CTRL) && (numColors == 2) ? SELECTED_BK : SELECTED_FG;
- drag = (event->key.keysym.mod & KMOD_SHIFT) ? 1 : 0;
- switch (combineKey(event->key.keysym.sym, event->key.keysym.mod & ~(KMOD_SHIFT | KMOD_CTRL))) {
- case SDLK_RIGHT:
- case SDLK_DOWN:
- colorSelection(selColor, selected[selColor] + 1, drag);
- return 1;
- case SDLK_LEFT:
- case SDLK_UP:
- colorSelection(selColor, selected[selColor] - 1, drag);
- return 1;
- case SDLK_HOME:
- colorSelection(selColor, 0, drag);
- return 1;
- case SDLK_END:
- colorSelection(selColor, NUM_COLORS - 1, drag);
- return 1;
- case SDLK_KP_ENTER:
- case SDLK_RETURN:
- editColor(selected[selColor]);
- return 1;
- }
- }
- return 0;
- }
- void ColorSelect::apply() { start_func
- // Currently, our parent never knows the selected ranges, but those aren't used yet
- storage->fg.r = color[selected[SELECTED_FG]][0];
- storage->fg.g = color[selected[SELECTED_FG]][1];
- storage->fg.b = color[selected[SELECTED_FG]][2];
- storage->fg.a = color[selected[SELECTED_FG]][3];
- storage->bk.r = color[selected[SELECTED_BK]][0];
- storage->bk.g = color[selected[SELECTED_BK]][1];
- storage->bk.b = color[selected[SELECTED_BK]][2];
- storage->bk.a = color[selected[SELECTED_BK]][3];
- // Remember defaults
- memcpy(defaultColors, color, NUM_COLORS * 4);
- // Scale default colors to depth
- for (int pos = 0; pos < NUM_COLORS; ++pos) {
- assert(maxComponent);
- for (int sub = 0; sub < 3; ++sub) {
- defaultColors[pos][sub] = scaleComponent(defaultColors[pos][sub], maxComponent, 255);
- }
- if (maxAlpha) {
- defaultColors[pos][3] = scaleComponent(defaultColors[pos][3], maxAlpha, 255);
- }
- else {
- defaultColors[pos][3] = 255;
- }
- }
- defaultSelected[SELECTED_FG] = selected[SELECTED_FG];
- if (numColors == 2) defaultSelected[SELECTED_BK] = selected[SELECTED_BK];
-
- // Inform parent
- if (parent) parent->childModified(this);
- }
- void ColorSelect::display(SDL_Surface* destSurface, Rect& toDisplay, const Rect& clipArea, int xOffset, int yOffset) { start_func
- assert(destSurface);
- if (visible) {
- // If dirty, redraw all
- if (dirty) {
- getRect(toDisplay);
- toDisplay.x += xOffset;
- toDisplay.y += yOffset;
- dirty = 0;
- intersectRects(toDisplay, clipArea);
- }
- // Anything to draw?
- if (toDisplay.w) {
- SDL_SetClipRect(destSurface, &toDisplay);
- xOffset += x;
- yOffset += y;
- // This widget only redraws colors that are part of the dirty area
- SDL_FillRect(destSurface, &toDisplay, guiPacked[COLOR_FILL]);
- // Selection markers
- if (selectedFirst[SELECTED_FG] != selectedLast[SELECTED_FG]) {
- int xBox = xOffset + (COLOR_SIZE + COLOR_SEPARATION) * selectedFirst[SELECTED_FG];
- int wBox = (COLOR_SIZE + COLOR_SEPARATION) * (selectedLast[SELECTED_FG] - selectedFirst[SELECTED_FG] + 1) - COLOR_SEPARATION;
- drawGuiBox(xBox, yOffset, wBox, arrowHeight, 1, destSurface);
- drawGradient(xBox + 1, yOffset + 1, wBox - 2, arrowHeight - 2, guiRGB[COLOR_SELECTION1], guiRGB[COLOR_SELECTION2], destSurface);
- }
- if ((numColors == 2) && (selectedFirst[SELECTED_BK] != selectedLast[SELECTED_BK])) {
- int xBox = xOffset + (COLOR_SIZE + COLOR_SEPARATION) * selectedFirst[SELECTED_BK];
- int wBox = (COLOR_SIZE + COLOR_SEPARATION) * (selectedLast[SELECTED_BK] - selectedFirst[SELECTED_BK] + 1) - COLOR_SEPARATION;
- drawGuiBox(xBox, arrowHeight + COLOR_HEIGHT + yOffset, wBox, arrowHeight, 1, destSurface);
- drawGradient(xBox + 1, arrowHeight + COLOR_HEIGHT + yOffset + 1, wBox - 2, arrowHeight - 2, guiRGB[COLOR_SELECTION1], guiRGB[COLOR_SELECTION2], destSurface);
- }
- int last = (toDisplay.x + toDisplay.w - xOffset) / (COLOR_SIZE + COLOR_SEPARATION);
- for (int pos = (toDisplay.x - xOffset) / (COLOR_SIZE + COLOR_SEPARATION); pos <= last; ++pos) {
- int xPos = xOffset + (COLOR_SIZE + COLOR_SEPARATION) * pos;
- // 0 alpha shows different
- if ((color[pos][3] == 0) && (maxAlpha)) {
- drawRect(xPos + COLOR_BEVEL, arrowHeight + yOffset + COLOR_BEVEL, COLOR_SIZE - COLOR_BEVEL * 2, COLOR_HEIGHT - COLOR_BEVEL * 2,
- guiPacked[COLOR_TRANSPARENT1], destSurface);
- drawLine(xPos + COLOR_BEVEL, arrowHeight + yOffset + COLOR_BEVEL, xPos + COLOR_SIZE - COLOR_BEVEL - 1, arrowHeight + yOffset + COLOR_HEIGHT - COLOR_BEVEL - 1,
- guiPacked[COLOR_TRANSPARENT2], destSurface);
- drawLine(xPos + COLOR_BEVEL, arrowHeight + yOffset + COLOR_HEIGHT - COLOR_BEVEL - 1, xPos + COLOR_SIZE - COLOR_BEVEL - 1, arrowHeight + yOffset + COLOR_BEVEL,
- guiPacked[COLOR_TRANSPARENT2], destSurface);
- }
- else {
- assert(maxComponent);
- drawRect(xPos + COLOR_BEVEL, arrowHeight + yOffset + COLOR_BEVEL, COLOR_SIZE - COLOR_BEVEL * 2, COLOR_HEIGHT - COLOR_BEVEL * 2,
- SDL_MapRGB(destSurface->format,
- scaleComponent(color[pos][0], maxComponent, 255),
- scaleComponent(color[pos][1], maxComponent, 255),
- scaleComponent(color[pos][2], maxComponent, 255)), destSurface);
- // < 255 alpha shows shaded part on lower half
- if ((color[pos][3] < maxAlpha) && (maxAlpha)) {
- drawRect(xPos + COLOR_BEVEL, arrowHeight + yOffset + COLOR_HEIGHT / 2, COLOR_SIZE - COLOR_BEVEL * 2, COLOR_HEIGHT - COLOR_HEIGHT / 2 - COLOR_BEVEL,
- SDL_MapRGB(destSurface->format,
- scaleComponent(color[pos][0] * color[pos][3] >> bitDepthAlpha, maxComponent, 255),
- scaleComponent(color[pos][1] * color[pos][3] >> bitDepthAlpha, maxComponent, 255),
- scaleComponent(color[pos][2] * color[pos][3] >> bitDepthAlpha, maxComponent, 255)),
- destSurface);
- }
- }
- drawGuiBoxInvert(xPos, arrowHeight + yOffset,
- COLOR_SIZE, COLOR_HEIGHT, COLOR_BEVEL, destSurface);
- // Selected?
- if (pos == selected[SELECTED_FG]) {
- drawText(wtArrowDown, guiRGB[selectedFirst[SELECTED_FG] == selectedLast[SELECTED_FG] ? COLOR_TEXT : COLOR_TEXTBOX], xPos + arrowXOffset, yOffset, destSurface, FONT_WIDGET);
- }
- if ((numColors == 2) && (pos == selected[SELECTED_BK])) {
- drawText(wtArrowUp, guiRGB[selectedFirst[SELECTED_BK] == selectedLast[SELECTED_BK] ? COLOR_TEXT : COLOR_TEXTBOX], xPos + arrowXOffset, arrowHeight + COLOR_HEIGHT + yOffset, destSurface, FONT_WIDGET);
- }
- }
- }
- }
- }
- const char* ColorSelect::tooltip(int xPos, int yPos) const { start_func
- // @TODO: more specific tool tips?
- return "Select color; double-click to edit";
- }
- void ColorSelect::alphaDepth(int newAlphaDepth) { start_func
- bitDepthAlpha = newAlphaDepth;
- int oldMaxAlpha = maxAlpha;
- maxAlpha = (1 << bitDepthAlpha) - 1;
- // Scale to new depth
- for (int pos = 0; pos < NUM_COLORS; ++pos) {
- if ((oldMaxAlpha) && (maxAlpha)) {
- color[pos][3] = scaleComponent(color[pos][3], oldMaxAlpha, maxAlpha);
- }
- else {
- color[pos][3] = maxAlpha;
- }
- }
- setDirty();
- apply();
- }
- Window::CommandSupport ColorSelect::supportsCommand(int code) const { start_func
- switch (code) {
- case EDIT_GRADIENT:
- if (selectedFirst[SELECTED_FG] < selectedLast[SELECTED_FG] - 1) return Window::COMMAND_ENABLE;
- return Window::COMMAND_DISABLE;
- case EDIT_COPY:
- return Window::COMMAND_ENABLE;
- case EDIT_PASTE:
- if (selected[SELECTED_FG] == 0) return Window::COMMAND_DISABLE;
- if (canConvertClipboard(CLIPBOARD_RGBA)) return Window::COMMAND_ENABLE;
- return Window::COMMAND_DISABLE;
- }
- return Window::COMMAND_HIDE;
- }
|