/************************************************************************* * sbCore.cpp - application core * * 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 "sbCore.h" #include #include #include #include #include #pragma warning(disable : 4018) // signed/unsigned mismatch #pragma warning(disable : 4244) // Globals sbCore * Core; sbList * sbCore::backgroundList; sbCore::sbCore () { sbMessage ("Launch SlideBible Core.\n"); sbAssert ( Core != NULL ); // only one instance of sbCore allowed Core = this; time(&systemStarted); // init data currentList = sbList::TYPE_HOME; redrawTimerOn = false; requestedRedraw = false; listShift = 0; showBanner = false; backgroundThread = NULL; options = NULL; swordMgr = NULL; bScrollTimer = false; swordMgr = NULL; installManager = NULL; currentModule = NULL; changedGlobalOptions = false; waitMode = false; // load options options = new sword::SWConfig(".\\options.conf"); sbAssert ( options == NULL ); options->Load(); sword::SWLog::getSystemLog()->setLogLevel(atoi((*options)["Sword"].getWithDefault("DebugLevel", "4"))); cineticDamping = atof((*options)["Ui"].getWithDefault("CineticDamping", "0.95")); cineticFactor = atof((*options)["Ui"].getWithDefault("CineticFactor", "0.5")); refreshRate = 1000/atoi((*options)["Ui"].getWithDefault("RefreshRate", "30")); scrollStep = atoi((*options)["Ui"].getWithDefault("scrollStep", "1")); versesMax = atoi((*options)["Ui"].getWithDefault("versesMax", "500")); versesOptimal = atoi((*options)["Ui"].getWithDefault("versesOptimal", "50")); showFps = atoi((*options)["Ui"].getWithDefault("showFps", "0")) == 0 ? false : true; stretchSurface = atoi((*options)["Ui"].getWithDefault("stretchSurface", "0")) == 0 ? false : true; stretchVertical = atof((*options)["Ui"].getWithDefault("stretchVertical", "1.0")); stretchHorizontal = atof((*options)["Ui"].getWithDefault("stretchHorizontal", "1.0")); // init surfaces sbGetScreenRect (rClient); if (stretchSurface) { rClient.bottom = rClient.height() * stretchVertical + rClient.top; rClient.right = rClient.width() * stretchHorizontal + rClient.left; } Theme = new sbTheme (rClient); drawSurface = sbSurfaceCreate (rClient.width(), rClient.height()); sbUpdateScreen(); // user locale sword::LocaleMgr::getSystemLocaleMgr()->setDefaultLocaleName((*options)["General"].getWithDefault("Locale", sbGetLocale())); initSword(); features.load(); sbSetTimer( TIMER_SAVE_CONFIG, 5*60*1000 ); // create ui switchList ( sbList::TYPE_HOME ); sbMessage ("Finish Launch SlideBible Core.\n"); } sbCore::~sbCore() { threadDestroy(); // save configs onTimer ( TIMER_SAVE_CONFIG ); // remove views for (std::map::iterator it=lists.begin(); it != lists.end(); it++) { while (it->second->getPrev() != NULL) delete it->second->getPrev(); while (it->second->getNext() != NULL) delete it->second->getNext(); delete it->second; } // clear sword if (swordMgr != NULL) delete swordMgr; if (installManager != NULL) delete installManager; if (options != NULL) delete options; sbSurfaceDestroy(drawSurface); for (int i=0; i < strings.size(); i++) free((char*)strings[i]); delete Theme; sbMessage ("Shutdown SlideBible Core.\n"); } void sbCore::onPaint ( sbSurface hdc ) { long startTicks = sbGetTickCount(); if (lists[currentList] != NULL) { if (listShift == 0) { lists[currentList]->render(drawSurface,showBanner); } else { lists[currentList]->render(drawSurface,true,-listShift); if (listShift > 0) lists[currentList]->getNext()->render(drawSurface,true,-listShift+rClient.width()); else lists[currentList]->getPrev()->render(drawSurface,true,-listShift-rClient.width()); } for (std::vector::const_iterator it=lists[currentList]->getControls()->begin(); it != lists[currentList]->getControls()->end(); it++) (*it)->onPaint(drawSurface); if (lists[currentList]->controlSelected >= 0) { Theme->drawFrame(drawSurface,&(lists[currentList]->getControls()->at(lists[currentList]->controlSelected))->getRect()); } if (lists[currentList]->needScroll != 0) { if (!bScrollTimer) { bScrollTimer = true; sbSetTimer(TIMER_SCROLL, refreshRate); } if (lists[currentList]->needScroll < 0) { cineticPower--; if (cineticPower < 0.11f && cineticPower > -0.11f) //fix timer stopping cineticPower = -0.11f; cineticPower = max(cineticPower, lists[currentList]->needScroll*0.11f); } else { cineticPower++; if (cineticPower < 0.11f && cineticPower > -0.11f) //fix timer stopping cineticPower = 0.11f; cineticPower = min(cineticPower, lists[currentList]->needScroll*0.11f); } } } else { Theme->drawElement(drawSurface, &rClient, sbTheme::ELEMENT::BACKGROUND); } if (input.tapCounter > 0) { sbRect rect (input.start.x-(input.tapCounter*4), input.start.y-(input.tapCounter*4), input.start.x+(input.tapCounter*4), input.start.y+(input.tapCounter*4)); Theme->drawElement(drawSurface, &rect, sbTheme::ELEMENT::TAPPED); } if (showFps) { TCHAR text [16]; long delta = sbGetTickCount() - startTicks; static long lastDelta = delta; lastDelta = max(lastDelta-1,max(delta,1)); _sntprintf(text,16,L"%i fps",1000/lastDelta); sbSelectFont(drawSurface,Theme->styles[sbTheme::STYLE::BUTTON_SMALL].font); sbSelectColor(drawSurface,Theme->ItemSubTextColor); sbDrawText(drawSurface, 4, 3, text, _tcslen(text)); } if (waitMode) { sbPoint s = sbGetTextExtent(Theme->styles[sbTheme::STYLE::TEXT].font,waitMessage, _tcslen(waitMessage)); s.x = (rClient.width()-s.x)/2; s.y = rClient.height()-Theme->PanelSize-(s.y*1.5f); sbSelectFont(drawSurface,Theme->styles[sbTheme::STYLE::TEXT].font); sbSelectColor(drawSurface, Theme->fadeColor(Theme->ItemSubTextColor, s.x, s.y)); sbDrawText(drawSurface, s.x, s.y, waitMessage, _tcslen(waitMessage)); } if (stretchSurface) { sbRect rect; sbGetScreenRect(rect); sbStretchBlt(hdc, rect, drawSurface, rClient); } else sbBitBlt(hdc, rClient, drawSurface, sbPoint(0,0)); } void sbCore::onMouse ( int x, int y, const int state ) { keypadController = false; if (stretchSurface) { sbRect rect; sbGetScreenRect(rect); x = x * rClient.width() / rect.width(); y = y * rClient.height() / rect.height(); } if ( state == MSG_MOUSE_L_DOWN ) { if (listShift != 0) return; input.last.x = input.start.x = x; input.last.y = input.start.y = y; input.leaveZone = false; input.mousePressed = true; if (lists[currentList] != NULL) lists[currentList]->onPressed(x,y); cineticPower = 0.0f; input.tapCounter = 0; sbSetTimer(TIMER_TAP, 1000/10); input.scrollDataY[0] = y; input.scrollDataTicks[0] = sbGetTickCount(); } else if ( state == MSG_MOUSE_MOVE ) { if (!input.mousePressed) return; const int max = Theme->size / 20; int dx = x - input.last.x; int dy = y - input.last.y; if ( abs(input.start.x-x) > max || abs(input.start.y-y) > max ) { input.leaveZone = true; sbKillTimer(TIMER_TAP); input.tapCounter = 0; } input.scrollDataTicks[1] = sbGetTickCount(); input.scrollDataY[1] = y; if (input.scrollDataTicks[1] - input.scrollDataTicks[0] > 200) { input.scrollDataY[0] = input.scrollDataY[1] + (int)((input.scrollDataY[0]-input.scrollDataY[1]) * (200.0f/(input.scrollDataTicks[1]-input.scrollDataTicks[0]))); input.scrollDataTicks[0] = input.scrollDataTicks[1] - 200; } if (lists[currentList] != NULL) { if (!input.block) { if (abs(input.start.x-x) > (rClient.right/6)+(abs(input.start.y-y)/2)) listShift = input.start.x-x; else listShift = 0; if (listShift < 0 && lists[currentList]->getPrev() == NULL) listShift = 0; if (listShift > 0 && lists[currentList]->getNext() == NULL) listShift = 0; } //if (dy != 0 && listShift == 0) lists[currentList]->scroll((float)dy); redraw(); } input.last.y = y; input.last.x = x; } else if ( state == MSG_MOUSE_L_UP ) { if (!input.mousePressed) return; input.scrollDataTicks[1] = sbGetTickCount(); input.scrollDataY[1] = y; if (input.scrollDataTicks[1] - input.scrollDataTicks[0] > 200) { input.scrollDataY[0] = input.scrollDataY[1] + ((input.scrollDataY[0]-input.scrollDataY[1]) * (200.0f/(input.scrollDataTicks[1]-input.scrollDataTicks[0]))); input.scrollDataTicks[0] = input.scrollDataTicks[1] - 200; } float speed = (input.scrollDataY[1]-input.scrollDataY[0])/(float)max((input.scrollDataTicks[1]-input.scrollDataTicks[0]),120); cineticPower = (speed*50.0f) + (cineticPower*min(fabs(speed)*200.0f/Theme->size,1.0f)); //sbMessage ("Ticks %i\t Y %i\t cineticPower %f\n", input.scrollDataTicks[0], input.scrollDataY[0], cineticPower); //sbMessage ("Ticks %i\t Y %i\t Duration %i\t Speed %f\n", input.scrollDataTicks[1], input.scrollDataY[1],input.scrollDataTicks[1]-input.scrollDataTicks[0],speed); sbKillTimer ( TIMER_TAP ); if (lists[currentList] != NULL) { lists[currentList]->onReleased(x,y); if (!input.leaveZone) input.tapCounter >= 8 ? lists[currentList]->onTapped(x,y) : lists[currentList]->onClicked(x,y); } if (listShift != 0) { sbSetTimer(TIMER_SLIDE, refreshRate); cineticPower = 0.0f; } if (fabsf(cineticPower) > 0.0f) { bScrollTimer = true; sbSetTimer ( TIMER_SCROLL , refreshRate ); } input.mousePressed = false; input.tapCounter = 0; redraw(); } } void sbCore::onTimer (const int timer) { if (currentList != sbList::TYPE_NONE && lists[currentList] != NULL) lists[currentList]->onTimer(timer); switch (timer) { case TIMER_SAVE_CONFIG: { sbMessage ("Save configs.\n"); features.save(); if (options != NULL) options->Save(); if (installManager != NULL) installManager->saveInstallConf(); } break; case TIMER_REDRAW: { if ( requestedRedraw ) { if ( lists[currentList] != NULL ) lists[currentList]->onTimer( TIMER_BEFORE_REDRAW ); requestedRedraw = false; sbUpdateScreen(); } } break; case TIMER_SCROLL: { if (fabsf(cineticPower) > 0.1f) { cineticPower *= cineticDamping; if (!input.mousePressed && lists[currentList] != NULL) { if (lists[currentList]->scroll(cineticPower) || lists[currentList]->needFill) redraw(); } } else { bScrollTimer = false; sbKillTimer(TIMER_SCROLL); } } break; case TIMER_TAP: { if (input.tapCounter < 8) { input.tapCounter++; redraw(); } else sbKillTimer(TIMER_TAP); } break; case TIMER_SLIDE: { if (abs(listShift) >= rClient.width()) { switchList(listShift > 0 ? sbList::PIN_NEXT : sbList::PIN_PREV); // reset information for slide listShift = 0; //surfaces[SURFACE::TYPE_LIST ].needRedraw = true; //surfaces[SURFACE::TYPE_BUFFER].needRedraw = true; sbKillTimer(TIMER_SLIDE); } else { if (listShift > 0) listShift += (int)ceil((rClient.width()-listShift)*0.44f); else listShift -= (int)ceil((rClient.width()+listShift)*0.44f); if (listShift > rClient.width()) listShift = rClient.width(); if (listShift < -rClient.width()) listShift = -rClient.width(); redraw(); } } break; case TIMER_BANNER: { showBanner = false; sbKillTimer ( TIMER_BANNER ); redraw(); } break; } } void sbCore::redraw() { // TODO: receive item to update // TODO: receive displace to shift and redraw if (!redrawTimerOn) { redrawTimerOn = true; sbSetTimer(TIMER_REDRAW, refreshRate); requestedRedraw = false; sbUpdateScreen(); } else { requestedRedraw = true; } } void sbCore::switchList ( sbList::TYPE type ) { sbList * oldList = lists[currentList]; sbMessage("Switch view to : %i\n", type); switch (type) { case sbList::PIN_NEXT: if (lists[currentList]->getNext() != NULL) { lists[currentList] = lists[currentList]->getNext(); } type = currentList; break; case sbList::PIN_PREV: if (lists[currentList]->getPrev() != NULL) { lists[currentList] = lists[currentList]->getPrev(); } type = currentList; break; case sbList::TYPE_CURRENT: switchList(currentList); return; case sbList::TYPE_NONE: return; default: if (lists[type] == NULL) lists[type] = new sbList (type); sbAssert (lists[type] == NULL); if (type == sbList::TYPE_MODULE_VIEW && currentModule == NULL) { switchList( sbList::TYPE_HOME ); return; } } currentList = type; threadDestroy(); if (oldList != lists[currentList]) lists[currentList]->onActivate(); if (currentList == sbList::TYPE_MODULE_VIEW && lists[currentList]->module != NULL) Core->threadCreate(lists[currentList]); showBanner = oldList != NULL && lists[currentList] != NULL && oldList->type == sbList::TYPE_MODULE_VIEW && lists[currentList]->type == sbList::TYPE_MODULE_VIEW; if (showBanner) sbSetTimer ( TIMER_BANNER , 5000 ); else sbKillTimer ( TIMER_BANNER ); redraw(); } void sbCore::threadMain ( bool * abort ) { sbAssert ( backgroundList == NULL ); //sbMessage ("threadMain : start\n"); // backgroundList can be with itemCurrent == NULL on first call while (!*abort) { backgroundList->process ( abort ); sbSleep(5); } sbAssert(*abort == false); //sbMessage ("threadMain : end , abort %i\n", *abort); } void sbCore::threadCreate (sbList * list) { sbMessage ("threadCreate\n"); backgroundList = list; backgroundThread = sbThreadCreate ( & this->threadMain ); } void sbCore::threadDestroy () { sbMessage ("threadDestroy\n"); if ( backgroundThread != NULL ) { sbThreadDestroy( backgroundThread ); backgroundThread = NULL; backgroundList = NULL; } } void sbCore::controlPressed ( sbControl::TYPE type ) // REMOVE { if (!lists[currentList]->onControl(type)) sbAssert(true); } void sbCore::onChar ( TCHAR character ) { if ( lists[currentList] != NULL ) lists[currentList]->onChar( character ); } void sbCore::onKey ( int key , bool down ) { keypadController = true; if ( lists[currentList] != NULL && down ) lists[currentList]->onKey ( key ); } const char * sbCore::holdString ( const char * string ) { if (string == NULL || strlen(string) == 0) return NULL; for (int i=0; i < strings.size(); i++) { if (!strcmp(string,strings[i])) return strings[i]; } strings.push_back(_strdup(string)); return strings.back(); } void sbCore::initSword () { const char * curMod = currentModule == NULL ? NULL : holdString(currentModule->Name()); sbMessage ("Init Sword\n"); if (swordMgr != NULL) delete swordMgr; swordMgr = new sword::SWMgr(new sword::MarkupFilterMgr(sword::FMT_OSIS, sword::ENC_UTF16)); sbAssert(swordMgr == NULL); defaultModule = swordMgr->getModule((*options)["General"].getWithDefault("DefaultModule", "KJV")); if (defaultModule == NULL ) { for (sword::ModMap::iterator it = swordMgr->Modules.begin(); it != swordMgr->Modules.end(); it++) { if (!strcmp((*it).second->Type(), "Biblical Texts")) { defaultModule = swordMgr->Modules.begin()->second; break; } } } if (defaultModule != NULL) { currentVerse.copyFrom(defaultModule->getKey()); sword::StringList sl = swordMgr->getGlobalOptions(); for (sword::StringList::iterator it = sl.begin(); it != sl.end(); it++) { const char * option = holdString(*it); std::vector::iterator f = std::find(moduleOptions.begin(),moduleOptions.end(),option); if (f == moduleOptions.end()) moduleOptions.push_back(option); #ifdef _DEBUG swordMgr->setGlobalOption(option,"On"); #endif } } currentModule = curMod == NULL ? NULL : swordMgr->getModule( curMod ); } sword::InstallMgr * sbCore::getInstallMgr() { if (installManager == NULL) installManager = new sword::InstallMgr ("./install/",&installStatus); return installManager; }