xref: /haiku/src/apps/stylededit/StyledEditWindow.cpp (revision 805cd0be8c456c8c19aea06a1b12bcd93eada2f2)
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_TRANSLATE_CONTEXT
56 #define B_TRANSLATE_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_TRANSLATE_CONTEXT
118 #define B_TRANSLATE_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_TRANSLATE_CONTEXT
653 #define B_TRANSLATE_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_TRANSLATE_CONTEXT
734 #define B_TRANSLATE_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 < linesInDocument) {
866 			currentHeight += fTextView->LineHeight(currentLine);
867 			if (currentHeight < printableRect.Height())
868 				currentLine++;
869 		}
870 		if (pagesInDocument == lastPage)
871 			lastLine = currentLine - 1;
872 
873 		if (currentHeight >= printableRect.Height()) {
874 			pagesInDocument++;
875 			if (pagesInDocument == firstPage)
876 				firstLine = currentLine;
877 		}
878 	}
879 
880 	if (lastPage > pagesInDocument - 1) {
881 		lastPage = pagesInDocument - 1;
882 		lastLine = currentLine - 1;
883 	}
884 
885 
886 	printJob.BeginJob();
887 	if (fTextView->CountLines() > 0 && fTextView->TextLength() > 0) {
888 		int32 printLine = firstLine;
889 		while (printLine <= lastLine) {
890 			float currentHeight = 0;
891 			int32 firstLineOnPage = printLine;
892 			while (currentHeight < printableRect.Height() && printLine <= lastLine) {
893 				currentHeight += fTextView->LineHeight(printLine);
894 				if (currentHeight < printableRect.Height())
895 					printLine++;
896 			}
897 
898 			float top = 0;
899 			if (firstLineOnPage != 0)
900 				top = fTextView->TextHeight(0, firstLineOnPage - 1);
901 
902 			float bottom = fTextView->TextHeight(0, printLine - 1);
903 			BRect textRect(0.0, top + TEXT_INSET,
904 				printableRect.Width(), bottom + TEXT_INSET);
905 			printJob.DrawView(fTextView, textRect, B_ORIGIN);
906 			printJob.SpoolPage();
907 		}
908 	}
909 
910 
911 	printJob.CommitJob();
912 }
913 
914 
915 void
916 StyledEditWindow::SearchAllWindows(BString find, BString replace,
917 	bool caseSensitive)
918 {
919 	int32 numWindows;
920 	numWindows = be_app->CountWindows();
921 
922 	BMessage* message;
923 	message= new BMessage(MSG_REPLACE_ALL);
924 	message->AddString("FindText", find);
925 	message->AddString("ReplaceText", replace);
926 	message->AddBool("casesens", caseSensitive);
927 
928 	while (numWindows >= 0) {
929 		StyledEditWindow* window = dynamic_cast<StyledEditWindow *>(
930 			be_app->WindowAt(numWindows));
931 
932 		BMessenger messenger(window);
933 		messenger.SendMessage(message);
934 
935 		numWindows--;
936 	}
937 }
938 
939 
940 bool
941 StyledEditWindow::IsDocumentEntryRef(const entry_ref* ref)
942 {
943 	if (ref == NULL)
944 		return false;
945 
946 	if (fSaveMessage == NULL)
947 		return false;
948 
949 	entry_ref dir;
950 	const char* name;
951 	if (fSaveMessage->FindRef("directory", &dir) != B_OK
952 		|| fSaveMessage->FindString("name", &name) != B_OK)
953 		return false;
954 
955 	entry_ref documentRef;
956 	BPath documentPath(&dir);
957 	documentPath.Append(name);
958 	get_ref_for_path(documentPath.Path(), &documentRef);
959 
960 	return *ref == documentRef;
961 }
962 
963 
964 // #pragma mark - private methods
965 
966 
967 #undef B_TRANSLATE_CONTEXT
968 #define B_TRANSLATE_CONTEXT "Menus"
969 
970 
971 void
972 StyledEditWindow::_InitWindow(uint32 encoding)
973 {
974 	fPrintSettings = NULL;
975 	fSaveMessage = NULL;
976 
977 	// undo modes
978 	fUndoFlag = false;
979 	fCanUndo = false;
980 	fRedoFlag = false;
981 	fCanRedo = false;
982 
983 	// clean modes
984 	fUndoCleans = false;
985 	fRedoCleans = false;
986 	fClean = true;
987 
988 	// search- state
989 	fReplaceString = "";
990 	fStringToFind = "";
991 	fCaseSensitive = false;
992 	fWrapAround = false;
993 	fBackSearch = false;
994 
995 	// add menubar
996 	fMenuBar = new BMenuBar(BRect(0, 0, 0, 0), "menubar");
997 	AddChild(fMenuBar);
998 
999 	// add textview and scrollview
1000 
1001 	BRect viewFrame = Bounds();
1002 	viewFrame.top = fMenuBar->Bounds().Height() + 1;
1003 	viewFrame.right -=  B_V_SCROLL_BAR_WIDTH;
1004 	viewFrame.left = 0;
1005 	viewFrame.bottom -= B_H_SCROLL_BAR_HEIGHT;
1006 
1007 	BRect textBounds = viewFrame;
1008 	textBounds.OffsetTo(B_ORIGIN);
1009 	textBounds.InsetBy(TEXT_INSET, TEXT_INSET);
1010 
1011 	fTextView= new StyledEditView(viewFrame, textBounds, this);
1012 	fTextView->SetDoesUndo(true);
1013 	fTextView->SetStylable(true);
1014 	fTextView->SetEncoding(encoding);
1015 
1016 	fScrollView = new BScrollView("scrollview", fTextView, B_FOLLOW_ALL, 0,
1017 		true, true, B_PLAIN_BORDER);
1018 	AddChild(fScrollView);
1019 	fTextView->MakeFocus(true);
1020 
1021 	// Add "File"-menu:
1022 	BMenu* menu = new BMenu(B_TRANSLATE("File"));
1023 	fMenuBar->AddItem(menu);
1024 
1025 	BMenuItem* menuItem;
1026 	menu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("New"),
1027 		new BMessage(MENU_NEW), 'N'));
1028 	menuItem->SetTarget(be_app);
1029 
1030 	menu->AddItem(menuItem = new BMenuItem(fRecentMenu =
1031 		new BMenu(B_TRANSLATE("Open" B_UTF8_ELLIPSIS)),
1032 			new BMessage(MENU_OPEN)));
1033 	menuItem->SetShortcut('O', 0);
1034 	menuItem->SetTarget(be_app);
1035 	menu->AddSeparatorItem();
1036 
1037 	menu->AddItem(fSaveItem = new BMenuItem(B_TRANSLATE("Save"),
1038 		new BMessage(MENU_SAVE), 'S'));
1039 	fSaveItem->SetEnabled(false);
1040 	menu->AddItem(menuItem = new BMenuItem(
1041 		B_TRANSLATE("Save as" B_UTF8_ELLIPSIS), new BMessage(MENU_SAVEAS)));
1042 	menuItem->SetShortcut('S', B_SHIFT_KEY);
1043 	menuItem->SetEnabled(true);
1044 
1045 	menu->AddItem(fRevertItem =
1046 		new BMenuItem(B_TRANSLATE("Revert to saved" B_UTF8_ELLIPSIS),
1047 		new BMessage(MENU_REVERT)));
1048 	fRevertItem->SetEnabled(false);
1049 	menu->AddItem(new BMenuItem(B_TRANSLATE("Close"),
1050 		new BMessage(MENU_CLOSE), 'W'));
1051 
1052 	menu->AddSeparatorItem();
1053 	menu->AddItem(new BMenuItem(B_TRANSLATE("Page setup" B_UTF8_ELLIPSIS),
1054 		new BMessage(MENU_PAGESETUP)));
1055 	menu->AddItem(new BMenuItem(B_TRANSLATE("Print" B_UTF8_ELLIPSIS),
1056 		new BMessage(MENU_PRINT), 'P'));
1057 
1058 	menu->AddSeparatorItem();
1059 	menu->AddItem(new BMenuItem(B_TRANSLATE("Quit"),
1060 		new BMessage(MENU_QUIT), 'Q'));
1061 
1062 	// Add the "Edit"-menu:
1063 	menu = new BMenu(B_TRANSLATE("Edit"));
1064 	fMenuBar->AddItem(menu);
1065 
1066 	menu->AddItem(fUndoItem = new BMenuItem(B_TRANSLATE("Can't undo"),
1067 		new BMessage(B_UNDO), 'Z'));
1068 	fUndoItem->SetEnabled(false);
1069 
1070 	menu->AddSeparatorItem();
1071 	menu->AddItem(fCutItem = new BMenuItem(B_TRANSLATE("Cut"),
1072 		new BMessage(B_CUT), 'X'));
1073 	fCutItem->SetEnabled(false);
1074 	fCutItem->SetTarget(fTextView);
1075 
1076 	menu->AddItem(fCopyItem = new BMenuItem(B_TRANSLATE("Copy"),
1077 		new BMessage(B_COPY), 'C'));
1078 	fCopyItem->SetEnabled(false);
1079 	fCopyItem->SetTarget(fTextView);
1080 
1081 	menu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("Paste"),
1082 		new BMessage(B_PASTE), 'V'));
1083 	menuItem->SetTarget(fTextView);
1084 
1085 	menu->AddSeparatorItem();
1086 	menu->AddItem(menuItem = new BMenuItem(B_TRANSLATE("Select all"),
1087 		new BMessage(B_SELECT_ALL), 'A'));
1088 	menuItem->SetTarget(fTextView);
1089 
1090 	menu->AddSeparatorItem();
1091 	menu->AddItem(new BMenuItem(B_TRANSLATE("Find" B_UTF8_ELLIPSIS),
1092 		new BMessage(MENU_FIND), 'F'));
1093 	menu->AddItem(fFindAgainItem= new BMenuItem(B_TRANSLATE("Find again"),
1094 		new BMessage(MENU_FIND_AGAIN), 'G'));
1095 	fFindAgainItem->SetEnabled(false);
1096 
1097 	menu->AddItem(new BMenuItem(B_TRANSLATE("Find selection"),
1098 		new BMessage(MENU_FIND_SELECTION), 'H'));
1099 	menu->AddItem(new BMenuItem(B_TRANSLATE("Replace" B_UTF8_ELLIPSIS),
1100 		new BMessage(MENU_REPLACE), 'R'));
1101 	menu->AddItem(fReplaceSameItem = new BMenuItem(B_TRANSLATE("Replace next"),
1102 		new BMessage(MENU_REPLACE_SAME), 'T'));
1103 	fReplaceSameItem->SetEnabled(false);
1104 
1105 	// Add the "Font"-menu:
1106 	fFontMenu = new BMenu(B_TRANSLATE("Font"));
1107 	fMenuBar->AddItem(fFontMenu);
1108 
1109 	//"Size"-subMenu
1110 	fFontSizeMenu = new BMenu(B_TRANSLATE("Size"));
1111 	fFontSizeMenu->SetRadioMode(true);
1112 	fFontMenu->AddItem(fFontSizeMenu);
1113 
1114 	const int32 fontSizes[] = {9, 10, 11, 12, 14, 18, 24, 36, 48, 72};
1115 	for (uint32 i = 0; i < sizeof(fontSizes) / sizeof(fontSizes[0]); i++) {
1116 		BMessage* fontMessage = new BMessage(FONT_SIZE);
1117 		fontMessage->AddFloat("size", fontSizes[i]);
1118 
1119 		char label[64];
1120 		snprintf(label, sizeof(label), "%ld", fontSizes[i]);
1121 		fFontSizeMenu->AddItem(menuItem = new BMenuItem(label, fontMessage));
1122 
1123 		if (fontSizes[i] == (int32)be_plain_font->Size())
1124 			menuItem->SetMarked(true);
1125 	}
1126 
1127 	// "Color"-subMenu
1128 	fFontColorMenu = new BMenu(B_TRANSLATE("Color"));
1129 	fFontColorMenu->SetRadioMode(true);
1130 	fFontMenu->AddItem(fFontColorMenu);
1131 
1132 	fFontColorMenu->AddItem(fBlackItem = new BMenuItem(B_TRANSLATE("Black"),
1133 		new BMessage(FONT_COLOR)));
1134 	fBlackItem->SetMarked(true);
1135 	fFontColorMenu->AddItem(fRedItem = new ColorMenuItem(B_TRANSLATE("Red"),
1136 		RED, new BMessage(FONT_COLOR)));
1137 	fFontColorMenu->AddItem(fGreenItem = new ColorMenuItem(B_TRANSLATE("Green"),
1138 		GREEN, new BMessage(FONT_COLOR)));
1139 	fFontColorMenu->AddItem(fBlueItem = new ColorMenuItem(B_TRANSLATE("Blue"),
1140 		BLUE, new BMessage(FONT_COLOR)));
1141 	fFontColorMenu->AddItem(fCyanItem = new ColorMenuItem(B_TRANSLATE("Cyan"),
1142 		CYAN, new BMessage(FONT_COLOR)));
1143 	fFontColorMenu->AddItem(fMagentaItem = new ColorMenuItem(B_TRANSLATE("Magenta"),
1144 		MAGENTA, new BMessage(FONT_COLOR)));
1145 	fFontColorMenu->AddItem(fYellowItem = new ColorMenuItem(B_TRANSLATE("Yellow"),
1146 		YELLOW, new BMessage(FONT_COLOR)));
1147 	fFontMenu->AddSeparatorItem();
1148 
1149 	// "Bold" & "Italic" menu items
1150 	fFontMenu->AddItem(fBoldItem = new BMenuItem(B_TRANSLATE("Bold"),
1151 		new BMessage(kMsgSetBold)));
1152 	fFontMenu->AddItem(fItalicItem = new BMenuItem(B_TRANSLATE("Italic"),
1153 		new BMessage(kMsgSetItalic)));
1154 	fBoldItem->SetShortcut('B', 0);
1155 	fItalicItem->SetShortcut('I', 0);
1156 	fFontMenu->AddSeparatorItem();
1157 
1158 	// Available fonts
1159 
1160 	fCurrentFontItem = 0;
1161 	fCurrentStyleItem = 0;
1162 
1163 	BMenu* subMenu;
1164 	int32 numFamilies = count_font_families();
1165 	for (int32 i = 0; i < numFamilies; i++) {
1166 		font_family family;
1167 		if (get_font_family(i, &family) == B_OK) {
1168 			subMenu = new BMenu(family);
1169 			subMenu->SetRadioMode(true);
1170 			fFontMenu->AddItem(new BMenuItem(subMenu,
1171 				new BMessage(FONT_FAMILY)));
1172 
1173 			int32 numStyles = count_font_styles(family);
1174 			for (int32 j = 0; j < numStyles; j++) {
1175 				font_style style;
1176 				uint32 flags;
1177 				if (get_font_style(family, j, &style, &flags) == B_OK) {
1178 					subMenu->AddItem(new BMenuItem(style,
1179 						new BMessage(FONT_STYLE)));
1180 				}
1181 			}
1182 		}
1183 	}
1184 
1185 	// Add the "Document"-menu:
1186 	menu = new BMenu(B_TRANSLATE("Document"));
1187 	fMenuBar->AddItem(menu);
1188 
1189 	// "Align"-subMenu:
1190 	subMenu = new BMenu(B_TRANSLATE("Align"));
1191 	subMenu->SetRadioMode(true);
1192 
1193 	subMenu->AddItem(fAlignLeft = new BMenuItem(B_TRANSLATE("Left"),
1194 		new BMessage(ALIGN_LEFT)));
1195 	fAlignLeft->SetMarked(true);
1196 	fAlignLeft->SetShortcut('L', B_OPTION_KEY);
1197 
1198 	subMenu->AddItem(fAlignCenter = new BMenuItem(B_TRANSLATE("Center"),
1199 		new BMessage(ALIGN_CENTER)));
1200 	fAlignCenter->SetShortcut('C', B_OPTION_KEY);
1201 
1202 	subMenu->AddItem(fAlignRight = new BMenuItem(B_TRANSLATE("Right"),
1203 		new BMessage(ALIGN_RIGHT)));
1204 	fAlignRight->SetShortcut('R', B_OPTION_KEY);
1205 
1206 	menu->AddItem(subMenu);
1207 	menu->AddItem(fWrapItem = new BMenuItem(B_TRANSLATE("Wrap lines"),
1208 		new BMessage(WRAP_LINES)));
1209 	fWrapItem->SetMarked(true);
1210 	fWrapItem->SetShortcut('W', B_OPTION_KEY);
1211 
1212 	menu->AddSeparatorItem();
1213 	menu->AddItem(new BMenuItem(B_TRANSLATE("Statistics" B_UTF8_ELLIPSIS),
1214 		new BMessage(SHOW_STATISTICS)));
1215 
1216 	fSavePanel = NULL;
1217 	fSavePanelEncodingMenu = NULL;
1218 		// build lazily
1219 }
1220 
1221 
1222 void
1223 StyledEditWindow::_LoadAttrs()
1224 {
1225 	entry_ref dir;
1226 	const char* name;
1227 	if (fSaveMessage->FindRef("directory", &dir) != B_OK
1228 		|| fSaveMessage->FindString("name", &name) != B_OK)
1229 		return;
1230 
1231 	BPath documentPath(&dir);
1232 	documentPath.Append(name);
1233 
1234 	BNode documentNode(documentPath.Path());
1235 	if (documentNode.InitCheck() != B_OK)
1236 		return;
1237 
1238 	BRect newFrame;
1239 	ssize_t bytesRead = documentNode.ReadAttr(kInfoAttributeName, B_RECT_TYPE,
1240 		0, &newFrame, sizeof(BRect));
1241 	if (bytesRead != sizeof(BRect))
1242 		return;
1243 
1244 	swap_data(B_RECT_TYPE, &newFrame, sizeof(BRect), B_SWAP_BENDIAN_TO_HOST);
1245 
1246 	// Check if the frame in on screen, otherwise, ignore it
1247 	BScreen screen(this);
1248 	if (newFrame.Width() > 32 && newFrame.Height() > 32
1249 		&& screen.Frame().Contains(newFrame)) {
1250 		MoveTo(newFrame.left, newFrame.top);
1251 		ResizeTo(newFrame.Width(), newFrame.Height());
1252 	}
1253 }
1254 
1255 
1256 void
1257 StyledEditWindow::_SaveAttrs()
1258 {
1259 	if (!fSaveMessage)
1260 		return;
1261 
1262 	entry_ref dir;
1263 	const char* name;
1264 	if (fSaveMessage->FindRef("directory", &dir) != B_OK
1265 		|| fSaveMessage->FindString("name", &name) != B_OK)
1266 		return;
1267 
1268 	BPath documentPath(&dir);
1269 	documentPath.Append(name);
1270 
1271 	BNode documentNode(documentPath.Path());
1272 	if (documentNode.InitCheck() != B_OK)
1273 		return;
1274 
1275 	BRect frame(Frame());
1276 	swap_data(B_RECT_TYPE, &frame, sizeof(BRect), B_SWAP_HOST_TO_BENDIAN);
1277 
1278 	documentNode.WriteAttr(kInfoAttributeName, B_RECT_TYPE, 0, &frame,
1279 		sizeof(BRect));
1280 }
1281 
1282 
1283 #undef B_TRANSLATE_CONTEXT
1284 #define B_TRANSLATE_CONTEXT "LoadAlert"
1285 
1286 
1287 status_t
1288 StyledEditWindow::_LoadFile(entry_ref* ref)
1289 {
1290 	BEntry entry(ref, true);
1291 		// traverse an eventual link
1292 
1293 	status_t status = entry.InitCheck();
1294 	if (status == B_OK && entry.IsDirectory())
1295 		status = B_IS_A_DIRECTORY;
1296 
1297 	BFile file;
1298 	if (status == B_OK)
1299 		status = file.SetTo(&entry, B_READ_ONLY);
1300 	if (status == B_OK)
1301 		status = fTextView->GetStyledText(&file);
1302 
1303 	if (status == B_ENTRY_NOT_FOUND) {
1304 		// Treat non-existing files consideratley; we just want to get an
1305 		// empty window for them - to create this new document
1306 		status = B_OK;
1307 	}
1308 
1309 	if (status != B_OK) {
1310 		// If an error occured, bail out and tell the user what happened
1311 		BEntry entry(ref, true);
1312 		char name[B_FILE_NAME_LENGTH];
1313 		if (entry.GetName(name) != B_OK)
1314 			strlcpy(name, B_TRANSLATE("???"), sizeof(name));
1315 
1316 		BString text;
1317 		if (status == B_BAD_TYPE)
1318 			bs_printf(&text,
1319 				B_TRANSLATE("Error loading \"%s\":\n\tUnsupported format"), name);
1320 		else
1321 			bs_printf(&text, B_TRANSLATE("Error loading \"%s\":\n\t%s"),
1322 				name, strerror(status));
1323 
1324 		_ShowAlert(text, B_TRANSLATE("OK"), "", "", B_STOP_ALERT);
1325 		return status;
1326 	}
1327 
1328 	// update alignment
1329 	switch (fTextView->Alignment()) {
1330 		case B_ALIGN_LEFT:
1331 		default:
1332 			fAlignLeft->SetMarked(true);
1333 			break;
1334 		case B_ALIGN_CENTER:
1335 			fAlignCenter->SetMarked(true);
1336 			break;
1337 		case B_ALIGN_RIGHT:
1338 			fAlignRight->SetMarked(true);
1339 			break;
1340 	}
1341 
1342 	// update word wrapping
1343 	fWrapItem->SetMarked(fTextView->DoesWordWrap());
1344 	return B_OK;
1345 }
1346 
1347 
1348 #undef B_TRANSLATE_CONTEXT
1349 #define B_TRANSLATE_CONTEXT "RevertToSavedAlert"
1350 
1351 
1352 void
1353 StyledEditWindow::_RevertToSaved()
1354 {
1355 	entry_ref ref;
1356 	const char* name;
1357 
1358 	fSaveMessage->FindRef("directory", &ref);
1359 	fSaveMessage->FindString("name", &name);
1360 
1361 	BDirectory dir(&ref);
1362 	status_t status = dir.InitCheck();
1363 	BEntry entry;
1364 	if (status == B_OK)
1365 		status = entry.SetTo(&dir, name);
1366 
1367 	if (status == B_OK)
1368 		status = entry.GetRef(&ref);
1369 
1370 	if (status != B_OK || !entry.Exists()) {
1371 		BString alertText;
1372 		bs_printf(&alertText,
1373 			B_TRANSLATE("Cannot revert, file not found: \"%s\"."), name);
1374 		_ShowAlert(alertText, B_TRANSLATE("OK"), "", "", B_STOP_ALERT);
1375 		return;
1376 	}
1377 
1378 	BString alertText;
1379 	bs_printf(&alertText,
1380 		B_TRANSLATE("Revert to the last version of \"%s\"? "), Title());
1381 	if (_ShowAlert(alertText, B_TRANSLATE("Cancel"), B_TRANSLATE("OK"),
1382 		"", B_WARNING_ALERT) != 1)
1383 		return;
1384 
1385 	fTextView->Reset();
1386 	if (_LoadFile(&ref) != B_OK)
1387 		return;
1388 
1389 #undef B_TRANSLATE_CONTEXT
1390 #define B_TRANSLATE_CONTEXT "Menus"
1391 
1392 	// clear undo modes
1393 	fUndoItem->SetLabel(B_TRANSLATE("Can't undo"));
1394 	fUndoItem->SetEnabled(false);
1395 	fUndoFlag = false;
1396 	fCanUndo = false;
1397 	fRedoFlag = false;
1398 	fCanRedo = false;
1399 
1400 	// clear clean modes
1401 	fSaveItem->SetEnabled(false);
1402 	fRevertItem->SetEnabled(false);
1403 	fUndoCleans = false;
1404 	fRedoCleans = false;
1405 	fClean = true;
1406 }
1407 
1408 
1409 bool
1410 StyledEditWindow::_Search(BString string, bool caseSensitive, bool wrap,
1411 	bool backSearch)
1412 {
1413 	int32 start;
1414 	int32 finish;
1415 
1416 	start = B_ERROR;
1417 
1418 	int32 length = string.Length();
1419 	if (length == 0)
1420 		return false;
1421 
1422 	BString viewText(fTextView->Text());
1423 	int32 textStart, textFinish;
1424 	fTextView->GetSelection(&textStart, &textFinish);
1425 	if (backSearch) {
1426 		if (caseSensitive)
1427 			start = viewText.FindLast(string, textStart);
1428 		else
1429 			start = viewText.IFindLast(string, textStart);
1430 	} else {
1431 		if (caseSensitive)
1432 			start = viewText.FindFirst(string, textFinish);
1433 		else
1434 			start = viewText.IFindFirst(string, textFinish);
1435 	}
1436 	if (start == B_ERROR && wrap) {
1437 		if (backSearch) {
1438 			if (caseSensitive)
1439 				start = viewText.FindLast(string, viewText.Length());
1440 			else
1441 				start = viewText.IFindLast(string, viewText.Length());
1442 		} else {
1443 			if (caseSensitive)
1444 				start = viewText.FindFirst(string, 0);
1445 			else
1446 				start = viewText.IFindFirst(string, 0);
1447 		}
1448 	}
1449 
1450 	if (start != B_ERROR) {
1451 		finish = start + length;
1452 		fTextView->Select(start, finish);
1453 		fTextView->ScrollToSelection();
1454 		return true;
1455 	}
1456 
1457 	return false;
1458 }
1459 
1460 
1461 void
1462 StyledEditWindow::_FindSelection()
1463 {
1464 	int32 selectionStart, selectionFinish;
1465 	fTextView->GetSelection(&selectionStart, &selectionFinish);
1466 
1467 	int32 selectionLength = selectionFinish- selectionStart;
1468 
1469 	BString viewText = fTextView->Text();
1470 	viewText.CopyInto(fStringToFind, selectionStart, selectionLength);
1471 	fFindAgainItem->SetEnabled(true);
1472 	_Search(fStringToFind, fCaseSensitive, fWrapAround, fBackSearch);
1473 }
1474 
1475 
1476 bool
1477 StyledEditWindow::_Replace(BString findThis, BString replaceWith,
1478 	bool caseSensitive, bool wrap, bool backSearch)
1479 {
1480 	if (_Search(findThis, caseSensitive, wrap, backSearch)) {
1481 		int32 start, finish;
1482 		fTextView->GetSelection(&start, &finish);
1483 
1484 		_UpdateCleanUndoRedoSaveRevert();
1485 		fTextView->SetSuppressChanges(true);
1486 		fTextView->Delete(start, start + findThis.Length());
1487 		fTextView->Insert(start, replaceWith.String(), replaceWith.Length());
1488 		fTextView->SetSuppressChanges(false);
1489 		fTextView->Select(start, start + replaceWith.Length());
1490 		fTextView->ScrollToSelection();
1491 		return true;
1492 	}
1493 
1494 	return false;
1495 }
1496 
1497 
1498 void
1499 StyledEditWindow::_ReplaceAll(BString findThis, BString replaceWith,
1500 	bool caseSensitive)
1501 {
1502 	bool first = true;
1503 	fTextView->SetSuppressChanges(true);
1504 	while (_Search(findThis, caseSensitive, true, false)) {
1505 		if (first) {
1506 			_UpdateCleanUndoRedoSaveRevert();
1507 			first = false;
1508 		}
1509 		int32 start, finish;
1510 		fTextView->GetSelection(&start, &finish);
1511 
1512 		fTextView->Delete(start, start + findThis.Length());
1513 		fTextView->Insert(start, replaceWith.String(), replaceWith.Length());
1514 	}
1515 	fTextView->SetSuppressChanges(false);
1516 }
1517 
1518 
1519 void
1520 StyledEditWindow::_SetFontSize(float fontSize)
1521 {
1522 	uint32 sameProperties;
1523 	BFont font;
1524 
1525 	fTextView->GetFontAndColor(&font, &sameProperties);
1526 	font.SetSize(fontSize);
1527 	fTextView->SetFontAndColor(&font, B_FONT_SIZE);
1528 
1529 	_UpdateCleanUndoRedoSaveRevert();
1530 }
1531 
1532 
1533 void
1534 StyledEditWindow::_SetFontColor(const rgb_color* color)
1535 {
1536 	uint32 sameProperties;
1537 	BFont font;
1538 
1539 	fTextView->GetFontAndColor(&font, &sameProperties, NULL, NULL);
1540 	fTextView->SetFontAndColor(&font, 0, color);
1541 
1542 	_UpdateCleanUndoRedoSaveRevert();
1543 }
1544 
1545 
1546 void
1547 StyledEditWindow::_SetFontStyle(const char* fontFamily, const char* fontStyle)
1548 {
1549 	BFont font;
1550 	uint32 sameProperties;
1551 
1552 	// find out what the old font was
1553 	font_family oldFamily;
1554 	font_style oldStyle;
1555 	fTextView->GetFontAndColor(&font, &sameProperties);
1556 	font.GetFamilyAndStyle(&oldFamily, &oldStyle);
1557 
1558 	// clear that family's bit on the menu, if necessary
1559 	if (strcmp(oldFamily, fontFamily)) {
1560 		BMenuItem* oldItem = fFontMenu->FindItem(oldFamily);
1561 		if (oldItem != NULL) {
1562 			oldItem->SetMarked(false);
1563 			BMenu* menu = oldItem->Submenu();
1564 			if (menu != NULL) {
1565 				oldItem = menu->FindItem(oldStyle);
1566 				if (oldItem != NULL)
1567 					oldItem->SetMarked(false);
1568 			}
1569 		}
1570 	}
1571 
1572 	font.SetFamilyAndStyle(fontFamily, fontStyle);
1573 
1574 	uint16 face = 0;
1575 
1576 	if (!(font.Face() & B_REGULAR_FACE))
1577 		face = font.Face();
1578 
1579 	if (fBoldItem->IsMarked())
1580 		face |= B_BOLD_FACE;
1581 
1582 	if (fItalicItem->IsMarked())
1583 		face |= B_ITALIC_FACE;
1584 
1585 	font.SetFace(face);
1586 
1587 	fTextView->SetFontAndColor(&font);
1588 
1589 	BMenuItem* superItem;
1590 	superItem = fFontMenu->FindItem(fontFamily);
1591 	if (superItem != NULL) {
1592 		superItem->SetMarked(true);
1593 		fCurrentFontItem = superItem;
1594 	}
1595 
1596 	_UpdateCleanUndoRedoSaveRevert();
1597 }
1598 
1599 
1600 #undef B_TRANSLATE_CONTEXT
1601 #define B_TRANSLATE_CONTEXT "Statistics"
1602 
1603 
1604 int32
1605 StyledEditWindow::_ShowStatistics()
1606 {
1607 	size_t words = 0;
1608 	bool inWord = false;
1609 	size_t length = fTextView->TextLength();
1610 
1611 	for (size_t i = 0; i < length; i++)	{
1612 		if (BUnicodeChar::IsSpace(fTextView->Text()[i])) {
1613 			inWord = false;
1614 		} else if (!inWord)	{
1615 			words++;
1616 			inWord = true;
1617 		}
1618 	}
1619 
1620 	BString result;
1621 	result << B_TRANSLATE("Document statistics") << '\n' << '\n'
1622 		<< B_TRANSLATE("Lines:") << ' ' << fTextView->CountLines() << '\n'
1623 		<< B_TRANSLATE("Characters:") << ' ' << length << '\n'
1624 		<< B_TRANSLATE("Words:") << ' ' << words;
1625 
1626 	BAlert* alert = new BAlert("Statistics", result, B_TRANSLATE("OK"), NULL,
1627 		NULL, B_WIDTH_AS_USUAL, B_EVEN_SPACING, B_INFO_ALERT);
1628 
1629 	return alert->Go();
1630 }
1631 
1632 
1633 #undef B_TRANSLATE_CONTEXT
1634 #define B_TRANSLATE_CONTEXT "Menus"
1635 
1636 
1637 void
1638 StyledEditWindow::_UpdateCleanUndoRedoSaveRevert()
1639 {
1640 	fClean = false;
1641 	fUndoCleans = false;
1642 	fRedoCleans = false;
1643 	fRevertItem->SetEnabled(fSaveMessage != NULL);
1644 	fSaveItem->SetEnabled(true);
1645 	fUndoItem->SetLabel(B_TRANSLATE("Can't undo"));
1646 	fUndoItem->SetEnabled(false);
1647 	fCanUndo = false;
1648 	fCanRedo = false;
1649 }
1650 
1651 
1652 int32
1653 StyledEditWindow::_ShowAlert(const BString& text, const BString& label,
1654 	const BString& label2, const BString& label3, alert_type type) const
1655 {
1656 	const char* button2 = NULL;
1657 	if (label2.Length() > 0)
1658 		button2 = label2.String();
1659 
1660 	const char* button3 = NULL;
1661 	button_spacing spacing = B_EVEN_SPACING;
1662 	if (label3.Length() > 0) {
1663 		button3 = label3.String();
1664 		spacing = B_OFFSET_SPACING;
1665 	}
1666 
1667 	BAlert* alert = new BAlert("Alert", text.String(), label.String(), button2,
1668 		button3, B_WIDTH_AS_USUAL, spacing, type);
1669 	alert->SetShortcut(0, B_ESCAPE);
1670 
1671 	return alert->Go();
1672 }
1673