/************************************************************************* * sbItem.cpp - common item * * author: Konstantin Maslyuk "Kalemas" mailto:kalemas@mail.ru * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation version 2. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. ************************************************************************/ #include "sbTheme.h" #include "sbItem.h" #pragma warning(disable : 4018) // signed/unsigned mismatch sbItem::sbItem ( sbItem::TYPE _type_, const TCHAR *_text_, int _width_, long _index_) : type ( _type_ ) , width ( _width_ ) , index ( _index_ ) , next (NULL) , prev (NULL) , left (NULL) , right (NULL) { style = sbTheme::STYLE::TEXT; highlighted = false; height = Theme->styles[style].size + Theme->itemMargins.top + Theme->itemMargins.bottom; expanding = 0; processed = false; setText ( _text_ ); } sbItem::~sbItem() { if (next != NULL) next->prev = prev; if (prev != NULL) prev->next = next; if (right != NULL) delete right; } void sbItem::attach (sbItem * item, sbDirection to) { sbAssert (this == item); if (to == DIRECTION_NEXT) { // check space if (next != NULL) { item->prev = this; item->next = this->next; next->prev = item; next = item; } else { next = item; item->prev = this; } } else if (to == DIRECTION_PREV) { if (prev != NULL) { item->next = this; item->prev = this->prev; prev->next = item; prev = item; } else { prev = item; item->next = this; } } else if (to == DIRECTION_LEFT) { sbAssert(left != NULL); left = item; item->right = this; } else if (to == DIRECTION_RIGHT) { sbAssert(right != NULL); // place next if line filled right = item; item->left = this; if (height < item->height) { sbItem *parse = this; while(parse != NULL) { parse->height = item->height; parse = parse->left; } } if (height > item->height) { item->height = height; } } else { sbAssert(true); } } void sbItem::setText ( const TCHAR * newText ) { int lineWidth = 0 , textWidth = width - Theme->itemMargins.left - Theme->itemMargins.right; int length = _tcslen( newText ); int minHeight = 0; bool skip = false; bool alignCenter = false, alignRight = false; // align works before next formating tag, bool manualLines = false; sbFont font; LINE currentLine; std::vector underline; sbAssert( newText == NULL ); if (!lines.empty()) lines.clear(); text = newText; switch (type) { case DO_SEARCH_RANGE_START: case DO_SEARCH_RANGE_END: minHeight = Theme->size/6; manualLines = true; break; case LOOK_BANNER: case TYPE_HISTORY: manualLines = true; break; case LOOK_SEPARATOR: style = sbTheme::STYLE::DESCRIPTION; alignCenter = true; break; case TYPE_TEXT_FIELD: style = sbTheme::STYLE::BUTTON; minHeight = Theme->styles[style].size + Theme->itemMargins.top + Theme->itemMargins.bottom; break; case TYPE_BUTTON_SELECTION_MODULE: case TYPE_BUTTON_SELECTION_BOOK: case TYPE_BUTTON_SELECTION_CHAPTER: case TYPE_SWITCH_MODULE_OPTION: case TYPE_START_SEARCH: minHeight = Theme->size/6; break; case TYPE_BOOKMARK_VERSE_NUMBER: textWidth = width; break; case TYPE_OPEN_MODULE_INSTALLER: minHeight = Theme->size/4; break; } font = Theme->styles[style].font; currentLine.style = style; if (manualLines) textWidth = 1000000; // TODO use 'const TCHAR *' instead 'i', but for LINE::pos i use displacement // parse text for(int i = 0; i 0) { // finish current lines.push_back(currentLine); currentLine.count = currentLine.width = 0; } // and begin new skip = true; if (_tcsncmp(newText+i+1,L"verse ",6) == 0) // { i += 5; int state = 0; for (int ii = i+1; ii < length && newText[ii] != L'>'; i = ii++) { if (state == 3 && newText[ii] != L'\"') { if (currentLine.count == 0) { currentLine.pos = ii; currentLine.type = LINE::TYPE_VERSE_NUMBER; currentLine.style = sbTheme::STYLE::STANZA; } sbPoint sz = sbGetTextExtent(Theme->styles[currentLine.style].font,c,1); if (sz.y > currentLine.height) currentLine.height = (unsigned short) sz.y; currentLine.width += (unsigned short) sz.x; //lineWidth += (unsigned short) sz.x; currentLine.count++; } else if (_tcsncmp(newText+ii,L"osisID=\"",8) == 0) { ii += 8; state = 1; } else if ((newText[ii] == L'.' || newText[ii] == L'\"') && state > 0) { state++; } } lines.push_back(currentLine); currentLine = LINE(); currentLine.style = style; } else if (_tcsncmp(newText+i+1,L"transChange type=\"added\"",24) == 0) { i += 23; currentLine.style = sbTheme::STYLE::TEXT_ITALIC; } else if (_tcsncmp(newText+i+1,L"/transChange",12) == 0) { i += 11; currentLine.style = style; } else if (_tcsncmp(newText+i+1,L"w ",2) == 0) { i += 2; if ( underline.size() == 0 && lineWidth > 0) { underline.push_back(LINE()); underline.back().width = lineWidth; } else { int width = 0; for (int i = 0; i < underline.size(); i++) width += underline[i].width; if (width < lineWidth) { underline.push_back(LINE()); underline.back().width = lineWidth - width; } } bool lemma = false; bool morph = false; for (int ii = i+1; ii < length && newText[ii] != L'>'; i = ii++) { if (_tcsncmp(newText+ii,L"lemma=\"",7) == 0) { ii += 7; lemma = true; } else if (_tcsncmp(newText+ii,L"morph=\"",7) == 0) { ii += 7; morph = true; } if (_tcsncmp(newText+ii,L"strong:",7) == 0) { sbAssert(!lemma); ii += 7; for (int e = ii;;e++) { if (newText[e] == L' ' || newText[e] == L'\"') { underline.push_back(LINE()); underline.back().pos = ii; underline.back().type = LINE::TYPE_STRONG; underline.back().count = e-ii; underline.back().height = Theme->styles[sbTheme::STYLE::TEXT_UNDER].size; underline.back().style = sbTheme::STYLE::TEXT_UNDER; underline.back().width = sbGetTextExtent(Theme->styles[sbTheme::STYLE::TEXT_UNDER].font,newText+ii,e-ii).x; underline.back().width += sbGetTextExtent(Theme->styles[sbTheme::STYLE::TEXT_UNDER].font,L" ",1).x; ii = e; break; } } } else if (_tcsncmp(newText+ii,L"strongMorph:",12) == 0) { sbAssert(!morph); ii += 12; for (int e = ii;;e++) { if (newText[e] == L' ' || newText[e] == L'\"') { underline.push_back(LINE()); underline.back().pos = ii; underline.back().type = LINE::TYPE_MORPH; underline.back().count = e-ii; underline.back().height = Theme->styles[sbTheme::STYLE::TEXT_UNDER].size; underline.back().style = sbTheme::STYLE::TEXT_UNDER; underline.back().width = sbGetTextExtent(Theme->styles[sbTheme::STYLE::TEXT_UNDER].font,newText+ii,e-ii).x; underline.back().width += sbGetTextExtent(Theme->styles[sbTheme::STYLE::TEXT_UNDER].font,L" ",1).x; ii = e; break; } } } else if (_tcsncmp(newText+ii,L"robinson:",9) == 0) { sbAssert(!morph); ii += 9; for (int e = ii;;e++) { if (newText[e] == L' ' || newText[e] == L'\"') { underline.push_back(LINE()); underline.back().pos = ii; underline.back().type = LINE::TYPE_MORPH; underline.back().count = e-ii; underline.back().height = Theme->styles[sbTheme::STYLE::TEXT_UNDER].size; underline.back().style = sbTheme::STYLE::TEXT_UNDER; underline.back().width = sbGetTextExtent(Theme->styles[sbTheme::STYLE::TEXT_UNDER].font,newText+ii,e-ii).x; underline.back().width += sbGetTextExtent(Theme->styles[sbTheme::STYLE::TEXT_UNDER].font,L" ",1).x; ii = e; break; } } } } } else if (_tcsncmp(newText+i+1,L"s ",2) == 0) { i += 1; for (int ii = i+1; ii < length && newText[ii] != L'>'; i = ii++) { if (_tcsncmp(newText+ii,L" t=",3) == 0) { if (_tcsncmp(newText+ii+3,L"\"6\"",3) == 0) currentLine.style = sbTheme::STYLE::DESCRIPTION; else if (_tcsncmp(newText+ii+3,L"\"5\"",3) == 0) currentLine.style = sbTheme::STYLE::CAPTION; else if (_tcsncmp(newText+ii+3,L"\"4\"",3) == 0) currentLine.style = sbTheme::STYLE::STANZA; else if (_tcsncmp(newText+ii+3,L"\"3\"",3) == 0) currentLine.style = sbTheme::STYLE::BUTTON; else if (_tcsncmp(newText+ii+3,L"\"1\"",3) == 0) currentLine.style = sbTheme::STYLE::TEXT_ITALIC; else sbAssert (true); ii += 5; } else if (_tcsncmp(newText+ii,L" a=",3) == 0) { if (_tcsncmp(newText+ii+3,L"\"c\"",3) == 0) { alignCenter = true; alignRight = false; } else if (_tcsncmp(newText+ii+3,L"\"r\"",3) == 0) { alignCenter = false; alignRight = true; } else if (_tcsncmp(newText+ii+3,L"\"l\"",3) == 0) { alignCenter = false; alignRight = false; } else sbAssert (true); ii += 5; } } } else if (_tcsncmp(newText+i+1,L"/s>",3) == 0) { i += 2; currentLine.style = style; currentLine.type = LINE::TYPE_NONE; } } else if (*c == L'>') { sbAssert (!skip); skip = false; } else if (*c == L'\n') { endLine = true; } else if (*c == L'\t' && currentLine.count == 0) // fix \t\tstyles[currentLine.style].font,c,1); // add character if (currentLine.count == 0) currentLine.pos = i; if (sz.y > currentLine.height) currentLine.height = (unsigned short) sz.y; currentLine.width += (unsigned short) sz.x; lineWidth += (unsigned short) sz.x; currentLine.count++; // check line break at space if (*c == L' ' && lineWidth + (textWidth/4) > textWidth) { // forward processing for (int ii = 1, skipped = 0, width = 0; i+ii < length && ii < 16; ii++) { const TCHAR * nc = newText+i+ii+skipped; if (*nc == L'<') { for (;*(newText+i+ii+skipped) != L'>'; skipped++) ; continue; } if (_tcschr(L" \n",*nc) != NULL) break; if (lineWidth + width >= textWidth) { endLine = true; break; } width += sbGetTextExtent(Theme->styles[currentLine.style].font,nc,1).x; } } if (!endLine) { // ask next character const TCHAR * nc = c+1; while (*nc == L'<') { while (*nc != L'>' && *nc != L'\0') nc++; nc++; } if (lineWidth >= textWidth && !manualLines && _tcschr(L"., !?;:»)",*nc) == NULL) endLine = true; } } // now process line end only here if (endLine) { if (alignRight || alignCenter) { LINE l; l.width = max(0,(textWidth-lineWidth)/(alignRight ? 1 : 2)); l.type = LINE::TYPE_SPACE; if (l.width > 0) { std::vector::reverse_iterator it = lines.rbegin(); while (it != lines.rend() && !(*it).ending) ++it; lines.insert(it.base(),l); // TODO work with underline, if necessary } } // TODO compensate at space a very short lines somehow if (currentLine.count > 0) { lines.push_back(currentLine); lines.back().ending = true; } else if (lines.size() > 0) { lines.back().ending = true; } if (underline.size()) { for (int i = 0; i < underline.size(); i++) lines.push_back(underline[i]); underline.clear(); lines.back().ending = true; } lineWidth = 0; currentLine.count = currentLine.width = currentLine.height = 0; } } // calculate height if (type == sbItem::TYPE_HISTORY) { height = Theme->itemMargins.top + Theme->itemMargins.bottom; height += max (Theme->styles[sbTheme::STYLE::CAPTION].size,Theme->styles[sbTheme::STYLE::TEXT].size+Theme->styles[sbTheme::STYLE::DESCRIPTION].size); } else if (type == LOOK_BANNER && lines.size() == 2) { height = (Theme->BannerBorderSize * 2) + Theme->styles[lines[0].style].size + Theme->styles[lines[1].style].size; } else if (type == DO_SEARCH_RANGE_START || type == DO_SEARCH_RANGE_END) { height = Theme->itemMargins.top + Theme->itemMargins.bottom + lines[0].height; width = Theme->itemMargins.left + Theme->itemMargins.left + lines[0].width; } else { height = Theme->itemMargins.top + Theme->itemMargins.bottom; //if (type == TYPE_TEXT_FIELD && lines.size() == 0) height += Theme->styles[style].size; for (int i=0; idrawElement( surface, &rect, sbTheme::ELEMENT::BANNER ); sbSelectColor(surface, Theme->fadeColor(Theme->elements[sbTheme::ELEMENT::BANNER].textColor,rect.left,rect.top)); sbSelectFont (surface, Theme->styles[lines[0].style].font); sbDrawText (surface, rect.left + Theme->BannerBorderSize, rect.top + Theme->BannerBorderSize, text.c_str() + lines[0].pos, lines[0].count); sbSelectFont (surface, Theme->styles[lines[1].style].font); sbDrawText (surface, rect.left + Theme->BannerBorderSize, rect.top + Theme->BannerBorderSize + lines[0].height, text.c_str() + lines[1].pos, lines[1].count); } else if (type == TYPE_HISTORY) { sbAssert (lines.size() != 3); Theme->drawElement( surface, &rect, sbTheme::ELEMENT::ITEM ); sbSelectFont (surface, Theme->styles[lines[0].style].font); sbSelectColor(surface, Theme->fadeColor(Theme->elements[sbTheme::ELEMENT::ITEM].textColor,rect.left,rect.top)); sbDrawText (surface, rect.left + Theme->itemMargins.left, rect.top + Theme->itemMargins.top, text.c_str() + lines[0].pos, lines[0].count); sbSelectFont (surface, Theme->styles[lines[1].style].font); sbDrawText (surface, rect.right - Theme->itemMargins.right - lines[1].width, rect.top + Theme->itemMargins.top, text.c_str() + lines[1].pos, lines[1].count); sbSelectFont (surface, Theme->styles[lines[2].style].font); sbDrawText (surface, rect.left + Theme->itemMargins.left, rect.top + Theme->itemMargins.top + lines[0].height, text.c_str() + lines[2].pos, lines[2].count); } else if (type > LOOK_MENU && type < LOOK_MENU_COUNT) { Theme->drawElement( surface, &rect, sbTheme::ELEMENT::ANOTHER_MENU ); for (int i = 0, lx = 0, ly = rect.top + Theme->itemMargins.top; i < lines.size(); i++) { int x = lx + Theme->itemMargins.left + rect.left; int y = ly; lines[i].ending ? lx = 0, ly += lines[i].height : lx += lines[i].width; if (lines[i].count == 0) continue; sbSelectFont(surface, Theme->styles[lines[i].style].font); sbSelectColor (surface, Theme->fadeColor(Theme->elements[sbTheme::ELEMENT::ANOTHER_MENU].textColor,x,y)); sbDrawText (surface, x, y, text.c_str() + lines[i].pos, lines[i].count); } } else if (type > LOOK_BUTTON && type < LOOK_BUTTON_COUNT) { Theme->drawElement( surface, &rect, sbTheme::ELEMENT::PANEL ); for (int i = 0, lx = 0, ly = rect.top + Theme->itemMargins.top; i < lines.size(); i++) { int x = lx + Theme->itemMargins.left + rect.left; int y = ly; lines[i].ending ? lx = 0, ly += lines[i].height : lx += lines[i].width; if (lines[i].count == 0) continue; sbSelectFont(surface, Theme->styles[lines[i].style].font); sbSelectColor (surface, Theme->fadeColor(Theme->elements[sbTheme::ELEMENT::PANEL].textColor,x,y)); sbDrawText (surface, x, y, text.c_str() + lines[i].pos, lines[i].count); } } else if (type == LOOK_SEPARATOR) { Theme->drawElement( surface, &rect, sbTheme::ELEMENT::ITEM_SEPARATOR ); for (int i = 0, lx = 0, ly = rect.top + Theme->itemMargins.top; i < lines.size(); i++) { int x = lx + Theme->itemMargins.left + rect.left; int y = ly; lines[i].ending ? lx = 0, ly += lines[i].height : lx += lines[i].width; if (lines[i].count == 0) continue; sbSelectFont(surface, Theme->styles[lines[i].style].font); sbSelectColor (surface, Theme->fadeColor(Theme->elements[sbTheme::ELEMENT::ITEM_SEPARATOR].textColor,x,y)); sbDrawText (surface, x, y, text.c_str() + lines[i].pos, lines[i].count); } } else if (type > LOOK_PANEL && type < LOOK_PANEL_COUNT) { // TODO multiline panel Theme->drawElement( surface, &rect, sbTheme::ELEMENT::PANEL ); for (int i = 0, lx = 0, ly = rect.top + Theme->itemMargins.top; i < lines.size(); i++) { int x = lx + Theme->itemMargins.left + rect.left; int y = ly; if (lines[i].ending) { lx = 0; ly += lines[i].height; } else lx += lines[i].width; if (lines[i].count == 0) continue; sbSelectFont(surface, Theme->styles[lines[i].style].font); sbSelectColor (surface, Theme->fadeColor(Theme->elements[sbTheme::ELEMENT::PANEL].textColor,x,y)); sbDrawText (surface, x, y, text.c_str() + lines[i].pos, lines[i].count); } } else { Theme->drawElement( surface, &rect, highlighted ? sbTheme::ELEMENT::ITEM_HIGHLIGHTED : sbTheme::ELEMENT::ITEM ); if ( lines.size() > 0 ) { int i = 0; if ( lines[0].type == LINE::TYPE_VERSE_NUMBER ) { sbPoint pos (rect.left+((Theme->itemMargins.left-lines[0].width)/2),rect.top); sbSelectFont(surface, Theme->styles[lines[0].style].font); sbSelectColor(surface, Theme->fadeColor(Theme->ItemSubTextColor, pos.x, pos.y)); sbDrawText(surface, pos.x, pos.y, text.c_str()+lines[0].pos, lines[0].count); i++; } for (int lx = 0, ly = rect.top + Theme->itemMargins.top; i < lines.size(); i++) { int x = lx + Theme->itemMargins.left + rect.left; int y = ly; if (lines[i].ending) { lx = 0; ly += lines[i].height; } else lx += lines[i].width; if (lines[i].count == 0) continue; if (y + Theme->styles[lines[i].style].size >= rect.top + height) break; // item expansion if (y < -Theme->styles[lines[i].style].size) continue; // clip top if (y >= rect.bottom) break; // clip bottom sbSelectFont(surface, Theme->styles[lines[i].style].font); switch (lines[i].type) { case sbItem::LINE::TYPE_NONE: sbSelectColor(surface, Theme->fadeColor(Theme->elements[sbTheme::ELEMENT::ITEM].textColor,x,y)); break; case sbItem::LINE::TYPE_STRONG: sbSelectColor(surface, Theme->fadeColor(Theme->ItemStrongColor,x,y)); break; case sbItem::LINE::TYPE_MORPH: sbSelectColor(surface, Theme->fadeColor(Theme->ItemMorphColor,x,y)); break; case sbItem::LINE::TYPE_REDWORDS: sbSelectColor(surface, Theme->fadeColor(Theme->ItemRedColor,x,y)); break; } sbDrawText (surface, x, y, text.c_str() + lines[i].pos, lines[i].count); } } } } sbItem * sbItem::append ( sbItem::TYPE type, const TCHAR * text, int width, long index ) { sbItem * n = create (type,text,width,index); if (this != NULL) { sbItem * l = getItem(DIRECTION_NEXT,true); l->attach(n,DIRECTION_NEXT); } return n; } sbItem * sbItem::getItem ( TYPE type ) { sbItem * w = this; while (w->prev != NULL) w = w->prev; while (w->type != type && w->next != NULL) w = w->next; if (w->type == type) return w; else return NULL; } sbItem * sbItem::getItem( sbDirection direction, bool last /*= false */ ) { sbItem * i = this; switch (direction) { case DIRECTION_NEXT: if (last) { while ( i->left != NULL ) i = i->left; while ( i->next != NULL ) i = i->next; while ( i->right != NULL ) i = i->right; return i; } else { int c = 0; for ( ; i->left != NULL; c++, i = i->left ) ; if ((i = i->next) != NULL) for ( ; c > 0 && i != NULL; c--, i = i->right ) ; return i; } default: sbAssert(true); } return i; } sbItem * sbItem::create( TYPE type, const TCHAR * text, int width, long index /*= -1 */ ) { return new sbItem (type,text,width,index); }