/* * Copyright (c) 1987, 1988, 1989 Stanford University * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Stanford not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Stanford makes no representations about * the suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * STANFORD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ // implements MapIPaint subclasses. #include "ipaint.h" #include "istring.h" #include "listibrush.h" #include "listicolor.h" #include "listifont.h" #include "listipattern.h" #include "mapipaint.h" #include #include #include #ifdef osx #include #include #else #include #include #endif // Init creates the list's entries, initializes the number denoting // the initial entry, and ensures both list and initial are valid. void MapIPaint::Init (BaseList* list, Interactor* i, const char* menu) { DefineEntries(list, i, menu); DefineInitial(i, menu); if (list->Size() == 0) { fprintf(stderr, "No entries in %s menu, ", menu); fprintf(stderr, "creating a default entry\n"); list->Append(CreateEntry(nil)); } if (list->Index(initial-1) == nil) { fprintf(stderr, "No attribute at %s entry %d, ", menu, initial); fprintf(stderr, "setting initial attribute from first entry\n"); initial = 1; } } // DefineEntries reads predefined or user-defined attributes and // creates entries in the list from these attributes. It retrieves // each attribute using the concatenation of the menu's name and a // number which increments from 1. The first undefined or empty // attribute terminates the menu's definition. void MapIPaint::DefineEntries (BaseList* list, Interactor* i, const char* menu) { char menuproperty[20]; int num = 1; sprintf(menuproperty, "%s%d", menu, num); const char* attribute = i->GetAttribute(menuproperty); while (attribute != nil && strlen(attribute) > 0) { BaseNode* entry = CreateEntry(attribute); if (entry != nil) { list->Append(entry); } else { fprintf(stderr, "couldn't parse attribute for %s\n", menuproperty); } sprintf(menuproperty, "%s%d", menu, ++num); attribute = i->GetAttribute(menuproperty); } } // DefineInitial reads a predefined or user-defined attribute to // define the number denoting the initial entry. For example, // "idraw.initialfont: 2" sets the initial font from the second entry. void MapIPaint::DefineInitial (Interactor* i, const char* menu) { char property[20]; sprintf(property, "initial%s", menu); const char* def = i->GetAttribute(property); char* definition = strdup(def); // some sscanfs write to their format... if (sscanf(definition, "%d", &initial) != 1) { initial = 2; // default if we can't parse definition fprintf(stderr, "can't parse attribute for %s, ", property); fprintf(stderr, "value set to %d\n", initial); } delete definition; } // CreateEntry creates and returns an entry containing an attribute // defined by the given string. A subclass must implement it. BaseNode* MapIPaint::CreateEntry (const char*) { return new BaseNode; } // Skew comments/code ratio to work around cpp bug ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; // MapIBrush creates its brushes from predefined or user-defined // attributes. MapIBrush::MapIBrush (Interactor* i, const char* menu) { ibrushlist = new IBrushList; Init(ibrushlist, i, menu); } // ~MapIBrush frees storage allocated for the brushes and the list. MapIBrush::~MapIBrush () { for (IBrush* brush = First(); !AtEnd(); brush = Next()) { delete brush; } delete ibrushlist; } // Implement functions to give readonly access to the list. int MapIBrush::Size () { return ibrushlist->Size(); } bool MapIBrush::AtEnd () { return ibrushlist->AtEnd(); } IBrush* MapIBrush::First () { return ibrushlist->First()->GetBrush(); } IBrush* MapIBrush::Last () { return ibrushlist->Last()->GetBrush(); } IBrush* MapIBrush::Prev () { return ibrushlist->Prev()->GetBrush(); } IBrush* MapIBrush::Next () { return ibrushlist->Next()->GetBrush(); } IBrush* MapIBrush::GetCur () { return ibrushlist->GetCur()->GetBrush(); } IBrush* MapIBrush::Index (int index) { return ibrushlist->Index(index)->GetBrush(); } bool MapIBrush::Find (IBrush* brush) { return ibrushlist->Find(brush); } IBrush* MapIBrush::GetInitial () { // remember index = 0-base, initial = 1-base return ibrushlist->Index(initial-1)->GetBrush(); } // FindOrAppend checks if the list already has a brush with the same // attributes as the given attributes. If so, it returns the brush // already in the list; otherwise, it creates a new brush, appends it // to the list, and returns it. IBrush* MapIBrush::FindOrAppend (bool none, int p, int w, int l, int r) { IBrush* brush = nil; if (none) { for (brush = First(); !AtEnd(); brush = Next()) { if (brush->None()) { return brush; } } brush = new IBrush; } else { for (brush = First(); !AtEnd(); brush = Next()) { if (!brush->None() && brush->GetLinePattern() == p && brush->Width() == w && brush->LeftArrow() == l && brush->RightArrow() == r) { return brush; } } brush = new IBrush(p, w, l, r); } ibrushlist->Append(new IBrushNode(brush)); return brush; } // CreateEntry returns an entry containing a brush created by parsing // the given definition or an entry containing a default brush if no // definition was given. The definition usually contains two numbers // and two bools: a 16-bit hexadecimal number to define the brush's // pattern, a decimal integer to define the brush's width in pixels, // either 0 or 1 to determine whether lines start from an arrowhead, // and either 0 or 1 to determine whether lines end in an arrowhead. // One definition may contain the string "none" to define the // nonexistent brush. I found out sscanf barfs if you put 0x before // the hexadecimal number so you can't do that. BaseNode* MapIBrush::CreateEntry (const char* def) { if (def == nil) { def = "ffff 1 0 0"; } char* definition = strdup(def); // some sscanfs write to their format... BaseNode* entry = nil; int p, w; bool l, r; bool none = (definition[0] == 'n' || definition[0] == 'N'); if (none) { entry = new IBrushNode(new IBrush); } else if (sscanf(definition, "%x %d %d %d", &p, &w, &l, &r) == 4) { entry = new IBrushNode(new IBrush(p, w, l, r)); } delete definition; return entry; } // MapIColor creates its colors from predefined or user-defined // attributes. MapIColor::MapIColor (Interactor* i, const char* menu) { icolorlist = new IColorList; Init(icolorlist, i, menu); } // ~MapIColor frees storage allocated for the colors and the list. MapIColor::~MapIColor () { for (IColor* color = First(); !AtEnd(); color = Next()) { delete color; } delete icolorlist; } // Implement functions to give readonly access to the list. int MapIColor::Size () { return icolorlist->Size(); } bool MapIColor::AtEnd () { return icolorlist->AtEnd(); } IColor* MapIColor::First () { return icolorlist->First()->GetColor(); } IColor* MapIColor::Last () { return icolorlist->Last()->GetColor(); } IColor* MapIColor::Prev () { return icolorlist->Prev()->GetColor(); } IColor* MapIColor::Next () { return icolorlist->Next()->GetColor(); } IColor* MapIColor::GetCur () { return icolorlist->GetCur()->GetColor(); } IColor* MapIColor::Index (int index) { return icolorlist->Index(index)->GetColor(); } bool MapIColor::Find (IColor* color) { return icolorlist->Find(color); } IColor* MapIColor::GetInitial () { // remember index = 0-base, initial = 1-base return icolorlist->Index(initial-1)->GetColor(); } // FindOrAppend checks if the list already has a color with the same // name as the given name. If so, it returns the color already in the // list; otherwise, it creates a new color, appends it to the list, // and returns it. IColor* MapIColor::FindOrAppend (const char* name, int r, int g, int b) { IColor* color = nil; for (color = First(); !AtEnd(); color = Next()) { if (strcmp(color->GetName(), name) == 0) { return color; } } color = new IColor(name); if (!color->Valid()) { delete color; color = new IColor(r, g, b, name); if (!color->Valid()) { fprintf(stderr, "invalid color name %s ", name); fprintf(stderr, "and intensities %d %d %d, ", r, g, b); fprintf(stderr, "substituting black\n"); delete color; color = new IColor(black, name); } } icolorlist->Append(new IColorNode(color)); return color; } // CreateEntry returns an entry containing a color created by using // the given name or an entry containing a default color if no name // was given. BaseNode* MapIColor::CreateEntry (const char* def) { if (def == nil) { def = "Black"; } char* definition = strdup(def); // some sscanfs write to their format... IColor* color = nil; char name[100]; int r = 0, g = 0, b = 0; if (sscanf(definition, "%s %d %d %d", name, &r, &g, &b) == 4) { color = new IColor(r, g, b, name); if (!color->Valid()) { fprintf(stderr, "invalid intensities %d %d %d, ", r, g, b); fprintf(stderr, "using color name %s\n", name); delete color; color = new IColor(name); if (!color->Valid()) { fprintf(stderr, "invalid color name %s, ", name); fprintf(stderr, "substituting black\n"); delete color; color = new IColor(black, name); } } } else if (sscanf(definition, "%s", name) == 1) { color = new IColor(name); if (!color->Valid()) { fprintf(stderr, "invalid color name %s, ", name); fprintf(stderr, "substituting black\n"); delete color; color = new IColor(black, name); } } delete definition; BaseNode* entry = nil; if (color != nil) { entry = new IColorNode(color); } return entry; } // MapIFont creates its fonts from predefined or user-defined // attributes. MapIFont::MapIFont (Interactor* i, const char* menu) { ifontlist = new IFontList; Init(ifontlist, i, menu); } // ~MapIFont frees storage allocated for the fonts and the list. MapIFont::~MapIFont () { for (IFont* font = First(); !AtEnd(); font = Next()) { delete font; } delete ifontlist; } // Implement functions to give readonly access to the list. int MapIFont::Size () { return ifontlist->Size(); } bool MapIFont::AtEnd () { return ifontlist->AtEnd(); } IFont* MapIFont::First () { return ifontlist->First()->GetFont(); } IFont* MapIFont::Last () { return ifontlist->Last()->GetFont(); } IFont* MapIFont::Prev () { return ifontlist->Prev()->GetFont(); } IFont* MapIFont::Next () { return ifontlist->Next()->GetFont(); } IFont* MapIFont::GetCur () { return ifontlist->GetCur()->GetFont(); } IFont* MapIFont::Index (int index) { return ifontlist->Index(index)->GetFont(); } bool MapIFont::Find (IFont* font) { return ifontlist->Find(font); } IFont* MapIFont::GetInitial () { // remember index = 0-base, initial = 1-base return ifontlist->Index(initial-1)->GetFont(); } // FindOrAppend checks if the list already has a font with the same // attributes as the given attributes (except for the name because it // depends on the window system used). If so, it returns the font // already in the list; otherwise, it creates a new font, appends it // to the list, and returns it. IFont* MapIFont::FindOrAppend (const char* name, const char* pf, const char* ps) { IFont* font = nil; for (font = First(); !AtEnd(); font = Next()) { if (strcmp(font->GetPrintFont(), pf) == 0 && strcmp(font->GetPrintSize(), ps) == 0) { return font; } } font = new IFont(name, pf, ps); if (!font->Valid()) { fprintf(stderr, "invalid font name %s, ", name); fprintf(stderr, "substituting stdfont\n"); delete font; font = new IFont("stdfont", pf, ps); } ifontlist->Append(new IFontNode(font)); return font; } // CreateEntry returns an entry containing a font created by parsing // the given definition or an entry containing a default font if no // definition was given. The definition must contain three strings // separated by whitespace to define a font. The first string defines // the font's name, the second string the corresponding print font, // and the third string the print size. BaseNode* MapIFont::CreateEntry (const char* def) { if (def == nil) { def = "stdfont Courier 10"; } char* definition = strdup(def); // some sscanfs write to their format... BaseNode* entry = nil; char name[100]; char pf[100]; char ps[10]; if (sscanf(definition, "%s %s %s", name, pf, ps) == 3) { IFont* font = new IFont(name, pf, ps); if (!font->Valid()) { fprintf(stderr, "invalid font name %s, ", name); fprintf(stderr, "substituting stdfont\n"); delete font; font = new IFont("stdfont", pf, ps); } entry = new IFontNode(font); } delete definition; return entry; } // MapIPattern creates its patterns from predefined or user-defined // attributes. MapIPattern::MapIPattern (Interactor* i, const char* menu) { ipatternlist = new IPatternList; Init(ipatternlist, i, menu); } // ~MapIPattern frees storage allocated for the patterns and the list. MapIPattern::~MapIPattern () { for (IPattern* pattern = First(); !AtEnd(); pattern = Next()) { delete pattern; } delete ipatternlist; } // Implement functions to give readonly access to the list. int MapIPattern::Size () { return ipatternlist->Size(); } bool MapIPattern::AtEnd () { return ipatternlist->AtEnd(); } IPattern* MapIPattern::First () { return ipatternlist->First()->GetPattern(); } IPattern* MapIPattern::Last () { return ipatternlist->Last()->GetPattern(); } IPattern* MapIPattern::Prev () { return ipatternlist->Prev()->GetPattern(); } IPattern* MapIPattern::Next () { return ipatternlist->Next()->GetPattern(); } IPattern* MapIPattern::GetCur () { return ipatternlist->GetCur()->GetPattern(); } IPattern* MapIPattern::Index (int index) { return ipatternlist->Index(index)->GetPattern(); } bool MapIPattern::Find (IPattern* pattern) { return ipatternlist->Find(pattern); } IPattern* MapIPattern::GetInitial () { // remember index = 0-base, initial = 1-base return ipatternlist->Index(initial-1)->GetPattern(); } // Skew comments/code ratio to work around cpp bug ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; // FindOrAppend checks if the list already has a pattern with the same // attributes as the given attributes. If so, it returns the pattern // already in the list; otherwise, it creates a new pattern, appends // it to the list, and returns it. IPattern* MapIPattern::FindOrAppend (bool none, float graylevel, int data[patternHeight], int size) { IPattern* pattern = nil; if (none) { for (pattern = First(); !AtEnd(); pattern = Next()) { if (pattern->None()) { return pattern; } } pattern = new IPattern; } else if (graylevel != -1) { for (pattern = First(); !AtEnd(); pattern = Next()) { if (pattern->GetGrayLevel() == graylevel) { return pattern; } } int shade = CalcBitmap(graylevel); pattern = new IPattern(shade, graylevel); } else { ExpandToFullSize(data, size); for (pattern = First(); !AtEnd(); pattern = Next()) { if (pattern->GetSize() != 0) { const int* cmpdata = pattern->GetData(); if (bcmp(data, cmpdata, patternHeight * sizeof(int)) == 0) { return pattern; } } } pattern = new IPattern(data, size); } ipatternlist->Append(new IPatternNode(pattern)); return pattern; } // CreateEntry returns an entry containing a pattern created by // parsing the given definition or an entry containing a default // pattern if no definition was given. The definition usually // contains either one or patternHeight 16-bit hexadecimal numbers to // define either a 4x4 pattern or a patternWidth x patternHeight // pattern. One definition may contain the string "none" to define // the nonexistent pattern. I found out sscanf barfs if you put 0x // before the hexadecimal number so you can't do that. BaseNode* MapIPattern::CreateEntry (const char* def) { if (def == nil) { def = "0.0"; } char* definition = strdup(def); // some sscanfs write to their format... BaseNode* entry = nil; bool none = (definition[0] == 'n' || definition[0] == 'N'); if (none) { entry = new IPatternNode(new IPattern); } else { if (strchr(definition, '.') != nil) { float graylevel; if (sscanf(definition, "%f", &graylevel) == 1) { int shade = CalcBitmap(graylevel); entry = new IPatternNode(new IPattern(shade, graylevel)); } } else { int i; #ifdef osx // class my_streambuf : streambuf { // public: // my_streambuf() : streambuf() {} // virtual streambuf* setbuf(char* p, int len) { // super->setbuf(p, len); // } // }; // my_streambuf sbuf; // istream from(sbuf.setbuf(definition, strlen(definition) + 1)); strstreambuf sbuf(definition, strlen(definition) + 1); istream from(&sbuf); #else string_parsebuf sbuf(definition, strlen(definition) + 1); istream from(&sbuf); #endif // istream from(strlen(definition) + 1, definition); char buffer[80]; int data[patternHeight]; for (i = 0; from >> buffer && i < patternHeight; i++) { if (sscanf(buffer, "%x", &data[i]) != 1) { break; } } if (i == 1 || i == 8 || i == patternHeight) { ExpandToFullSize(data, i); entry = new IPatternNode(new IPattern(data, i)); } } } delete definition; return entry; } // CalcBitmap calculates a 4x4 bitmap to represent a shade having the // given gray level. int MapIPattern::CalcBitmap (float graylevel) { static const int SHADES = 17; static int shades[SHADES] = { 0xffff, 0xefff, 0xefbf, 0xafbf, 0xafaf, 0xadaf, 0xada7, 0xa5a7, 0xa5a5, 0x85a5, 0x8525, 0x0525, 0x0505, 0x0405, 0x0401, 0x0001, 0x0000 }; return shades[(int)round(graylevel * (SHADES - 1))]; } // ExpandToFullSize expands the bitmap from 4x4 or 8x8 to 16x16. void MapIPattern::ExpandToFullSize (int data[patternHeight], int size) { if (size == 1) { int seed = data[0]; for (int i = 0; i < 4; i++) { data[i] = (seed & 0xf000) >> 12; data[i] |= data[i] << 4; data[i] |= data[i] << 8; data[i+4] = data[i]; data[i+8] = data[i]; data[i+12] = data[i]; seed <<= 4; } } else if (size == 8) { for (int i = 0; i < 8; i++) { data[i] &= 0xff; data[i] |= data[i] << 8; data[i+8] = data[i]; } } else if (size == patternHeight) { const unsigned int patternWidthMask = ~(~0 << patternWidth); for (int i = 0; i < patternHeight; i++) { data[i] &= patternWidthMask; } } else { fprintf(stderr, "invalid size passed to ExpandToFullSize\n"); } }