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