xref: /haiku/src/apps/mail/Signature.cpp (revision 1e36cfc2721ef13a187c6f7354dc9cbc485e89d3)
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 //--------------------------------------------------------------------
36 //
37 //	Signature.cpp
38 //
39 //--------------------------------------------------------------------
40 
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <Clipboard.h>
45 #include <InterfaceKit.h>
46 #include <StorageKit.h>
47 
48 #include "Mail.h"
49 #include "Signature.h"
50 
51 #include <MDRLanguage.h>
52 
53 extern BRect		signature_window;
54 extern const char	*kUndoStrings[];
55 extern const char	*kRedoStrings[];
56 
57 const char kNameText[] = MDR_DIALECT_CHOICE ("Title:", "署名の名称:");
58 const char kSigText[] = MDR_DIALECT_CHOICE ("Signature:", "署名:");
59 
60 
61 //====================================================================
62 
63 TSignatureWindow::TSignatureWindow(BRect rect)
64 	: BWindow (rect, MDR_DIALECT_CHOICE ("Signatures","署名の編集"), B_TITLED_WINDOW, 0),
65 	fFile(NULL)
66 {
67 	BMenu		*menu;
68 	BMenuBar	*menu_bar;
69 	BMenuItem	*item;
70 
71 	BRect r = Bounds();
72 	/*** Set up the menus ****/
73 	menu_bar = new BMenuBar(r, "MenuBar");
74 	menu = new BMenu(MDR_DIALECT_CHOICE ("Signature","S) 署名"));
75 	menu->AddItem(fNew = new BMenuItem(MDR_DIALECT_CHOICE ("New","N) 新規"), new BMessage(M_NEW), 'N'));
76 	fSignature = new TMenu(MDR_DIALECT_CHOICE ("Open","O) 開く"), INDEX_SIGNATURE, M_SIGNATURE);
77 	menu->AddItem(new BMenuItem(fSignature));
78 	menu->AddSeparatorItem();
79 	menu->AddItem(fSave = new BMenuItem(MDR_DIALECT_CHOICE ("Save","S) 保存"), new BMessage(M_SAVE), 'S'));
80 	menu->AddItem(fDelete = new BMenuItem(MDR_DIALECT_CHOICE ("Delete","T) 削除"), new BMessage(M_DELETE), 'T'));
81 	menu_bar->AddItem(menu);
82 
83 	menu = new BMenu(MDR_DIALECT_CHOICE ("Edit","E) 編集"));
84 	menu->AddItem(fUndo = new BMenuItem(MDR_DIALECT_CHOICE ("Undo","Z) やり直し"), new BMessage(B_UNDO), 'Z'));
85 	fUndo->SetTarget(NULL, this);
86 	menu->AddSeparatorItem();
87 	menu->AddItem(fCut = new BMenuItem(MDR_DIALECT_CHOICE ("Cut","X) 切り取り"), new BMessage(B_CUT), 'X'));
88 	fCut->SetTarget(NULL, this);
89 	menu->AddItem(fCopy = new BMenuItem(MDR_DIALECT_CHOICE ("Copy","C) コピー"), new BMessage(B_COPY), 'C'));
90 	fCopy->SetTarget(NULL, this);
91 	menu->AddItem(fPaste = new BMenuItem(MDR_DIALECT_CHOICE ("Paste","V) 貼り付け"), new BMessage(B_PASTE), 'V'));
92 	fPaste->SetTarget(NULL, this);
93 	menu->AddSeparatorItem();
94 	menu->AddItem(item = new BMenuItem(MDR_DIALECT_CHOICE ("Select All","A) 全文選択"), new BMessage(M_SELECT), 'A'));
95 	item->SetTarget(NULL, this);
96 	menu_bar->AddItem(menu);
97 
98 	AddChild(menu_bar);
99 	/**** Done with the menu set up *****/
100 
101 	/**** Add on the panel, giving it the width and at least one vertical pixel *****/
102 	fSigView = new TSignatureView(BRect(0, menu_bar->Frame().bottom+1,
103 										rect.Width(), menu_bar->Frame().bottom+2));
104 	AddChild(fSigView);
105 
106 	/* resize the window to the correct height */
107 	fSigView->SetResizingMode(B_FOLLOW_NONE);
108 	ResizeTo(rect.Width()-2, fSigView->Frame().bottom-2);
109 	fSigView->SetResizingMode(B_FOLLOW_ALL);
110 
111 	SetSizeLimits(kSigWidth, RIGHT_BOUNDARY, r.top + 100, RIGHT_BOUNDARY);
112 }
113 
114 
115 TSignatureWindow::~TSignatureWindow()
116 {
117 	signature_window = Frame();
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 
249 		be_app->PostMessage(&msg);
250 		return true;
251 	}
252 	return false;
253 }
254 
255 
256 void
257 TSignatureWindow::FrameResized(float width, float height)
258 {
259 	fSigView->FrameResized(width, height);
260 }
261 
262 
263 void
264 TSignatureWindow::Show()
265 {
266 	BTextView	*text_view;
267 
268 	Lock();
269 	text_view = (BTextView *)fSigView->fName->TextView();
270 	fSigView->fName->MakeFocus(true);
271 	text_view->Select(0, text_view->TextLength());
272 	Unlock();
273 
274 	BWindow::Show();
275 }
276 
277 
278 bool
279 TSignatureWindow::Clear()
280 {
281 	int32		result;
282 
283 	if (IsDirty()) {
284 		beep();
285 		BAlert *alert = new BAlert("",
286 			MDR_DIALECT_CHOICE ("Save changes to this signature?","変更した署名を保存しますか?"),
287 			MDR_DIALECT_CHOICE ("Don't Save","保存しない"),
288 			MDR_DIALECT_CHOICE ("Cancel","中止"),
289 			MDR_DIALECT_CHOICE ("Save","保存する"),
290 			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
291 		alert->SetShortcut(0,'d');
292 		alert->SetShortcut(1,B_ESCAPE);
293 		result = alert->Go();
294 		if (result == 1)
295 			return false;
296 		if (result == 2)
297 			Save();
298 	}
299 
300 	delete fFile;
301 	fFile = NULL;
302 	fSigView->fTextView->fDirty = false;
303 	return true;
304 }
305 
306 
307 bool
308 TSignatureWindow::IsDirty()
309 {
310 	char		name[B_FILE_NAME_LENGTH];
311 
312 	if (fFile) {
313 		fFile->ReadAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, name, sizeof(name));
314 		if ((strcmp(name, fSigView->fName->Text())) || (fSigView->fTextView->fDirty))
315 			return true;
316 	}
317 	else {
318 		if ((strlen(fSigView->fName->Text())) ||
319 			(fSigView->fTextView->TextLength()))
320 			return true;
321 	}
322 	return false;
323 }
324 
325 
326 void
327 TSignatureWindow::Save()
328 {
329 	char			name[B_FILE_NAME_LENGTH];
330 	int32			index = 0;
331 	status_t		result;
332 	BDirectory		dir;
333 	BEntry			entry;
334 	BNodeInfo		*node;
335 	BPath			path;
336 
337 	if (!fFile) {
338 		find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
339 		dir.SetTo(path.Path());
340 
341 		if (dir.FindEntry("bemail", &entry) == B_NO_ERROR)
342 			dir.SetTo(&entry);
343 		else
344 			dir.CreateDirectory("bemail", &dir);
345 
346 		if (dir.InitCheck() != B_NO_ERROR)
347 			goto err_exit;
348 
349 		if (dir.FindEntry("signatures", &entry) == B_NO_ERROR)
350 			dir.SetTo(&entry);
351 		else
352 			dir.CreateDirectory("signatures", &dir);
353 
354 		if (dir.InitCheck() != B_NO_ERROR)
355 			goto err_exit;
356 
357 		fFile = new BFile();
358 		while(true) {
359 			sprintf(name, "signature_%ld", index++);
360 			if ((result = dir.CreateFile(name, fFile, true)) == B_NO_ERROR)
361 				break;
362 			if (result != EEXIST)
363 				goto err_exit;
364 		}
365 		dir.FindEntry(name, &fEntry);
366 		node = new BNodeInfo(fFile);
367 		node->SetType("text/plain");
368 		delete node;
369 	}
370 
371 	fSigView->fTextView->fDirty = false;
372 	fFile->Seek(0, 0);
373 	fFile->Write(fSigView->fTextView->Text(),
374 				 fSigView->fTextView->TextLength());
375 	fFile->SetSize(fFile->Position());
376 	fFile->WriteAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, fSigView->fName->Text(),
377 					 strlen(fSigView->fName->Text()) + 1);
378 	return;
379 
380 err_exit:
381 	beep();
382 	(new BAlert("", MDR_DIALECT_CHOICE (
383 		"An error occurred trying to save this signature.",
384 		"署名を保存しようとした時にエラーが発生しました。"),
385 		MDR_DIALECT_CHOICE ("Sorry","了解")))->Go();
386 }
387 
388 
389 //====================================================================
390 //	#pragma mark -
391 
392 
393 TSignatureView::TSignatureView(BRect rect)
394 	: BBox(rect, "SigView", B_FOLLOW_ALL, B_WILL_DRAW)
395 {
396 }
397 
398 
399 void
400 TSignatureView::AttachedToWindow()
401 {
402 	BRect	rect = Bounds();
403 	float	name_text_length = StringWidth(kNameText);
404 	float	sig_text_length = StringWidth(kSigText);
405 	float	divide_length;
406 
407 	if (name_text_length > sig_text_length)
408 		divide_length = name_text_length;
409 	else
410 		divide_length = sig_text_length;
411 
412 	rect.InsetBy(8,0);
413 	rect.top+= 8;
414 
415 	fName = new TNameControl(rect, kNameText, new BMessage(NAME_FIELD));
416 	AddChild(fName);
417 
418 	fName->SetDivider(divide_length + 10);
419 	fName->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT);
420 
421 	rect.OffsetBy(0,fName->Bounds().Height()+5);
422 	rect.bottom = rect.top + kSigHeight;
423 	rect.left = fName->TextView()->Frame().left;
424 
425 	BRect text = rect;
426 	text.OffsetTo(10,0);
427 	fTextView = new TSigTextView(rect, text);
428 	BScrollView *scroller = new BScrollView("SigScroller", fTextView, B_FOLLOW_ALL, 0, false, true);
429 	AddChild(scroller);
430 	scroller->ResizeBy(-1 * scroller->ScrollBar(B_VERTICAL)->Frame().Width() - 9, 0);
431 	scroller->MoveBy(7,0);
432 
433 	/* back up a bit to make room for the label */
434 
435 	rect = scroller->Frame();
436 	BStringView *stringView = new BStringView(rect, "SigLabel", kSigText);
437 	AddChild(stringView);
438 
439 	float tWidth, tHeight;
440 	stringView->GetPreferredSize(&tWidth, &tHeight);
441 
442 	/* the 5 is for the spacer in the TextView */
443 
444 	rect.OffsetBy(-1 *(tWidth) - 5, 0);
445 	rect.right = rect.left + tWidth;
446 	rect.bottom = rect.top + tHeight;
447 
448 	stringView->MoveTo(rect.LeftTop());
449 	stringView->ResizeTo(rect.Width(), rect.Height());
450 
451 	/* Resize the View to the correct height */
452 	scroller->SetResizingMode(B_FOLLOW_NONE);
453 	ResizeTo(Frame().Width(), scroller->Frame().bottom + 8);
454 	scroller->SetResizingMode(B_FOLLOW_ALL);
455 }
456 
457 
458 //====================================================================
459 //	#pragma mark -
460 
461 
462 TNameControl::TNameControl(BRect rect, const char *label, BMessage *msg)
463 			 :BTextControl(rect, "", label, "", msg, B_FOLLOW_LEFT_RIGHT)
464 {
465 	strcpy(fLabel, label);
466 }
467 
468 
469 void
470 TNameControl::AttachedToWindow()
471 {
472 	BTextControl::AttachedToWindow();
473 
474 	SetDivider(StringWidth(fLabel) + 6);
475 	TextView()->SetMaxBytes(B_FILE_NAME_LENGTH - 1);
476 }
477 
478 
479 void
480 TNameControl::MessageReceived(BMessage *msg)
481 {
482 	switch (msg->what) {
483 		case M_SELECT:
484 			TextView()->Select(0, TextView()->TextLength());
485 			break;
486 
487 		default:
488 			BTextControl::MessageReceived(msg);
489 	}
490 }
491 
492 
493 //====================================================================
494 //	#pragma mark -
495 
496 
497 TSigTextView::TSigTextView(BRect frame, BRect text)
498 			 :BTextView(frame, "SignatureView", text, B_FOLLOW_ALL, B_NAVIGABLE | B_WILL_DRAW)
499 {
500 	fDirty = false;
501 	SetDoesUndo(true);
502 }
503 
504 
505 void
506 TSigTextView::FrameResized(float /*width*/, float /*height*/)
507 {
508 	BRect r(Bounds());
509 	r.InsetBy(3, 3);
510 	SetTextRect(r);
511 }
512 
513 
514 void
515 TSigTextView::DeleteText(int32 offset, int32 len)
516 {
517 	fDirty = true;
518 	BTextView::DeleteText(offset, len);
519 }
520 
521 
522 void
523 TSigTextView::InsertText(const char *text, int32 len, int32 offset,
524 	const text_run_array *runs)
525 {
526 	fDirty = true;
527 	BTextView::InsertText(text, len, offset, runs);
528 }
529 
530 
531 void
532 TSigTextView::KeyDown(const char *key, int32 count)
533 {
534 	bool	up = false;
535 	int32	height;
536 	BRect	r;
537 
538 	switch (key[0]) {
539 		case B_HOME:
540 			Select(0, 0);
541 			ScrollToSelection();
542 			break;
543 
544 		case B_END:
545 			Select(TextLength(), TextLength());
546 			ScrollToSelection();
547 			break;
548 
549 		case B_PAGE_UP:
550 			up = true;
551 		case B_PAGE_DOWN:
552 			r = Bounds();
553 			height = (int32)((up ? r.top - r.bottom : r.bottom - r.top) - 25);
554 			if ((up) && (!r.top))
555 				break;
556 			ScrollBy(0, height);
557 			break;
558 
559 		default:
560 			BTextView::KeyDown(key, count);
561 	}
562 }
563 
564 
565 void
566 TSigTextView::MessageReceived(BMessage *msg)
567 {
568 	char		type[B_FILE_NAME_LENGTH];
569 	char		*text;
570 	int32		end;
571 	int32		start;
572 	BFile		file;
573 	BNodeInfo	*node;
574 	entry_ref	ref;
575 	off_t		size;
576 
577 	switch (msg->what) {
578 		case B_SIMPLE_DATA:
579 			if (msg->HasRef("refs")) {
580 				msg->FindRef("refs", &ref);
581 				file.SetTo(&ref, O_RDONLY);
582 				if (file.InitCheck() == B_NO_ERROR) {
583 					node = new BNodeInfo(&file);
584 					node->GetType(type);
585 					delete node;
586 					file.GetSize(&size);
587 					if ((!strncasecmp(type, "text/", 5)) && (size)) {
588 						text = (char *)malloc(size);
589 						file.Read(text, size);
590 						Delete();
591 						GetSelection(&start, &end);
592 						Insert(text, size);
593 						Select(start, start + size);
594 						free(text);
595 					}
596 				}
597 			}
598 			else
599 				BTextView::MessageReceived(msg);
600 			break;
601 
602 		case M_SELECT:
603 			if (IsSelectable())
604 				Select(0, TextLength());
605 			break;
606 
607 		default:
608 			BTextView::MessageReceived(msg);
609 	}
610 }
611 
612