xref: /haiku/src/apps/mail/Signature.cpp (revision c9ad965c81b08802fed0827fd1dd16f45297928a)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2001, Be Incorporated. All rights reserved.
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28 
29 BeMail(TM), Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
33 */
34 
35 #include "Signature.h"
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 
41 #include <Clipboard.h>
42 #include <InterfaceKit.h>
43 #include <StorageKit.h>
44 
45 #include "MailApp.h"
46 #include "MailPopUpMenu.h"
47 #include "MailSupport.h"
48 #include "MailWindow.h"
49 #include "Messages.h"
50 
51 #include <MDRLanguage.h>
52 
53 
54 extern BRect		signature_window;
55 extern const char	*kUndoStrings[];
56 extern const char	*kRedoStrings[];
57 
58 const char kNameText[] = MDR_DIALECT_CHOICE ("Title:", "署名の名称:");
59 const char kSigText[] = MDR_DIALECT_CHOICE ("Signature:", "署名:");
60 
61 
62 //====================================================================
63 
64 TSignatureWindow::TSignatureWindow(BRect rect)
65 	: BWindow (rect, MDR_DIALECT_CHOICE ("Signatures","署名の編集"), B_TITLED_WINDOW, 0),
66 	fFile(NULL)
67 {
68 	BMenu		*menu;
69 	BMenuBar	*menu_bar;
70 	BMenuItem	*item;
71 
72 	BRect r = Bounds();
73 	/*** Set up the menus ****/
74 	menu_bar = new BMenuBar(r, "MenuBar");
75 	menu = new BMenu(MDR_DIALECT_CHOICE ("Signature","S) 署名"));
76 	menu->AddItem(fNew = new BMenuItem(MDR_DIALECT_CHOICE ("New","N) 新規"), new BMessage(M_NEW), 'N'));
77 	fSignature = new TMenu(MDR_DIALECT_CHOICE ("Open","O) 開く"), INDEX_SIGNATURE, M_SIGNATURE);
78 	menu->AddItem(new BMenuItem(fSignature));
79 	menu->AddSeparatorItem();
80 	menu->AddItem(fSave = new BMenuItem(MDR_DIALECT_CHOICE ("Save","S) 保存"), new BMessage(M_SAVE), 'S'));
81 	menu->AddItem(fDelete = new BMenuItem(MDR_DIALECT_CHOICE ("Delete","T) 削除"), new BMessage(M_DELETE), 'T'));
82 	menu_bar->AddItem(menu);
83 
84 	menu = new BMenu(MDR_DIALECT_CHOICE ("Edit","E) 編集"));
85 	menu->AddItem(fUndo = new BMenuItem(MDR_DIALECT_CHOICE ("Undo","Z) やり直し"), new BMessage(B_UNDO), 'Z'));
86 	fUndo->SetTarget(NULL, this);
87 	menu->AddSeparatorItem();
88 	menu->AddItem(fCut = new BMenuItem(MDR_DIALECT_CHOICE ("Cut","X) 切り取り"), new BMessage(B_CUT), 'X'));
89 	fCut->SetTarget(NULL, this);
90 	menu->AddItem(fCopy = new BMenuItem(MDR_DIALECT_CHOICE ("Copy","C) コピー"), new BMessage(B_COPY), 'C'));
91 	fCopy->SetTarget(NULL, this);
92 	menu->AddItem(fPaste = new BMenuItem(MDR_DIALECT_CHOICE ("Paste","V) 貼り付け"), new BMessage(B_PASTE), 'V'));
93 	fPaste->SetTarget(NULL, this);
94 	menu->AddSeparatorItem();
95 	menu->AddItem(item = new BMenuItem(MDR_DIALECT_CHOICE ("Select All","A) 全文選択"), new BMessage(M_SELECT), 'A'));
96 	item->SetTarget(NULL, this);
97 	menu_bar->AddItem(menu);
98 
99 	AddChild(menu_bar);
100 	/**** Done with the menu set up *****/
101 
102 	/**** Add on the panel, giving it the width and at least one vertical pixel *****/
103 	fSigView = new TSignatureView(BRect(0, menu_bar->Frame().bottom+1,
104 										rect.Width(), menu_bar->Frame().bottom+2));
105 	AddChild(fSigView);
106 
107 	/* resize the window to the correct height */
108 	fSigView->SetResizingMode(B_FOLLOW_NONE);
109 	ResizeTo(rect.Width()-2, fSigView->Frame().bottom-2);
110 	fSigView->SetResizingMode(B_FOLLOW_ALL);
111 
112 	SetSizeLimits(kSigWidth, RIGHT_BOUNDARY, r.top + 100, RIGHT_BOUNDARY);
113 }
114 
115 
116 TSignatureWindow::~TSignatureWindow()
117 {
118 }
119 
120 
121 void
122 TSignatureWindow::MenusBeginning()
123 {
124 	int32		finish = 0;
125 	int32		start = 0;
126 	BTextView	*text_view;
127 
128 	fDelete->SetEnabled(fFile);
129 	fSave->SetEnabled(IsDirty());
130 	fUndo->SetEnabled(false);		// ***TODO***
131 
132 	text_view = (BTextView *)fSigView->fName->ChildAt(0);
133 	if (text_view->IsFocus())
134 		text_view->GetSelection(&start, &finish);
135 	else
136 		fSigView->fTextView->GetSelection(&start, &finish);
137 
138 	fCut->SetEnabled(start != finish);
139 	fCopy->SetEnabled(start != finish);
140 
141 	fNew->SetEnabled(text_view->TextLength() | fSigView->fTextView->TextLength());
142 	be_clipboard->Lock();
143 	fPaste->SetEnabled(be_clipboard->Data()->HasData("text/plain", B_MIME_TYPE));
144 	be_clipboard->Unlock();
145 
146 	// Undo stuff
147 	bool		isRedo = false;
148 	undo_state	undoState = B_UNDO_UNAVAILABLE;
149 
150 	BTextView *focusTextView = dynamic_cast<BTextView *>(CurrentFocus());
151 	if (focusTextView != NULL)
152 		undoState = focusTextView->UndoState(&isRedo);
153 
154 	fUndo->SetLabel((isRedo) ? kRedoStrings[undoState] : kUndoStrings[undoState]);
155 	fUndo->SetEnabled(undoState != B_UNDO_UNAVAILABLE);
156 }
157 
158 
159 void
160 TSignatureWindow::MessageReceived(BMessage* msg)
161 {
162 	char		*sig;
163 	char		name[B_FILE_NAME_LENGTH];
164 	BFont		*font;
165 	BTextView	*text_view;
166 	entry_ref	ref;
167 	off_t		size;
168 
169 	switch(msg->what) {
170 		case CHANGE_FONT:
171 			msg->FindPointer("font", (void **)&font);
172 			fSigView->fTextView->SetFontAndColor(font);
173 			fSigView->fTextView->Invalidate(fSigView->fTextView->Bounds());
174 			break;
175 
176 		case M_NEW:
177 			if (Clear()) {
178 				fSigView->fName->SetText("");
179 //				fSigView->fTextView->SetText(NULL, (int32)0);
180 				fSigView->fTextView->SetText("");
181 				fSigView->fName->MakeFocus(true);
182 			}
183 			break;
184 
185 		case M_SAVE:
186 			Save();
187 			break;
188 
189 		case M_DELETE:
190 			if (!(new BAlert("",MDR_DIALECT_CHOICE (
191 					"Really delete this signature? This cannot be undone.",
192 					"この署名を削除しますか?"),
193 					MDR_DIALECT_CHOICE ("Cancel","取消l"),
194 					MDR_DIALECT_CHOICE ("Delete","削除"), NULL, B_WIDTH_AS_USUAL,
195 					B_WARNING_ALERT))->Go())
196 				break;
197 
198 			if (fFile) {
199 				delete fFile;
200 				fFile = NULL;
201 				fEntry.Remove();
202 				fSigView->fName->SetText("");
203 				fSigView->fTextView->SetText(NULL, (int32)0);
204 				fSigView->fName->MakeFocus(true);
205 			}
206 			break;
207 
208 		case M_SIGNATURE:
209 			if (Clear()) {
210 				msg->FindRef("ref", &ref);
211 				fEntry.SetTo(&ref);
212 				fFile = new BFile(&ref, O_RDWR);
213 				if (fFile->InitCheck() == B_NO_ERROR) {
214 					fFile->ReadAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, name, sizeof(name));
215 					fSigView->fName->SetText(name);
216 					fFile->GetSize(&size);
217 					sig = (char *)malloc(size);
218 					size = fFile->Read(sig, size);
219 					fSigView->fTextView->SetText(sig, size);
220 					fSigView->fName->MakeFocus(true);
221 					text_view = (BTextView *)fSigView->fName->ChildAt(0);
222 					text_view->Select(0, text_view->TextLength());
223 					fSigView->fTextView->fDirty = false;
224 				}
225 				else {
226 					fFile = NULL;
227 					beep();
228 					(new BAlert("", MDR_DIALECT_CHOICE (
229 						"Couldn't open this signature. Sorry.",
230 						"署名を開く時にエラーが発生しました。"),
231 						MDR_DIALECT_CHOICE ("OK","了解")))->Go();
232 				}
233 			}
234 			break;
235 
236 		default:
237 			BWindow::MessageReceived(msg);
238 	}
239 }
240 
241 
242 bool
243 TSignatureWindow::QuitRequested()
244 {
245 	if (Clear()) {
246 		BMessage msg(WINDOW_CLOSED);
247 		msg.AddInt32("kind", SIG_WINDOW);
248 		msg.AddRect("window frame", Frame());
249 
250 		be_app->PostMessage(&msg);
251 		return true;
252 	}
253 	return false;
254 }
255 
256 
257 void
258 TSignatureWindow::FrameResized(float width, float height)
259 {
260 	fSigView->FrameResized(width, height);
261 }
262 
263 
264 void
265 TSignatureWindow::Show()
266 {
267 	BTextView	*text_view;
268 
269 	Lock();
270 	text_view = (BTextView *)fSigView->fName->TextView();
271 	fSigView->fName->MakeFocus(true);
272 	text_view->Select(0, text_view->TextLength());
273 	Unlock();
274 
275 	BWindow::Show();
276 }
277 
278 
279 bool
280 TSignatureWindow::Clear()
281 {
282 	int32		result;
283 
284 	if (IsDirty()) {
285 		beep();
286 		BAlert *alert = new BAlert("",
287 			MDR_DIALECT_CHOICE ("Save changes to this signature?","変更した署名を保存しますか?"),
288 			MDR_DIALECT_CHOICE ("Don't Save","保存しない"),
289 			MDR_DIALECT_CHOICE ("Cancel","中止"),
290 			MDR_DIALECT_CHOICE ("Save","保存する"),
291 			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
292 		alert->SetShortcut(0,'d');
293 		alert->SetShortcut(1,B_ESCAPE);
294 		result = alert->Go();
295 		if (result == 1)
296 			return false;
297 		if (result == 2)
298 			Save();
299 	}
300 
301 	delete fFile;
302 	fFile = NULL;
303 	fSigView->fTextView->fDirty = false;
304 	return true;
305 }
306 
307 
308 bool
309 TSignatureWindow::IsDirty()
310 {
311 	char		name[B_FILE_NAME_LENGTH];
312 
313 	if (fFile) {
314 		fFile->ReadAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, name, sizeof(name));
315 		if ((strcmp(name, fSigView->fName->Text())) || (fSigView->fTextView->fDirty))
316 			return true;
317 	}
318 	else {
319 		if ((strlen(fSigView->fName->Text())) ||
320 			(fSigView->fTextView->TextLength()))
321 			return true;
322 	}
323 	return false;
324 }
325 
326 
327 void
328 TSignatureWindow::Save()
329 {
330 	char			name[B_FILE_NAME_LENGTH];
331 	int32			index = 0;
332 	status_t		result;
333 	BDirectory		dir;
334 	BEntry			entry;
335 	BNodeInfo		*node;
336 	BPath			path;
337 
338 	if (!fFile) {
339 		find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
340 		dir.SetTo(path.Path());
341 
342 		if (dir.FindEntry("Mail", &entry) == B_NO_ERROR)
343 			dir.SetTo(&entry);
344 		else
345 			dir.CreateDirectory("Mail", &dir);
346 
347 		if (dir.InitCheck() != B_NO_ERROR)
348 			goto err_exit;
349 
350 		if (dir.FindEntry("signatures", &entry) == B_NO_ERROR)
351 			dir.SetTo(&entry);
352 		else
353 			dir.CreateDirectory("signatures", &dir);
354 
355 		if (dir.InitCheck() != B_NO_ERROR)
356 			goto err_exit;
357 
358 		fFile = new BFile();
359 		while(true) {
360 			sprintf(name, "signature_%ld", index++);
361 			if ((result = dir.CreateFile(name, fFile, true)) == B_NO_ERROR)
362 				break;
363 			if (result != EEXIST)
364 				goto err_exit;
365 		}
366 		dir.FindEntry(name, &fEntry);
367 		node = new BNodeInfo(fFile);
368 		node->SetType("text/plain");
369 		delete node;
370 	}
371 
372 	fSigView->fTextView->fDirty = false;
373 	fFile->Seek(0, 0);
374 	fFile->Write(fSigView->fTextView->Text(),
375 				 fSigView->fTextView->TextLength());
376 	fFile->SetSize(fFile->Position());
377 	fFile->WriteAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, fSigView->fName->Text(),
378 					 strlen(fSigView->fName->Text()) + 1);
379 	return;
380 
381 err_exit:
382 	beep();
383 	(new BAlert("", MDR_DIALECT_CHOICE (
384 		"An error occurred trying to save this signature.",
385 		"署名を保存しようとした時にエラーが発生しました。"),
386 		MDR_DIALECT_CHOICE ("Sorry","了解")))->Go();
387 }
388 
389 
390 //====================================================================
391 //	#pragma mark -
392 
393 
394 TSignatureView::TSignatureView(BRect rect)
395 	: BBox(rect, "SigView", B_FOLLOW_ALL, B_WILL_DRAW)
396 {
397 }
398 
399 
400 void
401 TSignatureView::AttachedToWindow()
402 {
403 	BRect	rect = Bounds();
404 	float	name_text_length = StringWidth(kNameText);
405 	float	sig_text_length = StringWidth(kSigText);
406 	float	divide_length;
407 
408 	if (name_text_length > sig_text_length)
409 		divide_length = name_text_length;
410 	else
411 		divide_length = sig_text_length;
412 
413 	rect.InsetBy(8,0);
414 	rect.top+= 8;
415 
416 	fName = new TNameControl(rect, kNameText, new BMessage(NAME_FIELD));
417 	AddChild(fName);
418 
419 	fName->SetDivider(divide_length + 10);
420 	fName->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
421 
422 	rect.OffsetBy(0,fName->Bounds().Height()+5);
423 	rect.bottom = rect.top + kSigHeight;
424 	rect.left = fName->TextView()->Frame().left;
425 
426 	BRect text = rect;
427 	text.OffsetTo(10,0);
428 	fTextView = new TSigTextView(rect, text);
429 	BScrollView *scroller = new BScrollView("SigScroller", fTextView, B_FOLLOW_ALL, 0, false, true);
430 	AddChild(scroller);
431 	scroller->ResizeBy(-1 * scroller->ScrollBar(B_VERTICAL)->Frame().Width() - 9, 0);
432 	scroller->MoveBy(7,0);
433 
434 	/* back up a bit to make room for the label */
435 
436 	rect = scroller->Frame();
437 	BStringView *stringView = new BStringView(rect, "SigLabel", kSigText);
438 	AddChild(stringView);
439 
440 	float tWidth, tHeight;
441 	stringView->GetPreferredSize(&tWidth, &tHeight);
442 
443 	/* the 5 is for the spacer in the TextView */
444 
445 	rect.OffsetBy(-1 *(tWidth) - 5, 0);
446 	rect.right = rect.left + tWidth;
447 	rect.bottom = rect.top + tHeight;
448 
449 	stringView->MoveTo(rect.LeftTop());
450 	stringView->ResizeTo(rect.Width(), rect.Height());
451 
452 	/* Resize the View to the correct height */
453 	scroller->SetResizingMode(B_FOLLOW_NONE);
454 	ResizeTo(Frame().Width(), scroller->Frame().bottom + 8);
455 	scroller->SetResizingMode(B_FOLLOW_ALL);
456 }
457 
458 
459 //====================================================================
460 //	#pragma mark -
461 
462 
463 TNameControl::TNameControl(BRect rect, const char *label, BMessage *msg)
464 			 :BTextControl(rect, "", label, "", msg, B_FOLLOW_LEFT_RIGHT)
465 {
466 	strcpy(fLabel, label);
467 }
468 
469 
470 void
471 TNameControl::AttachedToWindow()
472 {
473 	BTextControl::AttachedToWindow();
474 
475 	SetDivider(StringWidth(fLabel) + 6);
476 	TextView()->SetMaxBytes(B_FILE_NAME_LENGTH - 1);
477 }
478 
479 
480 void
481 TNameControl::MessageReceived(BMessage *msg)
482 {
483 	switch (msg->what) {
484 		case M_SELECT:
485 			TextView()->Select(0, TextView()->TextLength());
486 			break;
487 
488 		default:
489 			BTextControl::MessageReceived(msg);
490 	}
491 }
492 
493 
494 //====================================================================
495 //	#pragma mark -
496 
497 
498 TSigTextView::TSigTextView(BRect frame, BRect text)
499 			 :BTextView(frame, "SignatureView", text, B_FOLLOW_ALL, B_NAVIGABLE | B_WILL_DRAW)
500 {
501 	fDirty = false;
502 	SetDoesUndo(true);
503 }
504 
505 
506 void
507 TSigTextView::FrameResized(float /*width*/, float /*height*/)
508 {
509 	BRect r(Bounds());
510 	r.InsetBy(3, 3);
511 	SetTextRect(r);
512 }
513 
514 
515 void
516 TSigTextView::DeleteText(int32 offset, int32 len)
517 {
518 	fDirty = true;
519 	BTextView::DeleteText(offset, len);
520 }
521 
522 
523 void
524 TSigTextView::InsertText(const char *text, int32 len, int32 offset,
525 	const text_run_array *runs)
526 {
527 	fDirty = true;
528 	BTextView::InsertText(text, len, offset, runs);
529 }
530 
531 
532 void
533 TSigTextView::KeyDown(const char *key, int32 count)
534 {
535 	bool	up = false;
536 	int32	height;
537 	BRect	r;
538 
539 	switch (key[0]) {
540 		case B_HOME:
541 			Select(0, 0);
542 			ScrollToSelection();
543 			break;
544 
545 		case B_END:
546 			Select(TextLength(), TextLength());
547 			ScrollToSelection();
548 			break;
549 
550 		case B_PAGE_UP:
551 			up = true;
552 		case B_PAGE_DOWN:
553 			r = Bounds();
554 			height = (int32)((up ? r.top - r.bottom : r.bottom - r.top) - 25);
555 			if ((up) && (!r.top))
556 				break;
557 			ScrollBy(0, height);
558 			break;
559 
560 		default:
561 			BTextView::KeyDown(key, count);
562 	}
563 }
564 
565 
566 void
567 TSigTextView::MessageReceived(BMessage *msg)
568 {
569 	char		type[B_FILE_NAME_LENGTH];
570 	char		*text;
571 	int32		end;
572 	int32		start;
573 	BFile		file;
574 	BNodeInfo	*node;
575 	entry_ref	ref;
576 	off_t		size;
577 
578 	switch (msg->what) {
579 		case B_SIMPLE_DATA:
580 			if (msg->HasRef("refs")) {
581 				msg->FindRef("refs", &ref);
582 				file.SetTo(&ref, O_RDONLY);
583 				if (file.InitCheck() == B_NO_ERROR) {
584 					node = new BNodeInfo(&file);
585 					node->GetType(type);
586 					delete node;
587 					file.GetSize(&size);
588 					if ((!strncasecmp(type, "text/", 5)) && (size)) {
589 						text = (char *)malloc(size);
590 						file.Read(text, size);
591 						Delete();
592 						GetSelection(&start, &end);
593 						Insert(text, size);
594 						Select(start, start + size);
595 						free(text);
596 					}
597 				}
598 			}
599 			else
600 				BTextView::MessageReceived(msg);
601 			break;
602 
603 		case M_SELECT:
604 			if (IsSelectable())
605 				Select(0, TextLength());
606 			break;
607 
608 		default:
609 			BTextView::MessageReceived(msg);
610 	}
611 }
612 
613