123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971 |
- /* GCSx
- ** TREEVIEW.CPP
- **
- ** Treeview window, for browsing worlds, libraries, etc.
- */
- /*****************************************************************************
- ** 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"
- int TreeView::iconX[2] = { 0, 0 };
- int TreeView::iconY[2] = { 0, 0 };
- int TreeView::iconWidth[2] = { 0, 0 };
- int TreeView::iconHeight[2] = { 0, 0 };
- int TreeView::iconIndent[2] = { 0, 0 };
- int TreeView::itemHeight = 0;
- int TreeView::itemYOffset = 0;
- const string TreeView::wtButtonDown("n");
- const string TreeView::wtButtonRight("k");
- SDL_Surface* TreeView::iconSurface = NULL;
- long int TreeView::iconsLoaded = 0;
- const string TreeView::resourceError("Error loading resource/treeicon.bmp");
- TreeView::TreeView(const string& name, void* tPtr, int tCode, int (*tAction)(void* ptr, int code, int command, int check), int hideTop, int caseSensitive) : item(name), sort(name), subtree() { start_func
- isCaseSensitive = caseSensitive;
- if (!isCaseSensitive) toLower(sort);
- if (!iconsLoaded) {
- iconWidth[0] = fontHeight(FONT_WIDGET);
- iconHeight[0] = iconWidth[0];
- iconIndent[0] = iconWidth[0] + GUI_TREEVIEW_ICON_PADDING * 2;
- iconWidth[1] = GUI_TREEVIEW_ICON_WIDTH;
- iconHeight[1] = GUI_TREEVIEW_ICON_HEIGHT;
- iconIndent[1] = iconWidth[1] + GUI_TREEVIEW_ICON_PADDING * 2;
- itemHeight = fontHeight();
- if (iconHeight[0] > itemHeight) itemHeight = iconHeight[0];
- if (iconHeight[1] > itemHeight) itemHeight = iconHeight[1];
- iconY[0] = (itemHeight - iconHeight[0]) / 2;
- iconX[0] = (iconIndent[0] - iconWidth[0]) / 2;
- iconY[1] = (itemHeight - iconHeight[1]) / 2;
- iconX[1] = iconIndent[0] + (iconIndent[1] - iconWidth[1]) / 2;
- iconIndent[1] += iconIndent[0];
- itemYOffset = (itemHeight - fontHeight()) / 2;
-
- string iconFile;
- createFilename(resourceDir->c_str(), "treeicon.bmp", iconFile);
- iconSurface = SDL_LoadBMP(iconFile.c_str());
- if (iconSurface == NULL) {
- guiErrorBox(resourceError, errorTitleResourceLoad);
- }
- else {
- SDL_SetColorKey(iconSurface, SDL_SRCCOLORKEY, SDL_MapRGB(iconSurface->format, 255, 0, 0));
- }
- }
- ptr = tPtr;
- code = tCode;
- action = tAction;
- hideItem = hideTop;
- myFrame = NULL;
- myScroll = NULL;
- parentIsTree = 0;
- // (limit length of display but don't affect actual data)
- itemWidth = hideItem ? 0 : fontWidth(item.substr(0, MAX_LINELENGTH));
- subitems = 0;
- expanded = hideItem; // If item is hidden, we're always expanded
- currentSelection = -1;
-
- iconOpen = -1;
- iconClosed = -1;
-
- open = 0;
- setDirty();
- width = (hideItem ? 0 : iconIndent[1]) + itemWidth;
- height = (hideItem ? 0 : itemHeight);
- ++iconsLoaded;
- }
- TreeView::~TreeView() { start_func
- // We assume we've been removed from any parent listview or from the desktop
- vector<TreeView*>::iterator end = subtree.end();
- for (vector<TreeView*>::iterator pos = subtree.begin(); pos != end; ++pos) {
- delete *pos;
- }
-
- if (--iconsLoaded == 0) {
- if (iconSurface) SDL_FreeSurface(iconSurface);
- iconSurface = NULL;
- }
- }
- int TreeView::wantsToBeDeleted() const { start_func
- return 0;
- }
- Window::CommandSupport TreeView::supportsCommand(int cmdCode) const { start_func
- // If we're not selected, propogate command downward to selected first
- if (currentSelection > 0) {
- CommandSupport result = subtree[currentSelection - 1]->supportsCommand(cmdCode);
- if (result != COMMAND_HIDE) return result;
- }
-
- // Either we're selected, or our child refused it, so we'll try it now
- // Note that SDL_COMMAND is the only action that propogates down the tree
- if (action) {
- return (Window::CommandSupport)action(ptr, code, cmdCode, 1);
- }
-
- return COMMAND_HIDE;
- }
- int TreeView::event(int hasFocus, const SDL_Event* event) { start_func
- assert(event);
- int handleKey = 0;
- int repeat = 1;
- SDL_Event eventCopy;
-
- switch (event->type) {
- case SDL_CLOSE:
- open = 0;
- myFrame = NULL;
- myScroll = NULL;
- return 1;
-
- case SDL_COMMAND:
- // If we're not selected, propogate command downward to selected first
- if (currentSelection > 0) {
- if (subtree[currentSelection - 1]->event(hasFocus, event)) return 1;
- }
-
- // Either we're selected, or our child refused it, so we'll try it now
- // Note that SDL_COMMAND is the only action that propogates down the tree
- if (action) {
- return action(ptr, code, event->user.code, 0);
- }
-
- return 0;
-
- case SDL_KEYDOWN:
- // Handle top-level events here (we assume we're top level, as we
- // don't allow these keys to propogate further)
- // Change PGDN / PGUP into repeated keypresses sent to ourselves
- // Handle END / HOME
- switch (combineKey(event->key.keysym.sym, event->key.keysym.mod)) {
- case SDLK_HOME:
- if ((subitems) && (hideItem) && (expanded)) {
- subtree[0]->selectMe(); // (start with top item selected)
- }
- else selectMe();
- return 1;
- case SDLK_END:
- selectLast();
- return 1;
- case SDLK_PAGEDOWN:
- eventCopy = *event;
- eventCopy.key.keysym.sym = SDLK_DOWN;
- repeat = viewHeight / fontHeight() - 1;
- if (repeat < 1) repeat = 1;
- for (; repeat > 0; --repeat) {
- TreeView::event(hasFocus, &eventCopy);
- }
- return 1;
- case SDLK_PAGEUP:
- eventCopy = *event;
- eventCopy.key.keysym.sym = SDLK_UP;
- repeat = viewHeight / fontHeight() - 1;
- if (repeat < 1) repeat = 1;
- for (; repeat > 0; --repeat) {
- TreeView::event(hasFocus, &eventCopy);
- }
- return 1;
- }
-
- // If we're not selected, propogate key downward to selected
- if (currentSelection > 0) {
- if (subtree[currentSelection - 1]->event(hasFocus, event)) return 1;
- handleKey = 1;
- }
-
- // If we're selected, handle key
- // If the item actually selected didn't use the key, we'll try to
- if ((handleKey) || (currentSelection == 0)) {
- switch (combineKey(event->key.keysym.sym, event->key.keysym.mod)) {
- case SDLK_KP_ENTER:
- case SDLK_RETURN:
- case SDLK_SPACE:
- if (action) {
- action(ptr, code, LV_SELECT, 0);
- }
- // We always return 1, we don't want lower level to intercept
- return 1;
- case SDLK_BACKSPACE:
- // Go up a level
- if (currentSelection > 0) {
- if (!hideItem) selectMe();
- return 1;
- }
- break;
- case SDLK_DELETE:
- if (action) {
- action(ptr, code, LV_DELETE, 0);
- }
- // We always return 1, we don't want lower level to intercept
- return 1;
-
- case SDLK_RIGHT:
- // Expand or go inward a level
- // We use function (not variable) to support directory view
- // where items aren't loaded until expand() is called
- if (hasSubItems()) {
- if ((currentSelection == 0) && (expanded) && (subitems)) subtree[0]->selectMe();
- else expand(1);
- }
- return 1;
- case SDLK_LEFT:
- // Collapse or go up a level
- if (currentSelection > 0) {
- if (!hideItem) selectMe();
- return 1;
- }
- else if ((subitems) && (expanded)) {
- expand(0);
- return 1;
- }
- break;
- case SDLK_DOWN:
- // Next line
- if ((currentSelection > 0) && (currentSelection < subitems)) {
- // currentSelection - 1, plus one for next item
- subtree[currentSelection]->selectMe();
- return 1;
- }
- // First item of us if expanded
- else if ((currentSelection == 0) && (expanded) && (subitems)) {
- subtree[0]->selectMe();
- return 1;
- }
- break;
- case SDLK_UP:
- // Previous line
- if (currentSelection > 1) {
- // currentSelection - 1, minus one for previous item
- subtree[currentSelection - 2]->selectLast();
- return 1;
- }
- // Select self
- else if ((currentSelection == 1) && (!hideItem)) {
- selectMe();
- return 1;
- }
- break;
-
- default: {
- // Jump to first/next matching item based on letter
- if (selectFind(tolower(event->key.keysym.sym))) return 1;
- break;
- }
- }
- }
-
- break;
-
- case SDL_MOUSEBUTTONDBL:
- case SDL_MOUSEBUTTONDOWN:
- // (either button OK)
- if ((event->button.button == SDL_BUTTON_LEFT) || (event->button.button == SDL_BUTTON_RIGHT)) {
- if (event->button.y < height) {
- // Ourselves?
- if ((event->button.y < itemHeight) && (!hideItem)) {
- // Button?
- if (event->button.x < iconIndent[0]) {
- expand(!expanded);
- }
- // Item?
- else if (event->button.x < itemWidth + iconIndent[1]) {
- selectMe();
-
- // Double-left-click?
- if ((event->button.button == SDL_BUTTON_LEFT) && (event->type == SDL_MOUSEBUTTONDBL)) {
- if (expanded) expand(0);
- else if (hasSubItems()) expand(1);
- else if (action) return action(ptr, code, LV_SELECT, 0);
- }
- else if ((action) && (event->type == SDL_MOUSEBUTTONDOWN)) {
- if (event->button.button == SDL_BUTTON_LEFT) return action(ptr, code, LV_LCLICK, 0);
- else if (event->button.button == SDL_BUTTON_RIGHT) return action(ptr, code, LV_RCLICK, 0);
- }
- }
- else {
- return 0;
- }
- return 1;
- }
- // A subitem
- else if (expanded) {
- // New event, move towards next item
- eventCopy = *event;
- if (!hideItem) {
- eventCopy.button.x -= iconIndent[0];
- eventCopy.button.y -= itemHeight;
- }
-
- vector<TreeView*>::iterator end = subtree.end();
- for (vector<TreeView*>::iterator pos = subtree.begin(); pos != end; ++pos) {
- // This item?
- if (eventCopy.button.y < (*pos)->height) {
- // Recurse
- return (*pos)->event(hasFocus, &eventCopy);
- }
-
- // Move towards next item
- eventCopy.button.y -= (*pos)->height;
- }
- return 0;
- }
- }
- }
- break;
- }
- return 0;
- }
- void TreeView::resolutionChange(int fromW, int fromH, int fromBpp, int toW, int toH, int toBpp) { start_func
- Window::resolutionChange(fromW, fromH, fromBpp, toW, toH, toBpp);
- vector<TreeView*>::iterator end = subtree.end();
- for (vector<TreeView*>::iterator pos = subtree.begin(); pos != end; ++pos) {
- (*pos)->resolutionChange(fromW, fromH, fromBpp, toW, toH, toBpp);
- }
- }
- void TreeView::display(SDL_Surface* destSurface, Rect& toDisplay, const Rect& clipArea, int xOffset, int yOffset) { start_func
- assert(destSurface);
- if (visible) {
- // This will be the final displayed amount, we update it as we go but still work
- // off of the original dirtied area for efficiency
- Rect updatedArea = toDisplay;
-
- xOffset += x;
- yOffset += y;
-
- // This window attempts to only redraw lines that have changed (or dirty)
- // Our top member-
- if (!hideItem) {
- // Determine clip area for our item
- Rect itemClip = { xOffset, yOffset, width, itemHeight };
- intersectRects(itemClip, clipArea);
- // If dirty and visible, or intersects with dirty area
- if (((dirty) && (itemClip.w)) || (intersectRects(itemClip, toDisplay))) {
- // Add rect into updated area
- boundRects(updatedArea, itemClip);
- SDL_SetClipRect(destSurface, &itemClip);
- // Our background- just our item
- SDL_FillRect(destSurface, &itemClip, guiPacked[COLOR_TEXTBOX]);
-
- // Icon +/-
- if (hasSubItems()) {
- drawGuiBox(xOffset + iconX[0], yOffset + iconY[0], iconWidth[0], iconHeight[0], 1, destSurface);
- drawText(expanded ? wtButtonDown : wtButtonRight, guiRGB[COLOR_TEXT], xOffset + iconX[0], yOffset + iconY[0], destSurface, FONT_WIDGET);
- }
-
- // Icon symbol
- int iconSymbol = 0;
- if ((iconSurface) && (iconOpen >= 0)) {
- iconSymbol = 1;
- blit((expanded ? iconOpen : iconClosed) * GUI_TREEVIEW_ICON_WIDTH, 0, iconSurface, xOffset + iconX[1], yOffset + iconY[1], destSurface, 16, 16);
- }
-
- // Our text- selected?
- if (currentSelection == 0) {
- drawGradient(xOffset + iconIndent[iconSymbol], yOffset + itemYOffset, itemWidth, itemHeight, guiRGB[COLOR_SELECTION1], guiRGB[COLOR_SELECTION2], destSurface);
- drawText(item, guiRGB[COLOR_TEXTBOX], xOffset + iconIndent[iconSymbol], yOffset + itemYOffset, destSurface);
- drawFocusBox(xOffset + iconIndent[iconSymbol], yOffset + itemYOffset, itemWidth, itemHeight, destSurface);
- }
- else {
- drawText(item, guiRGB[COLOR_TEXT], xOffset + iconIndent[iconSymbol], yOffset + itemYOffset, destSurface);
- }
- }
- }
- // Subtree?
- if ((expanded) && (subitems) && ((childDirty) || (totalDirty) || (toDisplay.w))) {
- int atY = yOffset + (hideItem ? 0 : itemHeight);
- vector<TreeView*>::iterator end = subtree.end();
- for (vector<TreeView*>::iterator pos = subtree.begin(); pos != end; ++pos) {
- // Determine clip area for this item
- Rect itemClip = { xOffset, atY, width, (*pos)->height };
- intersectRects(itemClip, clipArea);
-
- // Dirty area
- Rect itemDirty = itemClip;
- // If dirty and visible, or intersects with dirty area
- // Order of "if" evaluation matters here-
- // If total dirty, entire area will be redrawn; if just dirty,
- // only area clipped to "toDisplay" will be enforced as redrawn
- if ((itemDirty.w) && ((totalDirty) || (intersectRects(itemDirty, toDisplay)) || ((*pos)->isDirty()))) {
- // Give it a full background too- it will draw over it anyways
- // We have to do this because if item may not cover it's full width
- SDL_SetClipRect(destSurface, &itemClip);
- SDL_FillRect(destSurface, &itemDirty, guiPacked[COLOR_TEXTBOX]);
-
- // (don't use move, to prevent unneeded setdirties)
- (*pos)->x = hideItem ? 0 : iconIndent[0];
- (*pos)->y = atY - yOffset;
- (*pos)->display(destSurface, itemDirty, itemClip, xOffset, yOffset);
-
- // Add into redrawn area
- boundRects(updatedArea, itemDirty);
- }
- atY += (*pos)->height;
- }
- }
-
- toDisplay = updatedArea;
- dirty = totalDirty = childDirty = 0;
- }
- }
- Window::WindowType TreeView::windowType() const { start_func
- return WINDOW_CLIENT;
- }
- void TreeView::setIcon(int tIconClosed, int tIconOpen) { start_func
- iconClosed = tIconClosed;
- iconOpen = tIconOpen;
- if (iconOpen == -1) iconOpen = iconClosed;
- setDirty();
- }
- void TreeView::changeName(const string& name) { start_func
- item = name;
- sort = name;
- if (!isCaseSensitive) toLower(sort);
- setDirty();
- if ((parent) && (parentIsTree)) {
- dynamic_cast<TreeView*>(parent)->childRenamed(this);
- }
-
- if (myFrame) {
- myFrame->setTitle(item);
- }
- if (!hideItem) {
- // (limit length of display but don't affect actual data)
- itemWidth = fontWidth(item.substr(0, MAX_LINELENGTH));
- // Scan all items for widest (add in iconIndent when done)
- int widest = itemWidth;
- vector<TreeView*>::iterator end = subtree.end();
- for (vector<TreeView*>::iterator pos = subtree.begin(); pos != end; ++pos) {
- if ((*pos)->width > widest) widest = (*pos)->width;
- }
- resize(widest + iconIndent[1], height);
- }
- }
- void TreeView::childRenamed(TreeView* child) { start_func
- assert(child);
- // Selected?
- int wasSelected = 0;
- if ((currentSelection > 0) && (subtree[currentSelection - 1] == child)) {
- wasSelected = 1;
- currentSelection = 0;
- }
- // Find existing and remove
- vector<TreeView*>::iterator pos = subtree.begin();
- vector<TreeView*>::iterator end = subtree.end();
- int count = 0;
- for (; pos != end; ++count, ++pos) {
- if (*pos == child) break;
- }
- assert(pos != end);
- if ((currentSelection > 0) && (count < currentSelection)) --currentSelection;
- subtree.erase(pos);
-
- // Insert
- pos = subtree.begin();
- end = subtree.end();
- count = 0;
- for (; pos != end; ++count, ++pos) {
- if ((*pos)->sort > child->sort) break;
- }
- if ((currentSelection > 0) && (count < currentSelection)) ++currentSelection;
- subtree.insert(pos, child);
-
- // Select?
- if (wasSelected) currentSelection = count + 1;
- // We're 100% dirty
- setDirty(1);
- }
- void TreeView::insert(TreeView* toInsert, int makeCurrent) { start_func
- assert(toInsert);
-
- // Insert
- vector<TreeView*>::iterator pos = subtree.begin();
- vector<TreeView*>::iterator end = subtree.end();
- int count = 0;
- int atY = hideItem ? 0 : itemHeight;
- for (; pos != end; ++count, ++pos) {
- if ((*pos)->sort > toInsert->sort) break;
- atY += (*pos)->height;
- }
- if ((currentSelection > 0) && (count < currentSelection)) ++currentSelection;
- subtree.insert(pos, toInsert);
- toInsert->setParent(this);
- toInsert->parentIsTree = 1;
- ++subitems;
-
- // We may resize if needed
- int newWidth = width;
- int newHeight = height;
-
- // Ensure our width is large enough to include this item
- if (toInsert->width + (hideItem ? 0 : iconIndent[1]) > width) {
- newWidth = toInsert->width + (hideItem ? 0 : iconIndent[1]);
- }
-
- // If we're expanded, add subitem's height to ours
- if (expanded) {
- newHeight += toInsert->height;
- }
-
- // Modify size
- if ((newWidth != width) || (newHeight != height)) {
- // (sets dirty)
- resize(newWidth, newHeight);
- }
- toInsert->move(hideItem ? 0 : iconIndent[0], atY);
- // Set this item as selected?
- if (makeCurrent) toInsert->selectMe();
- // Otherwise, rescroll to view current selection
- else positionChildren(1);
- }
- void TreeView::childResized(int oldW, int oldH, int newW, int newH, Window* child) { start_func
- // We may resize if needed
- int newWidth = width;
- int newHeight = height;
-
- // Need to be wider?
- if ((newW > oldW) && (newW + (hideItem ? 0 : iconIndent[1]) > width)) {
- newWidth = newW + (hideItem ? 0 : iconIndent[1]);
- }
-
- // Possibly need to be thinner?
- if ((newW < oldW) && ((oldW + (hideItem ? 0 : iconIndent[1])) == width)) {
- // Scan all items for widest (add in iconIndent when done)
- int widest = itemWidth;
- vector<TreeView*>::iterator end = subtree.end();
- for (vector<TreeView*>::iterator pos = subtree.begin(); pos != end; ++pos) {
- if ((*pos)->width > widest) widest = (*pos)->width;
- }
- newWidth = widest + (hideItem ? 0 : iconIndent[1]);
- }
-
- // If we're expanded, then height changes also affect us, and all
- // other children after the child that resized
- if (expanded) {
- int heightDiff = newH - oldH;
- if (heightDiff) {
- newHeight += heightDiff;
-
- int after = 0;
- vector<TreeView*>::iterator end = subtree.end();
- for (vector<TreeView*>::iterator pos = subtree.begin(); pos != end; ++pos) {
- if (after) (*pos)->move((*pos)->x, (*pos)->y + heightDiff);
- if (*pos == child) after = 1;
- }
- }
- }
-
- // Resize?
- if ((newWidth != width) || (newHeight != height)) {
- // (sets dirty)
- resize(newWidth, newHeight);
- }
- }
- TreeView* TreeView::findRecursive(void* fPtr) { start_func
- vector<TreeView*>::iterator end = subtree.end();
- for (vector<TreeView*>::iterator pos = subtree.begin(); pos != end; ++pos) {
- TreeView* result = (*pos)->findRecursive(fPtr);
- if (result) return result;
- if ((*pos)->ptr == fPtr) return *pos;
- }
- return NULL;
- }
- TreeView* TreeView::find(void* fPtr) { start_func
- vector<TreeView*>::iterator end = subtree.end();
- for (vector<TreeView*>::iterator pos = subtree.begin(); pos != end; ++pos) {
- if ((*pos)->ptr == fPtr) return *pos;
- }
- return NULL;
- }
- TreeView* TreeView::findRecursive(int fCode, int (*fAction)(void* ptr, int code, int command, int check)) { start_func
- vector<TreeView*>::iterator end = subtree.end();
- for (vector<TreeView*>::iterator pos = subtree.begin(); pos != end; ++pos) {
- TreeView* result = (*pos)->findRecursive(fCode, fAction);
- if (result) return result;
- if (((*pos)->code == fCode) &&
- (((*pos)->action == fAction) || (fAction == NULL))) return *pos;
- }
- return NULL;
- }
- TreeView* TreeView::find(const string& fItem) { start_func
- vector<TreeView*>::iterator end = subtree.end();
- if (!isCaseSensitive) {
- string lowercase = fItem;
- for (int pos = lowercase.size() - 1; pos >= 0; --pos) {
- lowercase[pos] = tolower(lowercase[pos]);
- }
- for (vector<TreeView*>::iterator pos = subtree.begin(); pos != end; ++pos) {
- if ((*pos)->sort == lowercase) return *pos;
- }
- }
- else {
- for (vector<TreeView*>::iterator pos = subtree.begin(); pos != end; ++pos) {
- if ((*pos)->item == fItem) return *pos;
- }
- }
-
- return NULL;
- }
- TreeView* TreeView::findParent() { start_func
- if ((parent) && (parentIsTree)) {
- return dynamic_cast<TreeView*>(parent);
- }
-
- return NULL;
- }
- TreeView* TreeView::findSelected() { start_func
- if (currentSelection > 0) return subtree[currentSelection - 1]->findSelected();
- if (currentSelection == 0) return this;
- return NULL;
- }
- void TreeView::removeAll() { start_func
- expand(0);
- // Select self instead of any child
- if (currentSelection > 0) {
- selectMe();
- }
- vector<TreeView*>::iterator end = subtree.end();
- for (vector<TreeView*>::iterator pos = subtree.begin(); pos != end; ++pos) {
- (*pos)->parentIsTree = 0;
- (*pos)->setParent(NULL);
- delete (*pos);
- }
-
- subtree.clear();
- subitems = 0;
- // We may resize if needed
- int newWidth = itemWidth + (hideItem ? 0 : iconIndent[1]);
- int newHeight = (hideItem ? 0 : itemHeight);
- if ((newWidth != width) || (newHeight != height)) {
- // (sets dirty)
- resize(newWidth, newHeight);
- }
- }
- void TreeView::remove(TreeView* toRemove, int deleteIt) { start_func
- // Track if we passed selected item
- int sawSelected = 0;
- vector<TreeView*>::iterator end = subtree.end();
- for (vector<TreeView*>::iterator pos = subtree.begin(); pos != end; ++pos) {
- if (*pos == toRemove) {
- // If was selected, select self instead
- if ((currentSelection > 0) && (subtree[currentSelection - 1] == toRemove)) {
- sawSelected = 1;
- selectMe();
- }
- subtree.erase(pos);
- // Must use toRemove from here onward
- toRemove->parentIsTree = 0;
- toRemove->setParent(NULL);
- --subitems;
- childResized(toRemove->width, toRemove->height, 0, 0, toRemove);
- if (deleteIt) delete toRemove;
-
- // Decrease selected pointer because we removed above it?
- if ((currentSelection > 0) && (!sawSelected)) --currentSelection;
- break;
- }
- else if ((currentSelection > 0) && (subtree[currentSelection - 1] == *pos)) {
- sawSelected = 1;
- }
- }
- }
- void TreeView::expand(int state) { start_func
- int newHeight = height;
-
- // Can't change if top item hidden- always expanded
- if (hideItem) return;
- // Expanding?
- if ((state) && (!expanded)) {
- expanded = 1;
-
- // Add height of all items
- vector<TreeView*>::iterator end = subtree.end();
- for (vector<TreeView*>::iterator pos = subtree.begin(); pos != end; ++pos) {
- newHeight += (*pos)->height;
- }
- }
- // Contracting?
- else if ((!state) && (expanded)) {
- expanded = 0;
- newHeight = itemHeight;
- }
-
- // Size change?
- if (newHeight != height) {
- // (sets dirty)
- resize(width, newHeight);
- }
-
- // Select ourselves if a subitem of us is selected and we just contracted
- if ((!expanded) && (currentSelection > 0)) selectMe();
- }
- void TreeView::addTo(Dialog* dialog, int showWidth, int showLines, int cId) { start_func
- assert(!myFrame);
- assert(dialog);
- // Set to standard size
- if (showWidth <= 0) {
- string standard(GUI_TREEVIEW_DEFAULTWIDTH, 'X');
- showWidth = fontWidth(standard);
- }
- if (showLines <= 0) {
- showLines = GUI_TREEVIEW_DEFAULTLINES;
- }
- expand(1); // (start with top level expanded)
- if (hasSubItems()) {
- subtree[0]->selectMe(); // (start with top item selected)
- }
- else {
- selectMe(); // (start with self selected)
- }
- myScroll = new WidgetScroll(WidgetScroll::FRAMETYPE_BEVEL, this, showWidth, showLines * itemHeight, cId);
- dialog->addWidget(myScroll);
- }
- void TreeView::runWindowed() { start_func
- assert(!myScroll);
- // Prevent duplication
- if (myFrame) {
- desktop->bringToTop(myFrame);
- return;
- }
- expand(1); // (start with top level expanded)
- if (hasSubItems()) {
- subtree[0]->selectMe(); // (start with top item selected)
- }
- else {
- selectMe(); // (start with self selected)
- }
- // We remember the frame pointer even though it'll delete itself
- myFrame = new FrameWindow(item, FrameWindow::RESIZING_NORMAL, FrameWindow::FRAMETYPE_BEVEL_TEXT, this);
- open = 1;
-
- // Cascade, use cascade height, but use OUR width
- myFrame->show(FrameWindow::SHOW_CASCADE, FrameWindow::SHOW_CASCADE, FrameWindow::SHOW_CURRMIN, FrameWindow::SHOW_CASCADE);
- }
- void TreeView::scrollToView(int sX, int sY, int sWidth, int sHeight) { start_func
- if (parent) {
- if (parentIsTree) {
- dynamic_cast<TreeView*>(parent)->positionChildren(0);
- dynamic_cast<TreeView*>(parent)->scrollToView(sX + x, sY + y, sWidth, sHeight);
- }
- else if (parent == myFrame) {
- myFrame->scrollToView(sX, sY, sWidth, sHeight);
- }
- else if (parent == myScroll) {
- myScroll->scrollToView(sX, sY, sWidth, sHeight);
- }
- }
- }
- int TreeView::selectFind(int letter) { start_func
- if (expanded) {
- int pos = currentSelection - 1;
- if (pos < 0) pos = 0;
- for (; pos < subitems; ++pos) {
- // (doesn't match root of currently selected item
- if ((tolower(subtree[pos]->item[0]) == letter) && (pos >= currentSelection)) {
- subtree[pos]->selectMe();
- return 1;
- }
- // (search subtree)
- if (subtree[pos]->selectFind(letter)) return 1;
- }
- }
- return 0;
- }
- void TreeView::selectLast() { start_func
- if ((expanded) && (subitems)) subtree[subitems - 1]->selectLast();
- else selectMe();
- }
- void TreeView::selectFirst() { start_func
- if ((expanded) && (subitems)) subtree[0]->selectMe();
- else selectMe();
- }
- void TreeView::selectMe() { start_func
- // Already selected?
- if (currentSelection == 0) return;
- // Subitem currently selected?
- if (currentSelection > 0) {
- subtree[currentSelection - 1]->unselectMe();
- }
-
- // Scroll to view it
- currentSelection = 0;
- scrollToView(0, 0, iconIndent[1] + itemWidth, itemHeight);
-
- // Tell parent, if any, that we're selected
- if ((parent) && (parentIsTree)) {
- dynamic_cast<TreeView*>(parent)->selectChild(this);
- }
-
- // Event
- if (action) action(ptr, code, LV_MOVE, 0);
- setDirty();
- }
- void TreeView::positionChildren(int rescroll) { start_func
- if (subitems) {
- int atY = hideItem ? 0 : itemHeight;
- vector<TreeView*>::iterator end = subtree.end();
- for (vector<TreeView*>::iterator pos = subtree.begin(); pos != end; ++pos) {
- // (don't use move, to prevent unneeded setdirties)
- (*pos)->x = hideItem ? 0 : iconIndent[0];
- (*pos)->y = atY;
- if (expanded) atY += (*pos)->height;
- }
- }
- if (rescroll) {
- if (currentSelection == 0) scrollToView(0, 0, iconIndent[1] + itemWidth, itemHeight);
- else if (currentSelection > 0) scrollToView(subtree[currentSelection - 1]->x, subtree[currentSelection - 1]->y,
- subtree[currentSelection - 1]->itemWidth,
- subtree[currentSelection - 1]->itemHeight);
- }
- }
- void TreeView::selectChild(TreeView* child) { start_func
- // Find child
- int cPos = findChild(child);
-
- // Found?
- if (cPos >= 0) {
- // Already selected?
- if (currentSelection == cPos + 1) return;
-
- // Subitem currently selected?
- if (currentSelection > 0) {
- subtree[currentSelection - 1]->unselectMe();
- }
- else if (currentSelection == 0) {
- // We're dirty if we WERE selected
- setDirty();
- }
- currentSelection = cPos + 1;
-
- // Tell parent, if any, that we're selected
- if ((parent) && (parentIsTree)) {
- dynamic_cast<TreeView*>(parent)->selectChild(this);
- }
- }
- }
- void TreeView::unselectMe() { start_func
- // Already unselected?
- if (currentSelection == -1) return;
- // Subitem currently selected?
- if (currentSelection > 0) {
- subtree[currentSelection - 1]->unselectMe();
- }
- else {
- // We were selected, we're now dirty
- setDirty();
- }
-
- currentSelection = -1;
- }
- int TreeView::findChild(const TreeView* child) const { start_func
- for (int pos = 0; pos < subitems; ++pos) {
- if (subtree[pos] == child) return pos;
- }
- return -1;
- }
- int TreeView::hasSubItems() const { start_func
- return subitems > 0;
- }
|