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