1 // Handle high level buffer stuff
2 // Copyright (C) 2000 Core Technologies.
3
4 // This file is part of e93.
5 //
6 // e93 is free software; you can redistribute it and/or modify
7 // it under the terms of the e93 LICENSE AGREEMENT.
8 //
9 // e93 is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // e93 LICENSE AGREEMENT for more details.
13 //
14 // You should have received a copy of the e93 LICENSE AGREEMENT
15 // along with e93; see the file "LICENSE.TXT".
16
17 #include "includes.h"
18
19 // Some handy notes about buffers:
20 // shellBusy
21 // This is used exclusively by the the shell. It indicates to the
22 // shell that it is already doing something with the buffer, and that it is
23 // not allowed to affect it in any way (useful when doing search and replace,
24 // where replace is a shell script... keeps script from being able to modify
25 // the buffer that is in the middle of being modified by replace command)
26 // To that end, the buffer should be checked for "busy" before the shell attempts
27 // to change the text it contains, or change its selection.
28 // It should be set to busy whenever the shell is about to do something to the
29 // buffer that could run a Tcl script.
30
31 static EDITOR_BUFFER
32 *startBuffer; // pointer to start of editor buffer list
33
34 enum
35 {
36 NOT_FROM_FILE,
37 DUPLICATE_NAME
38 };
39
40 static const char *errorDescriptions[]=
41 {
42 "Buffer is not linked to a file",
43 "Duplicate buffer names are not allowed"
44 };
45
DisposeBuffer(EDITOR_BUFFER * buffer)46 static void DisposeBuffer(EDITOR_BUFFER *buffer)
47 // dispose of a buffer
48 {
49 EDITOR_BUFFER
50 *previousBuffer,
51 *testBuffer;
52 EDITOR_BUFFER_HANDLE
53 *handle;
54
55 handle=buffer->bufferHandles;
56 while(handle) // for each handle to this buffer, unlink and clear it
57 {
58 handle->buffer=NULL; // this buffer is gone...
59 handle=handle->nextHandle;
60 }
61
62 UnInitVariableBindingTable(&(buffer->variableTable)); // remove variables hanging off this buffer
63 if(buffer->window)
64 {
65 EditorCloseDocumentWindow(buffer->window); // close down the window if one exists
66 }
67 if(buffer->task)
68 {
69 DisconnectBufferTask(buffer); // close down the connection to any task that may be attached to this buffer
70 }
71 if(buffer->syntaxInstance) // if syntax instance has been assigned, remove it
72 {
73 CloseSyntaxInstance(buffer->syntaxInstance);
74 }
75 CloseStyleUniverse(buffer->styleUniverse);
76 while(buffer->marks)
77 {
78 CloseEditorMark(buffer,buffer->marks); // get rid of all marks on this buffer
79 }
80 CloseSelectionUniverse(buffer->auxSelectionUniverse);
81 CloseSelectionUniverse(buffer->xorSelectionUniverse);
82 CloseSelectionUniverse(buffer->selectionUniverse);
83 CloseUndoUniverse(buffer->undoUniverse);
84 CloseTextUniverse(buffer->textUniverse);
85 MDisposePtr(buffer->contentName);
86
87 previousBuffer=NULL;
88 testBuffer=startBuffer;
89 while(testBuffer&&testBuffer!=buffer)
90 {
91 previousBuffer=testBuffer;
92 testBuffer=testBuffer->nextBuffer;
93 }
94 if(testBuffer==buffer) // make sure we found it before trying to unlink
95 {
96 if(previousBuffer)
97 {
98 previousBuffer->nextBuffer=buffer->nextBuffer;
99 }
100 else
101 {
102 startBuffer=buffer->nextBuffer;
103 }
104 }
105
106 MDisposePtr(buffer);
107 }
108
CreateBuffer(const char * bufferName)109 static EDITOR_BUFFER *CreateBuffer(const char *bufferName)
110 // create a buffer (buffers contain both text and selection
111 // universes (which are created here))
112 // if there is a problem, set the error, and return NULL
113 {
114 EDITOR_BUFFER
115 *buffer;
116
117 if((buffer=(EDITOR_BUFFER *)MNewPtr(sizeof(EDITOR_BUFFER))))
118 {
119 buffer->bufferHandles=NULL; // not being held
120 buffer->shellBusy=false; // set this to a known value
121 buffer->fromFile=false;
122
123 if((buffer->contentName=(char *)MNewPtr(strlen(bufferName)+1)))
124 {
125 strcpy(buffer->contentName,bufferName);
126 if((buffer->textUniverse=OpenTextUniverse())) // create the text universe
127 {
128 if((buffer->undoUniverse=OpenUndoUniverse())) // create the undo universe
129 {
130 if((buffer->selectionUniverse=OpenSelectionUniverse())) // create the selection universe
131 {
132 if((buffer->xorSelectionUniverse=OpenSelectionUniverse())) // create the XOR selection universe
133 {
134 if((buffer->auxSelectionUniverse=OpenSelectionUniverse())) // create the aux selection universe (used for tasks, so they can write data into a place other than at the cursor)
135 {
136 buffer->marks=NULL; // no marks on this buffer yet
137 if((buffer->styleUniverse=OpenStyleUniverse())) // create the style universe
138 {
139 buffer->syntaxInstance=NULL; // no syntax highlight on this buffer yet
140 buffer->task=NULL;
141 buffer->taskBytes=0;
142 buffer->window=NULL;
143 buffer->firstView=NULL; // no views onto this buffer yet
144 InitVariableBindingTable(&(buffer->variableTable)); // initialize the variable table
145
146 buffer->replaceHaveRange=false;
147 buffer->replaceStartOffset=0;
148 buffer->replaceEndOffset=0;
149 buffer->replaceNumBytes=0;
150
151 buffer->anchorStartPosition=0; // clear selection handling variables
152 buffer->anchorEndPosition=0;
153 buffer->anchorLine=0;
154 buffer->anchorX=0;
155 buffer->columnarTopLine=0;
156 buffer->columnarBottomLine=0;
157 buffer->columnarLeftX=0;
158 buffer->columnarRightX=0;
159 buffer->haveStartX=false;
160 buffer->haveEndX=false;
161 buffer->desiredStartX=0;
162 buffer->desiredEndX=0;
163 buffer->haveCurrentEnd=false;
164 buffer->currentIsStart=false;
165
166 buffer->nextBuffer=startBuffer;
167 startBuffer=buffer;
168 return(buffer);
169 }
170 CloseSelectionUniverse(buffer->auxSelectionUniverse);
171 }
172 CloseSelectionUniverse(buffer->xorSelectionUniverse);
173 }
174 CloseSelectionUniverse(buffer->selectionUniverse);
175 }
176 CloseUndoUniverse(buffer->undoUniverse);
177 }
178 CloseTextUniverse(buffer->textUniverse);
179 }
180 MDisposePtr(buffer->contentName);
181 }
182 MDisposePtr(buffer);
183 }
184 return(NULL);
185 }
186
SetBufferBusy(EDITOR_BUFFER * buffer)187 void SetBufferBusy(EDITOR_BUFFER *buffer)
188 // set the buffer's busy state
189 {
190 buffer->shellBusy=true;
191 }
192
ClearBufferBusy(EDITOR_BUFFER * buffer)193 void ClearBufferBusy(EDITOR_BUFFER *buffer)
194 // clear the buffer's busy state
195 {
196 buffer->shellBusy=false;
197 }
198
BufferBusy(EDITOR_BUFFER * buffer)199 bool BufferBusy(EDITOR_BUFFER *buffer)
200 // check the given buffer's busy state
201 // if busy, return true, else false
202 {
203 return(buffer->shellBusy);
204 }
205
EditorGetFirstBuffer()206 EDITOR_BUFFER *EditorGetFirstBuffer()
207 // return the first buffer, or NULL if none
208 {
209 return(startBuffer);
210 }
211
EditorGetNextBuffer(EDITOR_BUFFER * buffer)212 EDITOR_BUFFER *EditorGetNextBuffer(EDITOR_BUFFER *buffer)
213 // return the next buffer, or NULL if none
214 {
215 return(buffer->nextBuffer);
216 }
217
LocateBuffer(const char * bufferName)218 EDITOR_BUFFER *LocateBuffer(const char *bufferName)
219 // search through the list of buffers, and attempt to locate
220 // one with the given name
221 // if one is found, return it, else return NULL
222 {
223 bool
224 found;
225 EDITOR_BUFFER
226 *testBuffer;
227
228 testBuffer=startBuffer;
229 found=false;
230 while(testBuffer&&!found)
231 {
232 if(strcmp(testBuffer->contentName,bufferName)==0)
233 {
234 found=true;
235 }
236 else
237 {
238 testBuffer=testBuffer->nextBuffer;
239 }
240 }
241 return(testBuffer);
242 }
243
EditorCloseBuffer(EDITOR_BUFFER * buffer)244 void EditorCloseBuffer(EDITOR_BUFFER *buffer)
245 // close the given edit buffer
246 // if the buffer has a task, it will be disconnected
247 // if the buffer has a window, it will be closed too
248 // this will close the window without saving, even if the text has been modified
249 {
250 DisposeBuffer(buffer);
251 }
252
EditorReleaseBufferHandle(EDITOR_BUFFER_HANDLE * handle)253 void EditorReleaseBufferHandle(EDITOR_BUFFER_HANDLE *handle)
254 // Remove handle from the buffer it was linked to
255 // NOTE: if handle is already released, this does nothing
256 {
257 EDITOR_BUFFER_HANDLE
258 *previousHandle,
259 *currentHandle;
260
261 if(handle->buffer) // make sure this handle is held
262 {
263 previousHandle=NULL;
264 currentHandle=handle->buffer->bufferHandles;
265 while(currentHandle&¤tHandle!=handle)
266 {
267 previousHandle=currentHandle;
268 currentHandle=currentHandle->nextHandle;
269 }
270 if(currentHandle)
271 {
272 if(previousHandle)
273 {
274 previousHandle->nextHandle=currentHandle->nextHandle;
275 }
276 else
277 {
278 currentHandle->buffer->bufferHandles=currentHandle->nextHandle;
279 }
280 }
281 handle->buffer=NULL;
282 }
283 }
284
EditorGrabBufferHandle(EDITOR_BUFFER_HANDLE * handle,EDITOR_BUFFER * buffer)285 void EditorGrabBufferHandle(EDITOR_BUFFER_HANDLE *handle,EDITOR_BUFFER *buffer)
286 // grab a handle to buffer
287 {
288 handle->buffer=buffer;
289 handle->nextHandle=buffer->bufferHandles;
290 buffer->bufferHandles=handle;
291 }
292
EditorGetBufferFromHandle(EDITOR_BUFFER_HANDLE * handle)293 EDITOR_BUFFER *EditorGetBufferFromHandle(EDITOR_BUFFER_HANDLE *handle)
294 // return the buffer that handle points to
295 {
296 return(handle->buffer);
297 }
298
EditorInitBufferHandle(EDITOR_BUFFER_HANDLE * handle)299 void EditorInitBufferHandle(EDITOR_BUFFER_HANDLE *handle)
300 // initialize a buffer handle so that it is in the released state
301 {
302 handle->buffer=NULL;
303 }
304
EditorRevertBuffer(EDITOR_BUFFER * buffer)305 bool EditorRevertBuffer(EDITOR_BUFFER *buffer)
306 // revert buffer, no questions asked
307 // if there is a problem, SetError, and return false
308 // NOTE: the window could be left with only part of the reverted text
309 // if there was a problem
310 {
311 if(buffer->fromFile)
312 {
313 if(LoadBuffer(buffer,buffer->contentName))
314 {
315 return(true);
316 }
317 }
318 else
319 {
320 if(ClearBuffer(buffer))
321 {
322 return(true);
323 }
324 }
325 return(false);
326 }
327
EditorSaveBufferTo(EDITOR_BUFFER * buffer,const char * path)328 bool EditorSaveBufferTo(EDITOR_BUFFER *buffer,const char *path)
329 // save the text of buffer into path
330 // NOTE: the text will be saved even if it is not dirty
331 // if there is a problem, SetError, and return false
332 // NOTE: the buffer "clean" point is not moved by this call
333 // NOTE: certain problems may cause the text to be only partially saved
334 // thus corrupting the file
335 {
336 return(SaveBuffer(buffer,path,false));
337 }
338
EditorSaveBufferAs(EDITOR_BUFFER * buffer,const char * path)339 bool EditorSaveBufferAs(EDITOR_BUFFER *buffer,const char *path)
340 // save the text of buffer into path, change buffer's name
341 // to the absolute version of path, and update its window if it has one
342 // NOTE: the text will be saved even if it is not dirty
343 // if there is a problem, SetError, and return false
344 // NOTE: certain problems may cause the text to be only partially saved
345 // thus corrupting the file
346 // NOTE: if after a save, the buffer's name becomes the same as that
347 // of another already existing buffer, the name change will fail, even though
348 // the contents of the buffer have been written into the file
349 // this is a little ugly, but all editors have to cope with this problem somehow.
350 {
351 EDITOR_BUFFER
352 *otherBuffer;
353 char
354 *absolutePath;
355
356 if(SaveBuffer(buffer,path,true)) // save the file using the regular path, mark it as clean
357 {
358 if((absolutePath=CreateAbsolutePath(path))) // attempt to make an absolute path out of the one given (this could fail if someone deleted the file we just saved before we get here!)
359 {
360 if(!(otherBuffer=LocateBuffer(absolutePath))||(otherBuffer==buffer)) // see if there is another buffer down this path
361 {
362 MDisposePtr(buffer->contentName); // wipe out the old content name
363 buffer->contentName=absolutePath; // point it at the new path
364 buffer->fromFile=true; // linked to file now
365 EditorReassignDocumentWindowTitle(buffer); // make new title to reflect new name
366 return(true);
367 }
368 else
369 {
370 SetError(errorDescriptions[DUPLICATE_NAME]);
371 }
372 MDisposePtr(absolutePath);
373 }
374 }
375 return(false);
376 }
377
EditorSaveBuffer(EDITOR_BUFFER * buffer)378 bool EditorSaveBuffer(EDITOR_BUFFER *buffer)
379 // save the text of buffer into its file if it has one
380 // if it does not have a file, this will fail
381 {
382 if(buffer->fromFile)
383 {
384 return(SaveBuffer(buffer,buffer->contentName,true)); // save buffer, mark it as clean
385 }
386 SetError(errorDescriptions[NOT_FROM_FILE]);
387 return(false);
388 }
389
EditorNewBuffer(const char * bufferName)390 EDITOR_BUFFER *EditorNewBuffer(const char *bufferName)
391 // create a new buffer with the given name
392 // NOTE: if there is already a buffer with the given name, SetError
393 // if there is a problem, SetError, return NULL
394 {
395 if(!LocateBuffer(bufferName))
396 {
397 return(CreateBuffer(bufferName));
398 }
399 else
400 {
401 SetError(errorDescriptions[DUPLICATE_NAME]);
402 }
403 return(NULL);
404 }
405
EditorOpenBuffer(const char * path)406 EDITOR_BUFFER *EditorOpenBuffer(const char *path)
407 // open an editor buffer to the file at path
408 // if possible, open it, and return the buffer
409 // if there is a problem, set the error, and return NULL
410 // NOTE: if path points to a buffer that is already open,
411 // then that buffer is simply returned
412 {
413 EDITOR_BUFFER
414 *buffer;
415 char
416 *absolutePath;
417
418 if((absolutePath=CreateAbsolutePath(path))) // attempt to make an absolute path out of the one given
419 {
420 if((buffer=LocateBuffer(absolutePath)))
421 {
422 MDisposePtr(absolutePath);
423 return(buffer);
424 }
425 else
426 {
427 if((buffer=CreateBuffer(absolutePath)))
428 {
429 if(LoadBuffer(buffer,absolutePath))
430 {
431 buffer->fromFile=true;
432 MDisposePtr(absolutePath);
433 return(buffer);
434 }
435 DisposeBuffer(buffer);
436 }
437 }
438 MDisposePtr(absolutePath);
439 }
440 return(NULL);
441 }
442
UnInitBuffers()443 void UnInitBuffers()
444 // undo whatever InitBuffers did
445 {
446 while(startBuffer)
447 {
448 if(startBuffer->bufferHandles)
449 {
450 fprintf(stderr,"Buffer dying with handles\n"); // this is a bug because no one should have a handle to a buffer at this moment
451 }
452 EditorCloseBuffer(startBuffer); // get rid of any buffers left lying about
453 }
454 }
455
InitBuffers()456 bool InitBuffers()
457 // initialize buffer handling
458 // if there is a problem, SetError, and return false
459 {
460 startBuffer=NULL; // no start buffer as of yet
461 return(true);
462 }
463