xref: /haiku/src/apps/stylededit/StyledEditWindow.cpp (revision 820dca4df6c7bf955c46e8f6521b9408f50b2900)
1 /*
2  * Copyright 2002-2012, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Mattias Sundblad
7  *		Andrew Bachmann
8  *		Philippe Saint-Pierre
9  *		Jonas Sundström
10  *		Ryan Leavengood
11  *		Vlad Slepukhin
12  *		Sarzhuk Zharski
13  */
14 
15 
16 #include "ColorMenuItem.h"
17 #include "Constants.h"
18 #include "FindWindow.h"
19 #include "ReplaceWindow.h"
20 #include "StatusView.h"
21 #include "StyledEditApp.h"
22 #include "StyledEditView.h"
23 #include "StyledEditWindow.h"
24 
25 #include <Alert.h>
26 #include <Autolock.h>
27 #include <Catalog.h>
28 #include <CharacterSet.h>
29 #include <CharacterSetRoster.h>
30 #include <Clipboard.h>
31 #include <Debug.h>
32 #include <File.h>
33 #include <FilePanel.h>
34 #include <fs_attr.h>
35 #include <Locale.h>
36 #include <Menu.h>
37 #include <MenuBar.h>
38 #include <MenuItem.h>
39 #include <NodeMonitor.h>
40 #include <Path.h>
41 #include <PrintJob.h>
42 #include <Rect.h>
43 #include <Roster.h>
44 #include <Screen.h>
45 #include <ScrollView.h>
46 #include <TextControl.h>
47 #include <TextView.h>
48 #include <TranslationUtils.h>
49 #include <UnicodeChar.h>
50 
51 
52 using namespace BPrivate;
53 
54 
55 const float kLineViewWidth = 30.0;
56 const char* kInfoAttributeName = "StyledEdit-info";
57 
58 
59 #undef B_TRANSLATION_CONTEXT
60 #define B_TRANSLATION_CONTEXT "StyledEditWindow"
61 
62 
63 // This is a temporary solution for building BString with printf like format.
64 // will be removed in the future.
65 static void
66 bs_printf(BString* string, const char* format, ...)
67 {
68 	va_list ap;
69 	va_start(ap, format);
70 	char* buf;
71 	vasprintf(&buf, format, ap);
72 	string->SetTo(buf);
73 	free(buf);
74 	va_end(ap);
75 }
76 
77 
78 // #pragma mark -
79 
80 
81 StyledEditWindow::StyledEditWindow(BRect frame, int32 id, uint32 encoding)
82 	: BWindow(frame, "untitled", B_DOCUMENT_WINDOW, B_ASYNCHRONOUS_CONTROLS)
83 {
84 	_InitWindow(encoding);
85 	BString unTitled(B_TRANSLATE("Untitled "));
86 	unTitled << id;
87 	SetTitle(unTitled.String());
88 	fSaveItem->SetEnabled(true);
89 		// allow saving empty files
90 	Show();
91 }
92 
93 
94 StyledEditWindow::StyledEditWindow(BRect frame, entry_ref* ref, uint32 encoding)
95 	: BWindow(frame, "untitled", B_DOCUMENT_WINDOW, B_ASYNCHRONOUS_CONTROLS)
96 {
97 	_InitWindow(encoding);
98 	OpenFile(ref);
99 	Show();
100 }
101 
102 
103 StyledEditWindow::~StyledEditWindow()
104 {
105 	delete fSaveMessage;
106 	delete fPrintSettings;
107 	delete fSavePanel;
108 }
109 
110 
111 void
112 StyledEditWindow::Quit()
113 {
114 	_SwitchNodeMonitor(false);
115 
116 	_SaveAttrs();
117 	if (StyledEditApp* app = dynamic_cast<StyledEditApp*>(be_app))
118 		app->CloseDocument();
119 	BWindow::Quit();
120 }
121 
122 
123 #undef B_TRANSLATION_CONTEXT
124 #define B_TRANSLATION_CONTEXT "QuitAlert"
125 
126 
127 bool
128 StyledEditWindow::QuitRequested()
129 {
130 	if (fClean)
131 		return true;
132 
133 	if (fTextView->TextLength() == 0 && fSaveMessage == NULL)
134 		return true;
135 
136 	BString alertText;
137 	bs_printf(&alertText,
138 		B_TRANSLATE("Save changes to the document \"%s\"? "), Title());
139 
140 	int32 index = _ShowAlert(alertText, B_TRANSLATE("Cancel"),
141 		B_TRANSLATE("Don't save"), B_TRANSLATE("Save"),	B_WARNING_ALERT);
142 
143 	if (index == 0)
144 		return false;	// "cancel": dont save, dont close the window
145 
146 	if (index == 1)
147 		return true;	// "don't save": just close the window
148 
149 	if (!fSaveMessage) {
150 		SaveAs(new BMessage(SAVE_THEN_QUIT));
151 		return false;
152 	}
153 
154 	return Save() == B_OK;
155 }
156 
157 
158 void
159 StyledEditWindow::MessageReceived(BMessage* message)
160 {
161 	if (message->WasDropped()) {
162 		entry_ref ref;
163 		if (message->FindRef("refs", 0, &ref)==B_OK) {
164 			message->what = B_REFS_RECEIVED;
165 			be_app->PostMessage(message);
166 		}
167 	}
168 
169 	switch (message->what) {
170 		// File menu
171 		case MENU_SAVE:
172 			if (!fSaveMessage)
173 				SaveAs();
174 			else
175 				Save(fSaveMessage);
176 			break;
177 
178 		case MENU_SAVEAS:
179 			SaveAs();
180 			break;
181 
182 		case B_SAVE_REQUESTED:
183 			Save(message);
184 			break;
185 
186 		case SAVE_THEN_QUIT:
187 			if (Save(message) == B_OK)
188 				Quit();
189 			break;
190 
191 		case MENU_RELOAD:
192 			_ReloadDocument(message);
193 			break;
194 
195 		case MENU_CLOSE:
196 			if (QuitRequested())
197 				Quit();
198 			break;
199 
200 		case MENU_PAGESETUP:
201 			PageSetup(fTextView->Window()->Title());
202 			break;
203 		case MENU_PRINT:
204 			Print(fTextView->Window()->Title());
205 			break;
206 		case MENU_QUIT:
207 			be_app->PostMessage(B_QUIT_REQUESTED);
208 			break;
209 
210 		// Edit menu
211 
212 		case B_UNDO:
213 			ASSERT(fCanUndo || fCanRedo);
214 			ASSERT(!(fCanUndo && fCanRedo));
215 			if (fCanUndo)
216 				fUndoFlag = true;
217 			if (fCanRedo)
218 				fRedoFlag = true;
219 
220 			fTextView->Undo(be_clipboard);
221 			break;
222 		case B_CUT:
223 			fTextView->Cut(be_clipboard);
224 			break;
225 		case B_COPY:
226 			fTextView->Copy(be_clipboard);
227 			break;
228 		case B_PASTE:
229 			fTextView->Paste(be_clipboard);
230 			break;
231 		case MENU_CLEAR:
232 			fTextView->Clear();
233 			break;
234 		case MENU_FIND:
235 		{
236 			BRect findWindowFrame(100, 100, 400, 235);
237 			BWindow* window = new FindWindow(findWindowFrame, this,
238 				&fStringToFind, fCaseSensitive, fWrapAround, fBackSearch);
239 			window->Show();
240 			break;
241 		}
242 		case MSG_SEARCH:
243 			message->FindString("findtext", &fStringToFind);
244 			fFindAgainItem->SetEnabled(true);
245 			message->FindBool("casesens", &fCaseSensitive);
246 			message->FindBool("wrap", &fWrapAround);
247 			message->FindBool("backsearch", &fBackSearch);
248 
249 			_Search(fStringToFind, fCaseSensitive, fWrapAround, fBackSearch);
250 			break;
251 		case MENU_FIND_AGAIN:
252 			_Search(fStringToFind, fCaseSensitive, fWrapAround, fBackSearch);
253 			break;
254 		case MENU_FIND_SELECTION:
255 			_FindSelection();
256 			break;
257 		case MENU_REPLACE:
258 		{
259 			BRect replaceWindowFrame(100, 100, 400, 284);
260 			BWindow* window = new ReplaceWindow(replaceWindowFrame, this,
261 				&fStringToFind, &fReplaceString, fCaseSensitive, fWrapAround,
262 				fBackSearch);
263 			window->Show();
264 			break;
265 		}
266 		case MSG_REPLACE:
267 		{
268 			message->FindBool("casesens", &fCaseSensitive);
269 			message->FindBool("wrap", &fWrapAround);
270 			message->FindBool("backsearch", &fBackSearch);
271 
272 			message->FindString("FindText", &fStringToFind);
273 			message->FindString("ReplaceText", &fReplaceString);
274 
275 			fFindAgainItem->SetEnabled(true);
276 			fReplaceSameItem->SetEnabled(true);
277 
278 			_Replace(fStringToFind, fReplaceString, fCaseSensitive, fWrapAround,
279 				fBackSearch);
280 			break;
281 		}
282 		case MENU_REPLACE_SAME:
283 			_Replace(fStringToFind, fReplaceString, fCaseSensitive, fWrapAround,
284 				fBackSearch);
285 			break;
286 
287 		case MSG_REPLACE_ALL:
288 		{
289 			message->FindBool("casesens", &fCaseSensitive);
290 			message->FindString("FindText", &fStringToFind);
291 			message->FindString("ReplaceText", &fReplaceString);
292 
293 			bool allWindows;
294 			message->FindBool("allwindows", &allWindows);
295 
296 			fFindAgainItem->SetEnabled(true);
297 			fReplaceSameItem->SetEnabled(true);
298 
299 			if (allWindows)
300 				SearchAllWindows(fStringToFind, fReplaceString, fCaseSensitive);
301 			else
302 				_ReplaceAll(fStringToFind, fReplaceString, fCaseSensitive);
303 			break;
304 		}
305 
306 		case B_NODE_MONITOR:
307 			_HandleNodeMonitorEvent(message);
308 			break;
309 
310 		// Font menu
311 
312 		case FONT_SIZE:
313 		{
314 			float fontSize;
315 			if (message->FindFloat("size", &fontSize) == B_OK)
316 				_SetFontSize(fontSize);
317 			break;
318 		}
319 		case FONT_FAMILY:
320 		{
321 			const char* fontFamily = NULL;
322 			const char* fontStyle = NULL;
323 			void* ptr;
324 			if (message->FindPointer("source", &ptr) == B_OK) {
325 				BMenuItem* item = static_cast<BMenuItem*>(ptr);
326 				fontFamily = item->Label();
327 			}
328 
329 			BFont font;
330 			font.SetFamilyAndStyle(fontFamily, fontStyle);
331 			fItalicItem->SetMarked((font.Face() & B_ITALIC_FACE) != 0);
332 			fBoldItem->SetMarked((font.Face() & B_BOLD_FACE) != 0);
333 
334 			_SetFontStyle(fontFamily, fontStyle);
335 			break;
336 		}
337 		case FONT_STYLE:
338 		{
339 			const char* fontFamily = NULL;
340 			const char* fontStyle = NULL;
341 			void* ptr;
342 			if (message->FindPointer("source", &ptr) == B_OK) {
343 				BMenuItem* item = static_cast<BMenuItem*>(ptr);
344 				fontStyle = item->Label();
345 				BMenu* menu = item->Menu();
346 				if (menu != NULL) {
347 					BMenuItem* super_item = menu->Superitem();
348 					if (super_item != NULL)
349 						fontFamily = super_item->Label();
350 				}
351 			}
352 
353 			BFont font;
354 			font.SetFamilyAndStyle(fontFamily, fontStyle);
355 			fItalicItem->SetMarked((font.Face() & B_ITALIC_FACE) != 0);
356 			fBoldItem->SetMarked((font.Face() & B_BOLD_FACE) != 0);
357 
358 			_SetFontStyle(fontFamily, fontStyle);
359 			break;
360 		}
361 		case kMsgSetItalic:
362 		{
363 			uint32 sameProperties;
364 			BFont font;
365 			fTextView->GetFontAndColor(&font, &sameProperties);
366 
367 			if (fItalicItem->IsMarked())
368 				font.SetFace(B_REGULAR_FACE);
369 			fItalicItem->SetMarked(!fItalicItem->IsMarked());
370 
371 			font_family family;
372 			font_style style;
373 			font.GetFamilyAndStyle(&family, &style);
374 
375 			_SetFontStyle(family, style);
376 			break;
377 		}
378 		case kMsgSetBold:
379 		{
380 			uint32 sameProperties;
381 			BFont font;
382 			fTextView->GetFontAndColor(&font, &sameProperties);
383 
384 			if (fBoldItem->IsMarked())
385 				font.SetFace(B_REGULAR_FACE);
386 			fBoldItem->SetMarked(!fBoldItem->IsMarked());
387 
388 			font_family family;
389 			font_style style;
390 			font.GetFamilyAndStyle(&family, &style);
391 
392 			_SetFontStyle(family, style);
393 			break;
394 		}
395 		case FONT_COLOR:
396 		{
397 			void* ptr;
398 			if (message->FindPointer("source", &ptr) == B_OK) {
399 				if (ptr == fBlackItem)
400 					_SetFontColor(&BLACK);
401 				else if (ptr == fRedItem)
402 					_SetFontColor(&RED);
403 				else if (ptr == fGreenItem)
404 					_SetFontColor(&GREEN);
405 				else if (ptr == fBlueItem)
406 					_SetFontColor(&BLUE);
407 				else if (ptr == fCyanItem)
408 					_SetFontColor(&CYAN);
409 				else if (ptr == fMagentaItem)
410 					_SetFontColor(&MAGENTA);
411 				else if (ptr == fYellowItem)
412 					_SetFontColor(&YELLOW);
413 			}
414 			break;
415 		}
416 
417 		// Document menu
418 
419 		case ALIGN_LEFT:
420 			fTextView->SetAlignment(B_ALIGN_LEFT);
421 			_UpdateCleanUndoRedoSaveRevert();
422 			break;
423 		case ALIGN_CENTER:
424 			fTextView->SetAlignment(B_ALIGN_CENTER);
425 			_UpdateCleanUndoRedoSaveRevert();
426 			break;
427 		case ALIGN_RIGHT:
428 			fTextView->SetAlignment(B_ALIGN_RIGHT);
429 			_UpdateCleanUndoRedoSaveRevert();
430 			break;
431 		case WRAP_LINES:
432 		{
433 			BRect textRect(fTextView->Bounds());
434 			textRect.OffsetTo(B_ORIGIN);
435 			textRect.InsetBy(TEXT_INSET, TEXT_INSET);
436 			if (fTextView->DoesWordWrap()) {
437 				fTextView->SetWordWrap(false);
438 				fWrapItem->SetMarked(false);
439 				// the width comes from stylededit R5. TODO: find a better way
440 				textRect.SetRightBottom(BPoint(1500.0, textRect.RightBottom().y));
441 			} else {
442 				fTextView->SetWordWrap(true);
443 				fWrapItem->SetMarked(true);
444 			}
445 			fTextView->SetTextRect(textRect);
446 
447 			_UpdateCleanUndoRedoSaveRevert();
448 			break;
449 		}
450 		case SHOW_STATISTICS:
451 			_ShowStatistics();
452 			break;
453 		case ENABLE_ITEMS:
454 			fCutItem->SetEnabled(true);
455 			fCopyItem->SetEnabled(true);
456 			break;
457 		case DISABLE_ITEMS:
458 			fCutItem->SetEnabled(false);
459 			fCopyItem->SetEnabled(false);
460 			break;
461 		case TEXT_CHANGED:
462 			if (fUndoFlag) {
463 				if (fUndoCleans) {
464 					// we cleaned!
465 					fClean = true;
466 					fUndoCleans = false;
467 				} else if (fClean) {
468 					// if we were clean
469 					// then a redo will make us clean again
470 					fRedoCleans = true;
471 					fClean = false;
472 				}
473 				// set mode
474 				fCanUndo = false;
475 				fCanRedo = true;
476 				fUndoItem->SetLabel(B_TRANSLATE("Redo typing"));
477 				fUndoItem->SetEnabled(true);
478 				fUndoFlag = false;
479 			} else {
480 				if (fRedoFlag && fRedoCleans) {
481 					// we cleaned!
482 					fClean = true;
483 					fRedoCleans = false;
484 				} else if (fClean) {
485 					// if we were clean
486 					// then an undo will make us clean again
487 					fUndoCleans = true;
488 					fClean = false;
489 				} else {
490 					// no more cleaning from undo now...
491 					fUndoCleans = false;
492 				}
493 				// set mode
494 				fCanUndo = true;
495 				fCanRedo = false;
496 				fUndoItem->SetLabel(B_TRANSLATE("Undo typing"));
497 				fUndoItem->SetEnabled(true);
498 				fRedoFlag = false;
499 			}
500 			if (fClean) {
501 				fSaveItem->SetEnabled(fSaveMessage == NULL);
502 			} else {
503 				fSaveItem->SetEnabled(true);
504 			}
505 			fReloadItem->SetEnabled(fSaveMessage != NULL);
506 			fEncodingItem->SetEnabled(fSaveMessage != NULL);
507 			break;
508 
509 		case SAVE_AS_ENCODING:
510 			void* ptr;
511 			if (message->FindPointer("source", &ptr) == B_OK
512 				&& fSavePanelEncodingMenu != NULL) {
513 				fTextView->SetEncoding(
514 					(uint32)fSavePanelEncodingMenu->IndexOf((BMenuItem*)ptr));
515 			}
516 			break;
517 
518 		case UPDATE_STATUS:
519 			message->AddBool("modified", !fClean);
520 			message->AddBool("readOnly", !fTextView->IsEditable());
521 			fStatusView->SetStatus(message);
522 			break;
523 
524 		case UNLOCK_FILE:
525 		{
526 			status_t status = _UnlockFile();
527 			if (status != B_OK) {
528 				BString text;
529 				bs_printf(&text,
530 					B_TRANSLATE("Unable to unlock file\n\t%s"),
531 					strerror(status));
532 				_ShowAlert(text, B_TRANSLATE("OK"), "", "", B_STOP_ALERT);
533 			}
534 			PostMessage(UPDATE_STATUS);
535 			break;
536 		}
537 
538 		case UPDATE_LINE_SELECTION:
539 		{
540 			int32 line;
541 			if (message->FindInt32("be:line", &line) == B_OK) {
542 				fTextView->GoToLine(line);
543 				fTextView->ScrollToSelection();
544 			}
545 
546 			int32 start, length;
547 			if (message->FindInt32("be:selection_offset", &start) == B_OK) {
548 				if (message->FindInt32("be:selection_length", &length) != B_OK)
549 					length = 0;
550 
551 				fTextView->Select(start, start + length);
552 				fTextView->ScrollToOffset(start);
553 			}
554 			break;
555 		}
556 		default:
557 			BWindow::MessageReceived(message);
558 			break;
559 	}
560 }
561 
562 
563 void
564 StyledEditWindow::MenusBeginning()
565 {
566 	// set up the recent documents menu
567 	BMessage documents;
568 	be_roster->GetRecentDocuments(&documents, 9, NULL, APP_SIGNATURE);
569 
570 	// delete old items..
571 	//    shatty: it would be preferable to keep the old
572 	//            menu around instead of continuously thrashing
573 	//            the menu, but unfortunately there does not
574 	//            seem to be a straightforward way to update it
575 	// going backwards may simplify memory management
576 	for (int i = fRecentMenu->CountItems(); i-- > 0;) {
577 		delete fRecentMenu->RemoveItem(i);
578 	}
579 
580 	// add new items
581 	int count = 0;
582 	entry_ref ref;
583 	while (documents.FindRef("refs", count++, &ref) == B_OK) {
584 		if (ref.device != -1 && ref.directory != -1) {
585 			// sanity check passed
586 			BMessage* openRecent = new BMessage(B_REFS_RECEIVED);
587 			openRecent->AddRef("refs", &ref);
588 			BMenuItem* item = new BMenuItem(ref.name, openRecent);
589 			item->SetTarget(be_app);
590 			fRecentMenu->AddItem(item);
591 		}
592 	}
593 
594 	// update the font menu
595 	// unselect the old values
596 	if (fCurrentFontItem != NULL) {
597 		fCurrentFontItem->SetMarked(false);
598 		BMenu* menu = fCurrentFontItem->Submenu();
599 		if (menu != NULL) {
600 			BMenuItem* item = menu->FindMarked();
601 			if (item != NULL) {
602 				item->SetMarked(false);
603 			}
604 		}
605 	}
606 
607 	if (fCurrentStyleItem != NULL) {
608 		fCurrentStyleItem->SetMarked(false);
609 	}
610 
611 	BMenuItem* oldColorItem = fFontColorMenu->FindMarked();
612 	if (oldColorItem != NULL)
613 		oldColorItem->SetMarked(false);
614 
615 	BMenuItem* oldSizeItem = fFontSizeMenu->FindMarked();
616 	if (oldSizeItem != NULL)
617 		oldSizeItem->SetMarked(false);
618 
619 	// find the current font, color, size
620 	BFont font;
621 	uint32 sameProperties;
622 	rgb_color color = BLACK;
623 	bool sameColor;
624 	fTextView->GetFontAndColor(&font, &sameProperties, &color, &sameColor);
625 
626 	if (sameColor && color.alpha == 255) {
627 		// select the current color
628 		if (color.red == 0) {
629 			if (color.green == 0) {
630 				if (color.blue == 0) {
631 					fBlackItem->SetMarked(true);
632 				} else if (color.blue == 255) {
633 					fBlueItem->SetMarked(true);
634 				}
635 			} else if (color.green == 255) {
636 				if (color.blue == 0) {
637 					fGreenItem->SetMarked(true);
638 				} else if (color.blue == 255) {
639 					fCyanItem->SetMarked(true);
640 				}
641 			}
642 		} else if (color.red == 255) {
643 			if (color.green == 0) {
644 				if (color.blue == 0) {
645 					fRedItem->SetMarked(true);
646 				} else if (color.blue == 255) {
647 					fMagentaItem->SetMarked(true);
648 				}
649 			} else if (color.green == 255) {
650 				if (color.blue == 0) {
651 					fYellowItem->SetMarked(true);
652 				}
653 			}
654 		}
655 	}
656 
657 	if (sameProperties & B_FONT_SIZE) {
658 		if ((int)font.Size() == font.Size()) {
659 			// select the current font size
660 			char fontSizeStr[16];
661 			snprintf(fontSizeStr, 15, "%i", (int)font.Size());
662 			BMenuItem* item = fFontSizeMenu->FindItem(fontSizeStr);
663 			if (item != NULL)
664 				item->SetMarked(true);
665 		}
666 	}
667 
668 	font_family family;
669 	font_style style;
670 	font.GetFamilyAndStyle(&family, &style);
671 
672 	fCurrentFontItem = fFontMenu->FindItem(family);
673 
674 	if (fCurrentFontItem != NULL) {
675 		fCurrentFontItem->SetMarked(true);
676 		BMenu* menu = fCurrentFontItem->Submenu();
677 		if (menu != NULL) {
678 			BMenuItem* item = menu->FindItem(style);
679 			fCurrentStyleItem = item;
680 			if (fCurrentStyleItem != NULL)
681 				item->SetMarked(true);
682 		}
683 	}
684 
685 	fBoldItem->SetMarked((font.Face() & B_BOLD_FACE) != 0);
686 	fItalicItem->SetMarked((font.Face() & B_ITALIC_FACE) != 0);
687 
688 	switch (fTextView->Alignment()) {
689 		case B_ALIGN_LEFT:
690 		default:
691 			fAlignLeft->SetMarked(true);
692 			break;
693 		case B_ALIGN_CENTER:
694 			fAlignCenter->SetMarked(true);
695 			break;
696 		case B_ALIGN_RIGHT:
697 			fAlignRight->SetMarked(true);
698 			break;
699 	}
700 
701 	// text encoding
702 	const BCharacterSet* charset
703 		= BCharacterSetRoster::GetCharacterSetByFontID(fTextView->GetEncoding());
704 	BMenu* encodingMenu = fEncodingItem->Submenu();
705 	if (charset != NULL && encodingMenu != NULL) {
706 		const char* mime = charset->GetMIMEName();
707 		BString name(charset->GetPrintName());
708 		if (mime)
709 			name << " (" << mime << ")";
710 
711 		BMenuItem* item = encodingMenu->FindItem(name);
712 		if (item != NULL)
713 			item->SetMarked(true);
714 	}
715 }
716 
717 
718 #undef B_TRANSLATION_CONTEXT
719 #define B_TRANSLATION_CONTEXT "SaveAlert"
720 
721 
722 status_t
723 StyledEditWindow::Save(BMessage* message)
724 {
725 	_NodeMonitorSuspender nodeMonitorSuspender(this);
726 
727 	if (!message)
728 		message = fSaveMessage;
729 
730 	if (!message)
731 		return B_ERROR;
732 
733 	entry_ref dirRef;
734 	const char* name;
735 	if (message->FindRef("directory", &dirRef) != B_OK
736 		|| message->FindString("name", &name) != B_OK)
737 		return B_BAD_VALUE;
738 
739 	BDirectory dir(&dirRef);
740 	BEntry entry(&dir, name);
741 
742 	status_t status = B_ERROR;
743 	if (dir.InitCheck() == B_OK && entry.InitCheck() == B_OK) {
744 		struct stat st;
745 		BFile file(&entry, B_READ_WRITE | B_CREATE_FILE);
746 		if (file.InitCheck() == B_OK
747 			&& (status = file.GetStat(&st)) == B_OK) {
748 			// check the file permissions
749 			if (!((getuid() == st.st_uid && (S_IWUSR & st.st_mode))
750 				|| (getgid() == st.st_gid && (S_IWGRP & st.st_mode))
751 				|| (S_IWOTH & st.st_mode))) {
752 				BString alertText;
753 				bs_printf(&alertText, B_TRANSLATE("This file is marked "
754 					"read-only. Save changes to the document \"%s\"? "), name);
755 				switch (_ShowAlert(alertText, B_TRANSLATE("Cancel"),
756 						B_TRANSLATE("Don't save"),
757 						B_TRANSLATE("Save"), B_WARNING_ALERT)) {
758 					case 0:
759 						return B_CANCELED;
760 					case 1:
761 						return B_OK;
762 					default:
763 						break;
764 				}
765 			}
766 
767 			status = fTextView->WriteStyledEditFile(&file);
768 		}
769 	}
770 
771 	if (status != B_OK) {
772 		BString alertText;
773 		bs_printf(&alertText, B_TRANSLATE("Error saving \"%s\":\n%s"), name,
774 			strerror(status));
775 
776 		_ShowAlert(alertText, B_TRANSLATE("OK"), "", "", B_STOP_ALERT);
777 		return status;
778 	}
779 
780 	SetTitle(name);
781 
782 	if (fSaveMessage != message) {
783 		delete fSaveMessage;
784 		fSaveMessage = new BMessage(*message);
785 	}
786 
787 	entry_ref ref;
788 	if (entry.GetRef(&ref) == B_OK)
789 		be_roster->AddToRecentDocuments(&ref, APP_SIGNATURE);
790 
791 	// clear clean modes
792 	fSaveItem->SetEnabled(false);
793 	fUndoCleans = false;
794 	fRedoCleans = false;
795 	fClean = true;
796 	fNagOnNodeChange = true;
797 
798 	return status;
799 }
800 
801 
802 #undef B_TRANSLATION_CONTEXT
803 #define B_TRANSLATION_CONTEXT "Open_and_SaveAsPanel"
804 
805 
806 status_t
807 StyledEditWindow::SaveAs(BMessage* message)
808 {
809 	if (fSavePanel == NULL) {
810 		entry_ref* directory = NULL;
811 		entry_ref dirRef;
812 		if (fSaveMessage != NULL) {
813 			if (fSaveMessage->FindRef("directory", &dirRef) == B_OK)
814 				directory = &dirRef;
815 		}
816 
817 		BMessenger target(this);
818 		fSavePanel = new BFilePanel(B_SAVE_PANEL, &target,
819 			directory, B_FILE_NODE, false);
820 
821 		BMenuBar* menuBar = dynamic_cast<BMenuBar*>(
822 			fSavePanel->Window()->FindView("MenuBar"));
823 		if (menuBar != NULL) {
824 			fSavePanelEncodingMenu = new BMenu(B_TRANSLATE("Encoding"));
825 			fSavePanelEncodingMenu->SetRadioMode(true);
826 			menuBar->AddItem(fSavePanelEncodingMenu);
827 
828 			BCharacterSetRoster roster;
829 			BCharacterSet charset;
830 			while (roster.GetNextCharacterSet(&charset) == B_NO_ERROR) {
831 				BString name(charset.GetPrintName());
832 				const char* mime = charset.GetMIMEName();
833 				if (mime) {
834 					name.Append(" (");
835 					name.Append(mime);
836 					name.Append(")");
837 				}
838 				BMenuItem * item = new BMenuItem(name.String(),
839 					new BMessage(SAVE_AS_ENCODING));
840 				item->SetTarget(this);
841 				fSavePanelEncodingMenu->AddItem(item);
842 				if (charset.GetFontID() == fTextView->GetEncoding())
843 					item->SetMarked(true);
844 			}
845 		}
846 	}
847 
848 	fSavePanel->SetSaveText(Title());
849 	if (message != NULL)
850 		fSavePanel->SetMessage(message);
851 
852 	fSavePanel->Show();
853 	return B_OK;
854 }
855 
856 
857 void
858 StyledEditWindow::OpenFile(entry_ref* ref)
859 {
860 	if (_LoadFile(ref) != B_OK) {
861 		fSaveItem->SetEnabled(true);
862 			// allow saving new files
863 		return;
864 	}
865 
866 	be_roster->AddToRecentDocuments(ref, APP_SIGNATURE);
867 	fSaveMessage = new BMessage(B_SAVE_REQUESTED);
868 	if (fSaveMessage) {
869 		BEntry entry(ref, true);
870 		BEntry parent;
871 		entry_ref parentRef;
872 		char name[B_FILE_NAME_LENGTH];
873 
874 		entry.GetParent(&parent);
875 		entry.GetName(name);
876 		parent.GetRef(&parentRef);
877 		fSaveMessage->AddRef("directory", &parentRef);
878 		fSaveMessage->AddString("name", name);
879 		SetTitle(name);
880 
881 		_LoadAttrs();
882 	}
883 
884 	_SwitchNodeMonitor(true, ref);
885 
886 	fReloadItem->SetEnabled(fSaveMessage != NULL);
887 	fEncodingItem->SetEnabled(fSaveMessage != NULL);
888 }
889 
890 
891 status_t
892 StyledEditWindow::PageSetup(const char* documentName)
893 {
894 	BPrintJob printJob(documentName);
895 
896 	if (fPrintSettings != NULL)
897 		printJob.SetSettings(new BMessage(*fPrintSettings));
898 
899 	status_t result = printJob.ConfigPage();
900 	if (result == B_OK) {
901 		delete fPrintSettings;
902 		fPrintSettings = printJob.Settings();
903 	}
904 
905 	return result;
906 }
907 
908 
909 void
910 StyledEditWindow::Print(const char* documentName)
911 {
912 	BPrintJob printJob(documentName);
913 	if (fPrintSettings)
914 		printJob.SetSettings(new BMessage(*fPrintSettings));
915 
916 	if (printJob.ConfigJob() != B_OK)
917 		return;
918 
919 	delete fPrintSettings;
920 	fPrintSettings = printJob.Settings();
921 
922 	// information from printJob
923 	BRect printableRect = printJob.PrintableRect();
924 	int32 firstPage = printJob.FirstPage();
925 	int32 lastPage = printJob.LastPage();
926 
927 	// lines eventually to be used to compute pages to print
928 	int32 firstLine = 0;
929 	int32 lastLine = fTextView->CountLines();
930 
931 	// values to be computed
932 	int32 pagesInDocument = 1;
933 	int32 linesInDocument = fTextView->CountLines();
934 
935 	int32 currentLine = 0;
936 	while (currentLine < linesInDocument) {
937 		float currentHeight = 0;
938 		while (currentHeight < printableRect.Height() && currentLine
939 				< linesInDocument) {
940 			currentHeight += fTextView->LineHeight(currentLine);
941 			if (currentHeight < printableRect.Height())
942 				currentLine++;
943 		}
944 		if (pagesInDocument == lastPage)
945 			lastLine = currentLine - 1;
946 
947 		if (currentHeight >= printableRect.Height()) {
948 			pagesInDocument++;
949 			if (pagesInDocument == firstPage)
950 				firstLine = currentLine;
951 		}
952 	}
953 
954 	if (lastPage > pagesInDocument - 1) {
955 		lastPage = pagesInDocument - 1;
956 		lastLine = currentLine - 1;
957 	}
958 
959 
960 	printJob.BeginJob();
961 	if (fTextView->CountLines() > 0 && fTextView->TextLength() > 0) {
962 		int32 printLine = firstLine;
963 		while (printLine <= lastLine) {
964 			float currentHeight = 0;
965 			int32 firstLineOnPage = printLine;
966 			while (currentHeight < printableRect.Height()
967 				&& printLine <= lastLine)
968 			{
969 				currentHeight += fTextView->LineHeight(printLine);
970 				if (currentHeight < printableRect.Height())
971 					printLine++;
972 			}
973 
974 			float top = 0;
975 			if (firstLineOnPage != 0)
976 				top = fTextView->TextHeight(0, firstLineOnPage - 1);
977 
978 			float bottom = fTextView->TextHeight(0, printLine - 1);
979 			BRect textRect(0.0, top + TEXT_INSET,
980 				printableRect.Width(), bottom + TEXT_INSET);
981 			printJob.DrawView(fTextView, textRect, B_ORIGIN);
982 			printJob.SpoolPage();
983 		}
984 	}
985 
986 
987 	printJob.CommitJob();
988 }
989 
990 
991 void
992 StyledEditWindow::SearchAllWindows(BString find, BString replace,
993 	bool caseSensitive)
994 {
995 	int32 numWindows;
996 	numWindows = be_app->CountWindows();
997 
998 	BMessage* message;
999 	message= new BMessage(MSG_REPLACE_ALL);
1000 	message->AddString("FindText", find);
1001 	message->AddString("ReplaceText", replace);
1002 	message->AddBool("casesens", caseSensitive);
1003 
1004 	while (numWindows >= 0) {
1005 		StyledEditWindow* window = dynamic_cast<StyledEditWindow *>(
1006 			be_app->WindowAt(numWindows));
1007 
1008 		BMessenger messenger(window);
1009 		messenger.SendMessage(message);
1010 
1011 		numWindows--;
1012 	}
1013 }
1014 
1015 
1016 bool
1017 StyledEditWindow::IsDocumentEntryRef(const entry_ref* ref)
1018 {
1019 	if (ref == NULL)
1020 		return false;
1021 
1022 	if (fSaveMessage == NULL)
1023 		return false;
1024 
1025 	entry_ref dir;
1026 	const char* name;
1027 	if (fSaveMessage->FindRef("directory", &dir) != B_OK
1028 		|| fSaveMessage->FindString("name", &name) != B_OK)
1029 		return false;
1030 
1031 	entry_ref documentRef;
1032 	BPath documentPath(&dir);
1033 	documentPath.Append(name);
1034 	get_ref_for_path(documentPath.Path(), &documentRef);
1035 
1036 	return *ref == documentRef;
1037 }
1038 
1039 
1040 // #pragma mark - private methods
1041 
1042 
1043 #undef B_TRANSLATION_CONTEXT
1044 #define B_TRANSLATION_CONTEXT "Menus"
1045 
1046 
1047 void
1048 StyledEditWindow::_InitWindow(uint32 encoding)
1049 {
1050 	fPrintSettings = NULL;
1051 	fSaveMessage = NULL;
1052 
1053 	// undo modes
1054 	fUndoFlag = false;
1055 	fCanUndo = false;
1056 	fRedoFlag = false;
1057 	fCanRedo = false;
1058 
1059 	// clean modes
1060 	fUndoCleans = false;
1061 	fRedoCleans = false;
1062 	fClean = true;
1063 
1064 	// search- state
1065 	fReplaceString = "";
1066 	fStringToFind = "";
1067 	fCaseSensitive = false;
1068 	fWrapAround = false;
1069 	fBackSearch = false;
1070 
1071 	fNagOnNodeChange = true;
1072 
1073 	// add menubar
1074 	fMenuBar = new BMenuBar(BRect(0, 0, 0, 0), "menubar");
1075 	AddChild(fMenuBar);
1076 
1077 	// add textview and scrollview
1078 
1079 	BRect viewFrame = Bounds();
1080 	viewFrame.top = fMenuBar->Bounds().Height() + 1;
1081 	viewFrame.right -=  B_V_SCROLL_BAR_WIDTH;
1082 	viewFrame.left = 0;
1083 	viewFrame.bottom -= B_H_SCROLL_BAR_HEIGHT;
1084 
1085 	BRect textBounds = viewFrame;
1086 	textBounds.OffsetTo(B_ORIGIN);
1087 	textBounds.InsetBy(TEXT_INSET, TEXT_INSET);
1088 
1089 	fTextView= new StyledEditView(viewFrame, textBounds, this);
1090 	fTextView->SetDoesUndo(true);
1091 	fTextView->SetStylable(true);
1092 	fTextView->SetEncoding(encoding);
1093 
1094 	fScrollView = new BScrollView("scrollview", fTextView, B_FOLLOW_ALL, 0,
1095 		true, true, B_PLAIN_BORDER);
1096 	AddChild(fScrollView);
1097 	fTextView->MakeFocus(true);
1098 
1099 	fStatusView = new StatusView(fScrollView);
1100 	fScrollView->AddChild(fStatusView);
1101 
1102 	// Add "File"-menu:
1103 	BMenu* menu = new BMenu(B_TRANSLATE("File"));
1104 	fMenuBar->AddItem(menu);
1105 
1106 	BMenuItem* menuItem;
1107 	menu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("New"),
1108 		new BMessage(MENU_NEW), 'N'));
1109 	menuItem->SetTarget(be_app);
1110 
1111 	menu->AddItem(menuItem = new BMenuItem(fRecentMenu
1112 		= new BMenu(B_TRANSLATE("Open" B_UTF8_ELLIPSIS)),
1113 			new BMessage(MENU_OPEN)));
1114 	menuItem->SetShortcut('O', 0);
1115 	menuItem->SetTarget(be_app);
1116 	menu->AddSeparatorItem();
1117 
1118 	menu->AddItem(fSaveItem = new BMenuItem(B_TRANSLATE("Save"),
1119 		new BMessage(MENU_SAVE), 'S'));
1120 	fSaveItem->SetEnabled(false);
1121 	menu->AddItem(menuItem = new BMenuItem(
1122 		B_TRANSLATE("Save as" B_UTF8_ELLIPSIS), new BMessage(MENU_SAVEAS)));
1123 	menuItem->SetShortcut('S', B_SHIFT_KEY);
1124 	menuItem->SetEnabled(true);
1125 
1126 	menu->AddItem(fReloadItem
1127 		= new BMenuItem(B_TRANSLATE("Reload" B_UTF8_ELLIPSIS),
1128 		new BMessage(MENU_RELOAD), 'L'));
1129 	fReloadItem->SetEnabled(false);
1130 
1131 	menu->AddItem(new BMenuItem(B_TRANSLATE("Close"),
1132 		new BMessage(MENU_CLOSE), 'W'));
1133 
1134 	menu->AddSeparatorItem();
1135 	menu->AddItem(new BMenuItem(B_TRANSLATE("Page setup" B_UTF8_ELLIPSIS),
1136 		new BMessage(MENU_PAGESETUP)));
1137 	menu->AddItem(new BMenuItem(B_TRANSLATE("Print" B_UTF8_ELLIPSIS),
1138 		new BMessage(MENU_PRINT), 'P'));
1139 
1140 	menu->AddSeparatorItem();
1141 	menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
1142 		new BMessage(MENU_QUIT), 'Q'));
1143 
1144 	// Add the "Edit"-menu:
1145 	menu = new BMenu(B_TRANSLATE("Edit"));
1146 	fMenuBar->AddItem(menu);
1147 
1148 	menu->AddItem(fUndoItem = new BMenuItem(B_TRANSLATE("Can't undo"),
1149 		new BMessage(B_UNDO), 'Z'));
1150 	fUndoItem->SetEnabled(false);
1151 
1152 	menu->AddSeparatorItem();
1153 	menu->AddItem(fCutItem = new BMenuItem(B_TRANSLATE("Cut"),
1154 		new BMessage(B_CUT), 'X'));
1155 	fCutItem->SetEnabled(false);
1156 	fCutItem->SetTarget(fTextView);
1157 
1158 	menu->AddItem(fCopyItem = new BMenuItem(B_TRANSLATE("Copy"),
1159 		new BMessage(B_COPY), 'C'));
1160 	fCopyItem->SetEnabled(false);
1161 	fCopyItem->SetTarget(fTextView);
1162 
1163 	menu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("Paste"),
1164 		new BMessage(B_PASTE), 'V'));
1165 	menuItem->SetTarget(fTextView);
1166 
1167 	menu->AddSeparatorItem();
1168 	menu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("Select all"),
1169 		new BMessage(B_SELECT_ALL), 'A'));
1170 	menuItem->SetTarget(fTextView);
1171 
1172 	menu->AddSeparatorItem();
1173 	menu->AddItem(new BMenuItem(B_TRANSLATE("Find" B_UTF8_ELLIPSIS),
1174 		new BMessage(MENU_FIND), 'F'));
1175 	menu->AddItem(fFindAgainItem= new BMenuItem(B_TRANSLATE("Find again"),
1176 		new BMessage(MENU_FIND_AGAIN), 'G'));
1177 	fFindAgainItem->SetEnabled(false);
1178 
1179 	menu->AddItem(new BMenuItem(B_TRANSLATE("Find selection"),
1180 		new BMessage(MENU_FIND_SELECTION), 'H'));
1181 	menu->AddItem(fReplaceItem = new BMenuItem(B_TRANSLATE("Replace" B_UTF8_ELLIPSIS),
1182 		new BMessage(MENU_REPLACE), 'R'));
1183 	menu->AddItem(fReplaceSameItem = new BMenuItem(B_TRANSLATE("Replace next"),
1184 		new BMessage(MENU_REPLACE_SAME), 'T'));
1185 	fReplaceSameItem->SetEnabled(false);
1186 
1187 	// Add the "Font"-menu:
1188 	fFontMenu = new BMenu(B_TRANSLATE("Font"));
1189 	fMenuBar->AddItem(fFontMenu);
1190 
1191 	// "Size"-subMenu
1192 	fFontSizeMenu = new BMenu(B_TRANSLATE("Size"));
1193 	fFontSizeMenu->SetRadioMode(true);
1194 	fFontMenu->AddItem(fFontSizeMenu);
1195 
1196 	const int32 fontSizes[] = {9, 10, 11, 12, 14, 18, 24, 36, 48, 72};
1197 	for (uint32 i = 0; i < sizeof(fontSizes) / sizeof(fontSizes[0]); i++) {
1198 		BMessage* fontMessage = new BMessage(FONT_SIZE);
1199 		fontMessage->AddFloat("size", fontSizes[i]);
1200 
1201 		char label[64];
1202 		snprintf(label, sizeof(label), "%" B_PRId32, fontSizes[i]);
1203 		fFontSizeMenu->AddItem(menuItem = new BMenuItem(label, fontMessage));
1204 
1205 		if (fontSizes[i] == (int32)be_plain_font->Size())
1206 			menuItem->SetMarked(true);
1207 	}
1208 
1209 	// "Color"-subMenu
1210 	fFontColorMenu = new BMenu(B_TRANSLATE("Color"));
1211 	fFontColorMenu->SetRadioMode(true);
1212 	fFontMenu->AddItem(fFontColorMenu);
1213 
1214 	fFontColorMenu->AddItem(fBlackItem = new BMenuItem(B_TRANSLATE("Black"),
1215 		new BMessage(FONT_COLOR)));
1216 	fBlackItem->SetMarked(true);
1217 	fFontColorMenu->AddItem(fRedItem = new ColorMenuItem(B_TRANSLATE("Red"),
1218 		RED, new BMessage(FONT_COLOR)));
1219 	fFontColorMenu->AddItem(fGreenItem = new ColorMenuItem(B_TRANSLATE("Green"),
1220 		GREEN, new BMessage(FONT_COLOR)));
1221 	fFontColorMenu->AddItem(fBlueItem = new ColorMenuItem(B_TRANSLATE("Blue"),
1222 		BLUE, new BMessage(FONT_COLOR)));
1223 	fFontColorMenu->AddItem(fCyanItem = new ColorMenuItem(B_TRANSLATE("Cyan"),
1224 		CYAN, new BMessage(FONT_COLOR)));
1225 	fFontColorMenu->AddItem(fMagentaItem = new ColorMenuItem(B_TRANSLATE("Magenta"),
1226 		MAGENTA, new BMessage(FONT_COLOR)));
1227 	fFontColorMenu->AddItem(fYellowItem = new ColorMenuItem(B_TRANSLATE("Yellow"),
1228 		YELLOW, new BMessage(FONT_COLOR)));
1229 	fFontMenu->AddSeparatorItem();
1230 
1231 	// "Bold" & "Italic" menu items
1232 	fFontMenu->AddItem(fBoldItem = new BMenuItem(B_TRANSLATE("Bold"),
1233 		new BMessage(kMsgSetBold)));
1234 	fFontMenu->AddItem(fItalicItem = new BMenuItem(B_TRANSLATE("Italic"),
1235 		new BMessage(kMsgSetItalic)));
1236 	fBoldItem->SetShortcut('B', 0);
1237 	fItalicItem->SetShortcut('I', 0);
1238 	fFontMenu->AddSeparatorItem();
1239 
1240 	// Available fonts
1241 
1242 	fCurrentFontItem = 0;
1243 	fCurrentStyleItem = 0;
1244 
1245 	BMenu* subMenu;
1246 	int32 numFamilies = count_font_families();
1247 	for (int32 i = 0; i < numFamilies; i++) {
1248 		font_family family;
1249 		if (get_font_family(i, &family) == B_OK) {
1250 			subMenu = new BMenu(family);
1251 			subMenu->SetRadioMode(true);
1252 			fFontMenu->AddItem(new BMenuItem(subMenu,
1253 				new BMessage(FONT_FAMILY)));
1254 
1255 			int32 numStyles = count_font_styles(family);
1256 			for (int32 j = 0; j < numStyles; j++) {
1257 				font_style style;
1258 				uint32 flags;
1259 				if (get_font_style(family, j, &style, &flags) == B_OK) {
1260 					subMenu->AddItem(new BMenuItem(style,
1261 						new BMessage(FONT_STYLE)));
1262 				}
1263 			}
1264 		}
1265 	}
1266 
1267 	// Add the "Document"-menu:
1268 	menu = new BMenu(B_TRANSLATE("Document"));
1269 	fMenuBar->AddItem(menu);
1270 
1271 	// "Align"-subMenu:
1272 	subMenu = new BMenu(B_TRANSLATE("Align"));
1273 	subMenu->SetRadioMode(true);
1274 
1275 	subMenu->AddItem(fAlignLeft = new BMenuItem(B_TRANSLATE("Left"),
1276 		new BMessage(ALIGN_LEFT)));
1277 	fAlignLeft->SetMarked(true);
1278 	fAlignLeft->SetShortcut('L', B_OPTION_KEY);
1279 
1280 	subMenu->AddItem(fAlignCenter = new BMenuItem(B_TRANSLATE("Center"),
1281 		new BMessage(ALIGN_CENTER)));
1282 	fAlignCenter->SetShortcut('C', B_OPTION_KEY);
1283 
1284 	subMenu->AddItem(fAlignRight = new BMenuItem(B_TRANSLATE("Right"),
1285 		new BMessage(ALIGN_RIGHT)));
1286 	fAlignRight->SetShortcut('R', B_OPTION_KEY);
1287 
1288 	menu->AddItem(subMenu);
1289 	menu->AddItem(fWrapItem = new BMenuItem(B_TRANSLATE("Wrap lines"),
1290 		new BMessage(WRAP_LINES)));
1291 	fWrapItem->SetMarked(true);
1292 	fWrapItem->SetShortcut('W', B_OPTION_KEY);
1293 
1294 	BMessage *message = new BMessage(MENU_RELOAD);
1295 	message->AddString("encoding", "auto");
1296 	menu->AddItem(fEncodingItem = new BMenuItem(PopulateEncodingMenu(
1297 		new BMenu(B_TRANSLATE("Text encoding")), "UTF-8"),
1298 		message));
1299 	fEncodingItem->SetEnabled(false);
1300 
1301 	menu->AddSeparatorItem();
1302 	menu->AddItem(new BMenuItem(B_TRANSLATE("Statistics" B_UTF8_ELLIPSIS),
1303 		new BMessage(SHOW_STATISTICS)));
1304 
1305 	fSavePanel = NULL;
1306 	fSavePanelEncodingMenu = NULL;
1307 		// build lazily
1308 }
1309 
1310 
1311 void
1312 StyledEditWindow::_LoadAttrs()
1313 {
1314 	entry_ref dir;
1315 	const char* name;
1316 	if (fSaveMessage->FindRef("directory", &dir) != B_OK
1317 		|| fSaveMessage->FindString("name", &name) != B_OK)
1318 		return;
1319 
1320 	BPath documentPath(&dir);
1321 	documentPath.Append(name);
1322 
1323 	BNode documentNode(documentPath.Path());
1324 	if (documentNode.InitCheck() != B_OK)
1325 		return;
1326 
1327 	BRect newFrame;
1328 	ssize_t bytesRead = documentNode.ReadAttr(kInfoAttributeName, B_RECT_TYPE,
1329 		0, &newFrame, sizeof(BRect));
1330 	if (bytesRead != sizeof(BRect))
1331 		return;
1332 
1333 	swap_data(B_RECT_TYPE, &newFrame, sizeof(BRect), B_SWAP_BENDIAN_TO_HOST);
1334 
1335 	// Check if the frame in on screen, otherwise, ignore it
1336 	BScreen screen(this);
1337 	if (newFrame.Width() > 32 && newFrame.Height() > 32
1338 		&& screen.Frame().Contains(newFrame)) {
1339 		MoveTo(newFrame.left, newFrame.top);
1340 		ResizeTo(newFrame.Width(), newFrame.Height());
1341 	}
1342 
1343 	// info about position of caret may live in the file attributes
1344 	int32 position = 0;
1345 	if (documentNode.ReadAttr("be:caret_position", B_INT32_TYPE, 0,
1346 			&position, sizeof(position)) != sizeof(position))
1347 		position = 0;
1348 
1349 	fTextView->Select(position, position);
1350 	fTextView->ScrollToOffset(position);
1351 }
1352 
1353 
1354 void
1355 StyledEditWindow::_SaveAttrs()
1356 {
1357 	if (!fSaveMessage)
1358 		return;
1359 
1360 	entry_ref dir;
1361 	const char* name;
1362 	if (fSaveMessage->FindRef("directory", &dir) != B_OK
1363 		|| fSaveMessage->FindString("name", &name) != B_OK)
1364 		return;
1365 
1366 	BPath documentPath(&dir);
1367 	documentPath.Append(name);
1368 
1369 	BNode documentNode(documentPath.Path());
1370 	if (documentNode.InitCheck() != B_OK)
1371 		return;
1372 
1373 	BRect frame(Frame());
1374 	swap_data(B_RECT_TYPE, &frame, sizeof(BRect), B_SWAP_HOST_TO_BENDIAN);
1375 
1376 	documentNode.WriteAttr(kInfoAttributeName, B_RECT_TYPE, 0, &frame,
1377 		sizeof(BRect));
1378 
1379 	// preserve caret line and position
1380 	int32 start, end;
1381 	fTextView->GetSelection(&start, &end);
1382 	documentNode.WriteAttr("be:caret_position",
1383 			B_INT32_TYPE, 0, &start, sizeof(start));
1384 }
1385 
1386 
1387 #undef B_TRANSLATION_CONTEXT
1388 #define B_TRANSLATION_CONTEXT "LoadAlert"
1389 
1390 
1391 status_t
1392 StyledEditWindow::_LoadFile(entry_ref* ref, const char* forceEncoding)
1393 {
1394 	BEntry entry(ref, true);
1395 		// traverse an eventual link
1396 
1397 	status_t status = entry.InitCheck();
1398 	if (status == B_OK && entry.IsDirectory())
1399 		status = B_IS_A_DIRECTORY;
1400 
1401 	BFile file;
1402 	if (status == B_OK)
1403 		status = file.SetTo(&entry, B_READ_ONLY);
1404 	if (status == B_OK)
1405 		status = fTextView->GetStyledText(&file, forceEncoding);
1406 
1407 	if (status == B_ENTRY_NOT_FOUND) {
1408 		// Treat non-existing files consideratley; we just want to get an
1409 		// empty window for them - to create this new document
1410 		status = B_OK;
1411 	}
1412 
1413 	if (status != B_OK) {
1414 		// If an error occured, bail out and tell the user what happened
1415 		BEntry entry(ref, true);
1416 		char name[B_FILE_NAME_LENGTH];
1417 		if (entry.GetName(name) != B_OK)
1418 			strlcpy(name, B_TRANSLATE("???"), sizeof(name));
1419 
1420 		BString text;
1421 		if (status == B_BAD_TYPE)
1422 			bs_printf(&text,
1423 				B_TRANSLATE("Error loading \"%s\":\n\tUnsupported format"), name);
1424 		else
1425 			bs_printf(&text, B_TRANSLATE("Error loading \"%s\":\n\t%s"),
1426 				name, strerror(status));
1427 
1428 		_ShowAlert(text, B_TRANSLATE("OK"), "", "", B_STOP_ALERT);
1429 		return status;
1430 	}
1431 
1432 	struct stat st;
1433 	if (file.InitCheck() == B_OK && file.GetStat(&st) == B_OK) {
1434 		bool editable = (getuid() == st.st_uid && S_IWUSR & st.st_mode)
1435 					|| (getgid() == st.st_gid && S_IWGRP & st.st_mode)
1436 					|| (S_IWOTH & st.st_mode);
1437 		_SetReadOnly(!editable);
1438 	}
1439 
1440 	// update alignment
1441 	switch (fTextView->Alignment()) {
1442 		case B_ALIGN_LEFT:
1443 		default:
1444 			fAlignLeft->SetMarked(true);
1445 			break;
1446 		case B_ALIGN_CENTER:
1447 			fAlignCenter->SetMarked(true);
1448 			break;
1449 		case B_ALIGN_RIGHT:
1450 			fAlignRight->SetMarked(true);
1451 			break;
1452 	}
1453 
1454 	// update word wrapping
1455 	fWrapItem->SetMarked(fTextView->DoesWordWrap());
1456 	return B_OK;
1457 }
1458 
1459 
1460 #undef B_TRANSLATION_CONTEXT
1461 #define B_TRANSLATION_CONTEXT "RevertToSavedAlert"
1462 
1463 
1464 void
1465 StyledEditWindow::_ReloadDocument(BMessage* message)
1466 {
1467 	entry_ref ref;
1468 	const char* name;
1469 
1470 	if (fSaveMessage == NULL || message == NULL)
1471 		return;
1472 
1473 	fSaveMessage->FindRef("directory", &ref);
1474 	fSaveMessage->FindString("name", &name);
1475 
1476 	BDirectory dir(&ref);
1477 	status_t status = dir.InitCheck();
1478 	BEntry entry;
1479 	if (status == B_OK)
1480 		status = entry.SetTo(&dir, name);
1481 
1482 	if (status == B_OK)
1483 		status = entry.GetRef(&ref);
1484 
1485 	if (status != B_OK || !entry.Exists()) {
1486 		BString alertText;
1487 		bs_printf(&alertText,
1488 			B_TRANSLATE("Cannot revert, file not found: \"%s\"."), name);
1489 		_ShowAlert(alertText, B_TRANSLATE("OK"), "", "", B_STOP_ALERT);
1490 		return;
1491 	}
1492 
1493 	if (!fClean) {
1494 		BString alertText;
1495 		bs_printf(&alertText,
1496 			B_TRANSLATE("\"%s\" has unsaved changes.\n"
1497 				"Revert it to the last saved version? "), Title());
1498 		if (_ShowAlert(alertText, B_TRANSLATE("Cancel"), B_TRANSLATE("Revert"),
1499 			"", B_WARNING_ALERT) != 1)
1500 			return;
1501 	}
1502 
1503 	const char* forceEncoding = NULL;
1504 	if (message->FindString("encoding", &forceEncoding) != B_OK) {
1505 		const BCharacterSet* charset
1506 			= BCharacterSetRoster::GetCharacterSetByFontID(
1507 					fTextView->GetEncoding());
1508 		if (charset != NULL)
1509 			forceEncoding = charset->GetName();
1510 	}
1511 
1512 	BScrollBar* vertBar = fScrollView->ScrollBar(B_VERTICAL);
1513 	float vertPos = vertBar != NULL ? vertBar->Value() : 0.f;
1514 
1515 	DisableUpdates();
1516 
1517 	fTextView->Reset();
1518 
1519 	status = _LoadFile(&ref, forceEncoding);
1520 
1521 	if (vertBar != NULL)
1522 		vertBar->SetValue(vertPos);
1523 
1524 	EnableUpdates();
1525 
1526 	if (status != B_OK)
1527 		return;
1528 
1529 #undef B_TRANSLATION_CONTEXT
1530 #define B_TRANSLATION_CONTEXT "Menus"
1531 
1532 	// clear undo modes
1533 	fUndoItem->SetLabel(B_TRANSLATE("Can't undo"));
1534 	fUndoItem->SetEnabled(false);
1535 	fUndoFlag = false;
1536 	fCanUndo = false;
1537 	fRedoFlag = false;
1538 	fCanRedo = false;
1539 
1540 	// clear clean modes
1541 	fSaveItem->SetEnabled(false);
1542 
1543 	fUndoCleans = false;
1544 	fRedoCleans = false;
1545 	fClean = true;
1546 
1547 	fNagOnNodeChange = true;
1548 }
1549 
1550 
1551 status_t
1552 StyledEditWindow::_UnlockFile()
1553 {
1554 	_NodeMonitorSuspender nodeMonitorSuspender(this);
1555 
1556 	if (!fSaveMessage)
1557 		return B_ERROR;
1558 
1559 	entry_ref dirRef;
1560 	const char* name;
1561 	if (fSaveMessage->FindRef("directory", &dirRef) != B_OK
1562 		|| fSaveMessage->FindString("name", &name) != B_OK)
1563 		return B_BAD_VALUE;
1564 
1565 	BDirectory dir(&dirRef);
1566 	BEntry entry(&dir, name);
1567 
1568 	status_t status = dir.InitCheck();
1569 	if (status != B_OK)
1570 		return status;
1571 
1572 	status = entry.InitCheck();
1573 	if (status != B_OK)
1574 		return status;
1575 
1576 	struct stat st;
1577 	BFile file(&entry, B_READ_WRITE);
1578 	status = file.InitCheck();
1579 	if (status != B_OK)
1580 		return status;
1581 
1582 	status = file.GetStat(&st);
1583 	if (status != B_OK)
1584 		return status;
1585 
1586 	st.st_mode |= S_IWUSR;
1587 	status = file.SetPermissions(st.st_mode);
1588 	if (status == B_OK)
1589 		_SetReadOnly(false);
1590 
1591 	return status;
1592 }
1593 
1594 
1595 bool
1596 StyledEditWindow::_Search(BString string, bool caseSensitive, bool wrap,
1597 	bool backSearch, bool scrollToOccurence)
1598 {
1599 	int32 start;
1600 	int32 finish;
1601 
1602 	start = B_ERROR;
1603 
1604 	int32 length = string.Length();
1605 	if (length == 0)
1606 		return false;
1607 
1608 	BString viewText(fTextView->Text());
1609 	int32 textStart, textFinish;
1610 	fTextView->GetSelection(&textStart, &textFinish);
1611 	if (backSearch) {
1612 		if (caseSensitive)
1613 			start = viewText.FindLast(string, textStart);
1614 		else
1615 			start = viewText.IFindLast(string, textStart);
1616 	} else {
1617 		if (caseSensitive)
1618 			start = viewText.FindFirst(string, textFinish);
1619 		else
1620 			start = viewText.IFindFirst(string, textFinish);
1621 	}
1622 	if (start == B_ERROR && wrap) {
1623 		if (backSearch) {
1624 			if (caseSensitive)
1625 				start = viewText.FindLast(string, viewText.Length());
1626 			else
1627 				start = viewText.IFindLast(string, viewText.Length());
1628 		} else {
1629 			if (caseSensitive)
1630 				start = viewText.FindFirst(string, 0);
1631 			else
1632 				start = viewText.IFindFirst(string, 0);
1633 		}
1634 	}
1635 
1636 	if (start != B_ERROR) {
1637 		finish = start + length;
1638 		fTextView->Select(start, finish);
1639 
1640 		if (scrollToOccurence)
1641 			fTextView->ScrollToSelection();
1642 		return true;
1643 	}
1644 
1645 	return false;
1646 }
1647 
1648 
1649 void
1650 StyledEditWindow::_FindSelection()
1651 {
1652 	int32 selectionStart, selectionFinish;
1653 	fTextView->GetSelection(&selectionStart, &selectionFinish);
1654 
1655 	int32 selectionLength = selectionFinish- selectionStart;
1656 
1657 	BString viewText = fTextView->Text();
1658 	viewText.CopyInto(fStringToFind, selectionStart, selectionLength);
1659 	fFindAgainItem->SetEnabled(true);
1660 	_Search(fStringToFind, fCaseSensitive, fWrapAround, fBackSearch);
1661 }
1662 
1663 
1664 bool
1665 StyledEditWindow::_Replace(BString findThis, BString replaceWith,
1666 	bool caseSensitive, bool wrap, bool backSearch)
1667 {
1668 	if (_Search(findThis, caseSensitive, wrap, backSearch)) {
1669 		int32 start;
1670 		int32 finish;
1671 		fTextView->GetSelection(&start, &finish);
1672 
1673 		_UpdateCleanUndoRedoSaveRevert();
1674 		fTextView->SetSuppressChanges(true);
1675 		fTextView->Delete(start, start + findThis.Length());
1676 		fTextView->Insert(start, replaceWith.String(), replaceWith.Length());
1677 		fTextView->SetSuppressChanges(false);
1678 		fTextView->Select(start, start + replaceWith.Length());
1679 		fTextView->ScrollToSelection();
1680 		return true;
1681 	}
1682 
1683 	return false;
1684 }
1685 
1686 
1687 void
1688 StyledEditWindow::_ReplaceAll(BString findThis, BString replaceWith,
1689 	bool caseSensitive)
1690 {
1691 	bool first = true;
1692 	fTextView->SetSuppressChanges(true);
1693 
1694 	// start from the beginning of text
1695 	fTextView->Select(0, 0);
1696 
1697 	// iterate occurences of findThis without wrapping around
1698 	while (_Search(findThis, caseSensitive, false, false, false)) {
1699 		if (first) {
1700 			_UpdateCleanUndoRedoSaveRevert();
1701 			first = false;
1702 		}
1703 		int32 start;
1704 		int32 finish;
1705 
1706 		fTextView->GetSelection(&start, &finish);
1707 		fTextView->Delete(start, start + findThis.Length());
1708 		fTextView->Insert(start, replaceWith.String(), replaceWith.Length());
1709 
1710 		// advance the caret behind the inserted text
1711 		start += replaceWith.Length();
1712 		fTextView->Select(start, start);
1713 	}
1714 	fTextView->ScrollToSelection();
1715 	fTextView->SetSuppressChanges(false);
1716 }
1717 
1718 
1719 void
1720 StyledEditWindow::_SetFontSize(float fontSize)
1721 {
1722 	uint32 sameProperties;
1723 	BFont font;
1724 
1725 	fTextView->GetFontAndColor(&font, &sameProperties);
1726 	font.SetSize(fontSize);
1727 	fTextView->SetFontAndColor(&font, B_FONT_SIZE);
1728 
1729 	_UpdateCleanUndoRedoSaveRevert();
1730 }
1731 
1732 
1733 void
1734 StyledEditWindow::_SetFontColor(const rgb_color* color)
1735 {
1736 	uint32 sameProperties;
1737 	BFont font;
1738 
1739 	fTextView->GetFontAndColor(&font, &sameProperties, NULL, NULL);
1740 	fTextView->SetFontAndColor(&font, 0, color);
1741 
1742 	_UpdateCleanUndoRedoSaveRevert();
1743 }
1744 
1745 
1746 void
1747 StyledEditWindow::_SetFontStyle(const char* fontFamily, const char* fontStyle)
1748 {
1749 	BFont font;
1750 	uint32 sameProperties;
1751 
1752 	// find out what the old font was
1753 	font_family oldFamily;
1754 	font_style oldStyle;
1755 	fTextView->GetFontAndColor(&font, &sameProperties);
1756 	font.GetFamilyAndStyle(&oldFamily, &oldStyle);
1757 
1758 	// clear that family's bit on the menu, if necessary
1759 	if (strcmp(oldFamily, fontFamily)) {
1760 		BMenuItem* oldItem = fFontMenu->FindItem(oldFamily);
1761 		if (oldItem != NULL) {
1762 			oldItem->SetMarked(false);
1763 			BMenu* menu = oldItem->Submenu();
1764 			if (menu != NULL) {
1765 				oldItem = menu->FindItem(oldStyle);
1766 				if (oldItem != NULL)
1767 					oldItem->SetMarked(false);
1768 			}
1769 		}
1770 	}
1771 
1772 	font.SetFamilyAndStyle(fontFamily, fontStyle);
1773 
1774 	uint16 face = 0;
1775 
1776 	if (!(font.Face() & B_REGULAR_FACE))
1777 		face = font.Face();
1778 
1779 	if (fBoldItem->IsMarked())
1780 		face |= B_BOLD_FACE;
1781 
1782 	if (fItalicItem->IsMarked())
1783 		face |= B_ITALIC_FACE;
1784 
1785 	font.SetFace(face);
1786 
1787 	fTextView->SetFontAndColor(&font, B_FONT_FAMILY_AND_STYLE);
1788 
1789 	BMenuItem* superItem;
1790 	superItem = fFontMenu->FindItem(fontFamily);
1791 	if (superItem != NULL) {
1792 		superItem->SetMarked(true);
1793 		fCurrentFontItem = superItem;
1794 	}
1795 
1796 	_UpdateCleanUndoRedoSaveRevert();
1797 }
1798 
1799 
1800 #undef B_TRANSLATION_CONTEXT
1801 #define B_TRANSLATION_CONTEXT "Statistics"
1802 
1803 
1804 int32
1805 StyledEditWindow::_ShowStatistics()
1806 {
1807 	size_t words = 0;
1808 	bool inWord = false;
1809 	size_t length = fTextView->TextLength();
1810 
1811 	for (size_t i = 0; i < length; i++)	{
1812 		if (BUnicodeChar::IsSpace(fTextView->Text()[i])) {
1813 			inWord = false;
1814 		} else if (!inWord)	{
1815 			words++;
1816 			inWord = true;
1817 		}
1818 	}
1819 
1820 	BString result;
1821 	result << B_TRANSLATE("Document statistics") << '\n' << '\n'
1822 		<< B_TRANSLATE("Lines:") << ' ' << fTextView->CountLines() << '\n'
1823 		<< B_TRANSLATE("Characters:") << ' ' << length << '\n'
1824 		<< B_TRANSLATE("Words:") << ' ' << words;
1825 
1826 	BAlert* alert = new BAlert("Statistics", result, B_TRANSLATE("OK"), NULL,
1827 		NULL, B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_INFO_ALERT);
1828 	alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1829 
1830 	return alert->Go();
1831 }
1832 
1833 
1834 void
1835 StyledEditWindow::_SetReadOnly(bool readOnly)
1836 {
1837 	fReplaceItem->SetEnabled(!readOnly);
1838 	fReplaceSameItem->SetEnabled(!readOnly);
1839 	fFontMenu->SetEnabled(!readOnly);
1840 	fAlignLeft->Menu()->SetEnabled(!readOnly);
1841 	fWrapItem->SetEnabled(!readOnly);
1842 	fTextView->MakeEditable(!readOnly);
1843 }
1844 
1845 
1846 #undef B_TRANSLATION_CONTEXT
1847 #define B_TRANSLATION_CONTEXT "Menus"
1848 
1849 
1850 void
1851 StyledEditWindow::_UpdateCleanUndoRedoSaveRevert()
1852 {
1853 	fClean = false;
1854 	fUndoCleans = false;
1855 	fRedoCleans = false;
1856 	fReloadItem->SetEnabled(fSaveMessage != NULL);
1857 	fEncodingItem->SetEnabled(fSaveMessage != NULL);
1858 	fSaveItem->SetEnabled(true);
1859 	fUndoItem->SetLabel(B_TRANSLATE("Can't undo"));
1860 	fUndoItem->SetEnabled(false);
1861 	fCanUndo = false;
1862 	fCanRedo = false;
1863 }
1864 
1865 
1866 int32
1867 StyledEditWindow::_ShowAlert(const BString& text, const BString& label,
1868 	const BString& label2, const BString& label3, alert_type type) const
1869 {
1870 	const char* button2 = NULL;
1871 	if (label2.Length() > 0)
1872 		button2 = label2.String();
1873 
1874 	const char* button3 = NULL;
1875 	button_spacing spacing = B_EVEN_SPACING;
1876 	if (label3.Length() > 0) {
1877 		button3 = label3.String();
1878 		spacing = B_OFFSET_SPACING;
1879 	}
1880 
1881 	BAlert* alert = new BAlert("Alert", text.String(), label.String(), button2,
1882 		button3, B_WIDTH_AS_USUAL, spacing, type);
1883 	alert->SetShortcut(0, B_ESCAPE);
1884 
1885 	return alert->Go();
1886 }
1887 
1888 
1889 BMenu*
1890 StyledEditWindow::PopulateEncodingMenu(BMenu* menu, const char* currentEncoding)
1891 {
1892 	menu->SetRadioMode(true);
1893 	BString encoding(currentEncoding);
1894 	if (encoding.Length() == 0)
1895 		encoding.SetTo("UTF-8");
1896 
1897 	BCharacterSetRoster roster;
1898 	BCharacterSet charset;
1899 	while (roster.GetNextCharacterSet(&charset) == B_OK) {
1900 		const char* mime = charset.GetMIMEName();
1901 		BString name(charset.GetPrintName());
1902 
1903 		if (mime)
1904 			name << " (" << mime << ")";
1905 
1906 		BMessage *message = new BMessage(MENU_RELOAD);
1907 		if (message != NULL) {
1908 			message->AddString("encoding", charset.GetName());
1909 			BMenuItem* item = new BMenuItem(name, message);
1910 			if (encoding.Compare(charset.GetName()) == 0)
1911 				item->SetMarked(true);
1912 			menu->AddItem(item);
1913 		}
1914 	}
1915 
1916 	menu->AddSeparatorItem();
1917 	BMessage *message = new BMessage(MENU_RELOAD);
1918 	message->AddString("encoding", "auto");
1919 	menu->AddItem(new BMenuItem(B_TRANSLATE("Autodetect"), message));
1920 
1921 	return menu;
1922 }
1923 
1924 
1925 #undef B_TRANSLATION_CONTEXT
1926 #define B_TRANSLATION_CONTEXT "NodeMonitorAlerts"
1927 
1928 
1929 void
1930 StyledEditWindow::_ShowNodeChangeAlert(const char* name, bool removed)
1931 {
1932 	if (!fNagOnNodeChange)
1933 		return;
1934 
1935 	BString alertText(removed ? B_TRANSLATE("File \"%file%\" was removed by "
1936 		"another application, recover it?")
1937 		: B_TRANSLATE("File \"%file%\" was modified by "
1938 		"another application, reload it?"));
1939 	alertText.ReplaceAll("%file%", name);
1940 
1941 	if (_ShowAlert(alertText, removed ? B_TRANSLATE("Recover")
1942 			: B_TRANSLATE("Reload"), B_TRANSLATE("Ignore"), "",
1943 			B_WARNING_ALERT) == 0)
1944 	{
1945 		if (!removed) {
1946 			// supress the warning - user has already agreed
1947 			fClean = true;
1948 			BMessage msg(MENU_RELOAD);
1949 			_ReloadDocument(&msg);
1950 		} else
1951 			Save();
1952 	} else
1953 		fNagOnNodeChange = false;
1954 
1955 	fSaveItem->SetEnabled(!fClean);
1956 }
1957 
1958 
1959 void
1960 StyledEditWindow::_HandleNodeMonitorEvent(BMessage *message)
1961 {
1962 	int32 opcode = 0;
1963 	if (message->FindInt32("opcode", &opcode) != B_OK)
1964 		return;
1965 
1966 	if (opcode != B_ENTRY_CREATED
1967 		&& message->FindInt64("node") != fNodeRef.node)
1968 		// bypass foreign nodes' event
1969 		return;
1970 
1971 	switch (opcode) {
1972 		case B_STAT_CHANGED:
1973 			{
1974 				int32 fields = 0;
1975 				if (message->FindInt32("fields", &fields) == B_OK
1976 					&& (fields & (B_STAT_SIZE | B_STAT_MODIFICATION_TIME
1977 							| B_STAT_MODE)) == 0)
1978 					break;
1979 
1980 				const char* name = NULL;
1981 				if (fSaveMessage->FindString("name", &name) != B_OK)
1982 					break;
1983 
1984 				_ShowNodeChangeAlert(name, false);
1985 			}
1986 			break;
1987 
1988 		case B_ENTRY_MOVED:
1989 			{
1990 				int32 device = 0;
1991 				int64 srcFolder = 0;
1992 				int64 dstFolder = 0;
1993 				const char* name = NULL;
1994 				if (message->FindInt32("device", &device) != B_OK
1995 					|| message->FindInt64("to directory", &dstFolder) != B_OK
1996 					|| message->FindInt64("from directory", &srcFolder) != B_OK
1997 					|| message->FindString("name", &name) != B_OK)
1998 						break;
1999 
2000 				entry_ref newRef(device, dstFolder, name);
2001 				BEntry entry(&newRef);
2002 
2003 				BEntry dirEntry;
2004 				entry.GetParent(&dirEntry);
2005 
2006 				entry_ref ref;
2007 				dirEntry.GetRef(&ref);
2008 				fSaveMessage->ReplaceRef("directory", &ref);
2009 				fSaveMessage->ReplaceString("name", name);
2010 
2011 				// store previous name - it may be useful in case
2012 				// we have just moved to temporary copy of file (vim case)
2013 				const char* sourceName = NULL;
2014 				if (message->FindString("from name", &sourceName) == B_OK) {
2015 					fSaveMessage->RemoveName("org.name");
2016 					fSaveMessage->AddString("org.name", sourceName);
2017 					fSaveMessage->RemoveName("move time");
2018 					fSaveMessage->AddInt64("move time", system_time());
2019 				}
2020 
2021 				SetTitle(name);
2022 				be_roster->AddToRecentDocuments(&newRef, APP_SIGNATURE);
2023 
2024 				if (srcFolder != dstFolder) {
2025 					_SwitchNodeMonitor(false);
2026 					_SwitchNodeMonitor(true);
2027 				}
2028 			}
2029 			break;
2030 
2031 		case B_ENTRY_REMOVED:
2032 			{
2033 				_SwitchNodeMonitor(false);
2034 
2035 				fClean = false;
2036 
2037 				// some editors like vim save files in following way:
2038 				// 1) move t.txt -> t.txt~
2039 				// 2) re-create t.txt and write data to it
2040 				// 3) remove t.txt~
2041 				// go to catch this case
2042 				int32 device = 0;
2043 				int64 directory = 0;
2044 				BString orgName;
2045 				if (fSaveMessage->FindString("org.name", &orgName) == B_OK
2046 					&& message->FindInt32("device", &device) == B_OK
2047 					&& message->FindInt64("directory", &directory) == B_OK)
2048 				{
2049 					// reuse the source name if it is not too old
2050 					bigtime_t time = fSaveMessage->FindInt64("move time");
2051 					if ((system_time() - time) < 1000000) {
2052 						entry_ref ref(device, directory, orgName);
2053 						BEntry entry(&ref);
2054 						if (entry.InitCheck() == B_OK) {
2055 							_SwitchNodeMonitor(true, &ref);
2056 						}
2057 
2058 						fSaveMessage->ReplaceString("name", orgName);
2059 						fSaveMessage->RemoveName("org.name");
2060 						fSaveMessage->RemoveName("move time");
2061 
2062 						SetTitle(orgName);
2063 						_ShowNodeChangeAlert(orgName, false);
2064 						break;
2065 					}
2066 				}
2067 
2068 				const char* name = NULL;
2069 				if (message->FindString("name", &name) != B_OK
2070 					&& fSaveMessage->FindString("name", &name) != B_OK)
2071 					name = "Unknown";
2072 
2073 				_ShowNodeChangeAlert(name, true);
2074 			}
2075 			break;
2076 
2077 		default:
2078 			break;
2079 	}
2080 }
2081 
2082 
2083 void
2084 StyledEditWindow::_SwitchNodeMonitor(bool on, entry_ref* ref)
2085 {
2086 	if (!on) {
2087 		watch_node(&fNodeRef, B_STOP_WATCHING, this);
2088 		watch_node(&fFolderNodeRef, B_STOP_WATCHING, this);
2089 		fNodeRef = node_ref();
2090 		fFolderNodeRef = node_ref();
2091 		return;
2092 	}
2093 
2094 	BEntry entry, folderEntry;
2095 
2096 	if (ref != NULL) {
2097 		entry.SetTo(ref, true);
2098 		entry.GetParent(&folderEntry);
2099 
2100 	} else if (fSaveMessage != NULL) {
2101 		entry_ref ref;
2102 		const char* name = NULL;
2103 		if (fSaveMessage->FindRef("directory", &ref) != B_OK
2104 			|| fSaveMessage->FindString("name", &name) != B_OK)
2105 			return;
2106 
2107 		BDirectory dir(&ref);
2108 		entry.SetTo(&dir, name);
2109 		folderEntry.SetTo(&ref);
2110 
2111 	} else
2112 		return;
2113 
2114 	if (entry.InitCheck() != B_OK || folderEntry.InitCheck() != B_OK)
2115 		return;
2116 
2117 	entry.GetNodeRef(&fNodeRef);
2118 	folderEntry.GetNodeRef(&fFolderNodeRef);
2119 
2120 	watch_node(&fNodeRef, B_WATCH_STAT, this);
2121 	watch_node(&fFolderNodeRef, B_WATCH_DIRECTORY, this);
2122 }
2123