YSTest  PreAlpha_b400_20130424
The YSLib Test Project
 全部  命名空间 文件 函数 变量 类型定义 枚举 枚举值 友元 宏定义  
ShlReader.cpp
浏览该文件的文档.
1 /*
2  Copyright by FrankHB 2011 - 2013.
3 
4  This file is part of the YSLib project, and may only be used,
5  modified, and distributed under the terms of the YSLib project
6  license, LICENSE.TXT. By continuing to use, modify, or distribute
7  this file you indicate that you have read the license and
8  understand and accept it fully.
9 */
10 
28 #include "ShlReader.h"
29 #include "ShlExplorer.h"
30 #include <NPL/Lexical.h>
31 
32 YSL_BEGIN_NAMESPACE(YReader)
33 
34 namespace
35 {
36 // ResourceMap GlobalResourceMap;
37 
38 using namespace Text;
39 
40 /*
41 \brief 文本阅读器菜单项。
42 \since build 303
43 */
44 enum MNU_READER : Menu::IndexType
45 {
46  MR_Return = 0,
47  MR_Setting,
48  MR_FileInfo,
50  MR_Bookmark,
51  MR_LineUp,
52  MR_LineDown,
53  MR_ScreenUp,
54  MR_ScreenDown
55 };
56 
57 } // unnamed namespace;
58 
59 
60 ReaderBox::ReaderBox(const Rect& r)
61  : Control(r),
62  btnMenu({4, 12, 16, 16}), btnSetting({24, 12, 16, 16}),
63  btnInfo({44, 12, 16, 16}), btnBookmark({64, 12, 16, 16}),
64  btnReturn({84, 12, 16, 16}),
65  btnPrev({104, 12, 16, 16}), btnNext({124, 12, 16, 16}),
66  pbReader({4, 0, 248, 8}), lblProgress({216, 12, 40, 16})
67 {
68  Background = nullptr,
69  SetRenderer(make_unique<BufferedRenderer>()),
79  pbReader.ForeColor = Color(192, 192, 64),
80  lblProgress.SetRenderer(make_unique<BufferedRenderer>()),
81  lblProgress.Background = nullptr,
82  lblProgress.Font.SetSize(12),
83  InitializeProgress();
84 }
85 
86 void
87 ReaderBox::InitializeProgress()
88 {
89  yunseq(lblProgress.Text = u"--%", lblProgress.ForeColor = ColorSpace::Blue);
90 }
91 
92 void
94 {
95  if(YB_LIKELY(reader.IsBufferReady()))
96  {
97  const auto ts(reader.GetTextSize());
98 
99  if(YB_LIKELY(ts != 0))
100  {
101  const auto tp(reader.GetTopPosition());
102  char str[5];
103 
104  std::sprintf(str, "%2u%%", tp * 100 / ts);
105  yunseq(lblProgress.Text = str,
106  lblProgress.ForeColor = reader.GetBottomPosition() == ts
108  pbReader.SetMaxValue(ts),
109  pbReader.SetValue(tp);
110  }
111  else
112  InitializeProgress();
113  }
114  else
115  InitializeProgress();
118 }
119 
120 
122  : DialogBox({32, 32, 200, 108}),
123  lblEncoding({4, 20, 192, 18}),
124  lblSize({4, 40, 192, 18}),
125  lblTop({4, 60, 192, 18}),
126  lblBottom({4, 80, 192, 18})
127 {
129  FetchEvent<TouchMove>(*this) += OnTouchMove_Dragging;
130 }
131 
132 void
133 TextInfoBox::Refresh(PaintEventArgs&& e)
134 {
135  DialogBox::Refresh(std::move(e));
136 
138  e.ClipArea = Rect(e.Location, GetSizeOf(*this));
139 }
140 
141 void
143 {
144  yunseq(lblEncoding.Text = "Encoding: " + to_string(reader.GetEncoding())
145  + ';',
146  lblSize.Text = "Size: " + to_string(reader.GetTextSize()) + " B;",
147  lblTop.Text = "Top: " + to_string(reader.GetTopPosition()) + " B;",
148  lblBottom.Text = "Bottom: " + to_string(reader.GetBottomPosition())
149  + " B;");
152 }
153 
154 
157  lblPath({8, 20, 240, 16}),
158  lblSize({8, 40, 240, 16}),
159  lblAccessTime({8, 60, 240, 16}),
160  lblModifiedTime({8, 80, 240, 16}),
161  lblOperations({8, 120, 240, 16})
162 {
163  Background = SolidBrush(ColorSpace::Silver);
164 #if YCL_DS
165  lblOperations.Text = "<↑↓> 滚动一行 <LR> 滚动一屏 <B>退出";
166 #else
167  lblOperations.Text = "<↑↓> 滚动一行 <LR> 滚动一屏 <Esc>退出";
168 #endif
170  lblOperations);
171 }
172 
173 
175  const shared_ptr<Desktop>& h_dsk_up, const shared_ptr<Desktop>& h_dsk_dn)
176  : ShlDS(h_dsk_up, h_dsk_dn),
177  CurrentPath(pth), fBackgroundTask(), bExit()
178 {}
179 
180 void
182 {
183  if(bExit)
184  return;
185  bExit = true;
186  fBackgroundTask = nullptr;
187  // TODO: Use template %SetShellToNew.
188 // SetShellToNew<ShlExplorer>();
189  const auto h_up(GetDesktopUpHandle());
190  const auto h_dn(GetDesktopDownHandle());
191 
192  PostMessage<SM_TASK>(0xF8, [=]{
193  ResetDSDesktops(*h_up, *h_dn);
194  NowShellTo(ystdex::make_shared<ShlExplorer>(CurrentPath / u"..",
195  h_up, h_dn));
196  });
197 }
198 
200 ShlReader::LoadBookmarks(const string& group)
201 {
203 
204  try
205  {
206  // TODO: Complete unexpected input handling.
207  ystdex::split(Access<string>(FetchGlobalInstance().Root
208  .GetNode("YReader")["Bookmarks"].GetNode('"' + NPL::MakeEscape(
209  group) + '"')), static_cast<int(&)(int)>(std::isspace),
210  [&](string::iterator b, string::iterator e){
211  bookmarks.push_back(std::stoi(ystdex::ltrim(string(b, e))));
212  });
213  }
214  catch(std::exception& e) // TODO: Logging.
215  {}
216  return std::move(bookmarks);
217 }
218 
221 {
222  try
223  {
225  .GetNode("YReader")).GetNode("ReaderSetting").GetContainer());
226  }
227  catch(std::exception& e) // TODO: Logging.
228  {}
229  return {};
230 }
231 
232 void
234 {
235  PostMessage<SM_PAINT>(0xE0, nullptr);
236  if(fBackgroundTask)
237  PostMessage<SM_TASK>(0x20, fBackgroundTask);
238 }
239 
240 void
241 ShlReader::SaveBookmarks(const string& group, const BookmarkList& bookmarks)
242 {
243  try
244  {
245  FetchGlobalInstance().Root.GetNode("YReader")["Bookmarks"]
246  ['"' + NPL::MakeEscape(group) + '"'].Value = [&]{
247  string str;
248 
249  for(const auto& pos : bookmarks)
250  {
251  str += to_string(pos);
252  str += ' ';
253  }
254  return std::move(str);
255  }();
256  }
257  catch(std::exception& e) // TODO: Logging.
258  {}
259 }
260 
261 void
263 {
264  try
265  {
266  auto& root(FetchGlobalInstance().Root);
267 
268  root["YReader"]["ReaderSetting"].Value = ValueNode::Container(rs);
269  SaveConfiguration(root);
270  }
271  catch(std::exception& e) // TODO: Logging.
272  {}
273 }
274 
275 
278 {
279  shl.StopAutoScroll(),
280  Hide(shl.boxReader),
281  Hide(shl.boxTextInfo);
282 }
284 {
285  auto& shl(GetShell());
286 
287  shl.reader.SetVisible(true),
288  shl.boxReader.UpdateData(shl.reader),
289  shl.boxTextInfo.UpdateData(shl.reader),
290  Show(shl.boxReader);
291 }
292 
293 
295  : BaseSession(shl)
296 {
297  auto& dsk_up(shl.GetDesktopUp());
298  auto& dsk_dn(shl.GetDesktopDown());
299  auto& reader(shl.reader);
300  auto& CurrentSetting(shl.CurrentSetting);
301  auto& pnlSetting(shl.pnlSetting);
302 
303  shl.reader.SetVisible(false),
304  yunseq(CurrentSetting.UpColor = dsk_up.Background
305  .target<SolidBrush>()->Color, CurrentSetting.DownColor
306  = dsk_dn.Background.target<SolidBrush>()->Color,
307  CurrentSetting.FontColor = reader.GetColor(),
308  CurrentSetting.Font = reader.GetFont());
310  {
311  using ystdex::get_key;
312 
313  size_t i(std::find(Encodings | get_key,
315  reader.GetEncoding()) - Encodings);
316 
317  if(i == arrlen(Encodings))
318  i = 0;
320  pnlSetting.ddlEncoding.Text = Encodings[i].second);
321  }
323 }
325 {
326  auto& shl(GetShell());
327 
328  RemoveWidgets(shl.GetDesktopUp(),
329  shl.pnlSetting.lblAreaUp, shl.pnlSetting.lblAreaDown);
330 }
331 
332 
334  : BaseSession(shl)
335 {
337  Show(shl.pnlBookmark);
338 }
339 
340 
342  const shared_ptr<Desktop>& h_dsk_up, const shared_ptr<Desktop>& h_dsk_dn)
343  : ShlReader(pth, h_dsk_up, h_dsk_dn),
346  CurrentSetting.GetTimerSetting()), tmrInput(), reader(),
347  boxReader({0, 160, 256, 32}), boxTextInfo(), pnlSetting(),
348  pTextFile(), mhMain(GetDesktopDown()),
349  pnlBookmark(LoadBookmarks(pth.GetNativeString()), *this), session_ptr()
350 {
351  using ystdex::get_key;
352 
353  const auto exit_session([this](TouchEventArgs&&){
354  session_ptr.reset();
355  });
356 
357  SetVisibleOf(boxReader, false),
358  SetVisibleOf(boxTextInfo, false),
359  SetVisibleOf(pnlSetting, false),
360  SetVisibleOf(pnlBookmark, false);
361  yunseq(
362  reader.ViewChanged = [this]{
363  if(IsVisible(boxReader))
364  boxReader.UpdateData(reader);
366  boxTextInfo.UpdateData(reader);
367  },
368  mhMain.Roots[&boxReader.btnMenu] = 1U,
369  FetchEvent<Click>(boxReader.btnMenu) += [this](TouchEventArgs&&){
370  if(mhMain.IsShowing(1U))
371  mhMain.Hide(1U);
372  else
373  {
374  const auto& pt(LocateForWidget(GetDesktopDown(),
375  boxReader.btnMenu));
376 
377  ShowMenu(1U, Point(pt.X, pt.Y - mhMain[1U].GetHeight()));
378  }
379  },
380  FetchEvent<Click>(boxReader.btnSetting) += [this](TouchEventArgs&&){
381  Execute(MR_Setting);
382  },
383  FetchEvent<Click>(boxReader.btnInfo) += [this](TouchEventArgs&&){
384  Execute(MR_FileInfo);
385  },
386  FetchEvent<Click>(boxReader.btnBookmark) += [this](TouchEventArgs&&){
387  Execute(MR_Bookmark);
388  },
389  FetchEvent<Click>(boxReader.btnReturn) += [this](TouchEventArgs&&){
390  Execute(MR_Return);
391  },
392  FetchEvent<Click>(boxReader.btnPrev) += [this](TouchEventArgs&&){
393  UpdateReadingList(true);
394  },
395  FetchEvent<Click>(boxReader.btnNext) += [this](TouchEventArgs&&){
396  UpdateReadingList(false);
397  },
398  FetchEvent<TouchDown>(boxReader.pbReader) += [this](TouchEventArgs&& e){
399  const auto s(reader.GetTextSize());
400 
401  if(YB_LIKELY(s != 0))
402  Locate(e.X * s / boxReader.pbReader.GetWidth());
403  },
404  FetchEvent<Paint>(boxReader.pbReader) += [this](PaintEventArgs&& e){
405  auto& pb(boxReader.pbReader);
406  const auto mval(pb.GetMaxValue());
407  const auto w(pb.GetWidth() - 2);
408  auto& pt(e.Location);
409 
410  FillRect(e.Target, Point(pt.X + 1 + round(pb.GetValue() * w / mval),
411  pt.Y + 1), Size(round((reader.GetBottomPosition()
412  - GetReaderPosition()) * w / mval), pb.GetHeight() - 2),
414  },
415  FetchEvent<Click>(pnlSetting.btnClose) += exit_session,
416  FetchEvent<Click>(pnlSetting.btnOK) += [&, this](TouchEventArgs&&){
417  pnlSetting >> CurrentSetting;
418  tmrScroll.SetInterval(CurrentSetting.GetTimerSetting());
419  Switch(pnlSetting.current_encoding),
420  reader.SetColor(CurrentSetting.FontColor),
421  reader.SetFont(CurrentSetting.Font);
422  reader.UpdateView();
423  yunseq(GetDesktopUp().Background = pnlSetting.lblAreaUp.Background,
424  GetDesktopDown().Background = pnlSetting.lblAreaDown.Background
425  );
426  if(IsVisible(boxReader))
427  for(auto pr(boxReader.GetChildren()); pr.first != pr.second;
428  ++pr.first)
429  if(dynamic_cast<BufferedRenderer*>(
430  &pr.first->GetRenderer()))
431  Invalidate(*pr.first);
432  },
433  FetchEvent<Click>(pnlSetting.btnOK) += exit_session,
434  FetchEvent<Click>(pnlBookmark.btnClose) += exit_session,
435  FetchEvent<Click>(pnlBookmark.btnOK) += [this](TouchEventArgs&&){
436  if(pnlBookmark.lbPosition.IsSelected() && Locate(pnlBookmark
437  .bookmarks[pnlBookmark.lbPosition.GetSelectedIndex()]))
438  boxReader.UpdateData(reader);
439  },
440  FetchEvent<Click>(pnlBookmark.btnOK) += exit_session
441  );
442  {
443  Menu& mnu(*(ynew Menu({}, shared_ptr<Menu::ListType>(new
444  Menu::ListType{"返回", "设置...", "文件信息...", "书签...",
445  "向上一行", "向下一行", "向上一屏", "向下一屏"}), 1u)));
446 
447  mnu.GetConfirmed() += [this](IndexEventArgs&& e){
448  Execute(e.Value);
449  };
450  mhMain += mnu;
451  }
453 
454  auto& dsk_up(GetDesktopUp());
455  auto& dsk_dn(GetDesktopDown());
456 
457  reader.SetColor(CurrentSetting.FontColor),
458  reader.SetFont(CurrentSetting.Font),
459  yunseq(
460  dsk_up.Background = SolidBrush(CurrentSetting.UpColor),
461  dsk_dn.Background = SolidBrush(CurrentSetting.DownColor),
462  FetchEvent<Click>(dsk_dn).Add(*this, &ShlTextReader::OnClick),
463  FetchEvent<KeyDown>(dsk_dn).Add(*this, &ShlTextReader::OnKeyDown),
464  FetchEvent<KeyHeld>(dsk_dn) += OnEvent_Call<KeyDown>
465  );
466  reader.Attach(dsk_up, dsk_dn),
467  AddWidgets(dsk_dn, boxReader, boxTextInfo, pnlSetting, pnlBookmark);
468  LoadFile(pth);
469  LastRead.DropSubsequent();
470  UpdateButtons();
471  //置默认视图。
472  // TODO: Associate view setting state for user selection.
473  OnClick(TouchEventArgs(dsk_dn, 0));
474  RequestFocusCascade(dsk_dn);
475 }
476 
478 {
480  SaveGlobalConfiguration(CurrentSetting);
481  LastRead.Insert(CurrentPath, GetReaderPosition());
482 }
483 
484 string
485 ShlTextReader::GetSlice(Bookmark::PositionType pos, string::size_type len)
486 {
487  return CopySliceFrom(reader.GetTextBufferRef(), pos, len);
488 }
489 
490 void
491 ShlTextReader::Execute(IndexEventArgs::ValueType idx)
492 {
493  switch(idx)
494  {
495  case MR_Return:
496  Exit();
497  break;
498  case MR_Setting:
499  session_ptr.reset(new SettingSession(*this));
500  break;
501  case MR_Bookmark:
502  session_ptr.reset(new BookmarkSession(*this));
503  break;
504  case MR_FileInfo:
506  Show(boxTextInfo);
507  break;
508  case MR_LineUp:
510  break;
511  case MR_LineDown:
513  break;
514  case MR_ScreenUp:
516  break;
517  case MR_ScreenDown:
519  break;
520  }
521 }
522 
523 void
525 {
526  CurrentPath = pth;
527  pTextFile = make_unique<TextFile>(pth.GetNativeString().c_str(),
528  std::ios_base::in | std::ios_base::binary, CharSet::Null);
530 
531  const auto text_size(reader.GetTextSize());
532 
534  return pos >= text_size;
535  });
536 }
537 
538 bool
540 {
541  const auto s(reader.GetTextSize());
542 
543  if(YB_LIKELY(s != 0))
544  {
545  const auto old_pos(GetReaderPosition());
546 
547  reader.Locate(pos);
548  if(YB_LIKELY(old_pos != GetReaderPosition()))
549  {
550  LastRead.Insert(CurrentPath, old_pos);
551  LastRead.DropSubsequent();
552  UpdateButtons();
553  return true;
554  }
555  }
556  return false;
557 }
558 
559 void
561 {
562  if(tmrScroll.IsActive())
564  {
565  if(CurrentSetting.SmoothScroll)
566  reader.ScrollByPixel(1U);
567  else
569  }
570 }
571 
572 void
574 {
575  if(!mhMain.IsShowing(id))
576  {
577  auto& mnu(mhMain[id]);
578 
579  mnu.ClearSelected();
580  switch(id)
581  {
582  case 1u:
583  mnu.SetItemEnabled(MR_LineUp, !reader.IsTextTop());
584  mnu.SetItemEnabled(MR_LineDown, !reader.IsTextBottom());
585  mnu.SetItemEnabled(MR_ScreenUp, !reader.IsTextTop());
586  mnu.SetItemEnabled(MR_ScreenDown, !reader.IsTextBottom());
587  }
588  SetLocationOf(mnu, pt);
589  mhMain.Show(id);
590  }
591 }
592 
593 void
595 {
597  fBackgroundTask = nullptr,
599 }
600 
601 void
603 {
604  if(enc != Encoding() && pTextFile && bool(*pTextFile)
605  && pTextFile->Encoding != enc)
606  {
607  pTextFile->Encoding = enc;
609  }
610 }
611 
612 void
614 {
615  LastRead.Insert(CurrentPath, GetReaderPosition());
616 
617  const auto& bm(LastRead.Switch(is_prev));
618 
619  if(bm.Path != CurrentPath)
620  LoadFile(bm.Path);
621  if(reader.IsBufferReady())
622  reader.Locate(bm.Position);
623  UpdateButtons();
624 }
625 
626 void
628 {
629  const auto pr(LastRead.CheckBoundary());
630 
631  yunseq(Enable(boxReader.btnPrev, pr.first),
632  Enable(boxReader.btnNext, pr.second));
633 }
634 
635 void
636 #if YCL_MINGW32
637 ShlTextReader::OnClick(TouchEventArgs&& e)
638 #else
639 ShlTextReader::OnClick(TouchEventArgs&&)
640 #endif
641 {
642 #if YCL_MINGW32
643  if(e.Keys[VK_RBUTTON])
644  {
645  ShowMenu(1U, e);
646  return;
647  }
648 #endif
649  if(tmrScroll.IsActive())
650  {
651  StopAutoScroll();
652  Deactivate(tmrScroll);
653  return;
654  }
655  if(IsVisible(boxReader))
656  {
657  Close(boxReader);
658  reader.Stretch(0);
659  }
660  else
661  {
662  Show(boxReader);
663  reader.Stretch(boxReader.GetHeight());
664  }
665 }
666 
667 void
668 ShlTextReader::OnKeyDown(KeyEventArgs&& e)
669 {
670  using namespace Timers;
671  using namespace KeyCodes;
672 
673  if(e.Strategy != RoutedEventArgs::Tunnel && !mhMain.IsShowing(1u)
674  && RepeatHeld(tmrInput, FetchGUIState().KeyHeldState,
675  TimeSpan(240), TimeSpan(60)))
676  {
677  const auto ntick(HighResolutionClock::now());
678 
679  //这里可以考虑提供暂停,不调整视图。
680  if(tmrScroll.IsActive())
681  {
682  StopAutoScroll();
683  return;
684  }
685 
686  const auto& k(e.GetKeys());
687 
688  if(k.count() != 1)
689  return;
690  if(k[YCL_KEY_Start])
691  {
692  fBackgroundTask = std::bind(&ShlTextReader::Scroll, this);
693  tmrScroll.Reset();
695  return;
696  }
697  if(k[KeyCodes::Enter])
698  reader.UpdateView();
699  else if(k[Esc])
700  Exit();
701  else if(k[Up])
703  else if(k[Down])
705  else if(k[YCL_KEY(X)] || k[YCL_KEY(Y)])
706  {
707  auto size(reader.GetFont().GetSize());
708 
709  e.Handled = true;
710  if(k[YCL_KEY(X)])
711  {
712  if(YB_LIKELY(size > Font::MinimalSize))
713  --size;
714  else
715  return;
716  }
717  else if(YB_LIKELY(size < Font::MaximalSize))
718  ++size;
719  else
720  return;
721  reader.SetFontSize(size);
722  reader.UpdateView();
723  }
724  else if(k[YCL_KEY(L)])
725  {
726  if(YB_LIKELY(reader.GetLineGap() != 0))
727  reader.SetLineGap(reader.GetLineGap() - 1);
728  }
729  else if(k[YCL_KEY(R)])
730  {
731  if(YB_LIKELY(reader.GetLineGap() != 12))
732  reader.SetLineGap(reader.GetLineGap() + 1);
733  }
734  else if(k[Left])
735  // else if(k[PgUp])
737  else if(k[Right])
738  // else if(k[PgDn])
740  else
741  return;
742  tmrInput.Delay(HighResolutionClock::now() - ntick);
743  e.Handled = true;
744  }
745 }
746 
747 
749  const shared_ptr<Desktop>& h_dsk_up, const shared_ptr<Desktop>& h_dsk_dn)
750  : ShlReader(pth, h_dsk_up, h_dsk_dn),
752 {
753  HexArea.SetRenderer(make_unique<BufferedRenderer>(true));
754  yunseq(
755  FetchEvent<KeyDown>(HexArea) += [this](KeyEventArgs&& e){
756  if(e.GetKeys() == 1 << KeyCodes::Esc)
757  Exit();
758  },
759  HexArea.ViewChanged += [this](HexViewArea::ViewArgs&&){
760  pnlFileInfo.lblSize.Text = u"当前位置: "
761  + String(to_string(HexArea.GetModel().GetPosition())
762  + " / " + to_string(HexArea.GetModel().GetSize()));
763  Invalidate(pnlFileInfo.lblSize);
764  }
765  );
766 
767  auto& dsk_up(GetDesktopUp());
768  auto& dsk_dn(GetDesktopDown());
769  const auto& path_str(pth.GetNativeString());
770 
771  pnlFileInfo.lblPath.Text = u"文件路径:" + pth;
772 
773  struct ::stat file_stat;
774 
775  //在 DeSmuME 上无效; iDSL + DSTT 上访问时间精确不到日,修改时间正常。
776  ::stat(path_str.c_str(), &file_stat);
777  yunseq(pnlFileInfo.lblAccessTime.Text = u"访问时间:"
778  + String(TranslateTime(file_stat.st_atime)),
779  pnlFileInfo.lblModifiedTime.Text = u"修改时间:"
780  + String(TranslateTime(file_stat.st_mtime)));
781  dsk_up += pnlFileInfo;
782  HexArea.Load(path_str.c_str());
783  HexArea.UpdateData(0);
784  HexArea.ViewChanged(HexViewArea::ViewArgs(HexArea, true));
785  dsk_dn += HexArea;
786  RequestFocusCascade(HexArea);
787 }
788 
789 YSL_END_NAMESPACE(YReader)
790