xref: /haiku/src/apps/stylededit/StyledEditWindow.cpp (revision c90684742e7361651849be4116d0e5de3a817194)
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 	if (StyledEditApp* app = dynamic_cast<StyledEditApp*>(be_app))
865 		app->CloseDocument();
866 	BWindow::Quit();
867 }
868 
869 
870 #undef B_TRANSLATE_CONTEXT
871 #define B_TRANSLATE_CONTEXT "QuitAlert"
872 
873 
874 bool
875 StyledEditWindow::QuitRequested()
876 {
877 	if (fClean)
878 		return true;
879 
880 	BString alertText;
881 	bs_printf(&alertText,
882 		B_TRANSLATE("Save changes to the document \"%s\"? "), Title());
883 
884 	int32 index = _ShowAlert(alertText, B_TRANSLATE("Cancel"),
885 		B_TRANSLATE("Don't save"), B_TRANSLATE("Save"),	B_WARNING_ALERT);
886 
887 	if (index == 0)
888 		return false;	// "cancel": dont save, dont close the window
889 
890 	if (index == 1)
891 		return true;	// "don't save": just close the window
892 
893 	if (!fSaveMessage) {
894 		SaveAs(new BMessage(SAVE_THEN_QUIT));
895 		return false;
896 	}
897 
898 	return Save() == B_OK;
899 }
900 
901 
902 #undef B_TRANSLATE_CONTEXT
903 #define B_TRANSLATE_CONTEXT "SaveAlert"
904 
905 
906 status_t
907 StyledEditWindow::Save(BMessage* message)
908 {
909 	if (!message)
910 		message = fSaveMessage;
911 
912 	if (!message)
913 		return B_ERROR;
914 
915 	entry_ref dirRef;
916 	const char* name;
917 	if (message->FindRef("directory", &dirRef) != B_OK
918 		|| message->FindString("name", &name) != B_OK)
919 		return B_BAD_VALUE;
920 
921 	BDirectory dir(&dirRef);
922 	BEntry entry(&dir, name);
923 
924 	status_t status = B_ERROR;
925 	if (dir.InitCheck() == B_OK && entry.InitCheck() == B_OK) {
926 		struct stat st;
927 		BFile file(&entry, B_READ_WRITE | B_CREATE_FILE);
928 		if (file.InitCheck() == B_OK
929 			&& (status = file.GetStat(&st)) == B_OK) {
930 			// check the file permissions
931 			if (!((getuid() == st.st_uid && (S_IWUSR & st.st_mode))
932 				|| (getgid() == st.st_gid && (S_IWGRP & st.st_mode))
933 				|| (S_IWOTH & st.st_mode))) {
934 				BString alertText;
935 				bs_printf(&alertText, B_TRANSLATE("This file is marked "
936 					"Read-Only. Save changes to the document \"%s\"? "), name);
937 				switch (_ShowAlert(alertText, B_TRANSLATE("Cancel"),
938 					B_TRANSLATE("Don't save"),
939 					B_TRANSLATE("Save"), B_WARNING_ALERT)) {
940 					case 0:
941 						return B_CANCELED;
942 					case 1:
943 						return B_OK;
944 					default:
945 						break;
946 				}
947 			}
948 
949 			status = fTextView->WriteStyledEditFile(&file);
950 		}
951 	}
952 
953 	if (status != B_OK) {
954 		BString alertText;
955 		bs_printf(&alertText, B_TRANSLATE("Error saving \"%s\":\n%s"), name,
956 			strerror(status));
957 
958 		_ShowAlert(alertText, B_TRANSLATE("OK"), "", "", B_STOP_ALERT);
959 		return status;
960 	}
961 
962 	SetTitle(name);
963 
964 	if (fSaveMessage != message) {
965 		delete fSaveMessage;
966 		fSaveMessage = new BMessage(*message);
967 	}
968 
969 	entry_ref ref;
970 	if (entry.GetRef(&ref) == B_OK)
971 		be_roster->AddToRecentDocuments(&ref, APP_SIGNATURE);
972 
973 	// clear clean modes
974 	fSaveItem->SetEnabled(false);
975 	fRevertItem->SetEnabled(false);
976 	fUndoCleans = false;
977 	fRedoCleans = false;
978 	fClean = true;
979 	return status;
980 }
981 
982 
983 #undef B_TRANSLATE_CONTEXT
984 #define B_TRANSLATE_CONTEXT "Open_and_SaveAsPanel"
985 
986 
987 status_t
988 StyledEditWindow::SaveAs(BMessage* message)
989 {
990 	if (fSavePanel == NULL) {
991 		entry_ref* directory = NULL;
992 		if (fSaveMessage != NULL) {
993 			entry_ref dirRef;
994 			if (fSaveMessage->FindRef("directory", &dirRef) == B_OK)
995 				directory = &dirRef;
996 		}
997 
998 		BMessenger target(this);
999 		fSavePanel = new BFilePanel(B_SAVE_PANEL, &target,
1000 			directory, B_FILE_NODE, false);
1001 
1002 		BMenuBar* menuBar = dynamic_cast<BMenuBar*>(
1003 			fSavePanel->Window()->FindView("MenuBar"));
1004 		if (menuBar != NULL) {
1005 			fSavePanelEncodingMenu = new BMenu(B_TRANSLATE("Encoding"));
1006 			fSavePanelEncodingMenu->SetRadioMode(true);
1007 			menuBar->AddItem(fSavePanelEncodingMenu);
1008 
1009 			BCharacterSetRoster roster;
1010 			BCharacterSet charset;
1011 			while (roster.GetNextCharacterSet(&charset) == B_NO_ERROR) {
1012 				BString name(charset.GetPrintName());
1013 				const char* mime = charset.GetMIMEName();
1014 				if (mime) {
1015 					name.Append(" (");
1016 					name.Append(mime);
1017 					name.Append(")");
1018 				}
1019 				BMenuItem * item = new BMenuItem(name.String(),
1020 					new BMessage(SAVE_AS_ENCODING));
1021 				item->SetTarget(this);
1022 				fSavePanelEncodingMenu->AddItem(item);
1023 				if (charset.GetFontID() == fTextView->GetEncoding())
1024 					item->SetMarked(true);
1025 			}
1026 		}
1027 	}
1028 
1029 	fSavePanel->SetSaveText(Title());
1030 	if (message != NULL)
1031 		fSavePanel->SetMessage(message);
1032 
1033 	fSavePanel->Show();
1034 	return B_OK;
1035 }
1036 
1037 
1038 #undef B_TRANSLATE_CONTEXT
1039 #define B_TRANSLATE_CONTEXT "LoadAlert"
1040 
1041 
1042 status_t
1043 StyledEditWindow::_LoadFile(entry_ref* ref)
1044 {
1045 	BEntry entry(ref, true);
1046 		// traverse an eventual link
1047 
1048 	status_t status = entry.InitCheck();
1049 	if (status == B_OK && entry.IsDirectory())
1050 		status = B_IS_A_DIRECTORY;
1051 
1052 	BFile file;
1053 	if (status == B_OK)
1054 		status = file.SetTo(&entry, B_READ_ONLY);
1055 	if (status == B_OK)
1056 		status = fTextView->GetStyledText(&file);
1057 
1058 	if (status == B_ENTRY_NOT_FOUND) {
1059 		// Treat non-existing files consideratley; we just want to get an
1060 		// empty window for them - to create this new document
1061 		status = B_OK;
1062 	}
1063 
1064 	if (status != B_OK) {
1065 		// If an error occured, bail out and tell the user what happened
1066 		BEntry entry(ref, true);
1067 		char name[B_FILE_NAME_LENGTH];
1068 		if (entry.GetName(name) != B_OK)
1069 			strcpy(name, B_TRANSLATE("???"));
1070 
1071 		BString text;
1072 		if (status == B_BAD_TYPE)
1073 			bs_printf(&text,
1074 				B_TRANSLATE("Error loading \"%s\":\n\tUnsupported format"), name);
1075 		else
1076 			bs_printf(&text, B_TRANSLATE("Error loading \"%s\":\n\t%s"),
1077 				name, strerror(status));
1078 
1079 		_ShowAlert(text, B_TRANSLATE("OK"), "", "", B_STOP_ALERT);
1080 		return status;
1081 	}
1082 
1083 	// update alignment
1084 	switch (fTextView->Alignment()) {
1085 		case B_ALIGN_LEFT:
1086 		default:
1087 			fAlignLeft->SetMarked(true);
1088 			break;
1089 		case B_ALIGN_CENTER:
1090 			fAlignCenter->SetMarked(true);
1091 			break;
1092 		case B_ALIGN_RIGHT:
1093 			fAlignRight->SetMarked(true);
1094 			break;
1095 	}
1096 
1097 	// update word wrapping
1098 	fWrapItem->SetMarked(fTextView->DoesWordWrap());
1099 	return B_OK;
1100 }
1101 
1102 
1103 void
1104 StyledEditWindow::OpenFile(entry_ref* ref)
1105 {
1106 	if (_LoadFile(ref) != B_OK) {
1107 		fSaveItem->SetEnabled(true);
1108 			// allow saving new files
1109 		return;
1110 	}
1111 
1112 	be_roster->AddToRecentDocuments(ref, APP_SIGNATURE);
1113 	fSaveMessage = new BMessage(B_SAVE_REQUESTED);
1114 	if (fSaveMessage) {
1115 		BEntry entry(ref, true);
1116 		BEntry parent;
1117 		entry_ref parentRef;
1118 		char name[B_FILE_NAME_LENGTH];
1119 
1120 		entry.GetParent(&parent);
1121 		entry.GetName(name);
1122 		parent.GetRef(&parentRef);
1123 		fSaveMessage->AddRef("directory", &parentRef);
1124 		fSaveMessage->AddString("name", name);
1125 		SetTitle(name);
1126 
1127 		LoadAttrs();
1128 	}
1129 	fTextView->Select(0, 0);
1130 }
1131 
1132 
1133 #undef B_TRANSLATE_CONTEXT
1134 #define B_TRANSLATE_CONTEXT "RevertToSavedAlert"
1135 
1136 
1137 void
1138 StyledEditWindow::RevertToSaved()
1139 {
1140 	entry_ref ref;
1141 	const char* name;
1142 
1143 	fSaveMessage->FindRef("directory", &ref);
1144 	fSaveMessage->FindString("name", &name);
1145 
1146 	BDirectory dir(&ref);
1147 	status_t status = dir.InitCheck();
1148 	BEntry entry;
1149 	if (status == B_OK)
1150 		status = entry.SetTo(&dir, name);
1151 
1152 	if (status == B_OK)
1153 		status = entry.GetRef(&ref);
1154 
1155 	if (status != B_OK || !entry.Exists()) {
1156 		BString alertText;
1157 		bs_printf(&alertText,
1158 			B_TRANSLATE("Cannot revert, file not found: \"%s\"."), name);
1159 		_ShowAlert(alertText, B_TRANSLATE("OK"), "", "", B_STOP_ALERT);
1160 		return;
1161 	}
1162 
1163 	BString alertText;
1164 	bs_printf(&alertText,
1165 		B_TRANSLATE("Revert to the last version of \"%s\"? "), Title());
1166 	if (_ShowAlert(alertText, B_TRANSLATE("Cancel"), B_TRANSLATE("OK"),
1167 		"", B_WARNING_ALERT) != 1)
1168 		return;
1169 
1170 	fTextView->Reset();
1171 	if (_LoadFile(&ref) != B_OK)
1172 		return;
1173 
1174 #undef B_TRANSLATE_CONTEXT
1175 #define B_TRANSLATE_CONTEXT "Menus"
1176 
1177 	// clear undo modes
1178 	fUndoItem->SetLabel(B_TRANSLATE("Can't undo"));
1179 	fUndoItem->SetEnabled(false);
1180 	fUndoFlag = false;
1181 	fCanUndo = false;
1182 	fRedoFlag = false;
1183 	fCanRedo = false;
1184 
1185 	// clear clean modes
1186 	fSaveItem->SetEnabled(false);
1187 	fRevertItem->SetEnabled(false);
1188 	fUndoCleans = false;
1189 	fRedoCleans = false;
1190 	fClean = true;
1191 }
1192 
1193 
1194 status_t
1195 StyledEditWindow::PageSetup(const char* documentName)
1196 {
1197 	BPrintJob printJob(documentName);
1198 
1199 	if (fPrintSettings != NULL)
1200 		printJob.SetSettings(new BMessage(*fPrintSettings));
1201 
1202 	status_t result = printJob.ConfigPage();
1203 	if (result == B_OK) {
1204 		delete fPrintSettings;
1205 		fPrintSettings = printJob.Settings();
1206 	}
1207 
1208 	return result;
1209 }
1210 
1211 
1212 void
1213 StyledEditWindow::Print(const char* documentName)
1214 {
1215 	BPrintJob printJob(documentName);
1216 	if (fPrintSettings)
1217 		printJob.SetSettings(new BMessage(*fPrintSettings));
1218 
1219 	if (printJob.ConfigJob() != B_OK)
1220  		return;
1221 
1222 	delete fPrintSettings;
1223 	fPrintSettings = printJob.Settings();
1224 
1225 	// information from printJob
1226 	BRect printableRect = printJob.PrintableRect();
1227 	int32 firstPage = printJob.FirstPage();
1228 	int32 lastPage = printJob.LastPage();
1229 
1230 	// lines eventually to be used to compute pages to print
1231 	int32 firstLine = 0;
1232 	int32 lastLine = fTextView->CountLines();
1233 
1234 	// values to be computed
1235 	int32 pagesInDocument = 1;
1236 	int32 linesInDocument = fTextView->CountLines();
1237 
1238 	int32 currentLine = 0;
1239 	while (currentLine < linesInDocument) {
1240 		float currentHeight = 0;
1241 		while (currentHeight < printableRect.Height() && currentLine < linesInDocument) {
1242 			currentHeight += fTextView->LineHeight(currentLine);
1243 			if (currentHeight < printableRect.Height())
1244 				currentLine++;
1245 		}
1246 		if (pagesInDocument == lastPage)
1247 			lastLine = currentLine - 1;
1248 
1249 		if (currentHeight >= printableRect.Height()) {
1250 			pagesInDocument++;
1251 			if (pagesInDocument == firstPage)
1252 				firstLine = currentLine;
1253 		}
1254 	}
1255 
1256 	if (lastPage > pagesInDocument - 1) {
1257 		lastPage = pagesInDocument - 1;
1258 		lastLine = currentLine - 1;
1259 	}
1260 
1261 
1262 	printJob.BeginJob();
1263 	if (fTextView->CountLines() > 0 && fTextView->TextLength() > 0) {
1264 		int32 printLine = firstLine;
1265 		while (printLine <= lastLine) {
1266 			float currentHeight = 0;
1267 			int32 firstLineOnPage = printLine;
1268 			while (currentHeight < printableRect.Height() && printLine <= lastLine) {
1269 				currentHeight += fTextView->LineHeight(printLine);
1270 				if (currentHeight < printableRect.Height())
1271 					printLine++;
1272 			}
1273 
1274 			float top = 0;
1275 			if (firstLineOnPage != 0)
1276 				top = fTextView->TextHeight(0, firstLineOnPage - 1);
1277 
1278 			float bottom = fTextView->TextHeight(0, printLine - 1);
1279 			BRect textRect(0.0, top + TEXT_INSET,
1280 				printableRect.Width(), bottom + TEXT_INSET);
1281 			printJob.DrawView(fTextView, textRect, B_ORIGIN);
1282 			printJob.SpoolPage();
1283 		}
1284 	}
1285 
1286 
1287 	printJob.CommitJob();
1288 }
1289 
1290 
1291 bool
1292 StyledEditWindow::Search(BString string, bool caseSensitive, bool wrap, bool backsearch)
1293 {
1294 	int32 start;
1295 	int32 finish;
1296 
1297 	start = B_ERROR;
1298 
1299 	int32 length = string.Length();
1300 	if (length == 0)
1301 		return false;
1302 
1303 	BString viewText(fTextView->Text());
1304 	int32 textStart, textFinish;
1305 	fTextView->GetSelection(&textStart, &textFinish);
1306 	if (backsearch) {
1307 		if (caseSensitive) {
1308 			start = viewText.FindLast(string, textStart);
1309 		} else {
1310 			start = viewText.IFindLast(string, textStart);
1311 		}
1312 	} else {
1313 		if (caseSensitive == true) {
1314 			start = viewText.FindFirst(string, textFinish);
1315 		} else {
1316 			start = viewText.IFindFirst(string, textFinish);
1317 		}
1318 	}
1319 	if (start == B_ERROR && wrap) {
1320 		if (backsearch) {
1321 			if (caseSensitive) {
1322 				start = viewText.FindLast(string, viewText.Length());
1323 			} else {
1324 				start = viewText.IFindLast(string, viewText.Length());
1325 			}
1326 		} else {
1327 			if (caseSensitive) {
1328 				start = viewText.FindFirst(string, 0);
1329 			} else {
1330 				start = viewText.IFindFirst(string, 0);
1331 			}
1332 		}
1333 	}
1334 
1335 	if (start != B_ERROR) {
1336 		finish = start + length;
1337 		fTextView->Select(start, finish);
1338 		fTextView->ScrollToSelection();
1339 		return true;
1340 	}
1341 
1342 	return false;
1343 }
1344 
1345 
1346 void
1347 StyledEditWindow::FindSelection()
1348 {
1349 	int32 selectionStart, selectionFinish;
1350 	fTextView->GetSelection(&selectionStart, &selectionFinish);
1351 
1352 	int32 selectionLength = selectionFinish- selectionStart;
1353 
1354 	BString viewText = fTextView->Text();
1355 	viewText.CopyInto(fStringToFind, selectionStart, selectionLength);
1356 	fFindAgainItem->SetEnabled(true);
1357 	Search(fStringToFind, fCaseSens, fWrapAround, fBackSearch);
1358 }
1359 
1360 
1361 bool
1362 StyledEditWindow::Replace(BString findthis, BString replaceWith, bool caseSensitive,
1363 	bool wrap, bool backsearch)
1364 {
1365 	if (Search(findthis, caseSensitive, wrap, backsearch)) {
1366 		int32 start, finish;
1367 		fTextView->GetSelection(&start, &finish);
1368 
1369 		_UpdateCleanUndoRedoSaveRevert();
1370 		fTextView->SetSuppressChanges(true);
1371 		fTextView->Delete(start, start + findthis.Length());
1372 		fTextView->Insert(start, replaceWith.String(), replaceWith.Length());
1373 		fTextView->SetSuppressChanges(false);
1374 		fTextView->Select(start, start + replaceWith.Length());
1375 		fTextView->ScrollToSelection();
1376 		return true;
1377 	}
1378 
1379 	return false;
1380 }
1381 
1382 
1383 void
1384 StyledEditWindow::ReplaceAll(BString findthis, BString replaceWith, bool caseSensitive)
1385 {
1386 	bool first = true;
1387 	fTextView->SetSuppressChanges(true);
1388 	while (Search(findthis, caseSensitive, true, false)) {
1389 		if (first) {
1390 			_UpdateCleanUndoRedoSaveRevert();
1391 			first = false;
1392 		}
1393 		int32 start, finish;
1394 		fTextView->GetSelection(&start, &finish);
1395 
1396 		fTextView->Delete(start, start + findthis.Length());
1397 		fTextView->Insert(start, replaceWith.String(), replaceWith.Length());
1398 	}
1399 	fTextView->SetSuppressChanges(false);
1400 }
1401 
1402 
1403 void
1404 StyledEditWindow::SearchAllWindows(BString find, BString replace, bool caseSensitive)
1405 {
1406 	int32 numWindows;
1407 	numWindows = be_app->CountWindows();
1408 
1409 	BMessage* message;
1410 	message= new BMessage(MSG_REPLACE_ALL);
1411 	message->AddString("FindText", find);
1412 	message->AddString("ReplaceText", replace);
1413 	message->AddBool("casesens", caseSensitive);
1414 
1415 	while (numWindows >= 0) {
1416 		StyledEditWindow* window = dynamic_cast<StyledEditWindow *>(
1417 			be_app->WindowAt(numWindows));
1418 
1419 		BMessenger messenger(window);
1420 		messenger.SendMessage(message);
1421 
1422 		numWindows--;
1423 	}
1424 }
1425 
1426 
1427 void
1428 StyledEditWindow::SetFontSize(float fontSize)
1429 {
1430 	uint32 sameProperties;
1431 	BFont font;
1432 
1433 	fTextView->GetFontAndColor(&font, &sameProperties);
1434 	font.SetSize(fontSize);
1435 	fTextView->SetFontAndColor(&font, B_FONT_SIZE);
1436 
1437 	_UpdateCleanUndoRedoSaveRevert();
1438 }
1439 
1440 
1441 void
1442 StyledEditWindow::SetFontColor(const rgb_color* color)
1443 {
1444 	uint32 sameProperties;
1445 	BFont font;
1446 
1447 	fTextView->GetFontAndColor(&font, &sameProperties, NULL, NULL);
1448 	fTextView->SetFontAndColor(&font, 0, color);
1449 
1450 	_UpdateCleanUndoRedoSaveRevert();
1451 }
1452 
1453 
1454 void
1455 StyledEditWindow::SetFontStyle(const char* fontFamily, const char* fontStyle)
1456 {
1457 	BFont font;
1458 	uint32 sameProperties;
1459 
1460 	// find out what the old font was
1461 	font_family oldFamily;
1462 	font_style oldStyle;
1463 	fTextView->GetFontAndColor(&font, &sameProperties);
1464 	font.GetFamilyAndStyle(&oldFamily, &oldStyle);
1465 
1466 	// clear that family's bit on the menu, if necessary
1467 	if (strcmp(oldFamily, fontFamily)) {
1468 		BMenuItem* oldItem = fFontMenu->FindItem(oldFamily);
1469 		if (oldItem != NULL) {
1470 			oldItem->SetMarked(false);
1471 			BMenu* menu = oldItem->Submenu();
1472 			if (menu != NULL) {
1473 				oldItem = menu->FindItem(oldStyle);
1474 				if (oldItem != NULL)
1475 					oldItem->SetMarked(false);
1476 			}
1477 		}
1478 	}
1479 
1480 	font.SetFamilyAndStyle(fontFamily, fontStyle);
1481 	fTextView->SetFontAndColor(&font);
1482 
1483 	BMenuItem* superItem;
1484 	superItem = fFontMenu->FindItem(fontFamily);
1485 	if (superItem != NULL) {
1486 		superItem->SetMarked(true);
1487 		fCurrentFontItem = superItem;
1488 	}
1489 
1490 	_UpdateCleanUndoRedoSaveRevert();
1491 }
1492 
1493 
1494 #undef B_TRANSLATE_CONTEXT
1495 #define B_TRANSLATE_CONTEXT "Menus"
1496 
1497 
1498 void
1499 StyledEditWindow::_UpdateCleanUndoRedoSaveRevert()
1500 {
1501 	fClean = false;
1502 	fUndoCleans = false;
1503 	fRedoCleans = false;
1504 	fRevertItem->SetEnabled(fSaveMessage != NULL);
1505 	fSaveItem->SetEnabled(true);
1506 	fUndoItem->SetLabel(B_TRANSLATE("Can't Undo"));
1507 	fUndoItem->SetEnabled(false);
1508 	fCanUndo = false;
1509 	fCanRedo = false;
1510 }
1511 
1512 
1513 int32
1514 StyledEditWindow::_ShowAlert(const BString& text, const BString& label,
1515 	const BString& label2, const BString& label3, alert_type type) const
1516 {
1517 	const char* button2 = NULL;
1518 	if (label2.Length() > 0)
1519 		button2 = label2.String();
1520 
1521 	const char* button3 = NULL;
1522 	button_spacing spacing = B_EVEN_SPACING;
1523 	if (label3.Length() > 0) {
1524 		button3 = label3.String();
1525 		spacing = B_OFFSET_SPACING;
1526 	}
1527 
1528 	BAlert* alert = new BAlert("Alert", text.String(), label.String(), button2,
1529 		button3, B_WIDTH_AS_USUAL, spacing, type);
1530 	alert->SetShortcut(0, B_ESCAPE);
1531 
1532 	return alert->Go();
1533 }
1534 
1535 
1536 bool
1537 StyledEditWindow::IsDocumentEntryRef(const entry_ref* ref)
1538 {
1539 	if (ref == NULL)
1540 		return false;
1541 
1542 	if (fSaveMessage == NULL)
1543 		return false;
1544 
1545 	entry_ref dir;
1546 	const char* name;
1547 	if (fSaveMessage->FindRef("directory", &dir) != B_OK
1548 		|| fSaveMessage->FindString("name", &name) != B_OK)
1549 		return false;
1550 
1551 	entry_ref documentRef;
1552 	BPath documentPath(&dir);
1553 	documentPath.Append(name);
1554 	get_ref_for_path(documentPath.Path(), &documentRef);
1555 
1556 	if (*ref == documentRef)
1557 		return true;
1558 
1559 	return false;
1560 }
1561 
1562