Implemented Save() functionality. ApplyChanges() can now create the editor
version of the file from any state. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6763 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -59,6 +59,21 @@ class ReplaceChange : public DataChange {
|
||||
//---------------------
|
||||
|
||||
|
||||
static int
|
||||
CompareOffsets(const off_t *a, const off_t *b)
|
||||
{
|
||||
if (*a < *b)
|
||||
return -1;
|
||||
else if (*a > *b)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
StateWatcher::StateWatcher(DataEditor &editor)
|
||||
:
|
||||
fEditor(editor)
|
||||
@@ -297,6 +312,7 @@ DataEditor::SetTo(BEntry &entry, const char *attribute)
|
||||
}
|
||||
|
||||
fLastChange = fFirstChange = NULL;
|
||||
fChangesFromSaved = 0;
|
||||
fView = NULL;
|
||||
fRealViewOffset = 0;
|
||||
fViewOffset = 0;
|
||||
@@ -327,6 +343,7 @@ DataEditor::AddChange(DataChange *change)
|
||||
|
||||
fChanges.AddItem(change);
|
||||
fLastChange = change;
|
||||
fChangesFromSaved++;
|
||||
|
||||
fLastChange->Apply(fRealViewOffset, fView, fRealViewSize);
|
||||
// ToDo: try to join changes
|
||||
@@ -390,11 +407,25 @@ DataEditor::ApplyChanges()
|
||||
if (fLastChange == NULL)
|
||||
return;
|
||||
|
||||
int32 count = fChanges.IndexOf(fLastChange) + 1;
|
||||
// ToDo: we need to support ascending *and* descending the list of changes
|
||||
|
||||
for (int32 i = 0; i < count; i++) {
|
||||
DataChange *change = fChanges.ItemAt(i);
|
||||
change->Apply(fRealViewOffset, fView, fRealViewSize);
|
||||
int32 firstIndex = fFirstChange != NULL ? fChanges.IndexOf(fFirstChange) + 1 : 0;
|
||||
int32 lastIndex = fChanges.IndexOf(fLastChange);
|
||||
|
||||
if (fChangesFromSaved >= 0) {
|
||||
// ascend changes
|
||||
|
||||
for (int32 i = firstIndex; i <= lastIndex; i++) {
|
||||
DataChange *change = fChanges.ItemAt(i);
|
||||
change->Apply(fRealViewOffset, fView, fRealViewSize);
|
||||
}
|
||||
} else {
|
||||
// descend changes
|
||||
|
||||
for (int32 i = firstIndex; i >= lastIndex; i--) {
|
||||
DataChange *change = fChanges.ItemAt(i);
|
||||
change->Revert(fRealViewOffset, fView, fRealViewSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,9 +433,91 @@ DataEditor::ApplyChanges()
|
||||
status_t
|
||||
DataEditor::Save()
|
||||
{
|
||||
// collect ranges of data we need to write
|
||||
BAutolock locker(this);
|
||||
|
||||
if (!IsModified())
|
||||
return B_OK;
|
||||
|
||||
StateWatcher watcher(*this);
|
||||
|
||||
// Do we need to ascend or descend the list of changes?
|
||||
|
||||
int32 firstIndex = fFirstChange != NULL ? fChanges.IndexOf(fFirstChange) + 1 : 0;
|
||||
int32 lastIndex = fChanges.IndexOf(fLastChange);
|
||||
if (fChangesFromSaved < 0 && firstIndex != lastIndex) {
|
||||
// swap indices
|
||||
ASSERT(firstIndex > lastIndex);
|
||||
|
||||
int32 temp = firstIndex - 1;
|
||||
firstIndex = lastIndex;
|
||||
lastIndex = temp;
|
||||
}
|
||||
|
||||
// Collect ranges of data we need to write.
|
||||
|
||||
// ToDo: This is a very simple implementation and could be drastically
|
||||
// improved by having items that save ranges, not just offsets. If Insert()
|
||||
// and Remove() are going to be implemented, it should be improved that
|
||||
// way to reduce the memory footprint to something acceptable.
|
||||
|
||||
BObjectList<off_t> list;
|
||||
for (int32 i = firstIndex; i <= lastIndex; i++) {
|
||||
DataChange *change = fChanges.ItemAt(i);
|
||||
|
||||
off_t offset, size;
|
||||
change->GetRange(FileSize(), offset, size);
|
||||
offset -= offset % BlockSize();
|
||||
|
||||
while (size > 0) {
|
||||
list.BinaryInsertCopyUnique(offset, CompareOffsets);
|
||||
offset += BlockSize();
|
||||
size -= BlockSize();
|
||||
}
|
||||
}
|
||||
|
||||
// read in data and apply changes, write it back to disk
|
||||
|
||||
off_t oldOffset = fViewOffset;
|
||||
|
||||
for (int32 i = 0; i < list.CountItems(); i++) {
|
||||
off_t offset = *list.ItemAt(i);
|
||||
|
||||
// load the data into our view
|
||||
SetViewOffset(offset, false);
|
||||
|
||||
if (fNeedsUpdate) {
|
||||
status_t status = Update();
|
||||
if (status < B_OK)
|
||||
return status;
|
||||
}
|
||||
|
||||
// save back to disk
|
||||
|
||||
// don't try to change the file size
|
||||
size_t size = fRealViewSize;
|
||||
if (fRealViewOffset + fRealViewSize > fSize)
|
||||
size = fSize - fRealViewOffset;
|
||||
|
||||
ssize_t bytesWritten;
|
||||
if (IsAttribute())
|
||||
bytesWritten = fFile.WriteAttr(fAttribute, fType, fRealViewOffset, fView, fRealViewSize);
|
||||
else
|
||||
bytesWritten = fFile.WriteAt(fRealViewOffset, fView, fRealViewSize);
|
||||
|
||||
if (bytesWritten < B_OK)
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
// update state
|
||||
|
||||
SetViewOffset(oldOffset, false);
|
||||
if (fNeedsUpdate)
|
||||
Update();
|
||||
|
||||
fChangesFromSaved = 0;
|
||||
fFirstChange = fLastChange;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -443,6 +556,7 @@ DataEditor::Undo()
|
||||
DataChange *undoChange = fLastChange;
|
||||
|
||||
int32 index = fChanges.IndexOf(undoChange);
|
||||
fChangesFromSaved--;
|
||||
undoChange->Revert(fRealViewOffset, fView, fRealViewSize);
|
||||
|
||||
if (index > 0)
|
||||
@@ -470,6 +584,7 @@ DataEditor::Redo()
|
||||
|
||||
int32 index = fChanges.IndexOf(fLastChange);
|
||||
fLastChange = fChanges.ItemAt(index + 1);
|
||||
fChangesFromSaved++;
|
||||
|
||||
fLastChange->Apply(fRealViewOffset, fView, fRealViewSize);
|
||||
|
||||
@@ -503,7 +618,7 @@ DataEditor::SetFileSize(off_t size)
|
||||
|
||||
|
||||
status_t
|
||||
DataEditor::SetViewOffset(off_t offset)
|
||||
DataEditor::SetViewOffset(off_t offset, bool sendNotices)
|
||||
{
|
||||
if (fView == NULL) {
|
||||
status_t status = SetViewSize(fViewSize);
|
||||
@@ -514,15 +629,27 @@ DataEditor::SetViewOffset(off_t offset)
|
||||
if (offset < 0 || offset > fSize)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if (offset == fViewOffset)
|
||||
return B_OK;
|
||||
|
||||
fRealViewOffset = (offset / fBlockSize) * fBlockSize;
|
||||
fViewOffset = offset;
|
||||
fNeedsUpdate = true;
|
||||
|
||||
SendNotices(kMsgDataEditorOffsetChange);
|
||||
if (sendNotices)
|
||||
SendNotices(kMsgDataEditorOffsetChange);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
DataEditor::SetViewOffset(off_t offset)
|
||||
{
|
||||
return SetViewOffset(offset, true);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
DataEditor::SetViewSize(size_t size)
|
||||
{
|
||||
|
||||
@@ -78,6 +78,7 @@ class DataEditor : public BLocker {
|
||||
private:
|
||||
friend class StateWatcher;
|
||||
|
||||
status_t SetViewOffset(off_t offset, bool sendNotices);
|
||||
void SendNotices(uint32 what, BMessage *message = NULL);
|
||||
void SendNotices(DataChange *change);
|
||||
status_t Update();
|
||||
@@ -97,6 +98,7 @@ class DataEditor : public BLocker {
|
||||
BObjectList<DataChange> fChanges;
|
||||
DataChange *fFirstChange;
|
||||
DataChange *fLastChange;
|
||||
int32 fChangesFromSaved;
|
||||
|
||||
uint8 *fView;
|
||||
off_t fRealViewOffset, fViewOffset;
|
||||
|
||||
Reference in New Issue
Block a user