xref: /haiku/src/apps/debugger/user_interface/gui/inspector_window/InspectorWindow.cpp (revision 10ba334855ad0d7297e23f15dea292f7dbdd231c)
1 /*
2  * Copyright 2011-2016, Rene Gollent, rene@gollent.com. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "InspectorWindow.h"
7 
8 #include <stdio.h>
9 
10 #include <Alert.h>
11 #include <Application.h>
12 #include <AutoLocker.h>
13 #include <Button.h>
14 #include <ControlLook.h>
15 #include <LayoutBuilder.h>
16 #include <ScrollView.h>
17 #include <StringView.h>
18 #include <TextControl.h>
19 
20 #include "AppMessageCodes.h"
21 #include "Architecture.h"
22 #include "CppLanguage.h"
23 #include "GuiTeamUiSettings.h"
24 #include "MemoryView.h"
25 #include "MessageCodes.h"
26 #include "Team.h"
27 #include "UserInterface.h"
28 #include "Value.h"
29 
30 
31 enum {
32 	MSG_NAVIGATE_PREVIOUS_BLOCK 		= 'npbl',
33 	MSG_NAVIGATE_NEXT_BLOCK				= 'npnl',
34 	MSG_MEMORY_BLOCK_RETRIEVED			= 'mbre',
35 	MSG_EDIT_CURRENT_BLOCK				= 'mecb',
36 	MSG_COMMIT_MODIFIED_BLOCK			= 'mcmb',
37 	MSG_REVERT_MODIFIED_BLOCK			= 'mrmb'
38 };
39 
40 
InspectorWindow(::Team * team,UserInterfaceListener * listener,BHandler * target)41 InspectorWindow::InspectorWindow(::Team* team, UserInterfaceListener* listener,
42 	BHandler* target)
43 	:
44 	BWindow(BRect(100, 100, 700, 500), "Inspector", B_TITLED_WINDOW,
45 		B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS),
46 	fListener(listener),
47 	fAddressInput(NULL),
48 	fHexMode(NULL),
49 	fTextMode(NULL),
50 	fWritableBlockIndicator(NULL),
51 	fMemoryView(NULL),
52 	fCurrentBlock(NULL),
53 	fCurrentAddress(0LL),
54 	fTeam(team),
55 	fLanguage(NULL),
56 	fExpressionInfo(NULL),
57 	fTarget(target)
58 {
59 	AutoLocker< ::Team> teamLocker(fTeam);
60 	fTeam->AddListener(this);
61 }
62 
63 
~InspectorWindow()64 InspectorWindow::~InspectorWindow()
65 {
66 	_SetCurrentBlock(NULL);
67 
68 	if (fLanguage != NULL)
69 		fLanguage->ReleaseReference();
70 
71 	if (fExpressionInfo != NULL) {
72 		fExpressionInfo->RemoveListener(this);
73 		fExpressionInfo->ReleaseReference();
74 	}
75 
76 	AutoLocker< ::Team> teamLocker(fTeam);
77 	fTeam->RemoveListener(this);
78 }
79 
80 
81 /* static */ InspectorWindow*
Create(::Team * team,UserInterfaceListener * listener,BHandler * target)82 InspectorWindow::Create(::Team* team, UserInterfaceListener* listener,
83 	BHandler* target)
84 {
85 	InspectorWindow* self = new InspectorWindow(team, listener, target);
86 
87 	try {
88 		self->_Init();
89 	} catch (...) {
90 		delete self;
91 		throw;
92 	}
93 
94 	return self;
95 }
96 
97 
98 void
_Init()99 InspectorWindow::_Init()
100 {
101 	fLanguage = new CppLanguage();
102 	fExpressionInfo = new ExpressionInfo();
103 	fExpressionInfo->AddListener(this);
104 
105 	BScrollView* scrollView;
106 
107 	BMenu* hexMenu = new BMenu("Hex Mode");
108 	BMessage* message = new BMessage(MSG_SET_HEX_MODE);
109 	message->AddInt32("mode", HexModeNone);
110 	BMenuItem* item = new BMenuItem("<None>", message, '0');
111 	hexMenu->AddItem(item);
112 	message = new BMessage(*message);
113 	message->ReplaceInt32("mode", HexMode8BitInt);
114 	item = new BMenuItem("8-bit integer", message, '1');
115 	hexMenu->AddItem(item);
116 	message = new BMessage(*message);
117 	message->ReplaceInt32("mode", HexMode16BitInt);
118 	item = new BMenuItem("16-bit integer", message, '2');
119 	hexMenu->AddItem(item);
120 	message = new BMessage(*message);
121 	message->ReplaceInt32("mode", HexMode32BitInt);
122 	item = new BMenuItem("32-bit integer", message, '3');
123 	hexMenu->AddItem(item);
124 	message = new BMessage(*message);
125 	message->ReplaceInt32("mode", HexMode64BitInt);
126 	item = new BMenuItem("64-bit integer", message, '4');
127 	hexMenu->AddItem(item);
128 
129 	BMenu* endianMenu = new BMenu("Endian Mode");
130 	message = new BMessage(MSG_SET_ENDIAN_MODE);
131 	message->AddInt32("mode", EndianModeLittleEndian);
132 	item = new BMenuItem("Little Endian", message, 'L');
133 	endianMenu->AddItem(item);
134 	message = new BMessage(*message);
135 	message->ReplaceInt32("mode", EndianModeBigEndian);
136 	item = new BMenuItem("Big Endian", message, 'B');
137 	endianMenu->AddItem(item);
138 
139 	BMenu* textMenu = new BMenu("Text Mode");
140 	message = new BMessage(MSG_SET_TEXT_MODE);
141 	message->AddInt32("mode", TextModeNone);
142 	item = new BMenuItem("<None>", message, 'N');
143 	textMenu->AddItem(item);
144 	message = new BMessage(*message);
145 	message->ReplaceInt32("mode", TextModeASCII);
146 	item = new BMenuItem("ASCII", message, 'A');
147 	textMenu->AddItem(item);
148 
149 	BLayoutBuilder::Group<>(this, B_VERTICAL)
150 		.SetInsets(B_USE_DEFAULT_SPACING)
151 		.AddGroup(B_HORIZONTAL)
152 			.Add(fAddressInput = new BTextControl("addrInput",
153 			"Target Address:", "",
154 			new BMessage(MSG_INSPECT_ADDRESS)))
155 			.Add(fPreviousBlockButton = new BButton("navPrevious", "<",
156 				new BMessage(MSG_NAVIGATE_PREVIOUS_BLOCK)))
157 			.Add(fNextBlockButton = new BButton("navNext", ">",
158 				new BMessage(MSG_NAVIGATE_NEXT_BLOCK)))
159 		.End()
160 		.AddGroup(B_HORIZONTAL)
161 			.Add(fHexMode = new BMenuField("hexMode", "Hex Mode:",
162 				hexMenu))
163 			.AddGlue()
164 			.Add(fEndianMode = new BMenuField("endianMode", "Endian Mode:",
165 				endianMenu))
166 			.AddGlue()
167 			.Add(fTextMode = new BMenuField("viewMode",  "Text Mode:",
168 				textMenu))
169 		.End()
170 		.Add(scrollView = new BScrollView("memory scroll",
171 			NULL, 0, false, true), 3.0f)
172 		.AddGroup(B_HORIZONTAL)
173 			.Add(fWritableBlockIndicator = new BStringView("writableIndicator",
174 				_GetCurrentWritableIndicator()))
175 			.AddGlue()
176 			.Add(fEditBlockButton = new BButton("editBlock", "Edit",
177 				new BMessage(MSG_EDIT_CURRENT_BLOCK)))
178 			.Add(fCommitBlockButton = new BButton("commitBlock", "Commit",
179 				new BMessage(MSG_COMMIT_MODIFIED_BLOCK)))
180 			.Add(fRevertBlockButton = new BButton("revertBlock", "Revert",
181 				new BMessage(MSG_REVERT_MODIFIED_BLOCK)))
182 		.End()
183 	.End();
184 
185 	fHexMode->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
186 	fEndianMode->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
187 	fTextMode->SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
188 
189 	int32 targetEndian = fTeam->GetArchitecture()->IsBigEndian()
190 		? EndianModeBigEndian : EndianModeLittleEndian;
191 
192 	scrollView->SetTarget(fMemoryView = MemoryView::Create(fTeam, this));
193 
194 	fAddressInput->SetTarget(this);
195 	fPreviousBlockButton->SetTarget(this);
196 	fNextBlockButton->SetTarget(this);
197 	fPreviousBlockButton->SetEnabled(false);
198 	fNextBlockButton->SetEnabled(false);
199 
200 	fEditBlockButton->SetTarget(this);
201 	fCommitBlockButton->SetTarget(this);
202 	fRevertBlockButton->SetTarget(this);
203 
204 	fEditBlockButton->SetEnabled(false);
205 	fCommitBlockButton->Hide();
206 	fRevertBlockButton->Hide();
207 
208 	hexMenu->SetLabelFromMarked(true);
209 	hexMenu->SetTargetForItems(fMemoryView);
210 	endianMenu->SetLabelFromMarked(true);
211 	endianMenu->SetTargetForItems(fMemoryView);
212 	textMenu->SetLabelFromMarked(true);
213 	textMenu->SetTargetForItems(fMemoryView);
214 
215 	// default to 8-bit format w/ text display
216 	hexMenu->ItemAt(1)->SetMarked(true);
217 	textMenu->ItemAt(1)->SetMarked(true);
218 
219 	if (targetEndian == EndianModeBigEndian)
220 		endianMenu->ItemAt(1)->SetMarked(true);
221 	else
222 		endianMenu->ItemAt(0)->SetMarked(true);
223 
224 	fAddressInput->TextView()->MakeFocus(true);
225 
226 	AddShortcut(B_LEFT_ARROW, B_COMMAND_KEY, new BMessage(
227 			MSG_NAVIGATE_PREVIOUS_BLOCK));
228 	AddShortcut(B_RIGHT_ARROW, B_COMMAND_KEY, new BMessage(
229 			MSG_NAVIGATE_NEXT_BLOCK));
230 }
231 
232 
233 void
MessageReceived(BMessage * message)234 InspectorWindow::MessageReceived(BMessage* message)
235 {
236 	switch (message->what) {
237 		case MSG_THREAD_STATE_CHANGED:
238 		{
239 			::Thread* thread;
240 			if (message->FindPointer("thread",
241 					reinterpret_cast<void**>(&thread)) != B_OK) {
242 				break;
243 			}
244 
245 			BReference< ::Thread> threadReference(thread, true);
246 			if (thread->State() == THREAD_STATE_STOPPED) {
247 				if (fCurrentBlock != NULL) {
248 					_SetCurrentBlock(NULL);
249 					_SetToAddress(fCurrentAddress);
250 				}
251 			}
252 			break;
253 		}
254 		case MSG_INSPECT_ADDRESS:
255 		{
256 			target_addr_t address = 0;
257 			if (message->FindUInt64("address", &address) != B_OK) {
258 				if (fAddressInput->TextView()->TextLength() == 0)
259 					break;
260 
261 				fExpressionInfo->SetTo(fAddressInput->Text());
262 
263 				fListener->ExpressionEvaluationRequested(fLanguage,
264 					fExpressionInfo);
265 			} else
266 				_SetToAddress(address);
267 			break;
268 		}
269 		case MSG_EXPRESSION_EVALUATED:
270 		{
271 			BString errorMessage;
272 			BReference<ExpressionResult> reference;
273 			ExpressionResult* value = NULL;
274 			if (message->FindPointer("value",
275 					reinterpret_cast<void**>(&value)) == B_OK) {
276 				reference.SetTo(value, true);
277 				if (value->Kind() == EXPRESSION_RESULT_KIND_PRIMITIVE) {
278 					Value* primitive = value->PrimitiveValue();
279 					BVariant variantValue;
280 					primitive->ToVariant(variantValue);
281 					if (variantValue.Type() == B_STRING_TYPE) {
282 						errorMessage.SetTo(variantValue.ToString());
283 					} else {
284 						_SetToAddress(variantValue.ToUInt64());
285 						break;
286 					}
287 				}
288 			} else {
289 				status_t result = message->FindInt32("result");
290 				errorMessage.SetToFormat("Failed to evaluate expression: %s",
291 					strerror(result));
292 			}
293 
294 			BAlert* alert = new(std::nothrow) BAlert("Inspect Address",
295 				errorMessage.String(), "Close");
296 			if (alert != NULL)
297 				alert->Go();
298 			break;
299 		}
300 
301 		case MSG_NAVIGATE_PREVIOUS_BLOCK:
302 		case MSG_NAVIGATE_NEXT_BLOCK:
303 		{
304 			if (fCurrentBlock != NULL) {
305 				target_addr_t address = fCurrentBlock->BaseAddress();
306 				if (message->what == MSG_NAVIGATE_PREVIOUS_BLOCK)
307 					address -= fCurrentBlock->Size();
308 				else
309 					address += fCurrentBlock->Size();
310 
311 				BMessage setMessage(MSG_INSPECT_ADDRESS);
312 				setMessage.AddUInt64("address", address);
313 				PostMessage(&setMessage);
314 			}
315 			break;
316 		}
317 		case MSG_MEMORY_BLOCK_RETRIEVED:
318 		{
319 			TeamMemoryBlock* block = NULL;
320 			status_t result;
321 			if (message->FindPointer("block",
322 					reinterpret_cast<void **>(&block)) != B_OK
323 				|| message->FindInt32("result", &result) != B_OK) {
324 				break;
325 			}
326 
327 			if (result == B_OK) {
328 				_SetCurrentBlock(block);
329 				fPreviousBlockButton->SetEnabled(true);
330 				fNextBlockButton->SetEnabled(true);
331 			} else {
332 				BString errorMessage;
333 				errorMessage.SetToFormat("Unable to read address 0x%" B_PRIx64
334 					": %s", block->BaseAddress(), strerror(result));
335 
336 				BAlert* alert = new(std::nothrow) BAlert("Inspect address",
337 					errorMessage.String(), "Close");
338 				if (alert == NULL)
339 					break;
340 
341 				alert->Go(NULL);
342 				block->ReleaseReference();
343 			}
344 			break;
345 		}
346 		case MSG_EDIT_CURRENT_BLOCK:
347 		{
348 			_SetEditMode(true);
349 			break;
350 		}
351 		case MSG_MEMORY_DATA_CHANGED:
352 		{
353 			if (fCurrentBlock == NULL)
354 				break;
355 
356 			target_addr_t address;
357 			if (message->FindUInt64("address", &address) == B_OK
358 				&& address >= fCurrentBlock->BaseAddress()
359 				&& address < fCurrentBlock->BaseAddress()
360 					+ fCurrentBlock->Size()) {
361 				fCurrentBlock->Invalidate();
362 				_SetEditMode(false);
363 				fListener->InspectRequested(address, this);
364 			}
365 			break;
366 		}
367 		case MSG_COMMIT_MODIFIED_BLOCK:
368 		{
369 			// TODO: this could conceivably be extended to detect the
370 			// individual modified regions and only write those back.
371 			// That would require potentially submitting multiple separate
372 			// write requests, and thus require tracking all the writes being
373 			// waited upon for completion.
374 			fListener->MemoryWriteRequested(fCurrentBlock->BaseAddress(),
375 				fMemoryView->GetEditedData(), fCurrentBlock->Size());
376 			break;
377 		}
378 		case MSG_REVERT_MODIFIED_BLOCK:
379 		{
380 			_SetEditMode(false);
381 			break;
382 		}
383 		default:
384 		{
385 			BWindow::MessageReceived(message);
386 			break;
387 		}
388 	}
389 }
390 
391 
392 bool
QuitRequested()393 InspectorWindow::QuitRequested()
394 {
395 	BMessage settings(MSG_INSPECTOR_WINDOW_CLOSED);
396 	SaveSettings(settings);
397 
398 	BMessenger(fTarget).SendMessage(&settings);
399 	return true;
400 }
401 
402 
403 void
ThreadStateChanged(const Team::ThreadEvent & event)404 InspectorWindow::ThreadStateChanged(const Team::ThreadEvent& event)
405 {
406 	BMessage message(MSG_THREAD_STATE_CHANGED);
407 	BReference< ::Thread> threadReference(event.GetThread());
408 	message.AddPointer("thread", threadReference.Get());
409 
410 	if (PostMessage(&message) == B_OK)
411 		threadReference.Detach();
412 }
413 
414 
415 void
MemoryChanged(const Team::MemoryChangedEvent & event)416 InspectorWindow::MemoryChanged(const Team::MemoryChangedEvent& event)
417 {
418 	BMessage message(MSG_MEMORY_DATA_CHANGED);
419 	message.AddUInt64("address", event.GetTargetAddress());
420 	message.AddUInt64("size", event.GetSize());
421 
422 	PostMessage(&message);
423 }
424 
425 
426 void
MemoryBlockRetrieved(TeamMemoryBlock * block)427 InspectorWindow::MemoryBlockRetrieved(TeamMemoryBlock* block)
428 {
429 	BMessage message(MSG_MEMORY_BLOCK_RETRIEVED);
430 	message.AddPointer("block", block);
431 	message.AddInt32("result", B_OK);
432 	PostMessage(&message);
433 }
434 
435 
436 void
MemoryBlockRetrievalFailed(TeamMemoryBlock * block,status_t result)437 InspectorWindow::MemoryBlockRetrievalFailed(TeamMemoryBlock* block,
438 	status_t result)
439 {
440 	BMessage message(MSG_MEMORY_BLOCK_RETRIEVED);
441 	message.AddPointer("block", block);
442 	message.AddInt32("result", result);
443 	PostMessage(&message);
444 }
445 
446 
447 void
TargetAddressChanged(target_addr_t address)448 InspectorWindow::TargetAddressChanged(target_addr_t address)
449 {
450 	AutoLocker<BLooper> lock(this);
451 	if (lock.IsLocked()) {
452 		fCurrentAddress = address;
453 		BString computedAddress;
454 		computedAddress.SetToFormat("0x%" B_PRIx64, address);
455 		fAddressInput->SetText(computedAddress.String());
456 	}
457 }
458 
459 
460 void
HexModeChanged(int32 newMode)461 InspectorWindow::HexModeChanged(int32 newMode)
462 {
463 	AutoLocker<BLooper> lock(this);
464 	if (lock.IsLocked()) {
465 		BMenu* menu = fHexMode->Menu();
466 		if (newMode < 0 || newMode > menu->CountItems())
467 			return;
468 		BMenuItem* item = menu->ItemAt(newMode);
469 		item->SetMarked(true);
470 	}
471 }
472 
473 
474 void
EndianModeChanged(int32 newMode)475 InspectorWindow::EndianModeChanged(int32 newMode)
476 {
477 	AutoLocker<BLooper> lock(this);
478 	if (lock.IsLocked()) {
479 		BMenu* menu = fEndianMode->Menu();
480 		if (newMode < 0 || newMode > menu->CountItems())
481 			return;
482 		BMenuItem* item = menu->ItemAt(newMode);
483 		item->SetMarked(true);
484 	}
485 }
486 
487 
488 void
TextModeChanged(int32 newMode)489 InspectorWindow::TextModeChanged(int32 newMode)
490 {
491 	AutoLocker<BLooper> lock(this);
492 	if (lock.IsLocked()) {
493 		BMenu* menu = fTextMode->Menu();
494 		if (newMode < 0 || newMode > menu->CountItems())
495 			return;
496 		BMenuItem* item = menu->ItemAt(newMode);
497 		item->SetMarked(true);
498 	}
499 }
500 
501 
502 void
ExpressionEvaluated(ExpressionInfo * info,status_t result,ExpressionResult * value)503 InspectorWindow::ExpressionEvaluated(ExpressionInfo* info, status_t result,
504 	ExpressionResult* value)
505 {
506 	BMessage message(MSG_EXPRESSION_EVALUATED);
507 	message.AddInt32("result", result);
508 	BReference<ExpressionResult> reference;
509 	if (value != NULL) {
510 		reference.SetTo(value);
511 		message.AddPointer("value", value);
512 	}
513 
514 	if (PostMessage(&message) == B_OK)
515 		reference.Detach();
516 }
517 
518 
519 status_t
LoadSettings(const GuiTeamUiSettings & settings)520 InspectorWindow::LoadSettings(const GuiTeamUiSettings& settings)
521 {
522 	AutoLocker<BLooper> lock(this);
523 	if (!lock.IsLocked())
524 		return B_ERROR;
525 
526 	BMessage inspectorSettings;
527 	if (settings.Settings("inspectorWindow", inspectorSettings) != B_OK)
528 		return B_OK;
529 
530 	BRect frameRect;
531 	if (inspectorSettings.FindRect("frame", &frameRect) == B_OK) {
532 		ResizeTo(frameRect.Width(), frameRect.Height());
533 		MoveTo(frameRect.left, frameRect.top);
534 	}
535 
536 	_LoadMenuFieldMode(fHexMode, "Hex", inspectorSettings);
537 	_LoadMenuFieldMode(fEndianMode, "Endian", inspectorSettings);
538 	_LoadMenuFieldMode(fTextMode, "Text", inspectorSettings);
539 
540 	return B_OK;
541 }
542 
543 
544 status_t
SaveSettings(BMessage & settings)545 InspectorWindow::SaveSettings(BMessage& settings)
546 {
547 	AutoLocker<BLooper> lock(this);
548 	if (!lock.IsLocked())
549 		return B_ERROR;
550 
551 	settings.MakeEmpty();
552 
553 	status_t error = settings.AddRect("frame", Frame());
554 	if (error != B_OK)
555 		return error;
556 
557 	error = _SaveMenuFieldMode(fHexMode, "Hex", settings);
558 	if (error != B_OK)
559 		return error;
560 
561 	error = _SaveMenuFieldMode(fEndianMode, "Endian", settings);
562 	if (error != B_OK)
563 		return error;
564 
565 	error = _SaveMenuFieldMode(fTextMode, "Text", settings);
566 	if (error != B_OK)
567 		return error;
568 
569 	return B_OK;
570 }
571 
572 
573 void
_LoadMenuFieldMode(BMenuField * field,const char * name,const BMessage & settings)574 InspectorWindow::_LoadMenuFieldMode(BMenuField* field, const char* name,
575 	const BMessage& settings)
576 {
577 	BString fieldName;
578 	int32 mode;
579 	fieldName.SetToFormat("%sMode", name);
580 	if (settings.FindInt32(fieldName.String(), &mode) == B_OK) {
581 		BMenu* menu = field->Menu();
582 		for (int32 i = 0; i < menu->CountItems(); i++) {
583 			BInvoker* item = menu->ItemAt(i);
584 			if (item->Message()->FindInt32("mode") == mode) {
585 				item->Invoke();
586 				break;
587 			}
588 		}
589 	}
590 }
591 
592 
593 status_t
_SaveMenuFieldMode(BMenuField * field,const char * name,BMessage & settings)594 InspectorWindow::_SaveMenuFieldMode(BMenuField* field, const char* name,
595 	BMessage& settings)
596 {
597 	BMenuItem* item = field->Menu()->FindMarked();
598 	if (item && item->Message()) {
599 		int32 mode = item->Message()->FindInt32("mode");
600 		BString fieldName;
601 		fieldName.SetToFormat("%sMode", name);
602 		return settings.AddInt32(fieldName.String(), mode);
603 	}
604 
605 	return B_OK;
606 }
607 
608 
609 void
_SetToAddress(target_addr_t address)610 InspectorWindow::_SetToAddress(target_addr_t address)
611 {
612 	fCurrentAddress = address;
613 	if (fCurrentBlock == NULL
614 		|| !fCurrentBlock->Contains(address)) {
615 		fListener->InspectRequested(address, this);
616 	} else
617 		fMemoryView->SetTargetAddress(fCurrentBlock, address);
618 }
619 
620 
621 void
_SetCurrentBlock(TeamMemoryBlock * block)622 InspectorWindow::_SetCurrentBlock(TeamMemoryBlock* block)
623 {
624 	AutoLocker< ::Team> teamLocker(fTeam);
625 	if (fCurrentBlock != NULL) {
626 		fCurrentBlock->RemoveListener(this);
627 		fCurrentBlock->ReleaseReference();
628 	}
629 
630 	fCurrentBlock = block;
631 	fMemoryView->SetTargetAddress(fCurrentBlock, fCurrentAddress);
632 	_UpdateWritableOptions();
633 }
634 
635 
636 bool
_GetWritableState() const637 InspectorWindow::_GetWritableState() const
638 {
639 	return fCurrentBlock != NULL ? fCurrentBlock->IsWritable() : false;
640 }
641 
642 
643 void
_SetEditMode(bool enabled)644 InspectorWindow::_SetEditMode(bool enabled)
645 {
646 	if (enabled == fMemoryView->GetEditMode())
647 		return;
648 
649 	status_t error = fMemoryView->SetEditMode(enabled);
650 	if (error != B_OK)
651 		return;
652 
653 	if (enabled) {
654 		fEditBlockButton->Hide();
655 		fCommitBlockButton->Show();
656 		fRevertBlockButton->Show();
657 	} else {
658 		fEditBlockButton->Show();
659 		fCommitBlockButton->Hide();
660 		fRevertBlockButton->Hide();
661 	}
662 
663 	fHexMode->SetEnabled(!enabled);
664 	fEndianMode->SetEnabled(!enabled);
665 
666 	// while the block is being edited, disable block navigation controls.
667 	fAddressInput->SetEnabled(!enabled);
668 	fPreviousBlockButton->SetEnabled(!enabled);
669 	fNextBlockButton->SetEnabled(!enabled);
670 
671 	InvalidateLayout();
672 }
673 
674 
675 void
_UpdateWritableOptions()676 InspectorWindow::_UpdateWritableOptions()
677 {
678 	fEditBlockButton->SetEnabled(_GetWritableState());
679 	_UpdateWritableIndicator();
680 }
681 
682 
683 void
_UpdateWritableIndicator()684 InspectorWindow::_UpdateWritableIndicator()
685 {
686 	fWritableBlockIndicator->SetText(_GetCurrentWritableIndicator());
687 }
688 
689 
690 const char*
_GetCurrentWritableIndicator() const691 InspectorWindow::_GetCurrentWritableIndicator() const
692 {
693 	static char buffer[32];
694 	snprintf(buffer, sizeof(buffer), "Writable: %s", fCurrentBlock == NULL
695 			? "N/A" : fCurrentBlock->IsWritable() ? "Yes" : "No");
696 
697 	return buffer;
698 }
699