2 * Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3 * Distributed under the terms of the MIT License.
7 #include "DataEditor.h"
10 #include <NodeMonitor.h>
12 #include <Directory.h>
28 //#define TRACE_DATA_EDITOR
29 #ifdef TRACE_DATA_EDITOR
30 # define TRACE(x) printf x
38 StateWatcher(DataEditor
&editor
);
50 virtual ~DataChange();
52 virtual void Apply(off_t offset
, uint8
*buffer
, size_t size
) = 0;
53 virtual void Revert(off_t offset
, uint8
*buffer
, size_t size
) = 0;
54 virtual bool Merge(DataChange
*change
);
55 virtual void GetRange(off_t fileSize
, off_t
&_offset
, off_t
&_size
) = 0;
58 class ReplaceChange
: public DataChange
{
60 ReplaceChange(off_t offset
, const uint8
*data
, size_t size
);
63 virtual void Apply(off_t offset
, uint8
*buffer
, size_t size
);
64 virtual void Revert(off_t offset
, uint8
*buffer
, size_t size
);
65 virtual bool Merge(DataChange
*change
);
66 virtual void GetRange(off_t fileSize
, off_t
&_offset
, off_t
&_size
);
69 void Normalize(off_t bufferOffset
, size_t bufferSize
,
70 off_t
&offset
, size_t &dataOffset
, size_t &size
);
79 //---------------------
82 #ifdef TRACE_DATA_EDITOR
84 #define DUMPED_BLOCK_SIZE 16
87 dump_block(const uint8
*buffer
, int size
, const char *prefix
)
89 for (int i
= 0; i
< size
;) {
93 for (; i
< start
+ DUMPED_BLOCK_SIZE
; i
++) {
100 printf("%02x", *(unsigned char *)(buffer
+ i
));
104 for (i
= start
; i
< start
+ DUMPED_BLOCK_SIZE
; i
++) {
118 #endif // TRACE_DATA_EDITOR
122 CompareCaseInsensitive(const uint8
*a
, const uint8
*b
, size_t size
)
124 for (size_t i
= 0; i
< size
; i
++) {
125 uint8 diff
= tolower(a
[i
]) - tolower(b
[i
]);
135 CompareOffsets(const off_t
*a
, const off_t
*b
)
149 StateWatcher::StateWatcher(DataEditor
&editor
)
153 fCouldUndo
= editor
.CanUndo();
154 fCouldRedo
= editor
.CanRedo();
155 fWasModified
= editor
.IsModified();
159 StateWatcher::~StateWatcher()
162 if (fCouldRedo
!= fEditor
.CanRedo())
163 update
.AddBool("can_redo", fEditor
.CanRedo());
164 if (fCouldUndo
!= fEditor
.CanUndo())
165 update
.AddBool("can_undo", fEditor
.CanUndo());
166 if (fWasModified
!= fEditor
.IsModified())
167 update
.AddBool("modified", fEditor
.IsModified());
169 // only send an update if we have to report anything
170 if (!update
.IsEmpty())
171 fEditor
.SendNotices(kMsgDataEditorStateChange
, &update
);
178 DataChange::~DataChange()
184 DataChange::Merge(DataChange
*change
)
193 ReplaceChange::ReplaceChange(off_t offset
, const uint8
*data
, size_t size
)
197 fNewData
= (uint8
*)malloc(size
);
198 fOldData
= (uint8
*)malloc(size
);
199 if (fNewData
!= NULL
&& fOldData
!= NULL
) {
200 memcpy(fNewData
, data
, size
);
207 ReplaceChange::~ReplaceChange()
214 /** Normalizes the supplied offset & size pair to be limited by
215 * the buffer offset and size.
216 * All parameters must have been initialized before calling this
221 ReplaceChange::Normalize(off_t bufferOffset
, size_t bufferSize
, off_t
&offset
,
222 size_t &dataOffset
, size_t &size
)
224 if (fOffset
< bufferOffset
) {
225 offset
= bufferOffset
;
226 dataOffset
= bufferOffset
- fOffset
;
230 if (offset
+ size
> bufferOffset
+ bufferSize
)
231 size
= bufferOffset
+ bufferSize
- offset
;
236 ReplaceChange::Apply(off_t bufferOffset
, uint8
*buffer
, size_t bufferSize
)
238 // is it in our range?
239 if (fOffset
> (bufferOffset
+ (off_t
)bufferSize
)
240 || (fOffset
+ (off_t
)fSize
) < bufferOffset
) {
244 // don't change anything outside the supplied buffer
245 off_t offset
= fOffset
;
246 size_t dataOffset
= 0;
248 Normalize(bufferOffset
, bufferSize
, offset
, dataOffset
, size
);
252 #ifdef TRACE_DATA_EDITOR
253 printf("Apply %p (buffer offset = %Ld):\n", this, bufferOffset
);
254 dump_block(buffer
+ offset
- bufferOffset
, size
, "old:");
255 dump_block(fNewData
+ dataOffset
, size
, "new:");
258 // now we can safely exchange the buffer!
259 memcpy(fOldData
+ dataOffset
, buffer
+ offset
- bufferOffset
, size
);
260 memcpy(buffer
+ offset
- bufferOffset
, fNewData
+ dataOffset
, size
);
265 ReplaceChange::Revert(off_t bufferOffset
, uint8
*buffer
, size_t bufferSize
)
267 // is it in our range?
268 if (fOffset
- bufferOffset
> (off_t
)bufferSize
269 || fOffset
+ (off_t
)fSize
< bufferOffset
) {
273 // don't change anything outside the supplied buffer
274 off_t offset
= fOffset
;
275 size_t dataOffset
= 0;
277 Normalize(bufferOffset
, bufferSize
, offset
, dataOffset
, size
);
281 #ifdef TRACE_DATA_EDITOR
282 printf("Revert %p (buffer offset = %Ld):\n", this, bufferOffset
);
283 dump_block(buffer
+ offset
- bufferOffset
, size
, "old:");
284 dump_block(fOldData
+ dataOffset
, size
, "new:");
287 // now we can safely revert the buffer!
288 memcpy(buffer
+ offset
- bufferOffset
, fOldData
+ dataOffset
, size
);
293 ReplaceChange::Merge(DataChange
*_change
)
295 ReplaceChange
*change
= dynamic_cast<ReplaceChange
*>(_change
);
299 if (change
->fOffset
+ change
->fSize
== fOffset
+ fSize
300 && change
->fSize
== 1) {
301 // this is a special case - the new change changed the last byte of
302 // the old change: we do this since the same byte is changed twice
303 // in hex mode editing.
304 fNewData
[fSize
- 1] = change
->fNewData
[0];
305 #ifdef TRACE_DATA_EDITOR
306 printf("Merge one byte %p (offset = %Ld, size = %lu):\n", this, fOffset
,
308 dump_block(fOldData
, fSize
, "old:");
309 dump_block(fNewData
, fSize
, "new:");
314 // are the changes adjacent?
316 if (change
->fOffset
+ (off_t
)change
->fSize
!= fOffset
317 && fOffset
+ (off_t
)fSize
!= change
->fOffset
)
320 // okay, we can try to merge the two changes
322 uint8
*newData
= fOffset
< change
->fOffset
? fNewData
: change
->fNewData
;
323 size_t size
= fSize
+ change
->fSize
;
325 if ((newData
= (uint8
*)realloc(newData
, size
)) == NULL
)
328 uint8
*oldData
= fOffset
< change
->fOffset
? fOldData
: change
->fOldData
;
329 if ((oldData
= (uint8
*)realloc(oldData
, size
)) == NULL
) {
330 fNewData
= (uint8
*)realloc(newData
, fSize
);
331 // if this fails, we can't do anything about it
335 if (fOffset
< change
->fOffset
) {
336 memcpy(newData
+ fSize
, change
->fNewData
, change
->fSize
);
337 memcpy(oldData
+ fSize
, change
->fOldData
, change
->fSize
);
339 memcpy(newData
+ change
->fSize
, fNewData
, fSize
);
340 memcpy(oldData
+ change
->fSize
, fOldData
, fSize
);
341 change
->fNewData
= fNewData
;
342 change
->fOldData
= fOldData
;
343 // transfer ownership, so that they will be deleted with the change
344 fOffset
= change
->fOffset
;
351 #ifdef TRACE_DATA_EDITOR
352 printf("Merge %p (offset = %Ld, size = %lu):\n", this, fOffset
, fSize
);
353 dump_block(fOldData
, fSize
, "old:");
354 dump_block(fNewData
, fSize
, "new:");
362 ReplaceChange::GetRange(off_t
/*fileSize*/, off_t
&_offset
, off_t
&_size
)
372 DataEditor::DataEditor()
374 BLocker("data view"),
380 DataEditor::DataEditor(entry_ref
&ref
, const char *attribute
)
382 BLocker("data view"),
385 SetTo(ref
, attribute
);
389 DataEditor::DataEditor(BEntry
&entry
, const char *attribute
)
391 BLocker("data view"),
394 SetTo(entry
, attribute
);
398 DataEditor::DataEditor(const DataEditor
&editor
)
400 BLocker("data view"),
406 DataEditor::~DataEditor()
408 free((void*)fAttribute
);
413 DataEditor::SetTo(const char *path
, const char *attribute
)
416 return SetTo(entry
, attribute
);
421 DataEditor::SetTo(entry_ref
&ref
, const char *attribute
)
424 return SetTo(entry
, attribute
);
429 DataEditor::SetTo(BEntry
&entry
, const char *attribute
)
432 fLastChange
= fFirstChange
= NULL
;
433 fChangesFromSaved
= 0;
437 fRealViewSize
= fViewSize
= fBlockSize
= 512;
439 free((void*)fAttribute
);
440 if (attribute
!= NULL
)
441 fAttribute
= strdup(attribute
);
446 status_t status
= entry
.GetStat(&stat
);
450 entry
.GetRef(&fAttributeRef
);
452 bool isFileSystem
= false;
454 if (entry
.IsDirectory()) {
455 // we redirect root directories to their volumes
456 BDirectory
directory(&entry
);
457 if (directory
.InitCheck() == B_OK
&& directory
.IsRootDirectory()) {
459 if (fs_stat_dev(stat
.st_dev
, &info
) != 0)
462 status
= entry
.SetTo(info
.device_name
);
466 entry
.GetStat(&stat
);
468 fBlockSize
= info
.block_size
;
469 if (fBlockSize
> 0 && fBlockSize
<= 65536)
474 status
= fFile
.SetTo(&entry
, B_READ_WRITE
);
476 // try to open read only
477 status
= fFile
.SetTo(&entry
, B_READ_ONLY
);
486 fIsDevice
= S_ISBLK(stat
.st_mode
) || S_ISCHR(stat
.st_mode
);
489 BNode
node(&fAttributeRef
);
491 status
= node
.GetAttrInfo(fAttribute
, &info
);
492 if (status
!= B_OK
) {
499 } else if (fIsDevice
) {
500 device_geometry geometry
;
501 int device
= fFile
.Dup();
502 if (device
< 0 || ioctl(device
, B_GET_GEOMETRY
, &geometry
) < 0) {
510 fSize
= 1LL * geometry
.head_count
* geometry
.cylinder_count
511 * geometry
.sectors_per_track
* geometry
.bytes_per_sector
;
515 fBlockSize
= geometry
.bytes_per_sector
;
516 } else if (entry
.IsDirectory() || entry
.IsSymLink()) {
520 status
= fFile
.GetSize(&fSize
);
530 fRealViewSize
= fViewSize
= fBlockSize
;
538 DataEditor::InitCheck()
540 return fFile
.InitCheck();
545 DataEditor::AddChange(DataChange
*change
)
550 StateWatcher
watcher(*this);
551 // update state observers
554 change
->Apply(fRealViewOffset
, fView
, fRealViewSize
);
559 // try to join changes
560 if (fLastChange
== NULL
|| !fLastChange
->Merge(change
)) {
561 fChanges
.AddItem(change
);
562 fLastChange
= change
;
570 DataEditor::Replace(off_t offset
, const uint8
*data
, size_t length
)
573 return B_NOT_ALLOWED
;
575 BAutolock
locker(this);
579 if (offset
+ (off_t
)length
> fSize
)
580 length
= fSize
- offset
;
583 status_t status
= Update();
588 ReplaceChange
*change
= new ReplaceChange(offset
, data
, length
);
596 DataEditor::Remove(off_t offset
, off_t length
)
599 return B_NOT_ALLOWED
;
601 BAutolock
locker(this);
603 // not yet implemented
604 // ToDo: this needs some changes to the whole change mechanism
611 DataEditor::Insert(off_t offset
, const uint8
*text
, size_t length
)
614 return B_NOT_ALLOWED
;
616 BAutolock
locker(this);
618 // not yet implemented
619 // ToDo: this needs some changes to the whole change mechanism
626 DataEditor::ApplyChanges()
628 if (fLastChange
== NULL
&& fFirstChange
== NULL
)
631 int32 firstIndex
= fFirstChange
!= NULL
? fChanges
.IndexOf(fFirstChange
) + 1
633 int32 lastIndex
= fChanges
.IndexOf(fLastChange
);
635 if (fChangesFromSaved
>= 0) {
637 TRACE(("ApplyChanges(): ascend from %ld to %ld\n", firstIndex
,
640 for (int32 i
= firstIndex
; i
<= lastIndex
; i
++) {
641 DataChange
*change
= fChanges
.ItemAt(i
);
642 change
->Apply(fRealViewOffset
, fView
, fRealViewSize
);
646 TRACE(("ApplyChanges(): descend from %ld to %ld\n", firstIndex
- 1,
649 for (int32 i
= firstIndex
- 1; i
> lastIndex
; i
--) {
650 DataChange
*change
= fChanges
.ItemAt(i
);
651 change
->Revert(fRealViewOffset
, fView
, fRealViewSize
);
660 BAutolock
locker(this);
665 StateWatcher
watcher(*this);
667 // Do we need to ascend or descend the list of changes?
669 int32 firstIndex
= fFirstChange
!= NULL
? fChanges
.IndexOf(fFirstChange
) + 1
671 int32 lastIndex
= fChanges
.IndexOf(fLastChange
);
672 if (fChangesFromSaved
< 0 && firstIndex
!= lastIndex
) {
674 ASSERT(firstIndex
> lastIndex
);
676 int32 temp
= firstIndex
- 1;
677 firstIndex
= lastIndex
;
683 if (lastIndex
> fChanges
.CountItems() - 1)
684 lastIndex
= fChanges
.CountItems();
686 // Collect ranges of data we need to write.
688 // ToDo: This is a very simple implementation and could be drastically
689 // improved by having items that save ranges, not just offsets. If Insert()
690 // and Remove() are going to be implemented, it should be improved that
691 // way to reduce the memory footprint to something acceptable.
693 BObjectList
<off_t
> list
;
694 for (int32 i
= firstIndex
; i
<= lastIndex
; i
++) {
695 DataChange
*change
= fChanges
.ItemAt(i
);
698 change
->GetRange(FileSize(), offset
, size
);
699 offset
-= offset
% BlockSize();
702 list
.BinaryInsertCopyUnique(offset
, CompareOffsets
);
703 offset
+= BlockSize();
708 // read in data and apply changes, write it back to disk
710 off_t oldOffset
= fViewOffset
;
712 for (int32 i
= 0; i
< list
.CountItems(); i
++) {
713 off_t offset
= *list
.ItemAt(i
);
715 // load the data into our view
716 SetViewOffset(offset
, false);
719 status_t status
= Update();
726 // don't try to change the file size
727 size_t size
= fRealViewSize
;
728 if (fRealViewOffset
+ (off_t
)fRealViewSize
> (off_t
)fSize
)
729 size
= fSize
- fRealViewOffset
;
731 ssize_t bytesWritten
;
733 bytesWritten
= fFile
.WriteAttr(fAttribute
, fType
, fRealViewOffset
,
736 bytesWritten
= fFile
.WriteAt(fRealViewOffset
, fView
, size
);
738 if (bytesWritten
< B_OK
)
744 SetViewOffset(oldOffset
, false);
748 fChangesFromSaved
= 0;
749 fFirstChange
= fLastChange
;
755 /** This method will be called by DataEditor::AddChange()
756 * immediately before a change is applied.
757 * It removes all pending redo nodes from the list that would
758 * come after the current change.
762 DataEditor::RemoveRedos()
764 int32 start
= fChanges
.IndexOf(fLastChange
) + 1;
766 for (int32 i
= fChanges
.CountItems(); i
-- > start
; ) {
767 DataChange
*change
= fChanges
.RemoveItemAt(i
);
776 BAutolock
locker(this);
781 StateWatcher
watcher(*this);
782 // update state observers
784 DataChange
*undoChange
= fLastChange
;
786 int32 index
= fChanges
.IndexOf(undoChange
);
788 undoChange
->Revert(fRealViewOffset
, fView
, fRealViewSize
);
791 fLastChange
= fChanges
.ItemAt(index
- 1);
796 SendNotices(undoChange
);
805 BAutolock
locker(this);
810 StateWatcher
watcher(*this);
811 // update state observers
813 int32 index
= fChanges
.IndexOf(fLastChange
);
814 fLastChange
= fChanges
.ItemAt(index
+ 1);
817 fLastChange
->Apply(fRealViewOffset
, fView
, fRealViewSize
);
820 SendNotices(fLastChange
);
827 DataEditor::CanUndo() const
829 return fLastChange
!= NULL
;
834 DataEditor::CanRedo() const
836 return fChanges
.IndexOf(fLastChange
) < fChanges
.CountItems() - 1;
841 DataEditor::SetFileSize(off_t size
)
843 // ToDo: implement me!
851 DataEditor::SetViewOffset(off_t offset
, bool sendNotices
)
853 BAutolock
locker(this);
856 status_t status
= SetViewSize(fViewSize
);
861 if (offset
< 0 || offset
> fSize
)
864 offset
= (offset
/ fViewSize
) * fViewSize
;
865 if (offset
== fViewOffset
)
868 fViewOffset
= offset
;
869 fRealViewOffset
= (fViewOffset
/ fBlockSize
) * fBlockSize
;
874 update
.AddInt64("offset", fViewOffset
);
875 SendNotices(kMsgDataEditorParameterChange
, &update
);
883 DataEditor::SetViewOffset(off_t offset
)
885 return SetViewOffset(offset
, true);
890 DataEditor::SetViewSize(size_t size
, bool sendNotices
)
892 BAutolock
locker(this);
894 size_t realSize
= (size
+ fBlockSize
- 1) & ~(fBlockSize
- 1);
895 // round to the next multiple of block size
897 if (realSize
== fRealViewSize
&& fViewSize
== size
&& fView
!= NULL
)
903 if (realSize
!= fRealViewSize
|| fView
== NULL
) {
904 uint8
*view
= (uint8
*)realloc(fView
, realSize
);
909 fRealViewSize
= realSize
;
915 // let's correct the view offset if necessary
916 if (fViewOffset
% size
)
917 SetViewOffset(fViewOffset
);
921 update
.AddInt32("view_size", size
);
922 SendNotices(kMsgDataEditorParameterChange
, &update
);
930 DataEditor::SetViewSize(size_t size
)
932 return SetViewSize(size
, true);
937 DataEditor::SetBlockSize(size_t size
)
939 BAutolock
locker(this);
942 status_t status
= SetViewOffset(fViewOffset
, false);
943 // this will trigger an update of the real view offset
945 status
= SetViewSize(fViewSize
, false);
948 update
.AddInt32("block_size", size
);
949 update
.AddInt64("offset", fViewOffset
);
950 SendNotices(kMsgDataEditorParameterChange
, &update
);
961 BNode
node(&fAttributeRef
);
962 bytesRead
= node
.ReadAttr(fAttribute
, fType
, fRealViewOffset
, fView
,
965 bytesRead
= fFile
.ReadAt(fRealViewOffset
, fView
, fRealViewSize
);
967 if (bytesRead
< B_OK
)
970 if ((size_t)bytesRead
< fRealViewSize
) {
971 // make sure the rest of data is cleared
972 memset(fView
+ bytesRead
, 0, fRealViewSize
- bytesRead
);
976 fNeedsUpdate
= false;
983 DataEditor::UpdateIfNeeded(bool *_updated
)
991 status_t status
= B_OK
;
994 status
= SetViewOffset(fViewOffset
);
996 if (status
== B_OK
&& fNeedsUpdate
) {
998 if (status
== B_OK
&& _updated
)
1007 DataEditor::ForceUpdate()
1009 BAutolock
locker(this);
1011 status_t status
= B_OK
;
1013 off_t newSize
= fSize
;
1014 if (IsAttribute()) {
1015 // update attribute size (we ignore the type for now)
1017 status
= fFile
.GetAttrInfo(fAttribute
, &info
);
1018 if (status
!= B_OK
) {
1019 // The attribute may have just been removed before
1020 // it gets rewritten, so we don't do anything
1021 // else here (we just set the file size to 0)
1024 newSize
= info
.size
;
1025 } else if (!IsDevice()) {
1028 if (fFile
.GetSize(&newSize
) != B_OK
)
1032 if (fSize
!= newSize
) {
1037 update
.AddInt64("file_size", newSize
);
1038 SendNotices(kMsgDataEditorParameterChange
, &update
);
1042 status
= SetViewOffset(fViewOffset
);
1045 update
.AddInt64("offset", fViewOffset
);
1046 update
.AddInt64("size", fViewSize
);
1047 SendNotices(kMsgDataEditorUpdate
, &update
);
1058 DataEditor::GetViewBuffer(const uint8
**_buffer
)
1061 debugger("DataEditor: view not locked");
1063 status_t status
= UpdateIfNeeded();
1067 *_buffer
= fView
+ fViewOffset
- fRealViewOffset
;
1073 DataEditor::Find(off_t startPosition
, const uint8
*data
, size_t dataSize
,
1074 bool caseInsensitive
, bool cyclic
, BMessenger progressMonitor
,
1075 volatile bool *stop
)
1077 if (data
== NULL
|| dataSize
== 0)
1080 if (startPosition
< 0)
1083 BAutolock
locker(this);
1085 typedef int (*compare_func
)(const uint8
*a
, const uint8
*b
, size_t size
);
1086 compare_func compareFunc
;
1087 if (caseInsensitive
)
1088 compareFunc
= CompareCaseInsensitive
;
1090 compareFunc
= (compare_func
)memcmp
;
1092 bool savedIsReadOnly
= fIsReadOnly
;
1094 off_t savedOffset
= fViewOffset
;
1095 off_t position
= (startPosition
/ fRealViewSize
) * fRealViewSize
;
1096 size_t firstByte
= startPosition
% fRealViewSize
;
1097 size_t matchLastOffset
= 0;
1098 off_t foundAt
= B_ENTRY_NOT_FOUND
;
1099 bool noStop
= false;
1103 // start progress monitor
1105 BMessage
progress(kMsgDataEditorFindProgress
);
1106 progress
.AddBool("running", true);
1107 progressMonitor
.SendMessage(&progress
);
1110 bigtime_t lastReport
= 0;
1112 off_t blocks
= fSize
;
1115 blocks
= (blocks
+ fRealViewSize
- 1) / fRealViewSize
;
1117 for (; blocks
-- > 0 && !*stop
; position
+= fRealViewSize
) {
1118 if (position
> fSize
)
1121 SetViewOffset(position
, false);
1125 bigtime_t current
= system_time();
1126 if (lastReport
+ 500000LL < current
) {
1127 // report the progress two times a second
1128 BMessage
progress(kMsgDataEditorFindProgress
);
1129 progress
.AddInt64("position", position
);
1130 progressMonitor
.SendMessage(&progress
);
1132 lastReport
= current
;
1135 // search for data in current block
1137 if (matchLastOffset
!= 0) {
1138 // we had a partial match in the previous block, let's
1139 // check if it is a whole match
1140 if (!compareFunc(fView
, data
+ matchLastOffset
,
1141 dataSize
- matchLastOffset
)) {
1142 matchLastOffset
= 0;
1146 foundAt
= B_ENTRY_NOT_FOUND
;
1147 matchLastOffset
= 0;
1150 for (size_t i
= firstByte
; i
< fRealViewSize
; i
++) {
1151 if (position
+ (off_t
)(i
+ dataSize
) > (off_t
)fSize
)
1154 if (!compareFunc(fView
+ i
, data
, 1)) {
1155 // one byte matches, compare the rest
1156 size_t size
= dataSize
- 1;
1157 size_t offset
= i
+ 1;
1159 if (offset
+ size
> fRealViewSize
)
1160 size
= fRealViewSize
- offset
;
1162 if (size
== 0 || !compareFunc(fView
+ offset
, data
+ 1, size
)) {
1163 foundAt
= position
+ i
;
1165 if (size
!= dataSize
- 1) {
1166 // only a partial match, we have to check the start
1167 // of the next block
1168 matchLastOffset
= size
+ 1;
1175 if (foundAt
>= 0 && matchLastOffset
== 0)
1181 fIsReadOnly
= savedIsReadOnly
;
1183 if (foundAt
>= 0 && matchLastOffset
!= 0)
1184 foundAt
= B_ENTRY_NOT_FOUND
;
1186 // stop progress monitor
1188 BMessage
progress(kMsgDataEditorFindProgress
);
1189 progress
.AddBool("running", false);
1190 progress
.AddInt64("position", foundAt
>= 0 ? foundAt
: savedOffset
);
1191 progressMonitor
.SendMessage(&progress
);
1194 SetViewOffset(savedOffset
, false);
1195 // this is to ensure that we're sending an update when
1196 // we're set to the found data offset
1198 if (foundAt
< 0 && *stop
)
1199 return B_INTERRUPTED
;
1206 DataEditor::SendNotices(DataChange
*change
)
1209 change
->GetRange(FileSize(), offset
, size
);
1213 update
.AddInt64("offset", offset
);
1214 update
.AddInt64("size", size
);
1215 SendNotices(kMsgDataEditorUpdate
, &update
);
1220 DataEditor::SendNotices(uint32 what
, BMessage
*message
)
1222 if (fObservers
.CountItems() == 0)
1227 notice
= new BMessage(*message
);
1228 notice
->what
= what
;
1230 notice
= new BMessage(what
);
1232 for (int32 i
= fObservers
.CountItems(); i
-- > 0;) {
1233 BMessenger
*messenger
= fObservers
.ItemAt(i
);
1234 messenger
->SendMessage(notice
);
1242 DataEditor::StartWatching(BMessenger target
)
1244 BAutolock
locker(this);
1247 status_t status
= fFile
.GetNodeRef(&node
);
1251 fObservers
.AddItem(new BMessenger(target
));
1253 return watch_node(&node
, B_WATCH_STAT
| B_WATCH_ATTR
, target
);
1258 DataEditor::StartWatching(BHandler
*handler
, BLooper
*looper
)
1260 return StartWatching(BMessenger(handler
, looper
));
1265 DataEditor::StopWatching(BMessenger target
)
1267 BAutolock
locker(this);
1269 for (int32 i
= fObservers
.CountItems(); i
-- > 0;) {
1270 BMessenger
*messenger
= fObservers
.ItemAt(i
);
1271 if (*messenger
== target
) {
1272 fObservers
.RemoveItemAt(i
);
1278 stop_watching(target
);
1283 DataEditor::StopWatching(BHandler
*handler
, BLooper
*looper
)
1285 StopWatching(BMessenger(handler
, looper
));