diff options
Diffstat (limited to 'applications/rview/rviewUtils.cpp')
-rw-r--r-- | applications/rview/rviewUtils.cpp | 3630 |
1 files changed, 3630 insertions, 0 deletions
diff --git a/applications/rview/rviewUtils.cpp b/applications/rview/rviewUtils.cpp new file mode 100644 index 0000000..762ee10 --- /dev/null +++ b/applications/rview/rviewUtils.cpp @@ -0,0 +1,3630 @@ +/* +* This file is part of rasdaman community. +* +* Rasdaman community 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 3 of the License, or +* (at your option) any later version. +* +* Rasdaman community 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 rasdaman community. If not, see <http://www.gnu.org/licenses/>. +* +* Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009 Peter Baumann / +rasdaman GmbH. +* +* For more information please see <http://www.rasdaman.org> +* or contact Peter Baumann via <baumann@rasdaman.com>. +/ + +/** +* PURPOSE: +* +* Central definitions of shared objects and constants, global tool functions. +* +* - All menu codes used in the program (MENU_...) +* - Global tool functions for handling collections, base types, projection +* strings, X events, ... . See ``Global functions''. +* - rviewFrame class which must be the base class of all frames used in rView. +* - rviewFrameMgr class for handling all of rView's frames (dispatching events +* etc) +* - rviewMultiline class for displaying several lines of non-editable text. +* - rviewDialog class which is the base class for all rView's dialog windows. +* - rviewErrorbox class, derived from rviewDialog. +* - rviewProgress class, derived from rviewDialog. +* - rviewResults class for displaying database results and dispatching object +* viewers. Relies on code provided by rviewMDD for scaling/resampling/endian +* conversions. +* - rviewAbout class for displaying information about rView itself. +* - rviewStringSet class for displaying a set of strings in a scrollable +* list box. +* +* COMMENTS: +* none +*/ + + +// Was file included via header? Then mask out all non-template code +#if (!defined(EARLY_TEMPLATE) || !defined(__EXECUTABLE__)) + +// Standard wxWindows preamble. +#ifdef __GNUG__ +#pragma implementation +#endif + + +// changed in wxWindows 2.4.2: +//#include "wx_prec.h" +#include <wx/wxprec.h> + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + + +#ifndef WX_PRECOMP +#include <wx/wx.h> +#endif + +// #include "wb_timer.h" -- PB 2006-jan-01 + + +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <iostream.h> + + + +#ifdef EARLY_TEMPLATE +#define __EXECUTABLE__ +#endif + + +#include "raslib/rmdebug.hh" +#include "raslib/primitivetype.hh" +#include "raslib/structuretype.hh" + +#include "rviewUtils.hh" +#include "rviewMDD.hh" +#include "rviewPrefs.hh" +#include "rviewColMap.hh" +#include "rviewDModes.hh" +#include "rviewOSection.hh" +#include "rviewTypes.hh" +#include "rviewThumb.hh" +#include "rviewSound.hh" +#include "rviewTypeMan.hh" +#include "labelManager.hh" + + +#ifdef __VISUALC__ +const int rview_window_extra_height = 24; +const int rview_choice_sub_width = 0; +#else +const int rview_window_extra_height = 0; +const int rview_choice_sub_width = 32; +#endif + + + + + + +/* + * Global data + */ + +// Base type to string translation tables + +#if (MAXIMUM_DIMENSIONS != 4) +#error "Adapt tables for MAXIMUM DIMENSIONS!" +#endif + +// String names of base types +char *rviewBaseTypes[] = { + "none", + "Boolean", + "Char", + "Octet", + "Short", + "UShort", + "Long", + "ULong", + "RGBPixel", + "Float", + "Double" +}; + +// String names of objects of baseType x and dimension y (-> rviewBaseType) +char *rviewTypeNames[][MAXIMUM_DIMENSIONS] = { + {"?", "?", "?", "?"}, + {"BoolString", "BoolImage", "BoolCube", "BoolCube4"}, + {"GreyString", "GreyImage", "GreyCube", "GreyCube4"}, + {"OctetString", "OctetImage", "OctetCube", "OctetCube4"}, + {"ShortString", "ShortImage", "ShortCube", "ShortCube4"}, + {"UShortString", "UShortImage", "UShortCube", "UShortCube4"}, + {"LongString", "LongImage", "LongCube", "LongCube4"}, + {"ULongString", "ULongImage", "ULongCube", "ULongCube4"}, + {"RGBString", "RGBImage", "RGBCube", "RGBCube4"}, + {"FloatString", "FloatImage", "FloatCube", "FloatCube4"}, + {"DoubleString", "DoubleImage" "DoubleCube", "DoubleCube4"} +}; + +// The same for sets +char *rviewSetNames[][MAXIMUM_DIMENSIONS] = { + {"?", "?", "?", "?"}, + {"BoolSet1", "BoolSet", "BoolSet3", "BoolSet4"}, + {"GreySet1", "GreySet", "GreySet3", "GreySet4"}, + {"OctetSet1", "OctetSet", "OctetSet3", "OctetSet4"}, + {"ShortSet1", "ShortSet", "ShortSet3", "ShortSet4"}, + {"UShortSet1", "UShortSet", "UShortSet3", "UShortSet4"}, + {"LongSet1", "LongSet", "LongSet3", "LongSet4"}, + {"ULongSet1", "ULongSet", "ULongSet3", "ULongSet4"}, + {"RGBSet1", "RGBSet", "RGBSet3", "RGBSet4"}, + {"FloatSet1", "FloatSet", "FloatSet3", "FloatSet4"}, + {"DoubleSet1", "DoubleSet", "DoubleSet3", "DoubleSet4"} +}; + + + +unsigned char lowerCaseTable[256]; + + + +// Various support classes +rviewFrameMgr *frameManager = NULL; + +labelManager *lman = NULL; + + + + + + +/* + * GLOBAL functions + */ + +// Frees all memory allocated by a collection descriptor. +void rviewDeleteCollection(collection_desc *coll) +{ + if (coll != NULL) + { + int i; + collection_desc *ptr = coll; + + if (coll->collName != NULL) delete [] coll->collName; + if (coll->collType != NULL) delete [] coll->collType; + if (coll->collInfo != NULL) delete [] coll->collInfo; + + for (i=0; i<coll->number; i++) + { + if (coll->mddObjs != NULL) + { + if (!coll->mddObjs[i].mdd.is_null()) + { + coll->mddObjs[i].mdd.destroy(); + } + } + else if (coll->strObjs != NULL) + { + if (coll->strObjs[i] != NULL) + { + delete [] coll->strObjs[i]; + } + } + } + if (coll->mddObjs != NULL) delete [] coll->mddObjs; + if (coll->strObjs != NULL) delete [] coll->strObjs; + + delete coll; + } +} + + + +/* + * Support function for rviewParseProjection + */ +static const char *rviewParseIndexMapping(const char *s, int idx, int dims, r_Point *mapIndex) +{ + const char *b, *d; + r_Range value; + + b = s+1; + if (mapIndex == NULL) + { + cerr << "Mode doesn't support reordering of dimensions." << endl; + } + + value = (r_Range)strtol(b, (char**)&d, 0); + if (b == d) return NULL; + + if ((value < 0) || (value >= (r_Range)dims)) + { + cerr << "Bad dimension index " << value << endl; + value = (r_Range)idx; + } + if (mapIndex != NULL) (*mapIndex)[idx] = value; + + while ((*d == ' ') || (*d == '\t')) d++; + if (*d == ']') return d+1; + return NULL; +} + + +/* + * Translate a projection string (now RasDaMan style, i.e. things + * like *:* are also allowed) into two diagonal r_Points. Returns + * number of dimension of projection string for success, 0 for an error. + */ + +int rviewParseProjection(const r_Minterval &interv, r_Point &pt1, r_Point &pt2, const char *projString, unsigned int *freeDims, r_Point *mapIndex) +{ + int dims, i; + const char *b, *d; + r_Range value; + + if (freeDims != NULL) *freeDims = 0; + dims = interv.dimension(); + pt1 = r_Point(dims); pt2 = r_Point(dims); + + b = projString; i = 0; + while (*b != '\0') + { + while ((*b == ' ') || (*b == '\t')) b++; + if (*b == ',') b++; + while ((*b == ' ') || (*b == '\t')) b++; + if (*b == '\0') break; + if (i >= dims) return 0; + if (*b == '*') + { + pt1[i] = interv[i].low(); pt2[i] = interv[i].high(); b++; + } + else + { + value = (r_Range)strtol(b, (char**)&d, 0); + // no valid number found? + if (b == d) return 0; + b = d; + if ((value < interv[i].low()) || (value > interv[i].high())) return 0; + pt1[i] = value; pt2[i] = value; + } + if (mapIndex != NULL) (*mapIndex)[i] = i; + while ((*b == ' ') || (*b == '\t')) b++; + if (*b == '[') // Explicit index-mapping? + { + if ((b = rviewParseIndexMapping(b, i, dims, mapIndex)) == NULL) return 0; + } + else if (*b == ':') // the upper boundaries follow after a colon. + { + if (freeDims != NULL) *freeDims |= (1<<i); + b++; + if (*b == '\0') break; + if (*b == '*') + { + value = interv[i].high(); b++; + } + else + { + value = (r_Range)strtol(b, (char**)&d, 0); + if (b == d) return 0; + b = d; + if ((value < interv[i].low()) || (value > interv[i].high())) return 0; + } + pt2[i] = value; + while ((*b == ' ') || (*b == '\t')) b++; + if (*b == '[') + { + if ((b = rviewParseIndexMapping(b, i, dims, mapIndex)) == NULL) return 0; + } + } + i++; + } + return i; +} + + + + + +/* + * Determine the base type of an MDD object, returning a simple identifier. + */ +rviewBaseType rviewGetBasetype(r_Object *obj) +{ + rviewBaseType baseType; + const r_Type *bt; + + baseType = rbt_none; + + // New type schema... + bt = obj->get_type_schema(); + if (bt->type_id() == r_Type::MARRAYTYPE) + bt = ((r_GMarray*)obj)->get_base_type_schema(); + else if (bt->type_id() == r_Type::COLLECTIONTYPE) + bt = ((r_Collection<r_Ref_Any>*)obj)->get_element_type_schema(); + + if (bt != NULL) + { + if (((r_Type*)bt)->isStructType()) + { + if (((r_Base_Type*)bt)->size() == 3) + baseType = rbt_rgb; + else + baseType = rbt_none; + } + else + { + r_Primitive_Type *pt = (r_Primitive_Type *)bt; + + switch (pt->type_id()) + { + case r_Primitive_Type::BOOL: baseType = rbt_bool; break; + case r_Primitive_Type::CHAR: baseType = rbt_char; break; + case r_Primitive_Type::OCTET: baseType = rbt_uchar; break; + case r_Primitive_Type::SHORT: baseType = rbt_short; break; + case r_Primitive_Type::USHORT: baseType = rbt_ushort; break; + case r_Primitive_Type::LONG: baseType = rbt_long; break; + case r_Primitive_Type::ULONG: baseType = rbt_ulong; break; + case r_Primitive_Type::FLOAT: baseType = rbt_float; break; + case r_Primitive_Type::DOUBLE: baseType = rbt_double; break; + default: baseType = rbt_none; break; + } + } + } + else + { + char *name = (char*)(obj->get_type_name()); + if (name != NULL) + { + if (strcmp(name, "GreyImage") == 0) baseType = rbt_char; + else if (strcmp(name, "BoolImage") == 0) baseType = rbt_bool; + else if (strcmp(name, "RGBImage") == 0) baseType = rbt_rgb; + } + } + return baseType; +} + + + +/* + * rviewPrintTypedCell() prints the contents of a cell with a given base type into the buffer, + * returning the number of characters written. + */ + +// For internal use only +static void printPrimitiveCell(r_Primitive_Type *primType, char **buff, char *data, int numberBase) +{ + char *b = *buff; + + switch (numberBase) + { + case 8: + switch (primType->type_id()) + { + case r_Primitive_Type::BOOL: + b += sprintf(b, "%3o", *((r_Boolean*)data)); break; + case r_Primitive_Type::CHAR: + b += sprintf(b, "%3o", *((r_Char*)data)); break; + case r_Primitive_Type::OCTET: + b += sprintf(b, "%3o", *((r_Octet*)data)); break; + case r_Primitive_Type::SHORT: + b += sprintf(b, "%6o", *((r_Short*)data)); break; + case r_Primitive_Type::USHORT: + b += sprintf(b, "%6o", *((r_UShort*)data)); break; + case r_Primitive_Type::LONG: + b += sprintf(b, "%11lo", *((r_Long*)data)); break; + case r_Primitive_Type::ULONG: + b += sprintf(b, "%11lo", *((r_ULong*)data)); break; + case r_Primitive_Type::FLOAT: + b += sprintf(b, "%011o", *((r_Long*)data)); break; + case r_Primitive_Type::DOUBLE: + b += sprintf(b, "%011o:%011o", ((r_Long*)data)[0], ((r_Long*)data)[1]); break; + default: + break; + } + break; + case 16: + switch (primType->type_id()) + { + case r_Primitive_Type::BOOL: + b += sprintf(b, "%02x", *((r_Boolean*)data)); break; + case r_Primitive_Type::CHAR: + b += sprintf(b, "%02x", *((r_Char*)data)); break; + case r_Primitive_Type::OCTET: + b += sprintf(b, "%02x", *((r_Octet*)data)); break; + case r_Primitive_Type::SHORT: + b += sprintf(b, "%04x", *((r_Short*)data)); break; + case r_Primitive_Type::USHORT: + b += sprintf(b, "%04x", *((r_UShort*)data)); break; + case r_Primitive_Type::LONG: + b += sprintf(b, "%08x", *((r_Long*)data)); break; + case r_Primitive_Type::ULONG: + b += sprintf(b, "%08x", *((r_ULong*)data)); break; + case r_Primitive_Type::FLOAT: + b += sprintf(b, "%08x", *((r_Long*)data)); break; + case r_Primitive_Type::DOUBLE: + b += sprintf(b, "%08x:%08x", ((r_Long*)data)[0], ((r_Long*)data)[1]); break; + default: + break; + } + break; + default: + switch (primType->type_id()) + { + case r_Primitive_Type::BOOL: + b += sprintf(b, "%3d", *((r_Boolean*)data)); break; + case r_Primitive_Type::CHAR: + b += sprintf(b, "%3d", *((r_Char*)data)); break; + case r_Primitive_Type::OCTET: + b += sprintf(b, "%3d", *((r_Octet*)data)); break; + case r_Primitive_Type::SHORT: + b += sprintf(b, "%5d", *((r_Short*)data)); break; + case r_Primitive_Type::USHORT: + b += sprintf(b, "%5d", *((r_UShort*)data)); break; + case r_Primitive_Type::LONG: + b += sprintf(b, "%10ld", *((r_Long*)data)); break; + case r_Primitive_Type::ULONG: + b += sprintf(b, "%10ld", *((r_ULong*)data)); break; + case r_Primitive_Type::FLOAT: + b += sprintf(b, "%g", *((r_Float*)data)); break; + case r_Primitive_Type::DOUBLE: + b += sprintf(b, "%g", *((r_Double*)data)); break; + default: + break; + } + break; + } + *buff = b; +} + +// For internal use only +void printStructuredCell(r_Structure_Type *structType, char **buff, char *data, int numberBase) +{ + r_Type *newType; + unsigned long off; + char *b; + + b = *buff; + *b++ = '{'; + r_Structure_Type::attribute_iterator iter(structType->defines_attribute_begin()); + while (iter != structType->defines_attribute_end()) + { + newType = (*iter).type_of().clone(); + off = (*iter).offset(); + + if (newType->isStructType()) + { + r_Structure_Type *newStructType = (r_Structure_Type*)newType; + printStructuredCell(newStructType, &b, data + off, numberBase); + } + else + { + r_Primitive_Type *newPrimType = (r_Primitive_Type*)newType; + printPrimitiveCell(newPrimType, &b, data + off, numberBase); + } + delete newType; + *b++ = ','; + iter++; + } + b[-1] = '}'; *b = '\0'; + *buff = b; +} + +int rviewPrintTypedCell(const r_Type *baseType, char *buffer, char *data, int numberBase) +{ + char *b = buffer; + + if (((r_Type*)baseType)->isStructType()) + { + r_Structure_Type *structType = (r_Structure_Type*)baseType; + printStructuredCell(structType, &b, data, numberBase); + } + else + { + r_Primitive_Type *primType = (r_Primitive_Type*)baseType; + printPrimitiveCell(primType, &b, data, numberBase); + } + return (int)(b - buffer); +} + + + + +/* + * Quicksort a char *[] + */ + +#define QUICKSORT_STRING_SWAP(x,y) h=array[x]; array[x]=array[y]; array[y]=h; + +void rviewQuicksortStrings(char *array[], int from, int to) +{ + while (from < to) + { + int i, j; + char *h; + + j = (from+to)/2; + QUICKSORT_STRING_SWAP(from, j); j=from; + for (i=from+1; i<=to; i++) + { + if (strcmp(array[i], array[from]) < 0) + { + j++; + QUICKSORT_STRING_SWAP(i, j); + } + } + QUICKSORT_STRING_SWAP(from, j); + + if ((j-from) < (to-j)) + { + rviewQuicksortStrings(array, from, j-1); + from = j+1; + } + else + { + rviewQuicksortStrings(array, j+1, to); + to = j-1; + } + } +} + + + +/* + * Init character lookup tables (e.g. lower case table) + */ +void rviewInitCharacterTables(void) +{ + int i; + + for (i=0; i<256; i++) lowerCaseTable[i] = tolower(i); +} + + + +#define LOOKUP_KEYWORD_CORE \ + while (count != 0) \ + { \ + b = (const unsigned char*)(kti[pos].keyword); \ + d = (const unsigned char*)key; \ + while (TRANSLATE_CHARACTER(b) == TRANSLATE_CHARACTER(d)) \ + { \ + if (*b == 0) break; b++; d++; \ + } \ + if ((*b == 0) && (*d == 0)) return kti[pos].ident; \ + if (TRANSLATE_CHARACTER(b) >= TRANSLATE_CHARACTER(d)) \ + { \ + pos -= step; if (pos < 0) pos = 0; \ + } \ + else \ + { \ + pos += step; if (pos >= tabsize) pos = tabsize - 1; \ + } \ + step = (step+1) >> 1; count >>= 1; \ + } + +/* + * Lookup a keyword in a SORTED keyword_to_ident table. Case sensitivity + * is optional but has to match the sorting order of the table, of course. + */ +int rviewLookupKeyword(const char *key, const keyword_to_ident_c *kti, int tabsize, bool caseSensitive) +{ + int pos, step, count; + const unsigned char *b, *d; + + count = tabsize; + pos = (tabsize + 1) >> 1; if (pos >= tabsize) pos = tabsize - 1; + step = (pos + 1) >> 1; + if (caseSensitive) + { +#define TRANSLATE_CHARACTER(x) (*x) + LOOKUP_KEYWORD_CORE; +#undef TRANSLATE_CHARACTER + } + else + { +#define TRANSLATE_CHARACTER(x) lowerCaseTable[(*x)] + LOOKUP_KEYWORD_CORE; +#undef TRANSLATE_CHARACTER + } + return -1; +} + + + +/* Helper function making sure min/max of the init structure is used by the + colourspace mapper */ + +static void rviewEnsureCspaceRange(colourspaceMapper *csmap, const colourspace_params *cp) +{ + // make sure the min/max values are taken from the init structure as well + if (cp != NULL) + { + // assume it's initialized if either values differs from 0 + if ((cp->minVal != 0.0) || (cp->maxVal != 0.0)) + { + colourspace_params par; + csmap->getParameters(&par); + par.minVal = cp->minVal; + par.maxVal = cp->maxVal; + // do not do auto-update, otherwise we're stuck in an infinite loop + csmap->colourspaceChanged(&par, FALSE); + //cout << "MIN " << par.minVal << ", MAX " << par.maxVal << endl; + } + } +} + + +/* + * Check whether colourspace mapping is possible for the given + * base type and return 1 if so. *csmap must be a pointer to the + * colourspaceMapper to use or NULL if there isn't one yet in + * which case a new one will have been created on returning 1. + * If cmap == NULL the function just returns 0 or 1. If the int* + * arguments are not NULL they're set up correctly on exit too. + */ +int rviewCheckInitCspace(rviewBaseType baseType, colourspaceMapper **csmap, r_Ref<r_GMarray> &mddObj, bool fullRange, r_Minterval *domain, int w, int *newPitch, int *newDepth, int *newPad, int *virtualPitch, const colourspace_params *cp) +{ + colourspace_params par; + + switch (baseType) + { + case rbt_char: + case rbt_uchar: + case rbt_short: + case rbt_ushort: + if (csmap == NULL) return 1; + if (*csmap == NULL) + { + memcpy(&par, (cp == NULL) ? &(prefs->csp) : cp, sizeof(colourspace_params)); + *csmap = new colourspaceMapper(mddObj, baseType, &par, fullRange, domain); + } + else + (*csmap)->bindMapper(mddObj, baseType, fullRange, domain, cp); + + rviewEnsureCspaceRange(*csmap, cp); + + // always call this and let the mapper sort out whether it has to update + (*csmap)->buildCSTab15(); + if (newPitch == NULL) return 1; + if (*newPad < 32) *newPad = 32; + *newDepth = 15; + switch (*newPad) + { + case 32: *newPitch = (w*2 + 3) & ~3; break; + case 64: *newPitch = (w*2 + 7) & ~7; break; + case 128: *newPitch = (w*2 + 15) & ~15; break; + case 256: *newPitch = (w*2 + 31) & ~31; break; + default: + cerr << "Bad pad " << *newPad << endl; + return 0; + } + if (virtualPitch != NULL) *virtualPitch = *newPitch; + return 1; + break; + case rbt_long: + case rbt_ulong: // using 24bpp only makes sense for long + case rbt_float: + case rbt_double: // ... or larger types + { + int baseWidth; + + baseWidth = 4*w; + + if (csmap == NULL) return 1; + if (*csmap == NULL) + { + memcpy(&par, (cp == NULL) ? &(prefs->csp) : cp, sizeof(colourspace_params)); + *csmap = new colourspaceMapper(mddObj, baseType, &par, fullRange, domain); + } + else + (*csmap)->bindMapper(mddObj, baseType, fullRange, domain, cp); + + rviewEnsureCspaceRange(*csmap, cp); + + (*csmap)->buildCSTab24(); + if (newPitch == NULL) return 1; + if (*newPad < 32) *newPad = 32; + *newDepth = 32; + switch (*newPad) + { + case 32: *newPitch = (baseWidth + 3) & ~3; break; + case 64: *newPitch = (baseWidth + 7) & ~7; break; + case 128: *newPitch = (baseWidth + 15) & ~15; break; + case 256: *newPitch = (baseWidth + 31) & ~31; break; + default: cerr << "Bad pad " << *newPad << endl; + return 0; + } + if (virtualPitch != NULL) + *virtualPitch = (baseType == rbt_double) ? 2*(*newPitch) : (*newPitch); + return 1; + } + break; + default: break; + } + return 0; +} + + + +/* + * Smart number conversion functions. They also understand the 0x + * prefix and convert the number correctly. + */ + +long asctol(const char *str) +{ + return stringtol(str, NULL); +} + +int asctoi(const char *str) +{ + return (int)stringtol(str, NULL); +} + +double asctof(const char *str) +{ + return stringtof(str, NULL); +} + +long stringtol(const char *str, char **endptr) +{ + const char *b = str; + long value; + + while (isspace((unsigned int)(*b))) b++; + if ((b[0] == '0') && ((b[1] == 'x') || (b[1] == 'X'))) + { + b += 2; + value = strtol(b, endptr, 16); + } + else + { + value = strtol(b, endptr, 10); + } + if ((endptr != NULL) && ((const char*)(*endptr) == b)) *endptr = (char*)str; + return value; +} + +double stringtof(const char *str, char **endptr) +{ + const char *b = str; + double value; + + while (isspace((unsigned int)(*b))) b++; + if ((b[0] == '0') && ((b[1] == 'x') || (b[1] == 'X'))) + { + b += 2; + value = (double)strtol(b, endptr, 16); + } + else + { + value = strtod(b, endptr); + } + if ((endptr != NULL) && ((const char*)(*endptr) == b)) *endptr = (char*)str; + return value; +} + + + + + +/* + * Dynamic string class for easy management of dynamically allocated string storage + */ + +const char DynamicString::emptyString[] = ""; + + +DynamicString::DynamicString(void) +{ + myString = NULL; +} + +DynamicString::DynamicString(const DynamicString &ms) +{ + if (ms.myString == NULL) + { + myString = NULL; + } + else + { + myString = new char[strlen(ms.myString) + 1]; + strcpy(myString, ms.myString); + } +} + + +DynamicString::DynamicString(const char *str) +{ + if (str == NULL) + { + myString = NULL; + } + else + { + myString = new char[strlen(str) + 1]; + strcpy(myString, str); + } +} + + +DynamicString::~DynamicString(void) +{ + if (myString != NULL) delete [] myString; +} + + +DynamicString &DynamicString::first(const char *str, unsigned int num) +{ + if (myString != NULL) + { + delete [] myString; myString = NULL; + } + if (num != 0) + { + unsigned int len = strlen(str); + if (len > num) len = num; + myString = new char[len + 1]; + strncpy(myString, str, len); + myString[len] = '\0'; + } + return *this; +} + + +DynamicString &DynamicString::operator=(const DynamicString &ms) +{ + if (myString != NULL) + { + delete [] myString; myString = NULL; + } + if (ms.myString != NULL) + { + myString = new char[strlen(ms.myString) + 1]; + strcpy(myString, ms.myString); + } + return *this; +} + + +DynamicString &DynamicString::operator=(const char *str) +{ + if (myString != NULL) + { + delete [] myString; myString = NULL; + } + if (str != NULL) + { + myString = new char[strlen(str) + 1]; + strcpy(myString, str); + } + return *this; +} + + +const char *DynamicString::ptr(void) const +{ + if (myString == NULL) return emptyString; + return myString; +} + +DynamicString::operator const char*(void) const +{ + return ptr(); +} + + +bool DynamicString::operator==(const DynamicString &str) const +{ + return (strcmp(ptr(), str.ptr()) == 0); +} + + +bool DynamicString::operator==(const char *str) const +{ + return (strcmp(ptr(), str) == 0); +} + + + + + + +/* + * Generic handler function for events like button- and key-presses. + * It passes the event to the frameManager which then broadcasts + * it to all the frames it knows. If a frame recognizes obj as one + * of its children it claims the event, thus aborting the broadcast. + */ + +void rviewEventHandler(wxObject &obj, wxEvent &evt) +{ + if (frameManager != NULL) + frameManager->broadcastEvent(obj, evt); + //cout << endl; +} + + + + + + +/* + * rviewFrame member functions. + * This is an abstract class that is only used for deriving other + * (window/frame) classes from it. + */ + +rviewFrame::rviewFrame(wxFrame *parent, char *title, int x, int y, int w, int h) : wxFrame(parent, title, x, y, w, h) +{ + RMDBGONCE( 3, RMDebug::module_applications, "rviewFrame", "rviewFrame( " << this << ", ...)" ); + + if (frameManager != NULL) + frameManager->registerFrame(this); + + parentFrame = NULL; + // Init the size with illegal values to make sure the first OnSize gets through + frameWidth = -1; frameHeight = -1; + // All the frame's children should be destroyed when the frame is destroyed. + frames = new rviewFrameMgr(TRUE); +} + + +rviewFrame::~rviewFrame(void) +{ + RMDBGONCE( 3, RMDebug::module_applications, "rviewFrame", "~rviewFrame() " << this ); + + delete frames; + + if (frameManager != NULL) + frameManager->deregisterFrame(this); + + if (parentFrame != NULL) + { + parentFrame->deregisterChild(this); + } +} + + +const char *rviewFrame::getFrameName(void) const +{ + return "rviewFrame"; +} + +rviewFrameType rviewFrame::getFrameType(void) const +{ + return rviewFrameTypeGeneric; +} + + +void rviewFrame::setParent(rviewFrame *parent) +{ + parentFrame = parent; +} + + +void rviewFrame::registerChild(rviewFrame *child) +{ + child->setParent(this); + frames->registerFrame(child); +} + + +void rviewFrame::deregisterChild(rviewFrame *child) +{ + frames->deregisterFrame(child); + child->setParent(NULL); +} + + +// Called by the global frame manager when the application is about to die. +int rviewFrame::requestQuit(int level) +{ + // Default action on a hard quit: die + if (level != 0) + { + // Do not delete the children, that would upset the frame manager's loop. + frames->setDeleteMode(FALSE); setParent(NULL); + //delete this; + Close(TRUE); + } + return 0; +} + + + +// Called on a user event +int rviewFrame::userEvent(const user_event &ue) +{ + // Default action: nothing. Overload function in derived class if you're + // interested in user messages. + return 0; +} + + +// Called by the rviewFrameMgr object to determine which frame receives an event +int rviewFrame::checkobj(wxObject &obj) +{ + return checkobj_rec((wxWindow*)this, obj); +} + + +// Used internally by rviewFrame::checkobj(); don't access directly. Checks +// recursively all the children of a given frame against obj. +int rviewFrame::checkobj_rec(wxWindow *whence, wxObject &obj) +{ + if (&obj == (wxObject*)whence) return 1; + else + { + int i, n; + wxList *list; + + list = whence->GetChildren(); + n = list->Number(); + for (i=0; i<n; i++) + { + if (checkobj_rec((wxWindow*)(list->Nth(i)->Data()), obj) != 0) return 1; + } + } + return 0; +} + + + +// Virtual function. If a derived frame class should refuse to die in some +// circumstances, it must overload his function. +bool rviewFrame::OnClose(void) +{ + return TRUE; +} + + +void rviewFrame::childMouseEvent(wxWindow *child, wxMouseEvent &mevt) +{ + // by default this is ignored. +} + + + + + + +/* + * rviewFrameMgr member functions + */ + +rviewFrameMgr::rviewFrameMgr(void) +{ + listLength = 0; deleteChildren = FALSE; + frameList = NULL; tailList = NULL; +} + + +rviewFrameMgr::rviewFrameMgr(bool delChild) +{ + listLength = 0; deleteChildren = delChild; + frameList = NULL; tailList = NULL; +} + + +rviewFrameMgr::~rviewFrameMgr(void) +{ + int i; + frame_list *list, *last; + + // Deletes all the frames and their children + // Unlink all frames first! Otherwise there'll be trouble because the Frame Manager + // will loop over the frames and delete them; but if the frames have a valid parent + // they will deregister there which will screw up the Frame Manager's frame list + // _during the loop_ and hence crash. + list = frameList; + for (i=0; i<listLength; i++) + { + last = list; list = list->next; + // If it's the global frame manager it shouldn't delete the children, that's + // wxWindows' job. If it's a manager local to a frame it should delete all + // that frame's children, however. + if (deleteChildren) + { + last->frame->setParent(NULL); + //delete last->frame; + last->frame->Close(TRUE); + } + delete last; + } +} + + +void rviewFrameMgr::registerFrame(rviewFrame *client) +{ + frame_list *entry; + + //cout << "frameManager::registerFrame(" << (void*)client << ")" << endl; + if ((entry = new frame_list) == NULL) + { + //cerr << "frameManager: Unable to claim memory!\n" << endl; + return; + } + + entry->frame = client; entry->next = NULL; + + if (frameList == NULL) + { + frameList = entry; tailList = entry; + } + else + { + tailList->next = entry; + tailList = entry; + } + listLength++; + //cout << "Frame manager: Added frame to list. New length = " << listLength << endl; + //cout << "(anchor = " << (void*)frameList << ", tail = " << (void*)tailList << ")" << endl; +} + + +void rviewFrameMgr::deregisterFrame(rviewFrame *client) +{ + int i; + frame_list *list, *last; + + //cout << "frameManager::deregisterFrame(" << (void*)client << ")" << endl; + list = frameList; last = NULL; + for (i=0; i<listLength; i++) + { + if (list->frame == client) + { + list = list->next; + if (last == NULL) + { + delete frameList; frameList = list; last = list; + } + else + { + delete last->next; last->next = list; + } + listLength--; + if (last == NULL) tailList = NULL; + else + { + while (last->next != NULL) last = last->next; + tailList = last; + } + //cout << "Frame manager: Removed frame from list. New length = " << listLength << endl; + //cout << "(anchor = " << (void*)frameList << ", tail = " << (void*)tailList << ")" << endl; + return; + } + last = list; list = list->next; + } +} + + + +int rviewFrameMgr::numberOfFrames(void) const +{ + return listLength; +} + + + +void rviewFrameMgr::setDeleteMode(bool delChild) +{ + deleteChildren = delChild; +} + + +// Issues re-label requests to all frames. +void rviewFrameMgr::labelAll(void) +{ + int i; + frame_list *list; + + //cout << "frameManager::labelAll()" << endl; + list = frameList; + for (i=0; i<listLength; i++) + { + list->frame->label(); + list = list->next; + } +} + + + +/* + * Broadcasts the event to all frames until one claims the event by returning + * a value != 0. Warning: this function has to be re-entrant! + */ + +void rviewFrameMgr::broadcastEvent(wxObject &obj, wxEvent &evt) +{ + int i; + frame_list *list; + + // Note: the broadcast has to go in two steps, first determining which frame + // knows the object, then terminating the loop and calling the object as the + // last action. The reason is that this function is re-entrant and would + // crash badly if the frame list changed during the loop. + //cout << "frameManager::broadcastEvent()" << endl; + list = frameList; + for (i=0; i<listLength; i++) + { + //cout << "Broadcast to " << (void*)(list->frame) << endl; + if (list->frame->checkobj(obj) != 0) + { + //cout << "Frame manager: object known by frame " << (void*)list->frame << endl; + break; + } + list = list->next; + } + //cout << "Frame manager: broadcast " << ((list == NULL) ? "failed" : "successful") << endl; + + // Now call the frame. + if (list != NULL) {list->frame->process(obj, evt);} + // Uncomment this if there's any doubt it's re-entrant + //cout << "frameManager::broadcastEvent done" << endl; +} + + + +/* + * Broadcast a quit-message to all frames but the first one (the main frame). There + * are currently two levels, 0 means the frames can yet refuse to die by returning a + * value != 0, every other level doesn't give them this option. Returns 0 for failure. + * This must only be called for the _global_ frame manager! + */ + +int rviewFrameMgr::broadcastQuit(int level) +{ + int i, status; + frame_list *list; + + list = frameList->next; status = 1; + for (i=1; i<listLength; i++) + { + //if (level > 0) cout << "delete " << (void*)list << endl; + if (list->frame->requestQuit(level) != 0) status = 0; + list = list->next; + } + if (level == 0) return status; + + //cout << "Hard quit OK" << endl; + return 1; +} + + + +/* + * Broadcast a user event to all frames. + */ + +int rviewFrameMgr::broadcastUserEvent(const user_event &ue) +{ + int i, status; + frame_list *list; + + list = frameList; status = 0; + for (i=0; i<listLength; i++) + { + //cout << "broadcast to " << (void*)(list->frame) << endl; + status += list->frame->userEvent(ue); + list = list->next; + } + return status; // number of clients that recognised the event. +} + + + + + +/* + * rviewMultiline member functions + */ + +const int rviewMultiline::multiline_ppc10 = 63; + +void rviewMultiline::setupVariables(wxPanel *Panel, int X, int Y, int H, int Lines) +{ + int i; + + parent = Panel; lines = Lines; + if ((msg = new wxMessage *[lines]) == NULL) + { + cerr << "rviewMultiline::setupVariables(): " << lman->lookup("errorMemory") << endl; + return; + } + for (i=0; i<lines; i++) msg[i] = NULL; + + x = X; y = Y; lHeight = H; +} + + +rviewMultiline::rviewMultiline(wxPanel *Panel, int X, int Y, int H, int Lines) +{ + setupVariables(Panel, X, Y, H, Lines); +} + + +rviewMultiline::rviewMultiline(wxPanel *Panel, const char *Message, int X, int Y, int W, int H, int Lines) +{ + setupVariables(Panel, X, Y, H, Lines); + + rebuild(Message, W); +} + + +rviewMultiline::~rviewMultiline(void) +{ + // The wxMessage items have been created as children of the panel and will + // be deleted by the panel later on. So just delete the array. + delete [] msg; +} + + +void rviewMultiline::rebuild(const char *Message, int W) +{ + int length, i, cpl, pos; + char *buffer, *b, *base; + + if (msg == NULL) return; + + //cout << "rviewMultiline::rebuild()" << endl; + + cpl = (10*W) / multiline_ppc10; + + if ((buffer = new char[cpl + 1]) == NULL) + { + cerr << "rviewMultiline::rebuild(): " << lman->lookup("errorMemory") << endl; + return; + } + + length = strlen(Message); base = (char*)Message; + + for (i=0, pos=y; (length > 0) && (i<lines); i++, pos+=lHeight) + { + if (length > cpl) + { + b = base + cpl; + while ((*b > 32) && (b > base)) b--; + if (b <= base) b = base + cpl; else b++; + } + else + { + b = base + length; + } + memcpy(buffer, base, (int)(b - base)); buffer[(int)(b - base)] = '\0'; + //cout << buffer << endl; + if (msg[i] == NULL) + { + msg[i] = new wxMessage(parent, buffer, x, pos, W, lHeight, 0, "message"); + } + else + { + msg[i]->SetSize(x, pos, W, lHeight, 0); + msg[i]->SetLabel(buffer); + } + length -= (int)(b - base); base = b; + } + while (i<lines) + { + if (msg[i] != NULL) {delete msg[i]; msg[i] = NULL;} + i++; + } + delete [] buffer; +} + + + +int rviewMultiline::getMessageHeight(void) const +{ + int i; + + for (i=0; i<lines; i++) if (msg[i] == NULL) break; + return(i*lHeight); +} + + + +/* + * More convenient interface to standard widgets + */ + +rviewText::rviewText(wxPanel *parent, const char *value, char *label, int x, int y, int w, int h) : wxText(parent, (wxFunction)rviewEventHandler, label, (char*)value, x, y, w, h, wxTE_PROCESS_ENTER) +{ +} + +rviewText::rviewText(long style, wxPanel *parent, const char *value, char *label, int x, int y, int w, int h) : wxText(parent, (wxFunction)rviewEventHandler, label, (char*)value, x, y, w, h, style) +{ +} + +rviewText::rviewText(wxPanel *parent, const DynamicString &value, char *label, int x, int y, int w, int h) : wxText(parent, (wxFunction)rviewEventHandler, label, NULL, x, y, w, h, wxTE_PROCESS_ENTER) +{ + wxText::SetValue((char*)value.ptr()); +} + +rviewText::rviewText(wxPanel *parent, int value, char *label, int x, int y, int w, int h) : wxText(parent, (wxFunction)rviewEventHandler, label, NULL, x, y, w, h, wxTE_PROCESS_ENTER) +{ + char buffer[32]; + sprintf(buffer, "%d", value); + wxText::SetValue(buffer); +} + + +rviewText::rviewText(wxPanel *parent, long value, char *label, int x, int y, int w, int h) : wxText(parent, (wxFunction)rviewEventHandler, label, NULL, x, y, w, h, wxTE_PROCESS_ENTER) +{ + char buffer[32]; + sprintf(buffer, "%ld", value); + wxText::SetValue(buffer); +} + + +rviewText::rviewText(wxPanel *parent, double value, bool sciForm, char *label, int x, int y, int w, int h) : wxText(parent, (wxFunction)rviewEventHandler, label, NULL, x, y, w, h, wxTE_PROCESS_ENTER) +{ + char buffer[32]; + sprintf(buffer, (sciForm) ? "%g" : "%f", value); + wxText::SetValue(buffer); +} + +void rviewText::SetValue(char *value) +{ + wxText::SetValue(value); +} + +void rviewText::SetValue(const char *value) +{ + wxText::SetValue((char*)value); +} + +void rviewText::SetValue(const DynamicString &value) +{ + wxText::SetValue((char*)value.ptr()); +} + +void rviewText::SetValue(int value) +{ + char buffer[32]; + sprintf(buffer, "%d", value); + wxText::SetValue(buffer); +} + +void rviewText::SetValue(unsigned int value) +{ + char buffer[32]; + sprintf(buffer, "%u", value); + wxText::SetValue(buffer); +} + +void rviewText::SetValue(long value) +{ + char buffer[32]; + sprintf(buffer, "%ld", value); + wxText::SetValue(buffer); +} + +void rviewText::SetValue(double value, bool sciForm) +{ + char buffer[32]; + sprintf(buffer, (sciForm) ? "%g" : "%f", value); + wxText::SetValue(buffer); +} + +char *rviewText::GetValue(void) +{ + return wxText::GetValue(); +} + +void rviewText::GetValue(DynamicString &value) +{ + value = wxText::GetValue(); +} + +void rviewText::GetValue(int &value) +{ + value = asctoi(wxText::GetValue()); +} + +void rviewText::GetValue(long &value) +{ + value = asctol(wxText::GetValue()); +} + +void rviewText::GetValue(float &value) +{ + value = asctof(wxText::GetValue()); +} + +void rviewText::GetValue(double &value) +{ + value = asctof(wxText::GetValue()); +} + + + +rviewButton::rviewButton(wxPanel *parent, char *label, int x, int y, int w, int h, long style) : wxButton(parent, (wxFunction)rviewEventHandler, label, x, y, w, h, style) +{ +} + +rviewChoice::rviewChoice(wxPanel *parent, int n, char *choices[], char *label, int x, int y, int w, int h, long style) : wxChoice(parent, (wxFunction)rviewEventHandler, label, x, y, w, h, n, choices, style) +{ +} + +// hacked version to make up for wxWindows' lack of const +rviewChoice::rviewChoice(wxPanel *parent, int n, const char *choices[], char *label, int x, int y, int w, int h, long style) : wxChoice(parent, (wxFunction)rviewEventHandler, label, x, y, w, h, n, (char**)choices, style) +{ +} + +rviewCheckBox::rviewCheckBox(wxPanel *parent, char *label, int x, int y, int w, int h) : wxCheckBox(parent, (wxFunction)rviewEventHandler, label, x, y, w, h) +{ +} + +rviewRadioButton::rviewRadioButton(wxPanel *parent, char *label, bool value, int x, int y, int w, int h) : wxRadioButton(parent, (wxFunction)rviewEventHandler, label, value, x, y, w, h) +{ +} + +rviewScrollBar::rviewScrollBar(wxPanel *parent, int x, int y, int w, int h, long style) : wxScrollBar(parent, (wxFunction)rviewEventHandler, x, y, w, h, style) +{ +} + +rviewSlider::rviewSlider(wxPanel *parent, int value, int min_val, int max_val, int width, char *label, int x, int y, long style) : wxSlider(parent, (wxFunction)rviewEventHandler, label, value, min_val, max_val, x, y, style) +{ +} + + + +/* + * The special slider class when arbitrary mouse events are required. + */ + +const int rviewSpecialSlider::dflt_barwidth = 30; +const int rviewSpecialSlider::dflt_barheight = 10; +const int rviewSpecialSlider::dflt_width = 200; +const int rviewSpecialSlider::dflt_height = rviewSpecialSlider::dflt_barheight + 2*rviewSpecialSlider::dflt_border; +const int rviewSpecialSlider::dflt_border = 4; + +rviewSpecialSlider::rviewSpecialSlider(rviewFrame *logParent, wxPanel *parent, int val, int min, int max, int width, const char *label) : + wxCanvas(parent, -1, -1, (width==-1) ? dflt_width : width, dflt_height, wxRETAINED), + background(0x00,0x00,0x00), + foreground(0x00,0xff,0x00), + wellground(0xff,0xff,0xff), + outline(0x00,0x00,0x00), + labelColour(*(parent->GetLabelColour())), + bback(background, wxTRANSPARENT), + bfore(foreground, wxSOLID), + bwell(wellground, wxSOLID), + outlinePen(outline, 1, wxSOLID), + labelFont(12, wxROMAN, wxNORMAL, wxNORMAL), + myLabel(label) +{ + wxBrush *dcb; + logicalParent = logParent; + border = dflt_border; + barwidth = dflt_barwidth; barheight = dflt_barheight; + value = val; + vmin = min; vmax = max; + if (vmax <= vmin) + vmax = vmin+1; + dcb = ((wxPanel*)GetParent())->GetDC()->GetBackground(); + bback = wxBrush(dcb->GetColour(), dcb->GetStyle()); + SetBackground(&bback); + SetFont(&labelFont); + GetDC()->GetTextExtent(myLabel.ptr(), &textx, &texty); +} + +rviewSpecialSlider::~rviewSpecialSlider(void) +{ +} + +int rviewSpecialSlider::GetMin(void) const +{ + return vmin; +} + +int rviewSpecialSlider::GetMax(void) const +{ + return vmax; +} + +int rviewSpecialSlider::GetValue(void) const +{ + return value; +} + +void rviewSpecialSlider::SetRange(int min, int max) +{ + if (min < max) + { + vmin = min; vmax = max; + if (value < vmin) + value = vmin; + if (value > vmax) + value = vmax; + Refresh(TRUE); + } +} + +void rviewSpecialSlider::SetValue(int val) +{ + if ((vmin <= val) && (val <= vmax)) + { + float barx, bary, bheight, newbx; + + getBarParams(barx, bary, bheight); + value = val; + getBarParams(newbx, bary, bheight); + updateWell(barx, newbx, bary, bheight); + } +} + +void rviewSpecialSlider::SetLabel(const char *label) +{ + myLabel = label; + GetDC()->GetTextExtent(myLabel.ptr(), &textx, &texty); + Refresh(TRUE); +} + +bool rviewSpecialSlider::PositionInWell(float posx, float posy) +{ + int y0, y1; + + GetClientSize(&cwidth, &cheight); + getWellVert(y0, y1); + if (((float)y0 <= posy) && (posy <= (float)y1)) + { + if ((posx >= textx + 2*border) && (posx <= (float(cwidth - border)))) + return TRUE; + } + return FALSE; +} + + +void rviewSpecialSlider::getWellVert(int &y0, int &y1) +{ + y0 = cheight - 2*border - barheight; + if (y0 < 0) + y0 = 0; + y0 += border; + y1 = cheight - border; + if (y1 < y0) + y1 = y0; +} + +void rviewSpecialSlider::getBarParams(float &posx, float &posy, float &height) +{ + int y0, y1; + + GetClientSize(&cwidth, &cheight); + + posx = (float)((cwidth - 3*border - barwidth - textx) * (value-vmin)) / (vmax - vmin); + if (posx < 0) + posx = 0; + /*cout << "cwidth " << cwidth << ", textx " << textx << ", value " << value << ", border " << border + << ", barwidth " << barwidth << ", vmin " << vmin << ", vmax " << vmax << ", posx " << posx << endl;*/ + posx += (float)(2*border + textx); + getWellVert(y0, y1); + posy = (float)y0; + height = (float)(y1 - y0); +} + +int rviewSpecialSlider::calcNewValue(float posx, float posy, int &val, bool checky) +{ + int y0, y1; + + GetClientSize(&cwidth, &cheight); + + getWellVert(y0, y1); + if (!checky || ((float)y0 <= posy) && (posy <= (float)y1)) + { + float rem = (float)(cwidth - 3*border - barwidth - textx); + if (rem < 1.0) + rem = 1.0; + val = (int)((((posx - 2*border - textx) * (vmax - vmin)) / rem) + 0.5) + vmin; + if (val < vmin) + val = vmin; + if (val > vmax) + val = vmax; + + return 0; + } + return -1; +} + + +void rviewSpecialSlider::getUpdateInterval(float oldx, float newx, float &clipx, float &clipw) +{ + if (oldx < newx) + { + clipx = oldx; clipw = newx - oldx + barwidth; + } + else + { + clipx = newx; clipw = oldx - newx + barwidth; + } +} + + +void rviewSpecialSlider::updateWell(float oldx, float newx, float posy, float bheight) +{ + float clipx, clipw; + + getUpdateInterval(oldx, newx, clipx, clipw); + //cout << "CLIP " << clipx << ", " << posy << ", " << clipw << ", " << bheight << endl; + // need to enlarge the clipping region somewhat to make sure everything is redrawn + BeginDrawing(); + SetClippingRegion(clipx, 0, clipw+2, cheight); + //Clear(); + redrawCore(newx, posy, bheight); + DestroyClippingRegion(); + EndDrawing(); +} + + +void rviewSpecialSlider::redrawCore(float x, float y, float bheight) +{ + float extx, exty, nx, ny; + char number[32]; + + // first draw the label + SetFont(&labelFont); + SetTextForeground(&labelColour); + DrawText(myLabel.ptr(), 0, y); + + SetPen(&outlinePen); + SetBrush(&bwell); + DrawRectangle(textx + border, y-border, (float)cwidth - textx - border, bheight + 2*border); + SetBrush(&bfore); + DrawRectangle(x, y, (float)barwidth, bheight); + + // then draw the current value on top + sprintf(number, "%d", value); + GetDC()->GetTextExtent(number, &extx, &exty); + nx = x + (barwidth - extx)/2; ny = y - exty - border; + SetPen(NULL); + // must use a non-transparent brush. Finally found the right one! + SetBrush(((wxPanel*)GetParent())->GetDC()->GetBackground()); + DrawRectangle(0, ny, (float)cwidth, exty); + DrawText(number, nx, ny); +} + + +void rviewSpecialSlider::OnPaint(void) +{ + wxRect rect; + float x, y, bheight; + + getBarParams(x, y, bheight); + + BeginDrawing(); + + wxUpdateIterator upd(this); + while (upd) + { + upd.GetRect(&rect); + redrawCore(x, y, bheight); + upd++; + } + EndDrawing(); +} + +void rviewSpecialSlider::OnEvent(wxMouseEvent &mevt) +{ + int type = mevt.GetEventType(); + float barx, bary, bheight; + float mx, my; + int newVal = value; + + getBarParams(barx, bary, bheight); + mevt.Position(&mx, &my); + if ((type == wxEVENT_TYPE_LEFT_DOWN) || (type == wxEVENT_TYPE_RIGHT_DOWN)) + { + if (mx < barx) + { + if (calcNewValue(barx - barwidth, my, newVal, TRUE) != 0) + newVal = value; + } + else if (mx > barx + barwidth) + { + if (calcNewValue(barx + barwidth, my, newVal, TRUE) != 0) + newVal = value; + } + } + else if (type == wxEVENT_TYPE_MOTION) + { + if (mevt.LeftIsDown() || mevt.RightIsDown()) + { + if (calcNewValue(mx - barwidth/2, my, newVal, FALSE) != 0) + { + newVal = value; + } + } + } + if (newVal != value) + { + float newbx, newby, newbh; + + value = newVal; + getBarParams(newbx, newby, newbh); + updateWell(barx, newbx, bary, bheight); + + // additionally we build a regular command event + wxCommandEvent cmd(wxEVENT_TYPE_SLIDER_COMMAND); + //OnCommand(*GetParent(), cmd); + logicalParent->process(*this, cmd); + } + // the entire point of this class is to always pass on the actual mouse event to the parent + logicalParent->childMouseEvent(this, mevt); +} + + + + + + +/* + * rviewDialog member functions. + */ + +const int rviewDialog::dialog_width = 300; +const int rviewDialog::dialog_height = 170; +const int rviewDialog::dialog_border = 8; +const int rviewDialog::dialog_buttonsx = 80; +const int rviewDialog::dialog_buttonsy = 30; +const int rviewDialog::dialog_bheight = rviewDialog::dialog_buttonsy + 16; +const int rviewDialog::dialog_lines = 8; +const int rviewDialog::dialog_lheight = 20; + + +// The strings passed to this function may be labels (first char is a backslash) +// or explicit text. +rviewDialog::rviewDialog(const char *title, const char *message, int buttonNo, const char *buttons[]): + rviewFrame(NULL, "", 0, 0, dialog_width, dialog_height) +{ + int i, x, y; + char *string; + + panel = NULL; buttonNumber = 0; buttonPressed = 0; + + buttonText = new char *[buttonNo + 2]; + but = new rviewButton *[buttonNo]; + + if ((buttonText == NULL) || (but == NULL)) + { + cerr << "rviewDialog::rviewDialog(): " << lman->lookup("errorMemory") << endl; + if (buttonText != NULL) delete [] buttonText; + if (but != NULL) delete [] but; + return; + } + + buttonNumber = buttonNo; + + buttonText[0] = (char*)title; buttonText[1] = (char*)message; + for (i=0; i<buttonNumber; i++) + { + but[i] = NULL; buttonText[i+2] = (char*)buttons[i]; + } + + // Only memorize those strings that are a label identifier (first char a backslash) + for (i=0; i<2+buttonNumber; i++) + { + if (buttonText[i] != NULL) + { + // Don't combine the two ifs with && : there is no defined evaluation order for && ! + // Always memorize the message text. + if ((buttonText[i][0] == '\\') || (i == 1)) + { + // We remove the backslash but need one additional char for the + // line terminator ==> strlen -1 + 1. + if ((string = new char[strlen(buttonText[i]) + ((i==1) ? 1 : 0)]) == NULL) + { + cerr << "rviewDialog::rviewDialog(): " << lman->lookup("errorMemory") << endl; + while (--i >= 0) + { + if (buttonText[i] != NULL) delete [] buttonText[i]; + } + delete [] buttonText; buttonText = NULL; + delete [] but; but = NULL; + buttonNumber = 0; + return; + } + strcpy(string, buttonText[i] + ((i==1) ? 0 : 1)); buttonText[i] = string; + //cout << "buttonText[" << i << "] = " << buttonText[i] << endl; + } + else buttonText[i] = NULL; + } + } + + GetClientSize(&x, &y); + + panel = new wxPanel((wxWindow*)this, 100, 100, 10, 10); + + msg = new rviewMultiline(panel, dialog_border, dialog_border, dialog_lheight, dialog_lines); + + //cout << x << ", " << y << endl; + + // If the strings are not labels then set them now statically, otherwise they will + // be set in the following label() call. + if (title[0] != '\\') + { + SetTitle((char*)title); + } + + // Now handle all buttons. + for (i=0; i<buttonNumber; i++) + { + but[i] = new rviewButton(panel); + if (buttons[i][0] != '\\') + { + but[i]->SetLabel((char*)buttons[i]); + } + else // do this for the button size. + { + but[i]->SetLabel(lman->lookup(buttons[i]+1)); + } + } + + OnSize(x, y); + + label(); + + Show(TRUE); +} + + + +rviewDialog::~rviewDialog(void) +{ + int i; + + // Widgets that were created as children of other wx items are deleted by those items. + // They mustn't be destroyed here! + + if (msg != NULL) delete msg; + //if (panel != NULL) delete panel; + + if (but != NULL) + { + //for (i=0; i<buttonNumber; i++) {if (but[i] != NULL) delete but[i];} + delete [] but; + } + + if (buttonText != NULL) + { + for (i=0; i<2+buttonNumber; i++) {if (buttonText[i] != NULL) delete [] buttonText[i];} + delete [] buttonText; + } +} + + +const char *rviewDialog::getFrameName(void) const +{ + return "rviewDialog"; +} + +rviewFrameType rviewDialog::getFrameType(void) const +{ + return rviewFrameTypeDialog; +} + + +void rviewDialog::OnSize(int w, int h) +{ + int x, y, empty, pos, i; + + RMDBGONCE( 3, RMDebug::module_applications, "rviewDialog", "OnSize( " << x << ", " << h << " )" ); + + GetClientSize(&x, &y); + if ((frameWidth == x) && (frameHeight == y)) return; + frameWidth = x; frameHeight = y; + + x -= 2*dialog_border; y -= dialog_border; + panel->SetSize(0, 0, x, y); + + if (buttonText[1][0] == '\\') + msg->rebuild(lman->lookup(buttonText[1]+1), x); + else + msg->rebuild(buttonText[1], x); + + // Space out the buttons on the available space. + if (buttonNumber == 0) return; + + empty = (x - buttonNumber*dialog_buttonsx) / buttonNumber; + if(empty <0 ) + empty=0; + + pos = empty/2; + + for (i=0; i<buttonNumber; i++) + { + but[i]->SetSize(pos, y - (dialog_bheight + dialog_buttonsy)/2, dialog_buttonsx, dialog_buttonsy); + pos += empty + dialog_buttonsx; + } +} + + + +void rviewDialog::label(void) +{ + int i, x, y; + + //cout << "rviewDialog::label()" << endl; + // If the widgets are labels then set their new values. + + panel->GetClientSize(&x, &y); + if (buttonText[0] != NULL) + { + SetTitle(lman->lookup(buttonText[0])); + } + + // The message is always stored. + if (buttonText[1][0] == '\\') + msg->rebuild(lman->lookup(buttonText[1]+1), x); + else + msg->rebuild(buttonText[1], x); + + for (i=0; i<buttonNumber; i++) + { + if (buttonText[2+i] != NULL) + { + but[i]->SetLabel(lman->lookup(buttonText[2+i])); + } + } +} + + + + +/* + * Process events which are broadcast by the frame manager. + */ + +int rviewDialog::process(wxObject &obj, wxEvent &evt) +{ + int i, type; + + //cout << "rviewDialog::process()" << endl; + for (i=0; i<buttonNumber; i++) + { + if (&obj == (wxObject*)but[i]) break; + } + + if (i >= buttonNumber) return 0; + + type = evt.GetEventType(); + + if (type == wxEVENT_TYPE_BUTTON_COMMAND) + { + buttonPressed = i+1; + } + else + { + buttonPressed = 0; + } + //cout << "rviewDialog::process():" << (void*)this << " Button " << buttonPressed << endl; + + return buttonPressed; +} + + + +/* + * Polling interface to the dialog box. Returns the number of the button that + * was pressed, starting from 1 (0 means none). On reading the button number + * is reset to 0. + */ + +int rviewDialog::GetButton(void) +{ + int val = buttonPressed; + + buttonPressed = 0; + return val; +} + + + + + +/* + * Error box + * (an intrinsically modal special case of a dialog box) + */ + +char *rviewErrorboxDefaultButton[1] = {"\\textOK"}; + +rviewErrorbox::rviewErrorbox(const char *message): rviewDialog("\\errorFrom", message, 1, (const char**)rviewErrorboxDefaultButton) +{ + //place this in log file + cerr << lman->lookup("errorFrom") << ' ' << message << endl; +} + + +rviewErrorbox::rviewErrorbox(const char *title, const char *message, int buttonNo, const char *buttons[]): rviewDialog(title, message, buttonNo, buttons) +{ + //place this in log file + cerr << lman->lookup("errorFrom") << ' ' << message << endl; +} + + +rviewErrorbox::~rviewErrorbox(void) {;} + + +const char *rviewErrorbox::getFrameName(void) const +{ + return "rviewErrorbox"; +} + +rviewFrameType rviewErrorbox::getFrameType(void) const +{ + return rviewFrameTypeErrorbox; +} + + +int rviewErrorbox::activate(void) +{ + int pressed; + + MakeModal(TRUE); + while ((pressed = GetButton()) == 0) ::wxYield(); + MakeModal(FALSE); + return pressed; +} + + +// static member functions +char *rviewErrorbox::buildErrorMessage(const char *message, const char *classname, const char *funcname) +{ + char *buffer, *b; + int bsize = strlen(message) + 1; + + if (classname != NULL) + { + bsize += strlen(classname) + 3; // ": " + + // funcname is ignored unless class name is given + if (funcname != NULL) + bsize += strlen(funcname) + 3; // "::" + } + + buffer = new char[bsize]; + b = buffer; + + if (classname != NULL) + { + b += sprintf(b, "%s", classname); + + if (funcname != NULL) + b += sprintf(b, "::%s", funcname); + + b += sprintf(b, ": "); + } + b += sprintf(b, "%s", message); + + return buffer; +} + + +rviewErrorbox *rviewErrorbox::newErrorbox(const char *message, const char *classname, const char *funcname) +{ + char *msg; + rviewErrorbox *box; + + msg = buildErrorMessage(message, classname, funcname); + box = new rviewErrorbox(msg); + delete [] msg; + + return box; +} + + +rviewErrorbox *rviewErrorbox::newErrorbox(const char *title, const char *message, int buttonNo, const char *buttons[], const char *classname, const char *funcname) +{ + char *msg; + rviewErrorbox *box; + + msg = buildErrorMessage(message, classname, funcname); + box = new rviewErrorbox(msg); + delete [] msg; + + return box; +} + + +int rviewErrorbox::reportError(const char *message, const char *classname, const char *funcname) +{ + rviewErrorbox *box; + int but; + + box = newErrorbox(message, classname, funcname); + but = box->activate(); + box->Close(TRUE); + + return but; +} + + +int rviewErrorbox::reportError(const char *title, const char *message, int buttonNo, const char *buttons[], const char *classname, const char *funcname) +{ + rviewErrorbox *box; + int but; + + box = newErrorbox(title, message, buttonNo, buttons, classname, funcname); + but = box->activate(); + box->Close(TRUE); + + return but; +} + + + + + +/* + * rviewProgress members + */ + +rviewProgress::rviewProgress(const char *message): rviewDialog("\\messageFrom", message, 1, (const char**)rviewErrorboxDefaultButton) +{ + int x, y; + + RMDBGONCE( 3, RMDebug::module_applications, "rviewProgress", "rviewProgress( " << lman->lookup("messageFrom") << message << " )" ); + + GetSize(&x, &y); + y = 2*dialog_border + msg->getMessageHeight() + dialog_bheight; + SetSize(-1, -1, x, y); + + // Have to do this to make sure you actually see anything in the window + // Crude, but nothing else seems to work... + Refresh(TRUE); ::wxYield(); + // Wait 300ms + ::wxStartTimer(); + while (::wxGetElapsedTime(FALSE) < 300) ::wxYield(); +} + + + + +rviewProgress::~rviewProgress(void) {;} + + +int rviewProgress::process(wxObject &obj, wxEvent &evt) +{ + if (rviewDialog::process(obj, evt) != 0) + { + this->Close(TRUE); + return 1; + } + return 0; +} + + +const char *rviewProgress::getFrameName(void) const +{ + return "rviewProgress"; +} + +rviewFrameType rviewProgress::getFrameType(void) const +{ + return rviewFrameTypeProgress; +} + + + + +/* + * rviewResult members + */ + +const int rviewResult::result_x = 0; +const int rviewResult::result_y = 0; +const int rviewResult::result_width = 320; +const int rviewResult::result_height = 296; +const int rviewResult::result_border = 8; +const int rviewResult::result_lheight = 20; +const int rviewResult::result_header = (8 * rviewResult::result_lheight); +const int rviewResult::result_cwidth = 150; +const int rviewResult::result_twidth = 90; +const int rviewResult::result_theight = 30; +const int rviewResult::result_bwidth = 60; +const int rviewResult::result_bheight = 30; + +rviewResult::rviewResult(void): rviewFrame(NULL, "", result_x, result_y, result_width, result_height) +{ + RMDBGONCE( 3, RMDebug::module_applications, "rviewResult", "rviewResult() " << this); + + setupVariables(); + configureGrey(); + Show(FALSE); +} + +rviewResult::rviewResult(collection_desc *collection): rviewFrame(NULL, "", result_x, result_y, result_width, result_height) +{ + setupVariables(); + setCollection(collection); +} + + +rviewResult::~rviewResult(void) +{ + RMDBGONCE( 3, RMDebug::module_applications, "rviewResult", "rviewResult() " << this ); + + int i; + collection_desc *ptr = coll; + user_event ue; + + // give all viewers a chance to clean up + ue.type = usr_mdd_dying; + for (i=0; i<coll->number; i++) + { + ue.data = (void*)&(coll->mddObjs[i].mdd); + frames->broadcastUserEvent(ue); + } + + // All the widgets are deleted automatically. + if (selectedItems != NULL) delete [] selectedItems; + if (typeManager != NULL) {typeManager->unlinkParent(); typeManager->Close(TRUE);} + + rviewDeleteCollection(coll); +} + + +const char *rviewResult::getFrameName(void) const +{ + return "rviewResult"; +} + +rviewFrameType rviewResult::getFrameType(void) const +{ + return rviewFrameTypeResult; +} + + +void rviewResult::setCollection(collection_desc *collection) +{ + int x, y, i; + char res_string[STRINGSIZE]; + + // In case this is called when the frame already displays a result (shouldn't + // normally happen, though) + if (coll != NULL) + { + rviewDeleteCollection(coll); + delete list; + } + coll = collection; + + // Init flags + for (i=0; i<coll->number; i++) + { + coll->mddObjs[i].flags = 0; + } + + GetClientSize(&x, &y); + + list = new wxListBox(panel, (wxFunction)&rviewEventHandler, "", wxMULTIPLE, result_border, result_border + result_header, x, y - result_header, 0, NULL, wxALWAYS_SB | wxLB_MULTIPLE); + + ostrstream memstr(res_string, STRINGSIZE); + for (i=0; i<coll->number; i++) + { + list->Append(mddDescriptorString(memstr, i)); + } + + if (selectedItems != NULL) delete [] selectedItems; + i = (coll->number + 7) >> 3; + if ((selectedItems = new char[i]) == NULL) + { + cerr << "rviewResult::setCollection(): " << lman->lookup("errorMemory") << endl; + this->Close(TRUE); + return; + } + memset(selectedItems, 0, i); + + configureGrey(); + + label(); + + // Force resize + frameWidth = -1; frameHeight = -1; + OnSize(x, y); + + Show(TRUE); +} + + + +char *rviewResult::mddDescriptorString(ostrstream &memstr, int number) +{ + r_GMarray *mdd = coll->mddObjs[number].mdd.ptr(); + r_Data_Format fmt = mdd->get_current_format(); + + memstr.width(3); + memstr << number << ": ("; memstr.width(0); + memstr << mdd->get_type_length() << ") " + << mdd->spatial_domain() << ' ' + << fmt << '\0'; + memstr.seekp(0); + + return memstr.str(); +} + + +void rviewResult::label(void) +{ + char buffer[STRINGSIZE]; + + //cout << "labelling " << (void*)mBar << endl; + + SetTitle(lman->lookup("titleResult")); + + if (mBar != NULL) + { + // Configure all the text in the menu bar + mBar->SetLabel(MENU_RSLT_ITEM_OPENALL, lman->lookup("menRsltItemOpAll")); + mBar->SetLabel(MENU_RSLT_ITEM_THUMBALL, lman->lookup("menRsltItemThumbAll")); + mBar->SetLabel(MENU_RSLT_ITEM_CLOSE, lman->lookup("menRsltItemClose")); + mBar->SetHelpString(MENU_RSLT_ITEM_OPENALL, lman->lookup("helpRsltItemOpAll")); + mBar->SetHelpString(MENU_RSLT_ITEM_THUMBALL, lman->lookup("helpRsltItemThumbAll")); + mBar->SetHelpString(MENU_RSLT_ITEM_CLOSE, lman->lookup("helpRsltItemClose")); + + mBar->SetLabel(MENU_RSLT_SLCT_SLCTALL, lman->lookup("menRsltSlctSlctAll")); + mBar->SetLabel(MENU_RSLT_SLCT_CLEAR, lman->lookup("menRsltSlctClear")); + mBar->SetLabel(MENU_RSLT_SLCT_OPEN, lman->lookup("menRsltSlctOpen")); + mBar->SetLabel(MENU_RSLT_SLCT_THUMB, lman->lookup("menRsltSlctThumb")); + mBar->SetLabel(MENU_RSLT_SLCT_DELETE, lman->lookup("menRsltSlctDel")); + mBar->SetLabel(MENU_RSLT_SLCT_ENDIAN, lman->lookup("menRsltSlctEndian")); + mBar->SetLabel(MENU_RSLT_SLCT_TYPEMAN, lman->lookup("menRsltSlctTypeMan")); + mBar->SetLabel(MENU_RSLT_SLCT_INFO, lman->lookup("menRsltSlctInfo")); + mBar->SetHelpString(MENU_RSLT_SLCT_SLCTALL, lman->lookup("helpRsltSlctSlctAll")); + mBar->SetHelpString(MENU_RSLT_SLCT_CLEAR, lman->lookup("helpRsltSlctClear")); + mBar->SetHelpString(MENU_RSLT_SLCT_OPEN, lman->lookup("helpRsltSlctOpen")); + mBar->SetHelpString(MENU_RSLT_SLCT_THUMB, lman->lookup("helpRsltSlctThumb")); + mBar->SetHelpString(MENU_RSLT_SLCT_DELETE, lman->lookup("helpRsltSlctDel")); + mBar->SetHelpString(MENU_RSLT_SLCT_ENDIAN, lman->lookup("helpRsltSlctEndian")); + mBar->SetHelpString(MENU_RSLT_SLCT_TYPEMAN, lman->lookup("helpRsltSlctTypeMan")); + mBar->SetHelpString(MENU_RSLT_SLCT_INFO, lman->lookup("helpRsltSlctInfo")); + + mBar->SetLabelTop(0, lman->lookup("menRsltItem")); + mBar->SetLabelTop(1, lman->lookup("menRsltSlct")); + } + + if (list != NULL) + { + list->SetLabel(lman->lookup("textResult")); + } + + if (group != NULL) + { + group->SetLabel(lman->lookup("textCollHeader")); + sprintf(buffer, "%s: %s", lman->lookup("textCollName"), (coll->collName == NULL) ? "" : coll->collName); + collName->SetLabel(buffer); + sprintf(buffer, "%s: %s", lman->lookup("textCollType"), (coll->collType == NULL) ? "?" : coll->collType); + collType->SetLabel(buffer); + sprintf(buffer, (coll->collInfo == NULL) ? "" : coll->collInfo); + collInfo->SetLabel(buffer); + + if (choice != NULL) + { + choice->SetLabel(lman->lookup("dispModeLabel")); + } + } + resampBut->SetLabel(lman->lookup("textOK")); + + //cout << "labelled OK" << endl; +} + + + +void rviewResult::OnSize(int w, int h) +{ + if (panel != NULL) + { + int x, y; + + RMDBGONCE( 3, RMDebug::module_applications, "rviewResult", "OnSize( " << w << ", " << h << " )"); + + GetClientSize(&x, &y); + if ((frameWidth == x) && (frameHeight == y)) return; + frameWidth = x; frameHeight = y; + + panel->SetClientSize(x, y); + x -= 2*result_border; y -= 2*result_border; + + if (list != NULL) + list->SetSize(result_border, + result_lheight + result_border + result_header, + x, + y - result_header - result_lheight); + + if (group != NULL) + { + group->SetSize(result_border, + result_border, + x, + result_header); + //group->GetClientSize(&x, &y); + x -= 2*result_border; y -=2*result_border; + if (collName != NULL) + collName->SetSize(2*result_border, + result_lheight + result_border, + x, + result_lheight); + if (collType != NULL) + collType->SetSize(2*result_border, + 2*result_lheight + result_border, + x, + result_lheight); + if (collInfo != NULL) + collInfo->SetSize(2*result_border, + 3*result_lheight + result_border, + x, + result_lheight); + if (choice != NULL) + choice->SetSize(2*result_border, + 4*result_lheight + result_border, + result_cwidth - cbfactor, + result_lheight); + } + scaleMode->SetSize(3*result_border/2, + 6*result_lheight + result_border, + result_cwidth - 2*cbfactor, + result_lheight); + resampText->SetSize(3*result_border/2 + result_cwidth - cbfactor, + 6*result_lheight + result_border, + result_twidth, + result_theight); + resampBut->SetSize(3*result_border/2 + result_cwidth - cbfactor/2 + result_twidth, + 6*result_lheight + result_border, + result_bwidth, + result_bheight); + } +} + + + +void rviewResult::openViewer(int itemNo) +{ + + RMDBGONCE( 3, RMDebug::module_applications, "rviewResult", "openViewer( " << itemNo << " )" ); + + if ((itemNo < 0) || (itemNo >= coll->number)) return; + + rviewDisplay *newDisplay=NULL; + + switch (prefs->lastDisplay) + { + case 0: + { + if ((coll->mddObjs[itemNo].flags & RVIEW_RESDISP_IMGFLAT) == 0) + { + newDisplay = new rviewFlatImage(coll->mddObjs + itemNo); + } + } + break; + case 1: + { + if ((coll->mddObjs[itemNo].flags & RVIEW_RESDISP_IMGVOLM) == 0) + { + newDisplay = new rviewVolumeImage(coll->mddObjs + itemNo); + } + } + break; + case 2: + { + if ((coll->mddObjs[itemNo].flags & RVIEW_RESDISP_IMGOSECT) == 0) + { + newDisplay = new rviewOSectionFullImage(coll->mddObjs + itemNo); + } + } + break; + case 3: + { + if ((coll->mddObjs[itemNo].flags & RVIEW_RESDISP_IMGHGHT) == 0) + { + newDisplay = new rviewHeightImage(coll->mddObjs + itemNo); + } + } + break; + case 4: + { + if ((coll->mddObjs[itemNo].flags & RVIEW_RESDISP_CHART) == 0) + { + newDisplay = new rviewChart(coll->mddObjs + itemNo); + } + } + break; + case 5: + { + if ((coll->mddObjs[itemNo].flags & RVIEW_RESDISP_TABLE) == 0) + { + newDisplay = new rviewTable(coll->mddObjs + itemNo); + } + } + break; + case 6: + { + if ((coll->mddObjs[itemNo].flags & RVIEW_RESDISP_SOUND) == 0) + { + newDisplay = new rviewSoundPlayer(coll->mddObjs + itemNo); + } + } + break; + case 7: + { + if ((coll->mddObjs[itemNo].flags & RVIEW_RESDISP_STRVIEW) == 0) + { + newDisplay = new rviewStringViewer(coll->mddObjs + itemNo); + } + } + break; + default: + cerr << "Unsupported display mode " << prefs->lastDisplay << endl; + return; + } + + if (newDisplay != NULL) + { + if (newDisplay->openViewer() != 0) + newDisplay->Close(TRUE); + else + { + coll->mddObjs[itemNo].flags |= newDisplay->getViewerType(); + registerChild(newDisplay); + } + } +} + + + +/* + * This function must be called whenever the selections of the list box + * change (click / double click) or items in the listbox are modified or + * deleted (this seems to be some wxWindows-internal bug that always + * deselects listbox items if an item was changed). + */ +int rviewResult::updateSelection(void) +{ + int i, changes, sel=-1; + char val; + + if (selectedItems == NULL) return -1; + + changes = 0; + for (i=0; i<coll->number; i++) + { + val = selectedItems[i>>3] & (1<<(i&7)); + if (list->Selected(i)) + { + if (val == 0) {sel=i; changes++;} + selectedItems[i>>3] |= (1<<(i&7)); + } + else + { + if (val != 0) {sel=i; changes++;} + selectedItems[i>>3] &= ~(1<<(i&7)); + } + } + if (changes > 1) + { + cerr << "Warning: more than one selection changed!" << endl; + } + + configureGrey(); + + return sel; +} + + + +void rviewResult::operationPrologue(void) +{ + ::wxBeginBusyCursor(); + ::wxStartTimer(); +} + + +void rviewResult::operationEpilogue(const char *opname) +{ + int elapsed = ::wxGetElapsedTime(); + + ::wxEndBusyCursor(); + + if (opname != NULL) + { + char buffer[STRINGSIZE]; + + sprintf(buffer, "%s \"%s\": %dms", lman->lookup("textOpTime"), opname, elapsed); + cout << buffer << endl; + SetStatusText(buffer); + } +} + + +int rviewResult::parseResampleString(const char *resStr, double *values) +{ + int i; + const char *b; + + b = resStr; i = 0; + while (*b != 0) + { + while ((*b == ' ') || (*b == '\t')) b++; + if (*b == ',') b++; + if (*b != 0) + { + if (values != NULL) values[i] = atof(b); + while ((*b != ' ') && (*b != '\t') && (*b != ',') && (*b != 0)) b++; + i++; + } + } + return i; +} + + +int rviewResult::resampleSelection(void) +{ + int i, j, res; + double *scale; + r_Dimension dim; + r_Minterval oldInterv, useInterv, newInterv; + r_Ref<r_GMarray> newMdd; + rviewBaseType baseType; + char buffer[STRINGSIZE]; + user_event usr; + char *valStr; + int dimString; + int smode; + const char *operation=NULL; + + ostrstream memstr(buffer, STRINGSIZE); + + valStr = resampText->GetValue(); + dimString = parseResampleString(valStr, NULL); + scale = new double[dimString]; + parseResampleString(valStr, scale); + + for (i=0; i<dimString; i++) + { + if (scale[i] <= 0.0) return 0; + if (scale[i] != 1.0) break; + } + + smode = scaleMode->GetSelection(); + + operationPrologue(); + + res = 0; + for (i=0; i<coll->number; i++) + { + if (list->Selected(i)) + { + double totalScale = 1.0; + + baseType = rviewGetBasetype((r_Object*)(&(*(coll->mddObjs[i].mdd)))); + oldInterv = (coll->mddObjs[i].mdd)->spatial_domain(); + dim = oldInterv.dimension(); + newInterv = r_Minterval(dim); useInterv = r_Minterval(dim); + for (j=0; j<(int)dim; j++) + { + double h; + + // Use the entire source object for scaling + useInterv << oldInterv[j]; + h = (j < dimString) ? scale[j] : scale[dimString-1]; totalScale *= h; + newInterv << r_Sinterval((r_Range)(oldInterv[j].low()), (r_Range)(oldInterv[j].low() + h*(oldInterv[j].high() - oldInterv[j].low() + 1) - 1)); + } + if (smode == 0) // resampling mode + { + if (totalScale > 1.0) + { + operation = lman->lookup("operationUpsamp"); + j = mdd_objectScaleInter(coll->mddObjs[i].mdd, useInterv, newMdd, newInterv); + } + else + { + operation = lman->lookup("operationDownsamp"); + j = mdd_objectScaleAverage(coll->mddObjs[i].mdd, useInterv, newMdd, newInterv); + } + } + else // simple scale mode + { + operation = lman->lookup("operationScale"); + j = mdd_objectScaleSimple(coll->mddObjs[i].mdd, useInterv, newMdd, newInterv); + } + if (j != 0) + { + usr.type = usr_mdd_dying; usr.data = (void*)(&((coll->mddObjs[i]).mdd)); + frames->broadcastUserEvent(usr); + coll->mddObjs[i].flags = 0; + (coll->mddObjs[i].mdd).destroy(); + coll->mddObjs[i].mdd = newMdd; + res++; + list->SetString(i, mddDescriptorString(memstr, i)); + selectedItems[i>>3] &= ~(1<<(i&7)); + } + } + } + + operationEpilogue(operation); + + delete [] scale; + + lastSelected = updateSelection(); + + return res; +} + + +int rviewResult::process(wxObject &obj, wxEvent &evt) +{ + int type = evt.GetEventType(); + + if (&obj == (wxObject*)list) + { + if (type == wxEVENT_TYPE_LISTBOX_COMMAND) + { + //cout << "rviewResult::process Listbox command" << endl; + lastSelected = updateSelection(); + return 1; + } + if (type == wxEVENT_TYPE_LISTBOX_DCLICK_COMMAND) + { + if ((lastSelected >= 0) && (lastSelected < coll->number)) + { + // This check is necessary + if (!list->Selected(lastSelected)) + { + list->SetSelection(lastSelected, TRUE); + if (selectedItems != NULL) selectedItems[lastSelected>>3] |= (1<<(lastSelected&7)); + } + openViewer(lastSelected); + } + return 1; + } + } + if (&obj == (wxObject*)choice) + { + if (type == wxEVENT_TYPE_CHOICE_COMMAND) + { + prefs->lastDisplay = choice->GetSelection(); + prefs->markModified(); + return 1; + } + } + if (&obj == (wxObject*)resampText) + { + if (type == wxEVENT_TYPE_TEXT_ENTER_COMMAND) + { + resampleSelection(); + return 1; + } + } + if (&obj == (wxObject*)resampBut) + { + if (type == wxEVENT_TYPE_BUTTON_COMMAND) + { + resampleSelection(); + return 1; + } + } + + return 0; +} + + + +int rviewResult::userEvent(const user_event &ue) +{ + if (ue.type == usr_viewer_closed) + { + mdd_frame *mf = (mdd_frame*)(ue.data); + int i; + + //{FILE *fp=fopen("xxx", "a+"); fprintf(fp, "Received viewer closed %p --- %p, %d\n", mf->mdd.ptr(), coll->mddObjs[0].mdd.ptr(), mf->flags); fclose(fp);} + for (i=0; i<coll->number; i++) + { + // must compare pointers, comparing r_Refs has strange effects on Win + if (mf->mdd.ptr() == coll->mddObjs[i].mdd.ptr()) break; + } + if (i < coll->number) + { + coll->mddObjs[i].flags &= ~(mf->flags); + return 1; + } + } + else if (ue.type == usr_child_closed) + { + if (ue.data == (void*)typeManager) typeManager = NULL; + return 1; + } + else if (ue.type == usr_typeman_convert) + { + convertSelectedItems(); + typeManager->Close(TRUE); typeManager = NULL; + return 1; + } + else if (ue.type == usr_close_viewers) + { + Close(TRUE); + return 1; + } + + return 0; +} + + + +void rviewResult::convertSelectedItems(void) +{ + int itemNo; + user_event usr; + char buffer[STRINGSIZE]; + ostrstream memstr(buffer, STRINGSIZE); + + operationPrologue(); + + for (itemNo=0; itemNo < coll->number; itemNo++) + { + if ((list->Selected(itemNo)) && (!coll->mddObjs[itemNo].mdd.is_null())) + { + r_Ref<r_GMarray> newMdd; + + if (typeManager->convert(coll->mddObjs[itemNo].mdd, newMdd) == 0) + { + usr.type = usr_mdd_dying; usr.data = (void*)(&((coll->mddObjs[itemNo]).mdd)); + frames->broadcastUserEvent(usr); + coll->mddObjs[itemNo].mdd.destroy(); + coll->mddObjs[itemNo].mdd = newMdd; + list->SetString(itemNo, mddDescriptorString(memstr, itemNo)); + } + else + { + newMdd.destroy(); + } + } + } + + operationEpilogue(lman->lookup("operationTypeProj")); + + lastSelected = updateSelection(); +} + + + +// Set up some variables and widgets in the frame. +void rviewResult::setupVariables(void) +{ + int x, y; + char *displayModes[RVIEW_RESDISP_NUMBER]; + char *scaleModes[2]; + wxMenu *mbarMenus[2]; + char buffer[STRINGSIZE]; + + coll = NULL; panel = NULL; list = NULL; collName = NULL; collType = NULL; + group = NULL; mBar = NULL; typeManager = NULL; + lastSelected = -1; + selectedItems = NULL; + + CreateStatusLine(1); + + mbarMenus[0] = new wxMenu; + mbarMenus[0]->Append(MENU_RSLT_ITEM_OPENALL, ""); + mbarMenus[0]->Append(MENU_RSLT_ITEM_THUMBALL, ""); + mbarMenus[0]->Append(MENU_RSLT_ITEM_CLOSE, ""); + + mbarMenus[1] = new wxMenu; + mbarMenus[1]->Append(MENU_RSLT_SLCT_SLCTALL, ""); + mbarMenus[1]->Append(MENU_RSLT_SLCT_CLEAR, ""); + mbarMenus[1]->Append(MENU_RSLT_SLCT_OPEN, ""); + mbarMenus[1]->Append(MENU_RSLT_SLCT_THUMB, ""); + mbarMenus[1]->Append(MENU_RSLT_SLCT_DELETE, ""); + mbarMenus[1]->Append(MENU_RSLT_SLCT_ENDIAN, ""); + mbarMenus[1]->Append(MENU_RSLT_SLCT_TYPEMAN, ""); + mbarMenus[1]->Append(MENU_RSLT_SLCT_INFO, ""); + + mBar = new wxMenuBar; + sprintf(buffer, "&%s", lman->lookup("menRsltItem")); + mBar->Append(mbarMenus[0], buffer); + sprintf(buffer, "&%s", lman->lookup("menRsltSlct")); + mBar->Append(mbarMenus[1], buffer); + + GetClientSize(&x, &y); + panel = new wxPanel((wxWindow*)this, 0, 0, x, y); + x -= 2*result_border; y -= 2*result_border; + + // Create a group box surrounding the collection-items + group = new wxGroupBox(panel, "", 0, 0, x, result_header, wxBORDER); + x -= 2*result_border; y -= 2*result_border; + // Create the message widget showing the collection name + collName = new wxMessage(panel, "", 2*result_border, result_lheight + 2*result_border, x, result_lheight, 0, "message"); + // The same for the collection base type + collType = new wxMessage(panel, "", 2*result_border, 2*result_lheight + 2*result_border, x, result_lheight, 0, "message"); + // The info string + collInfo = new wxMessage(panel, "", 2*result_border, 3*result_lheight + 2*result_border, x, result_lheight, 0, "message"); + + // And the choice item for the display modes. + // In contrast to the menu stuff non-persistent objects are OK here. + displayModes[0] = lman->lookup("dispModeImage"); + displayModes[1] = lman->lookup("dispModeVolume"); + displayModes[2] = lman->lookup("dispModeOrtho"); + displayModes[3] = lman->lookup("dispModeHeight"); + displayModes[4] = lman->lookup("dispModeChart"); + displayModes[5] = lman->lookup("dispModeTable"); + displayModes[6] = lman->lookup("dispModeSound"); + displayModes[7] = lman->lookup("dispModeString"); + choice = new rviewChoice(panel, RVIEW_RESDISP_NUMBER, displayModes, lman->lookup("dispModeLabel")); + choice->SetSelection(prefs->lastDisplay); + + scaleModes[0] = lman->lookup("textResample"); + scaleModes[1] = lman->lookup("textScale"); + scaleMode = new rviewChoice(panel, 2, scaleModes); + scaleMode->SetSelection(0); + scaleMode->SetLabel(""); + + sprintf(buffer, "%f", 1.0); + resampText = new rviewText(panel, buffer); + resampBut = new rviewButton(panel, lman->lookup("textOK")); + + choice->GetSize(&x, &y); + cbfactor = x - result_cwidth; + + SetMenuBar(mBar); +} + + + + +void rviewResult::OnMenuCommand(int id) +{ + switch (id) + { + case MENU_RSLT_ITEM_OPENALL: + { + for (int i=0; i<coll->number; i++) + { + openViewer(i); + } + } + break; + case MENU_RSLT_ITEM_THUMBALL: + { + int i; + rviewThumb *thumb; + + thumb = new rviewThumb; + for (i=0; i<coll->number; i++) + { + thumb->addMDD(coll->mddObjs[i].mdd); + } + registerChild((rviewFrame*)thumb); + } + break; + case MENU_RSLT_ITEM_CLOSE: + { + this->Close(TRUE); + } + break; + case MENU_RSLT_SLCT_SLCTALL: + { + for (int i=0; i<coll->number; i++) + { + if (!list->Selected(i)) + { + list->SetSelection(i, TRUE); + } + } + if (selectedItems != NULL) memset(selectedItems, 0xff, (coll->number + 7) >> 3); + configureGrey(); + } + break; + case MENU_RSLT_SLCT_CLEAR: + { + for (int i=0; i<coll->number; i++) + { + if (list->Selected(i)) + { + list->SetSelection(i, FALSE); + } + } + if (selectedItems != NULL) memset(selectedItems, 0, (coll->number + 7) >> 3); + configureGrey(); + } + break; + case MENU_RSLT_SLCT_OPEN: + { + int i; + + for (i=0; i<coll->number; i++) + { + if (list->Selected(i)) + openViewer(i); + } + } + break; + case MENU_RSLT_SLCT_THUMB: + { + int i; + + for (i=0; i<coll->number; i++) + { + if (list->Selected(i)) break; + } + if (i < coll->number) + { + rviewThumb *thumb; + + thumb = new rviewThumb; + for (i=0; i<coll->number; i++) + { + if (list->Selected(i)) + thumb->addMDD(coll->mddObjs[i].mdd); + } + registerChild((rviewFrame*)thumb); + } + } + break; + case MENU_RSLT_SLCT_DELETE: + { + int itemNo, j; + user_event usr; + + do + { + for (itemNo=0; itemNo < coll->number; itemNo++) + if (list->Selected(itemNo)) break; + + if (itemNo >= coll->number) break; + list->Delete(itemNo); + // Notify all open display frames that a particular mdd object will die + usr.type = usr_mdd_dying; usr.data = (void*)(&((coll->mddObjs[itemNo]).mdd)); + frames->broadcastUserEvent(usr); + if (!coll->mddObjs[itemNo].mdd.is_null()) + { + coll->mddObjs[itemNo].mdd.destroy(); + } + (coll->number)--; + for (j=itemNo; j<coll->number; j++) + { + coll->mddObjs[j] = coll->mddObjs[j+1]; + // Don't forget to copy the selection too + selectedItems[j>>3] &= ~(1<<(j&7)); + if ((selectedItems[(j+1)>>3] & (1<<((j+1)&7))) != 0) selectedItems[j>>3] |= (1<<(j&7)); + } + } + while (1); + lastSelected = updateSelection(); + configureGrey(); + } + break; + case MENU_RSLT_SLCT_ENDIAN: + { + int itemNo; + user_event ue; + + operationPrologue(); + + ue.type = usr_mdd_dying; + for (itemNo = 0; itemNo < coll->number; itemNo++) + { + if ((list->Selected(itemNo)) && (!coll->mddObjs[itemNo].mdd.is_null())) + { + r_Minterval useInterv; + + ue.data = (void*)(&((coll->mddObjs[itemNo]).mdd)); + frames->broadcastUserEvent(ue); // close all open viewers + useInterv = coll->mddObjs[itemNo].mdd->spatial_domain(); + mdd_objectChangeEndianness(coll->mddObjs[itemNo].mdd, useInterv); + } + } + operationEpilogue(lman->lookup("operationEndian")); + } + break; + case MENU_RSLT_SLCT_TYPEMAN: + if (typeManager == NULL) + { + int itemNo; + const r_Type *sampleType = NULL; + + for (itemNo = 0; itemNo < coll->number; itemNo++) + { + if ((list->Selected(itemNo)) && (!coll->mddObjs[itemNo].mdd.is_null())) + { + sampleType = coll->mddObjs[itemNo].mdd->get_base_type_schema(); + break; + } + } + /*try { + sampleType = r_Type::get_any_type("struct { struct { float u, float v, float w }, float p, float te, float ed, float den, float vis }"); + } catch (r_Error &err) { + cerr << err.what() << endl; + }*/ + if (sampleType != NULL) + { + if (typeManager == NULL) + typeManager = new rviewTypeMan(this, sampleType); + } + } + break; + case MENU_RSLT_SLCT_INFO: break; + default: break; + } +} + + + +void rviewResult::configureGrey(void) +{ + int i; + + mBar->Enable(MENU_RSLT_ITEM_OPENALL, (coll->number > 0)); + mBar->Enable(MENU_RSLT_ITEM_THUMBALL, (coll->number> 0)); + + for (i=0; i<coll->number; i++) + { + if (list->Selected(i)) break; + } + bool enable = (i < coll->number); + + mBar->Enable(MENU_RSLT_SLCT_CLEAR, enable); + mBar->Enable(MENU_RSLT_SLCT_OPEN, enable); + mBar->Enable(MENU_RSLT_SLCT_THUMB, enable); + mBar->Enable(MENU_RSLT_SLCT_DELETE, enable); + mBar->Enable(MENU_RSLT_SLCT_ENDIAN, enable); + mBar->Enable(MENU_RSLT_SLCT_TYPEMAN, enable); + mBar->Enable(MENU_RSLT_SLCT_INFO, enable); +} + + + + + +/* + * rView about window + */ + +const int rviewAbout::about_width = 300; +const int rviewAbout::about_height = 150; +const int rviewAbout::about_border = 8; +const int rviewAbout::about_pheight = 50; +const int rviewAbout::about_bwidth = 80; +const int rviewAbout::about_bheight = 30; +const int rviewAbout::about_mheight = 16; + +rviewAbout::rviewAbout(void) : rviewFrame(NULL, "", -1, -1, about_width, about_height) +{ + labels = NULL; numlines = 0; + panel = new wxPanel((wxWindow*)this, 0, 0, about_width, about_height); + okBut = new rviewButton(panel); + + label(); + + OnSize(about_width, about_height); +} + + +rviewAbout::~rviewAbout(void) +{ + // the messages themselves will be sorted out by the panel destructor. + if (labels != NULL) delete [] labels; +} + + +const char *rviewAbout::getFrameName(void) const +{ + return "rviewAbout"; +} + +rviewFrameType rviewAbout::getFrameType(void) const +{ + return rviewFrameTypeAbout; +} + + +void rviewAbout::deleteLabels(void) +{ + if (labels != NULL) + { + int i; + + for (i=0; i<numlines; i++) delete labels[i]; + delete [] labels; labels = NULL; numlines = 0; + } +} + + +void rviewAbout::label(void) +{ + int i, number, x, y; + + SetTitle(lman->lookup("titleAbout")); + okBut->SetLabel(lman->lookup("textOK")); + + deleteLabels(); + + GetClientSize(&x, &y); + y = about_border; + number = atoi(lman->lookup("rviewAboutLineNum")); + if (number >= 1) + { + char lineName[64]; + + numlines = number; + labels = new wxMessage *[numlines]; x -= 2*about_border; + for (i=0; i<number; i++) + { + sprintf(lineName, "rviewAboutLine%d", i); + labels[i] = new wxMessage(panel, lman->lookup(lineName), about_border, y, x, about_mheight, 0, "message"); + y += about_mheight; + } + } + SetSize(-1, -1, x, y + about_pheight + rview_window_extra_height); +} + + +int rviewAbout::process(wxObject &obj, wxEvent &evt) +{ + if ((&obj == (wxObject*)okBut) && (evt.GetEventType() == wxEVENT_TYPE_BUTTON_COMMAND)) + { + Show(FALSE); return 1; + } + return 0; +} + + +void rviewAbout::OnSize(int w, int h) +{ + int x, y; + + GetClientSize(&x, &y); + + panel->SetSize(0, 0, x, y); + x -= 2*about_border; y = about_border; + if (panel != NULL) + { + for (int i=0; i<numlines; i++) + { + labels[i]->SetSize(about_border, y, x, about_mheight); + y += about_mheight; + } + } + x -= about_bwidth; y += (about_pheight - about_bheight)/2; + okBut->SetSize(x/2, y, about_bwidth, about_bheight); +} + + + + + +/* + * rviewStringSet window + */ + +const int rviewStringSet::strset_width = 300; +const int rviewStringSet::strset_height = 250; +const int rviewStringSet::strset_border = 8; +const int rviewStringSet::strset_reserve = 50; +const int rviewStringSet::strset_bwidth = 70; +const int rviewStringSet::strset_bheight = 40; +const int rviewStringSet::strset_mheight = 20; + +rviewStringSet::rviewStringSet(collection_desc *desc) : rviewFrame(NULL, "", -1, -1, strset_width, strset_height) +{ + int w, h; + char buffer[STRINGSIZE]; + + panel = new wxPanel((wxWindow*)this, 0, 0, strset_width, strset_height); + list = new wxListBox(panel, (wxFunction)rviewEventHandler, "", wxSINGLE, 0, 0, strset_width, strset_height, desc->number, desc->strObjs, wxALWAYS_SB); + dismiss = new rviewButton(panel); + + sprintf(buffer, "%s: %s", lman->lookup("textCollName"), (desc->collName == NULL) ? "" : desc->collName); + collName = new wxMessage(panel, buffer, 0, 0, 10, 10, 0, "message"); + sprintf(buffer, "%s: %s", lman->lookup("textCollType"), (desc->collType == NULL) ? "" : desc->collType); + collType = new wxMessage(panel, buffer, 0, 0, 10, 10, 0, "message"); + char* tmpChar1 = "message"; + char* tmpChar2 = ""; + collInfo = new wxMessage(panel, (desc->collInfo == NULL) ? tmpChar2 : desc->collInfo, 0, 0, 10, 10, 0, tmpChar1); + + GetClientSize(&w, &h); + + label(); + + OnSize(w, h); + + Show(TRUE); +} + + +rviewStringSet::~rviewStringSet(void) +{ +} + + +const char *rviewStringSet::getFrameName(void) const +{ + return "rviewStringSet"; +} + +rviewFrameType rviewStringSet::getFrameType(void) const +{ + return rviewFrameTypeStrSet; +} + + +void rviewStringSet::label(void) +{ + SetTitle(lman->lookup("titleStringSet")); + list->SetLabel(lman->lookup("strSetList")); + dismiss->SetLabel(lman->lookup("textClose")); +} + + +void rviewStringSet::OnSize(int w, int h) +{ + int x, y; + int head; + + GetClientSize(&x, &y); + panel->SetSize(0, 0, x, y); + head = 3*(strset_mheight + strset_border); + x -= 2*strset_border; y -= 2*strset_border; + collName->SetSize(strset_border, strset_border, x, strset_mheight); + collType->SetSize(strset_border, strset_mheight + 2*strset_border, x, strset_mheight); + collInfo->SetSize(strset_border, 2*strset_mheight + 3*strset_border, x, strset_mheight); + + list->SetSize(strset_border, strset_border + head, x, y - head - strset_reserve); + dismiss->SetSize((x - strset_bwidth) / 2, y + 2*strset_border - (strset_reserve + strset_bheight) / 2, strset_bwidth, strset_bheight); +} + + +int rviewStringSet::process(wxObject &obj, wxEvent &evt) +{ + if (&obj == (wxObject*)dismiss) + { + this->Close(TRUE); + return 1; + } + return 0; +} + + +int rviewStringSet::getNumItems(void) +{ + return list->Number(); +} + + +void rviewStringSet::addItem(const char *string) +{ + list->Append((char*)string); +} + + +char *rviewStringSet::getItem(int number) +{ + return list->GetString(number); +} + + +#ifdef EARLY_TEMPLATE +#undef __EXECUTABLE__ +#endif + +#endif // !defined(EARLY_TEMPLATE) || !defined(__EXECUTABLE__) + + + +#if (!defined(EARLY_TEMPLATE) || defined(__EXECUTABLE__)) + +/* + * Templates + */ + +// For placement new (DynamicStack template) +#include <new> + +/* + * Use malloc/free, placement new and explicit calls to destructors. + * Use placement new when initializing on the stack because that's + * raw memory, use assignment when initializing parameters + */ +template<class T> +DynamicStack<T>::DynamicStack(unsigned int gran) +{ + number = 0; max = 0; + if ((stack = (T*)malloc(gran * sizeof(T))) != NULL) + { + granularity = gran; + max = granularity; + memset(stack, 0, max * sizeof(T)); + } +} + + +template<class T> +DynamicStack<T>::DynamicStack(const DynamicStack<T> &src) +{ + number = 0; max = 0; + if ((stack = (T*)malloc(src.max * sizeof(T))) != NULL) + { + granularity = src.granularity; + max = src.max; + memset(stack, 0, max*sizeof(T)); + number = src.number; + + unsigned int i; + + // use loop for class safety + for (i=0; i<number; i++) + new(stack + i) T(src.stack[i]); // placement new + } +} + + +template<class T> +DynamicStack<T>::~DynamicStack(void) +{ + unsigned int i; + + for (i=0; i<number; i++) + stack[i].~T(); // direct destructor call + + if (stack != NULL) + free(stack); +} + + +template<class T> +int DynamicStack<T>::push(const T &item) +{ + if (ensureFree() != 0) return -1; + new(stack + number++) T(item); // placement new + return 0; +} + + +template<class T> +int DynamicStack<T>::pop(T &item) +{ + if (number == 0) return -1; + item = stack[--number]; // assignment + stack[number].~T(); // direct destructor call + return 0; +} + + +template<class T> +int DynamicStack<T>::peek(T &item) const +{ + if (number == 0) return -1; + item = stack[number-1]; // assignment + return 0; +} + + +template<class T> +unsigned int DynamicStack<T>::getNumber(void) const +{ + return number; +} + + +template<class T> +int DynamicStack<T>::ensureFree(void) +{ + if (number >= max) + { + if ((stack = (T*)realloc(stack, (max + granularity) * sizeof(T))) == NULL) + { + max = 0; number = 0; return -1; + } + memset(stack + max, 0, granularity * sizeof(T)); + max += granularity; + } + return 0; +} + +#endif |