00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #ifndef GUI_PREFSWINDOW_C_DEFINED
00039 #define GUI_PREFSWINDOW_C_DEFINED
00040
00041 #include "GUI/PrefsWindow.H"
00042
00043 #include "Component/ModelComponent.H"
00044 #include "Component/ModelParam.H"
00045 #include "GUI/XWinManaged.H"
00046 #include "Image/Image.H"
00047 #include "Image/Pixels.H"
00048 #include "Image/DrawOps.H"
00049 #include "Util/log.H"
00050 #include "Util/sformat.H"
00051
00052 #include <X11/keysym.h>
00053
00054
00055 template <class MP, class T>
00056 class PrefItemMPNum : public PrefItem
00057 {
00058 private:
00059 nub::ref<ModelComponent> comp;
00060
00061
00062 MP* param;
00063
00064 template <class U>
00065 static U adjustHelper(U val, int v)
00066 { return val + v; }
00067
00068 static bool adjustHelper(bool val, int v)
00069 { return (int(val) + v) % 2; }
00070
00071 public:
00072 PrefItemMPNum(PrefsWindow* pwin, MP* p, ModelComponent* c,
00073 bool pwinTakesOwnership)
00074 : PrefItem(pwin, p->getNameWithSpaces().c_str(), pwinTakesOwnership),
00075 comp(c),
00076 param(p)
00077 {}
00078
00079 void set(T v) { if (param->getVal() != v) { param->setVal(v); this->isChanged = true; } }
00080 T get() const { return param->getVal(); }
00081
00082 virtual void fromString(const std::string& s)
00083 {
00084 if (s.size() == 0) return;
00085 const T oldval = param->getVal();
00086 try { param->setValString(s); }
00087 catch (std::exception& e) { REPORT_CURRENT_EXCEPTION; }
00088 if (oldval != param->getVal()) this->isChanged = true;
00089 }
00090
00091 virtual std::string toString() const
00092 { return param->getValString(); }
00093
00094 virtual void adjust(int v)
00095 { this->set(adjustHelper(this->get(), v)); }
00096
00097 virtual bool isDisabled() const
00098 { return param->getInactive(); }
00099 };
00100
00101
00102 class PrefItemMPStr : public PrefItem
00103 {
00104 private:
00105 nub::ref<ModelComponent> comp;
00106
00107
00108
00109 ModelParamBase* param;
00110
00111 public:
00112 PrefItemMPStr(PrefsWindow* pwin, ModelParamBase* p, ModelComponent* c,
00113 bool pwinTakesOwnership)
00114 : PrefItem(pwin, p->getNameWithSpaces().c_str(), pwinTakesOwnership),
00115 comp(c),
00116 param(p)
00117 {}
00118
00119 virtual void fromString(const std::string& s)
00120 {
00121 if (s.size() == 0) return;
00122 const std::string oldval = param->getValString();
00123 try { param->setValString(s); }
00124 catch (std::exception& e) { REPORT_CURRENT_EXCEPTION; }
00125 if (oldval != param->getValString()) this->isChanged = true;
00126 }
00127
00128 virtual std::string toString() const
00129 { return param->getValString(); }
00130
00131 virtual void adjust(int v)
00132 { }
00133
00134 virtual bool isDisabled() const
00135 { return param->getInactive(); }
00136 };
00137
00138
00139 PrefItem::PrefItem(PrefsWindow* pwin, const std::string& nm,
00140 bool pwinTakesOwnership)
00141 :
00142 name(nm), isChanged(true), wasDisabled(false)
00143 {
00144 if (pwin != 0)
00145 pwin->addItem(this, pwinTakesOwnership);
00146 else if (pwinTakesOwnership)
00147
00148
00149 LFATAL("I can't pass ownership of PrefItem '%s' "
00150 "to a null PrefsWindow*", nm.c_str());
00151 }
00152
00153
00154 PrefItem::~PrefItem()
00155 {}
00156
00157
00158 std::string PrefItem::getName() const
00159 {
00160 return this->name;
00161 }
00162
00163
00164 bool PrefItem::isValueChanged()
00165 {
00166 const bool ret =
00167 this->isChanged || (this->wasDisabled != this->isDisabled());
00168 this->isChanged = false;
00169 this->wasDisabled = this->isDisabled();
00170 return ret;
00171 }
00172
00173
00174 PrefItemStr::PrefItemStr(PrefsWindow* pwin, const std::string& nm,
00175 const std::string& v,
00176 const bool pwinTakesOwnership)
00177 : PrefItem(pwin, nm, pwinTakesOwnership),
00178 itsHist(), itsMaxSize(100), itsPos(0),
00179 disabled(false)
00180 {
00181 itsHist.push_back(v);
00182 itsPos = itsHist.size() - 1;
00183 }
00184
00185
00186 void PrefItemStr::set(const std::string& v)
00187 {
00188 ASSERT(itsPos < itsHist.size());
00189 if (itsHist[itsPos] != v)
00190 {
00191 if (itsHist.back() != v)
00192 {
00193 itsHist.push_back(v);
00194 while (itsHist.size() > itsMaxSize)
00195 itsHist.pop_front();
00196 }
00197 itsPos = itsHist.size() - 1;
00198 this->isChanged = true;
00199 }
00200 }
00201
00202
00203 std::string PrefItemStr::get() const
00204 {
00205 ASSERT(itsPos < itsHist.size());
00206 return itsHist[itsPos];
00207 }
00208
00209
00210 std::string PrefItemStr::getName() const
00211 {
00212 ASSERT(itsPos < itsHist.size());
00213
00214 return sformat("%s#%02"ZU, this->name.c_str(), itsPos);
00215 }
00216
00217
00218 void PrefItemStr::fromString(const std::string& s)
00219 { this->set(s); }
00220
00221
00222 std::string PrefItemStr::toString() const
00223 { return this->get(); }
00224
00225
00226 void PrefItemStr::adjust(int val)
00227 {
00228 ASSERT(itsHist.size() > 0);
00229
00230
00231
00232
00233 if (val > 0)
00234 {
00235 itsPos += val;
00236 itsPos = itsPos % itsHist.size();
00237 }
00238 else if (val < 0)
00239 {
00240 const size_t sub = -val;
00241 for (size_t i = 0; i < sub; ++i)
00242 {
00243 if (itsPos == 0) itsPos = itsHist.size() - 1;
00244 else --itsPos;
00245 }
00246 }
00247
00248 this->isChanged = true;
00249 }
00250
00251
00252 PrefsWindow::PrefsWindow(const std::string& wintitle,
00253 const SimpleFont& font)
00254 :
00255 itsWinTitle(wintitle),
00256 itsWin(0),
00257 itsFont(font),
00258 itsItems(),
00259 itsEditBuffer(),
00260 itsCurrentItem(0),
00261 itsState(SCROLLING),
00262 itsNameWidth(1),
00263 itsNumWidth(10),
00264 itsDirty(true)
00265 {}
00266
00267
00268 PrefsWindow::~PrefsWindow()
00269 {
00270 delete itsWin;
00271
00272 while (itsOwnedItems.size() > 0)
00273 {
00274 delete itsOwnedItems.back();
00275 itsOwnedItems.pop_back();
00276 }
00277 }
00278
00279
00280 void PrefsWindow::setValueNumChars(const int n)
00281 {
00282 if (n < 1)
00283 LFATAL("expected n to be at least 1, but got n=%d", n);
00284
00285 if (n != itsNumWidth)
00286 {
00287 itsNumWidth = n;
00288 itsDirty = true;
00289 }
00290 }
00291
00292
00293 void PrefsWindow::setFont(const SimpleFont& font)
00294 {
00295 if (font != itsFont)
00296 {
00297 itsDirty = true;
00298 itsFont = font;
00299 }
00300 }
00301
00302
00303 void PrefsWindow::update()
00304 {
00305 if (itsItems.size() == 0)
00306
00307
00308 return;
00309
00310 if (itsWin == 0)
00311 {
00312 this->redraw();
00313 LINFO("Redraw");
00314 ASSERT(itsWin != 0);
00315 return;
00316 }
00317
00318 for (size_t i = 0; i < itsItems.size(); ++i)
00319 {
00320 if (itsItems[i]->isValueChanged())
00321 {
00322 itsDirty = true;
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332 }
00333 }
00334
00335 KeySym ks;
00336 std::string s;
00337 while ((ks = itsWin->getLastKeySym(&s)) != NoSymbol)
00338 {
00339 if (this->handleKeysym(ks, s))
00340 itsDirty = true;
00341 }
00342
00343 XButtonEvent ev;
00344 while (itsWin->getLastButtonEvent(&ev))
00345 {
00346 if (this->handleButtonPress(&ev))
00347 itsDirty = true;
00348 }
00349
00350 if (itsDirty)
00351 {
00352 this->redraw();
00353 }
00354
00355
00356
00357 ASSERT(itsDirty == false);
00358 }
00359
00360
00361 void PrefsWindow::addPrefForParam(ModelParamBase* mp, ModelComponent* comp)
00362 {
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372 #define HANDLE_NUM_PARAM_TYPE(T) \
00373 if (OModelParam<T>* p = dynamic_cast<OModelParam<T>*>(mp)) \
00374 { \
00375 new PrefItemMPNum<OModelParam<T>, T> \
00376 (this, p, comp, \
00377 true); \
00378 return; \
00379 } \
00380 if (NModelParam<T>* p = dynamic_cast<NModelParam<T>*>(mp)) \
00381 { \
00382 new PrefItemMPNum<NModelParam<T>, T> \
00383 (this, p, comp, \
00384 true); \
00385 return; \
00386 }
00387
00388 HANDLE_NUM_PARAM_TYPE(int);
00389 HANDLE_NUM_PARAM_TYPE(float);
00390 HANDLE_NUM_PARAM_TYPE(double);
00391 HANDLE_NUM_PARAM_TYPE(unsigned int);
00392 HANDLE_NUM_PARAM_TYPE(long);
00393 HANDLE_NUM_PARAM_TYPE(unsigned long);
00394 HANDLE_NUM_PARAM_TYPE(byte);
00395 HANDLE_NUM_PARAM_TYPE(bool);
00396
00397
00398
00399 new PrefItemMPStr(this, mp, comp, true);
00400
00401 #undef HANDLE_NUM_PARAM_TYPE
00402 }
00403
00404
00405 void PrefsWindow::addPrefsForComponent(ModelComponent* comp, bool recurse)
00406 {
00407 if (recurse)
00408 {
00409 const uint n = comp->numSubComp();
00410 for (uint i = 0; i < n; ++i)
00411 this->addPrefsForComponent(comp->subComponent(i).get(), recurse);
00412 }
00413
00414 const size_t n = comp->getNumModelParams();
00415 for (size_t i = 0; i < n; ++i)
00416 {
00417 ModelParamBase* mp = comp->getModelParam(i);
00418
00419 if (!mp->allowsOnlineChanges())
00420 continue;
00421
00422 this->addPrefForParam(mp, comp);
00423 }
00424 }
00425
00426
00427 void PrefsWindow::addItem(PrefItem* item, bool takeOwnership)
00428 {
00429 if (item->getName().length() > size_t(itsNameWidth))
00430 itsNameWidth = item->getName().length();
00431
00432 itsItems.push_back(item);
00433
00434 if (takeOwnership)
00435 itsOwnedItems.push_back(item);
00436 }
00437
00438
00439 bool PrefsWindow::handleKeysym(KeySym ks, const std::string& s)
00440 {
00441 LDEBUG("keysym is %s", XKeysymToString(ks));
00442
00443 bool dirty = true;
00444
00445 if (itsState == EDITING)
00446 {
00447 switch (ks)
00448 {
00449 case XK_Return: case XK_KP_Enter:
00450
00451 {
00452 if (itsEditBuffer.size() > 0)
00453 {
00454 itsItems[itsCurrentItem]->fromString(itsEditBuffer);
00455 itsEditBuffer = "";
00456 }
00457
00458 itsState = SCROLLING;
00459 }
00460 break;
00461
00462 case XK_Escape:
00463
00464 {
00465 itsEditBuffer = "";
00466 itsState = SCROLLING;
00467 }
00468 break;
00469
00470 case XK_Delete: case XK_BackSpace:
00471
00472 {
00473 if (itsEditBuffer.size() > 0)
00474 {
00475 itsEditBuffer.resize(itsEditBuffer.size() - 1);
00476 }
00477 }
00478 break;
00479
00480 default:
00481 {
00482 if (s.length() == 1 && isprint(s[0]))
00483 {
00484 itsEditBuffer += s[0];
00485 }
00486 else
00487 dirty = false;
00488 }
00489 break;
00490 }
00491 }
00492 else if (itsState == SCROLLING)
00493 {
00494 switch (ks)
00495 {
00496 case XK_Up:
00497 itsCurrentItem =
00498 (itsCurrentItem + itsItems.size() - 1) % itsItems.size();
00499 break;
00500 case XK_Down:
00501 itsCurrentItem =
00502 (itsCurrentItem + 1) % itsItems.size();
00503 break;
00504
00505 case XK_Left:
00506 if (!itsItems[itsCurrentItem]->isDisabled())
00507 itsItems[itsCurrentItem]->adjust(-1);
00508 break;
00509
00510 case XK_Right:
00511 if (!itsItems[itsCurrentItem]->isDisabled())
00512 itsItems[itsCurrentItem]->adjust(1);
00513 break;
00514
00515 case XK_less:
00516 if (!itsItems[itsCurrentItem]->isDisabled())
00517 itsItems[itsCurrentItem]->adjust(-10);
00518 break;
00519
00520 case XK_greater:
00521 if (!itsItems[itsCurrentItem]->isDisabled())
00522 itsItems[itsCurrentItem]->adjust(10);
00523 break;
00524
00525 case XK_Return: case XK_KP_Enter:
00526 if (!itsItems[itsCurrentItem]->isDisabled())
00527 itsState = EDITING;
00528 break;
00529
00530 default:
00531 dirty = false;
00532 break;
00533 }
00534 }
00535
00536 return dirty;
00537 }
00538
00539
00540 bool PrefsWindow::handleButtonPress(XButtonEvent* ev)
00541 {
00542 bool dirty = true;
00543
00544 if (itsState == SCROLLING)
00545 {
00546 switch (ev->button)
00547 {
00548 case Button1:
00549 {
00550 const int n = (ev->y - 1) / (itsFont.h() + 2);
00551 itsCurrentItem = clampValue(n, 0, int(itsItems.size() - 1));
00552 }
00553 break;
00554
00555 case Button2:
00556 case Button3:
00557
00558 dirty = false;
00559 break;
00560
00561 case Button4:
00562 if (itsCurrentItem > 0)
00563 --itsCurrentItem;
00564 break;
00565 case Button5:
00566 if (itsItems.size() > 0
00567 && itsCurrentItem + 1 < itsItems.size())
00568 ++itsCurrentItem;
00569 break;
00570 }
00571 }
00572
00573 return dirty;
00574 }
00575
00576
00577 void PrefsWindow::redraw()
00578 {
00579 const Dims d((itsNameWidth + itsNumWidth + 10) * itsFont.w() + 2,
00580 2 + itsItems.size() * (itsFont.h() + 2));
00581
00582 Image<PixRGB<byte> > img(d, ZEROS);
00583
00584 for (size_t i = 0; i < itsItems.size(); ++i)
00585 {
00586 std::string line;
00587 PixRGB<byte> col;
00588
00589 if (i == itsCurrentItem)
00590 {
00591 if (itsState == EDITING)
00592 {
00593 col = PixRGB<byte>(255, 0, 0);
00594
00595 line = sformat(">> %*s ?? %-*s <<",
00596 itsNameWidth, itsItems[i]->getName().c_str(),
00597 itsNumWidth, itsEditBuffer.c_str());
00598 }
00599 else
00600 {
00601 col = itsItems[i]->isDisabled()
00602 ? PixRGB<byte>(127, 127, 127) : PixRGB<byte>(0, 255, 0);
00603
00604 line = sformat(">> %*s -> %-*s <<",
00605 itsNameWidth, itsItems[i]->getName().c_str(),
00606 itsNumWidth, itsItems[i]->toString().c_str());
00607 }
00608 }
00609 else
00610 {
00611 col =
00612 itsState == EDITING
00613 ? PixRGB<byte>(64, 64, 64)
00614 : PixRGB<byte>(255, 255, 255);
00615
00616 if (itsItems[i]->isDisabled())
00617 col /= 2;
00618
00619 line = sformat(" %*s %-*s ",
00620 itsNameWidth, itsItems[i]->getName().c_str(),
00621 itsNumWidth, itsItems[i]->toString().c_str());
00622 }
00623
00624 writeText(img, Point2D<int>(1, 1 + i*(itsFont.h()+2)),
00625 line.c_str(), col, PixRGB<byte>(0, 0, 0), itsFont);
00626 }
00627
00628 if (itsWin == 0)
00629 itsWin = new XWinManaged(img.getDims(), -1, -1, itsWinTitle.c_str());
00630 else if (itsWin->getDims() != img.getDims())
00631 itsWin->setDims(img.getDims());
00632
00633 ASSERT(itsWin->getDims() == img.getDims());
00634
00635 itsWin->drawImage(img);
00636
00637 itsDirty = false;
00638 }
00639
00640
00641
00642
00643
00644
00645
00646
00647 #endif // GUI_PREFSWINDOW_C_DEFINED