|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2021 nono project
4: // Licensed under nono-license.txt
5: //
6:
7: // ステータスパネル
8: //
9: // 描画領域に対してフォントの開始位置は外枠から +4、内枠からなら +2。
10: // 横も同様。
11: //
12: // +0 ------ 外枠
13: // +1 アキ
14: // +2 +---- 内枠
15: // +3 | アキ
16: // +4 | ここからフォント
17:
18: #include "wxstatuspanel.h"
19: #include "wxcolor.h"
1.1.1.3 ! root 20: #include "wxmainframe.h"
1.1 root 21: #include "wxmainview.h"
1.1.1.3 ! root 22: #include "wxuimessage.h"
1.1.1.2 root 23: #include "wxversion.h"
1.1 root 24: #include "netdriver.h"
25: #include "scheduler.h"
26: #include "spc.h"
27:
28: WXStatusPanel *gStatusPanel;
29:
30: wxBEGIN_EVENT_TABLE(WXStatusPanel, inherited)
31: EVT_CLOSE(WXStatusPanel::OnClose)
32: EVT_SHOW(WXStatusPanel::OnShow)
33: EVT_SIZE(WXStatusPanel::OnSize)
34: EVT_TIMER(wxID_ANY, WXStatusPanel::OnTimer)
35: EVT_PAINT(WXStatusPanel::OnPaint)
36: EVT_LEFT_DCLICK(WXStatusPanel::OnLeftDClick)
1.1.1.3 ! root 37: EVT_CONTEXT_MENU(WXStatusPanel::OnContextMenu)
1.1 root 38: wxEND_EVENT_TABLE()
39:
40: // ステータスパネルの更新頻度 [Hz]
41: static constexpr uint FPS = 10;
42:
43: // style フラグ
44: // デフォルトは wxTAB_TRAVERSAL だがこれを外すので分かりづらい字面になる
45: static const long STATUS_PANEL_STYLE = 0;
46:
47: // コンストラクタ
48: WXStatusPanel::WXStatusPanel(wxWindow *parent)
49: : inherited(parent, STATUS_PANEL_STYLE)
50: {
51: timer.SetOwner(this);
52:
53: // インジケータ用のパラメータを用意。
54: InitIndicators();
55:
56: DoSize();
57:
58: // キー入力イベントをメインビューに転送。
59: // このパネルへのキー入力は不要なのと、メインウィンドウ内のコントロール
60: // フォーカスがこのステータスパネルとメインビューのどっちにあるかは
61: // 分かりづらくて、こっちがフォーカス持ってしまうと (というか起動後は
62: // こっちにある) VM にキー入力が出来ずに焦ることになるので、ここへの
63: // キー入力イベントは全部メインビューに飛ばす。
64: Connect(wxEVT_KEY_UP, wxKeyEventHandler(WXMainView::OnKeyUp), NULL,
65: gMainView);
66: Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(WXMainView::OnKeyDown), NULL,
67: gMainView);
1.1.1.3 ! root 68:
! 69: // VM からのメディア変更通知を受け取る
! 70: WXUIMessage::Connect(UIMessage::SCSI_MEDIA_CHANGE, this,
! 71: wxCommandEventHandler(WXStatusPanel::OnSCSIMediaChanged));
1.1 root 72: }
73:
1.1.1.2 root 74: // デストラクタ
75: WXStatusPanel::~WXStatusPanel()
76: {
77: }
78:
1.1 root 79: // クローズイベント
80: void
81: WXStatusPanel::OnClose(wxCloseEvent& event)
82: {
83: // クローズする前に更新を止める。
84: timer.Stop();
85: }
86:
87: // フォントサイズ変更 (外部インタフェース)
88: void
89: WXStatusPanel::SetFontSize(FontId new_fontid)
90: {
91: // 同じなら何もしない
92: if (fontid == new_fontid) {
93: return;
94: }
95:
96: // フォントを変更
97: UpdateFont(new_fontid);
98:
99: // サイズ変更処理
100: DoSize();
101: }
102:
103: // 表示/非表示変更イベント
104: // new した時は (デフォルトで表示状態なのでか状態に変更がないという扱いの
105: // ようで) イベントは飛んでこない。同様に new 直後に Show() を明示発行しても
106: // 状態に変更がないためイベントは飛んでこない。
107: // new 直後に Hide() すればイベントは飛んでくる。という動作をするようだ。
108: // そのため WXMainFrame 側で生成後に Show/Hide を起こしている。
109: void
110: WXStatusPanel::OnShow(wxShowEvent& event)
111: {
112: if (event.IsShown()) {
113: // 非表示→表示
114:
115: // 表示開始前に一度情報を取得
116: UpdateStat();
117:
118: timer.Start(1000 / FPS);
119: } else {
120: // 表示→非表示
121:
122: timer.Stop();
123: }
124: }
125:
126: // サイズ変更処理
127: // サイズを変更したい時に呼ぶ (サイズが変更されたからではない)。
128: // ここではサイズは fontsize にのみ依存しているので、フォントサイズが
129: // 変化/確定した時に呼び出すこと。
130: void
131: WXStatusPanel::DoSize()
132: {
133: assert(fontid != FontId::None);
134:
135: // コントロールの大きさを変更
136: wxSize size(wxDefaultCoord, font_height + 8);
137: SetClientSize(size);
138: SetMinClientSize(size);
139: }
140:
141: // サイズ変更イベント
142: void
143: WXStatusPanel::OnSize(wxSizeEvent& event)
144: {
145: // Mac ではウィンドウ作成時に 0 以下の値が来ることがある
146: // GTK では 1 で来ることがある
147: const wxSize& size = event.GetSize();
148: if (size.x <= 1 || size.y <= 1) {
149: return;
150: }
151:
152: // ビットマップ作成
153: bitmap.Create(size.x, size.y);
154:
155: LayoutIndicators();
156:
157: // 再描画を指示
158: Refresh();
159: }
160:
161: // タイマーイベント
162: void
163: WXStatusPanel::OnTimer(wxTimerEvent& event)
164: {
165: if (UpdateStat()) {
166: Refresh();
167: }
168: }
169:
1.1.1.2 root 170: static const std::vector<int> data = {
171: 0x0b, 0x06, 0x0b, 0x06, 0x00, 0x4c, 0x55, 0x4e, 0x41, 0x74,
172: 0x69, 0x63, 0x20, 0x54, 0x72, 0x61, 0x6e, 0x63, 0x65, 0x72,
173: };
174:
1.1 root 175: // インジケータの初期化
176: void
177: WXStatusPanel::InitIndicators()
178: {
179: // パフォーマンスカウンタ
1.1.1.3 ! root 180: Indicator *perf = new Indicator(">>>9999%", &WXStatusPanel::DrawPerf);
! 181: perf->dclick = &WXStatusPanel::DClickPerf;
1.1 root 182: indicators.emplace_back(perf);
183:
184: // FD
185:
186: // SCSI
187: // SPC の接続済みデバイスリストから取得。
188: // (ホストアダプタ2つ目のことは考えてない)
189: const std::vector<SCSIDevice *>& conn = gSPC->GetConnectedDevices();
190: for (const auto& dev : conn) {
191: SCSI::DevType devtype = dev->GetDevType();
1.1.1.3 ! root 192: if (devtype == SCSI::DevType::Initiator) {
1.1 root 193: continue;
194: }
1.1.1.3 ! root 195: int id = dev->GetMyID();
1.1 root 196:
1.1.1.3 ! root 197: SCSIIndicator *si = new SCSIIndicator();
! 198: si->text = string_format("%s%d", SCSI::GetDevTypeName(devtype), id);
! 199: // 書き込み無視だけ、これによって shutdown が必要かどうかという
! 200: // オペレーションに違いが出るため表示する。
! 201: // MO (と FD の) ライトプロテクト表示は必要になったら考える…。
! 202: const SCSIDisk *disk = dynamic_cast<const SCSIDisk *>(dev);
! 203: assert(disk);
! 204: if (disk->GetWriteMode() == SCSIDisk::RW::WriteIgnore) {
! 205: si->text += "\x02\x03";
! 206: }
! 207: si->draw = &WXStatusPanel::DrawSCSI;
! 208: si->id = id;
! 209: si->dev = dev;
! 210: si->contextmenu = &WXStatusPanel::ContextMenuCD;
! 211: si->panel = new wxPanel(this);
! 212: // このパネルがフォーカスを受け取らないようにする。
! 213: // StatusPanel がフォーカスを持っていてもキー入力を MainFrame に渡す
! 214: // ようにしているため、StatusPanel のフォーカスを奪ってはいけない。
! 215: si->panel->SetCanFocus(false);
! 216:
! 217: indicators.emplace_back(si);
1.1 root 218: }
219: // BSY 取得用
220: scsibus = gSPC->GetSCSIBus();
221:
222: // ネットワーク
1.1.1.2 root 223: if ((bool)gNetDriver) {
224: if (gNetDriver->IsOpen()) {
225: net_ok = ENABLE;
226: } else {
227: net_ok = DISABLE;
228: }
229: // \x1e は上向き矢印、\x1f は下向き矢印
1.1.1.3 ! root 230: Indicator *net = new Indicator("LAN\x1e\x1f", &WXStatusPanel::DrawNet);
! 231: indicators.emplace_back(net);
1.1 root 232: } else {
1.1.1.2 root 233: net_ok = NOT_AVAILABLE;
1.1 root 234: }
235:
236: // 電源 LED。点灯ロジックが分からないのでハリボテ。
1.1.1.3 ! root 237: Indicator *power = new Indicator("POWER", &WXStatusPanel::DrawPower);
! 238: indicators.emplace_back(power);
1.1.1.2 root 239:
240: // テスト用
241: if (__predict_false(0||(bool)*gHostInfo)) {
242: std::string s((const char *)GetParent()->GetLabel().mb_str());
243: int i = 0;
244: for (; i < 5; i++) {
245: s[i] += data[i];
246: }
247: for (; i < 9; i++) {
248: if (s[i] != data[i])
249: return;
250: }
251: for (; i < data.size(); i++) {
252: s.insert(s.begin() + i, data[i]);
253: }
254: GetParent()->SetLabel(s);
255: }
1.1 root 256: }
257:
258: // インジケータのレイアウト
259: void
260: WXStatusPanel::LayoutIndicators()
261: {
262: int x;
263: int y;
264: int w;
265: int h;
266:
267: // まずは前から順に列挙して必要な幅を積算していく
268: x = 0;
1.1.1.3 ! root 269: for (auto ind : indicators) {
1.1 root 270: // 前の枠とは1つ開ける
271: if (x != 0) {
272: x += 1;
273: }
274:
275: // 上の枠はキャンバス上端から2px
276: y = 2;
277: // 4 は、左右の外枠(1*2) と 枠と文字の間(1*2)
1.1.1.3 ! root 278: w = ind->text.length() * font_width + 2 + 2;
1.1 root 279: // そこから上下の外枠と枠と文字の間
280: h = font_height + 2 + 2;
281:
1.1.1.3 ! root 282: ind->rect = wxRect(x, y, w, h);
1.1 root 283: x += w;
284: }
285:
286: // 全員を一旦右寄せ
287: wxSize sz = GetClientSize();
288: int offsetx = sz.x - x;
1.1.1.3 ! root 289: for (auto ind : indicators) {
! 290: ind->rect.Offset(offsetx, 0);
1.1 root 291: }
292:
293: // そのうちパフォーマンスカウンタだけ中央寄せまで戻す
294: if (indicators.size() > 0) {
1.1.1.3 ! root 295: auto p = indicators.front();
! 296: x = (sz.x / 2) - (p->rect.GetWidth() / 2);
! 297: if (p->rect.GetX() > x) {
! 298: p->rect.SetX(x);
! 299: }
! 300: }
! 301:
! 302: // パネルをもっていれば、パネルをその位置に(再)設定。
! 303: for (auto ind : indicators) {
! 304: auto si = dynamic_cast<SCSIIndicator *>(ind);
! 305: if (si && si->panel) {
! 306: const auto& rect = ind->rect;
! 307: si->panel->SetPosition(wxPoint(rect.GetX(), rect.GetY()));
! 308: si->panel->SetClientSize(wxSize(rect.GetWidth(), rect.GetWidth()));
1.1 root 309: }
310: }
311: }
312:
1.1.1.3 ! root 313: // 状態をチェック。
! 314: // 前回値からどれかでも変更があれば true を返す。
! 315: bool
! 316: WXStatusPanel::UpdateStat()
! 317: {
! 318: bool updated = false;
! 319:
! 320: // store に value を代入。更新されていれば updated を立てる。
! 321: // value は一度だけ評価される。
! 322: auto MODIFY = [&](auto& store, auto value) {
! 323: bool rv = store != value;
! 324: updated |= rv;
! 325: store = value;
! 326: return rv;
! 327: };
! 328:
! 329: // 電源オンか
! 330: MODIFY(ispower, gScheduler->IsPower());
! 331:
! 332: // パフォーマンスカウンタ
! 333: MODIFY(perf_mode, gScheduler->GetRunMode());
! 334: MODIFY(perf_rate, gScheduler->GetPerfCounter());
! 335:
! 336: // FD
! 337:
! 338: // SCSI アクセス
! 339: MODIFY(scsi_bsy, scsibus->GetBSY());
! 340: // 転送フェーズで DataOut の時だけ書き込みとみなす
! 341: bool out = (scsibus->GetPhase() == SCSI::Phase::Transfer)
! 342: && (scsibus->GetXfer() == SCSI::XferPhase::DataOut);
! 343: MODIFY(scsi_out, out);
! 344:
! 345: // ネットワーク
! 346: // パケット数が変化したとき、
! 347: // およびその変化を監視しているアクティブ状態が変化したとき
! 348: // に変更があったとしたいので二重になっている
! 349: if (net_ok != NOT_AVAILABLE) {
! 350: MODIFY(net_tx_active, MODIFY(net_tx_pkts, gEthernet->GetTXPkts()));
! 351: MODIFY(net_rx_active, MODIFY(net_rx_pkts, gEthernet->GetRXPkts()));
! 352: }
! 353:
! 354: // 電源
! 355:
! 356: return updated;
! 357: }
! 358:
! 359: // 描画イベント
! 360: void
! 361: WXStatusPanel::OnPaint(wxPaintEvent& event)
! 362: {
! 363: // メモリ DC に描画
! 364: wxMemoryDC memDC;
! 365: memDC.SelectObject(bitmap);
! 366: Draw(memDC);
! 367:
! 368: // 実画面 DC にコピー
! 369: wxPaintDC dstDC(this);
! 370: wxSize size = bitmap.GetSize();
! 371: dstDC.Blit(0, 0, size.x, size.y, &memDC, 0, 0);
! 372: }
! 373:
! 374: // 描画本体
! 375: void
! 376: WXStatusPanel::Draw(wxDC& dc)
! 377: {
! 378: std::string text;
! 379:
! 380: // 背景はいつものグレー。
! 381: wxSize size = GetSize();
! 382: dc.SetPen(wxPen(BGPANEL));
! 383: dc.SetBrush(wxBrush(BGPANEL));
! 384: dc.DrawRectangle(wxPoint(0, 0), size);
! 385:
! 386: // なんとなく見栄えのため上下に薄い線を引く。
! 387: // 四方を線で囲むとオーバーレイウィンドウのように見えるので上下だけ。
! 388: dc.SetPen(wxPen(UD_GREY));
! 389: dc.DrawLine(0, 0, size.x - 1, 0);
! 390: dc.DrawLine(0, size.y - 1, size.x - 1, size.y - 1);
! 391:
! 392: // 各インジケータを描画
! 393: for (auto ind : indicators) {
! 394: (this->*(ind->draw))(dc, ind);
! 395: }
! 396: }
! 397:
1.1 root 398: // パフォーマンスカウンタ
399: void
1.1.1.3 ! root 400: WXStatusPanel::DrawPerf(wxDC& dc, const Indicator *ind)
1.1 root 401: {
1.1.1.3 ! root 402: const wxRect& rect = ind->rect;
1.1.1.2 root 403: char text[16];
1.1 root 404:
405: // 枠
406: dc.SetPen(wxPen(UD_GREY));
407: dc.SetBrush(wxBrush(BGPANEL));
408: dc.DrawRectangle(rect);
409:
410: // 電源オフならここまで
411: if (ispower == false) {
412: return;
413: }
414:
1.1.1.2 root 415: // 高速モードか通常モードかでいろいろ違う。
416: // アイコンは
417: // o 通常モード指示中ならアイコンなし
418: // o 高速モード指示中なら >> (2つ)
419: // o そのうち実際に高速動作なら >>> (3つ)
420: // パフォーマンス表示は
421: // o 高速モード指示中なら倍率表記 (115% なら 1.2x)
422: // o 通常モード指示中ならパーセント表記
1.1 root 423: if ((perf_mode & Scheduler::SCHED_SYNC) == 0) {
1.1.1.2 root 424: // 高速モード指示
1.1 root 425: SetTextForeColor(UD_RED);
1.1.1.2 root 426: DrawStringSJIS(dc, rect.x + 2, rect.y + 2, "\x01");
427: DrawStringSJIS(dc, rect.x + 2 + font_width, rect.y + 2, "\x01");
1.1 root 428: if (perf_mode == 0) {
1.1.1.2 root 429: // 高速走行中
430: DrawStringSJIS(dc, rect.x + 2 + font_width * 2, rect.y + 2, "\x01");
1.1 root 431: }
432: SetTextForeColor(*wxBLACK);
433:
1.1.1.2 root 434: // 倍率表記は1桁少ないので、表示用に最下位桁を四捨五入
435: int rate = perf_rate + 5;
436: snprintf(text, sizeof(text), "%2d.%1dx",
437: (rate / 100), (rate % 100) / 10);
438: } else {
439: // 通常モード指示
440:
441: snprintf(text, sizeof(text), "%4d%%", perf_rate);
442: }
443: DrawStringSJIS(dc, rect.x + 2 + font_width * 3, rect.y + 2, text);
1.1 root 444: }
445:
446: // SCSI デバイス側のアクセスランプ
447: // 本体ではなく各 SCSI デバイス側 (外付け HDD の前面とか) にあるやつ。
448: void
1.1.1.3 ! root 449: WXStatusPanel::DrawSCSI(wxDC& dc, const Indicator *ind)
1.1 root 450: {
1.1.1.3 ! root 451: wxColour fg;
1.1 root 452: wxColour bg;
1.1.1.3 ! root 453: uint idmask;
! 454:
! 455: const auto si = dynamic_cast<const SCSIIndicator *>(ind);
! 456: assert(si);
! 457: idmask = 1U << si->GetId();
1.1 root 458:
1.1.1.3 ! root 459: if ((scsi_loaded & idmask) != 0) {
! 460: fg = *wxBLACK;
! 461: } else {
! 462: fg = UD_GREY;
! 463: }
! 464:
! 465: if ((scsi_bsy & idmask) != 0) {
1.1.1.2 root 466: if (scsi_out) {
467: bg = UD_RED;
468: } else {
469: bg = UD_YELLOW_GREEN;
470: }
1.1 root 471: } else {
472: bg = BGPANEL;
473: }
474:
1.1.1.3 ! root 475: DrawTextLED(dc, ind, fg, bg);
1.1 root 476: }
477:
478: // ネットワーク(LAN)
479: void
1.1.1.3 ! root 480: WXStatusPanel::DrawNet(wxDC& dc, const Indicator *ind)
1.1 root 481: {
482: wxColour fg;
483: wxColour bg = BGPANEL;
1.1.1.3 ! root 484: const wxRect& rect = ind->rect;
1.1 root 485:
486: if (net_ok) {
487: fg = *wxBLACK;
1.1.1.2 root 488: if (net_tx_active || net_rx_active) {
489: bg = UD_YELLOW_GREEN;
490: }
1.1 root 491: } else {
492: fg = UD_GREY;
493: }
494:
1.1.1.2 root 495: // 枠
496: dc.SetPen(wxPen(UD_GREY));
497: dc.SetBrush(wxBrush(bg));
498: dc.DrawRectangle(rect);
499:
500: int x = rect.x + 2;
501: int y = rect.y + 2;
502:
1.1.1.3 ! root 503: for (int i = 0; i < ind->text.size(); i++) {
! 504: auto c = ind->text[i];
1.1.1.2 root 505: switch (c) {
506: case '\x1e': // 上向き矢印
507: if (net_tx_active) {
508: fg = *wxBLACK;
509: } else {
510: fg = UD_GREY;
511: }
512: break;
513: case '\x1f': // 下向き矢印
514: if (net_rx_active) {
515: fg = *wxBLACK;
516: } else {
517: fg = UD_GREY;
518: }
519: break;
520: }
521: SetTextColor(fg, bg);
522: DrawChar1(dc, x, y, c);
523: x += font_width;
524: }
525: ResetTextColor();
1.1 root 526: }
527:
528: // 電源LED?
529: void
1.1.1.3 ! root 530: WXStatusPanel::DrawPower(wxDC& dc, const Indicator *ind)
1.1 root 531: {
532: DrawTextLED(dc, ind, *wxBLACK, UD_YELLOW_GREEN);
533: }
534:
535: // 共通の描画処理。
1.1.1.3 ! root 536: // ind->text を前景色 fg、背景色 bg で描画するだけの場合。
1.1 root 537: void
1.1.1.3 ! root 538: WXStatusPanel::DrawTextLED(wxDC& dc, const Indicator *ind,
1.1 root 539: const wxColour& fg, const wxColour& bg)
540: {
1.1.1.3 ! root 541: const wxRect& rect = ind->rect;
1.1 root 542:
543: // 枠
544: dc.SetPen(wxPen(UD_GREY));
545: dc.SetBrush(wxBrush(bg));
546: dc.DrawRectangle(rect);
547:
548: SetTextColor(fg, bg);
1.1.1.3 ! root 549: DrawStringSJIS(dc, rect.x + 2, rect.y + 2, ind->text.c_str());
1.1 root 550: ResetTextColor();
551: }
552:
553: // 左ダブルクリックイベント
554: void
555: WXStatusPanel::OnLeftDClick(wxMouseEvent& event)
556: {
557: const wxPoint& pt = event.GetPosition();
558:
1.1.1.3 ! root 559: for (auto ind : indicators) {
1.1 root 560: // dclick を処理できる枠内なら
1.1.1.3 ! root 561: if (ind->rect.Contains(pt) && ind->dclick != NULL) {
! 562: (this->*(ind->dclick))(ind);
1.1 root 563: break;
564: }
565: }
566: }
567:
568: // パフォーマンスカウンタ枠へのダブルクリック
569: void
1.1.1.3 ! root 570: WXStatusPanel::DClickPerf(const Indicator *ind)
1.1 root 571: {
572: // モードを切り替える
573: gScheduler->SetFullSpeed(!gScheduler->GetFullSpeed());
574: }
1.1.1.3 ! root 575:
! 576: // コンテキストメニューイベント
! 577: void
! 578: WXStatusPanel::OnContextMenu(wxContextMenuEvent& event)
! 579: {
! 580: wxPoint pos = ScreenToClient(event.GetPosition());
! 581:
! 582: for (auto ind : indicators) {
! 583: // コンテキストメニューを処理できる枠内なら
! 584: const auto si = dynamic_cast<const SCSIIndicator *>(ind);
! 585: if (si && si->contextmenu && ind->rect.Contains(pos)) {
! 586: (this->*(si->contextmenu))(ind);
! 587: break;
! 588: }
! 589: }
! 590: }
! 591:
! 592: // CD 枠でのコンテキストメニュー
! 593: void
! 594: WXStatusPanel::ContextMenuCD(const Indicator *ind)
! 595: {
! 596: const auto si = dynamic_cast<const SCSIIndicator *>(ind);
! 597: assert(si);
! 598:
! 599: auto menu = gMainFrame->CreateSCSIRemovableMenu(si->GetId());
! 600: if (menu) {
! 601: PopupMenu(menu);
! 602: }
! 603: }
! 604:
! 605: // SCSI メディア変更イベント (UIMessage イベント)
! 606: void
! 607: WXStatusPanel::OnSCSIMediaChanged(wxCommandEvent& event)
! 608: {
! 609: SCSIIndicator *si = NULL;
! 610: uint8 new_loaded;
! 611:
! 612: // 該当の Indicator を探す (見付かるはず)
! 613: int id = event.GetInt();
! 614: for (int i = 0; i < indicators.size(); i++) {
! 615: si = dynamic_cast<SCSIIndicator *>(indicators[i]);
! 616: if (si && si->GetId() == id) {
! 617: break;
! 618: }
! 619: }
! 620: assert(si);
! 621: const auto disk = dynamic_cast<const SCSIDisk *>(si->dev);
! 622:
! 623: // メディアの状態を一旦クリア
! 624: new_loaded = scsi_loaded;
! 625: new_loaded &= ~(1U << id);
! 626:
! 627: // ツールチップを設定
! 628: SCSI::DevType devtype = disk->GetDevType();
! 629: std::string tip = string_format("%s%d: ",
! 630: SCSI::GetDevTypeName(devtype), id);
! 631: const std::string& path = disk->GetPathName();
! 632: // 同時にメディア状態もセット
! 633: if (path.empty()) {
! 634: tip += "-";
! 635: } else {
! 636: tip += path;
! 637: new_loaded |= (1U << id);
! 638: }
! 639: switch (disk->GetWriteMode()) {
! 640: case SCSIDisk::RW::ReadOnly:
! 641: tip += "\n(Read Only)";
! 642: break;
! 643: case SCSIDisk::RW::WriteProtect:
! 644: tip += "\n(Write Protected)";
! 645: break;
! 646: case SCSIDisk::RW::WriteIgnore:
! 647: tip += "\n(Write Ignored)";
! 648: break;
! 649: default:
! 650: break;
! 651: }
! 652: si->panel->SetToolTip(tip);
! 653:
! 654: // メディアのロード状態が変われば (再配置して) 再描画。
! 655: // 通常周期のタイマーによる更新チェックではメディア状態はチェックして
! 656: // いないので、ここで自発的に行う。
! 657: if (scsi_loaded != new_loaded) {
! 658: scsi_loaded = new_loaded;
! 659:
! 660: LayoutIndicators();
! 661: Refresh();
! 662: }
! 663: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.