xref: /haiku/src/apps/cortex/RouteApp/RouteApp.cpp (revision d3d8b26997fac34a84981e6d2b649521de2cc45a)
1 // RouteApp.cpp
2 // e.moon 14may99
3 
4 #include "RouteApp.h"
5 #include "RouteWindow.h"
6 #include "DormantNodeWindow.h"
7 #include "MediaRoutingView.h"
8 #include "MediaNodePanel.h"
9 
10 #include "RouteAppNodeManager.h"
11 #include "NodeRef.h"
12 
13 #include "TipManager.h"
14 
15 #include "AddOnHost.h"
16 
17 #include "route_app_io.h"
18 #include "XML.h"
19 #include "MessageIO.h"
20 #include "NodeSetIOContext.h"
21 
22 #include <Debug.h>
23 #include <OS.h>
24 #include <Roster.h>
25 #include <Directory.h>
26 #include <FindDirectory.h>
27 #include <NodeInfo.h>
28 #include <Path.h>
29 #include <Entry.h>
30 
31 extern "C" void SetNewLeakChecking(bool);
32 extern "C" void SetMallocLeakChecking(bool);
33 
34 __USE_CORTEX_NAMESPACE
35 
36 const char* const		RouteApp::s_settingsDirectory = "Cortex";
37 const char* const		RouteApp::s_settingsFile = "cortex_settings";
38 
39 const char* const		RouteApp::s_appSignature = "application/x-vnd.Cortex.Route";
40 
41 BMimeType						RouteApp::s_nodeSetType("text/x-vnd.Cortex.NodeSet");
42 
43 const char* const		RouteApp::s_rootElement = "cortex_settings";
44 const char* const		RouteApp::s_mediaRoutingViewElement = "MediaRoutingView";
45 const char* const		RouteApp::s_routeWindowElement = "RouteWindow";
46 
47 // -------------------------------------------------------- //
48 // ctor/dtor
49 // -------------------------------------------------------- //
50 
51 RouteApp::~RouteApp() {
52 //	PRINT((
53 //		"RouteApp::~RouteApp()\n"));
54 
55 	ASSERT(manager);
56 	thread_id id = manager->Thread();
57 	manager->release();
58 
59 //	PRINT((
60 //		"- waiting for manager to die\n"));
61 	if(id >= B_OK) {
62 		status_t err;
63 		while(wait_for_thread(id, &err) == B_INTERRUPTED) {
64 			PRINT((" * RouteApp::~RouteApp(): B_INTERRUPTED\n"));
65 		}
66 	}
67 //	PRINT((
68 //		"- RouteApp done.\n"));
69 
70 	// [e.moon 6nov99] kill off the AddOnHost app, if any
71 	AddOnHost::Kill();
72 
73 	if(m_settingsDocType)
74 		delete m_settingsDocType;
75 
76 	if(m_nodeSetDocType)
77 		delete m_nodeSetDocType;
78 }
79 
80 RouteApp::RouteApp() :
81 	BApplication(s_appSignature),
82 	manager(new RouteAppNodeManager(true)),
83 	routeWindow(0),
84 	m_settingsDocType(_createSettingsDocType()),
85 	m_nodeSetDocType(_createNodeSetDocType()),
86 	m_openPanel(B_OPEN_PANEL),
87 	m_savePanel(B_SAVE_PANEL) {
88 
89 	// register MIME type(s)
90 	_InitMimeTypes();
91 
92 	// create the window hierarchy
93 	RouteWindow*& r = const_cast<RouteWindow*&>(routeWindow);
94 	r = new RouteWindow(manager);
95 
96 	// restore settings
97 	_readSettings();
98 
99 	// fit windows to screen
100 	routeWindow->constrainToScreen();
101 
102 	// show main window & palettes
103 	routeWindow->Show();
104 }
105 
106 bool RouteApp::QuitRequested() {
107 
108 	// [e.moon 20oct99] make sure the main window is dead before quitting
109 
110 	// store window positions & other settings
111 	app_info ai;
112 	status_t err = GetAppInfo(&ai);
113 	ASSERT(err == B_OK);
114 
115 	BFile appFile(&ai.ref, B_READ_WRITE);
116 
117 	// write settings file
118 	_writeSettings();
119 
120 	routeWindow->_closePalettes();
121 	routeWindow->Lock();
122 	routeWindow->Quit();
123 	RouteWindow*& r = const_cast<RouteWindow*&>(routeWindow);
124 	r = 0;
125 
126 	// clean up the TipManager [e.moon 19oct99]
127 	TipManager::QuitInstance();
128 
129 	return true;
130 }
131 
132 // -------------------------------------------------------- //
133 // *** BHandler
134 // -------------------------------------------------------- //
135 
136 void RouteApp::MessageReceived(
137 	BMessage*									message) {
138 
139 	status_t err;
140 
141 	entry_ref ref;
142 	const char* name;
143 
144 	switch(message->what) {
145 
146 		case M_SHOW_OPEN_PANEL:
147 			m_openPanel.Show();
148 			break;
149 
150 		case M_SHOW_SAVE_PANEL:
151 			m_savePanel.Show();
152 			break;
153 
154 		case B_SAVE_REQUESTED: {
155 			err = message->FindRef("directory", &ref);
156 			if(err < B_OK)
157 				break;
158 			err = message->FindString("name", &name);
159 			if(err < B_OK)
160 				break;
161 
162 			_writeSelectedNodeSet(&ref, name);
163 
164 			m_savePanel.GetPanelDirectory(&ref);
165 			BEntry e(&ref);
166 			m_lastIODir.SetTo(&e);
167 			break;
168 		}
169 
170 		default:
171 			_inherited::MessageReceived(message);
172 	}
173 }
174 
175 // -------------------------------------------------------- //
176 // *** BApplication
177 // -------------------------------------------------------- //
178 
179 void RouteApp::RefsReceived(
180 	BMessage*									message) {
181 
182 	PRINT(("### RefsReceived\n"));
183 
184 	status_t err;
185 
186 	entry_ref ref;
187 
188 	for(int32 n = 0; ; ++n) {
189 		err = message->FindRef("refs", n, &ref);
190 		if(err < B_OK)
191 			break;
192 
193 		_readNodeSet(&ref);
194 
195 		m_openPanel.GetPanelDirectory(&ref);
196 		BEntry e(&ref);
197 		m_lastIODir.SetTo(&e);
198 	}
199 }
200 
201 // -------------------------------------------------------- //
202 // *** IPersistent
203 // -------------------------------------------------------- //
204 
205 // EXPORT
206 
207 void RouteApp::xmlExportBegin(
208 	ExportContext&						context) const {
209 	context.beginElement(s_rootElement);
210 }
211 
212 void RouteApp::xmlExportAttributes(
213 	ExportContext&						context) const {} //nyi: write version info +++++
214 
215 // +++++
216 void RouteApp::xmlExportContent(
217 	ExportContext&						context) const {
218 
219 	status_t err;
220 	context.beginContent();
221 
222 	// export app settings
223 	{
224 		BMessage m;
225 		exportState(&m);
226 		MessageIO io(&m);
227 		err = context.writeObject(&io);
228 		ASSERT(err == B_OK);
229 	}
230 
231 	if(routeWindow) {
232 		// export main routing window (frame/palette) settings
233 		context.beginElement(s_routeWindowElement);
234 		context.beginContent();
235 		BMessage m;
236 		if (routeWindow->Lock()) {
237 			routeWindow->exportState(&m);
238 			routeWindow->Unlock();
239 		}
240 		MessageIO io(&m);
241 		context.writeObject(&io);
242 		context.endElement();
243 
244 		// export routing view (content) settings
245 		m.MakeEmpty();
246 		ASSERT(routeWindow->m_routingView);
247 		context.beginElement(s_mediaRoutingViewElement);
248 		context.beginContent();
249 		routeWindow->m_routingView->exportState(&m);
250 		context.writeObject(&io);
251 		context.endElement();
252 	}
253 }
254 
255 void RouteApp::xmlExportEnd(
256 	ExportContext&						context) const {
257 	context.endElement();
258 }
259 
260 // IMPORT
261 
262 void RouteApp::xmlImportBegin(
263 	ImportContext&						context) {
264 
265 	m_readState = _READ_ROOT;
266 }
267 
268 void RouteApp::xmlImportAttribute(
269 	const char*								key,
270 	const char*								value,
271 	ImportContext&						context) {} //nyi
272 
273 void RouteApp::xmlImportContent(
274 	const char*								data,
275 	uint32										length,
276 	ImportContext&						context) {} //nyi
277 
278 void RouteApp::xmlImportChild(
279 	IPersistent*							child,
280 	ImportContext&						context) {
281 
282 	MessageIO* io = dynamic_cast<MessageIO*>(child);
283 	if(io) {
284 		ASSERT(io->message());
285 //		PRINT(("* RouteApp::xmlImportChild() [flat message]:\n"));
286 //		io->message()->PrintToStream();
287 
288 		switch(m_readState) {
289 			case _READ_ROOT:
290 				importState(io->message());
291 				break;
292 
293 			case _READ_ROUTE_WINDOW:
294 				ASSERT(routeWindow);
295 				routeWindow->importState(io->message());
296 				break;
297 
298 			case _READ_MEDIA_ROUTING_VIEW:
299 				ASSERT(routeWindow);
300 				ASSERT(routeWindow->m_routingView);
301 				routeWindow->m_routingView->importState(io->message());
302 				break;
303 
304 			default:
305 				PRINT(("! RouteApp::xmlImportChild(): unimplemented target\n"));
306 				break;
307 		}
308 	}
309 }
310 
311 void RouteApp::xmlImportComplete(
312 	ImportContext&						context) {} //nyi
313 
314 void RouteApp::xmlImportChildBegin(
315 	const char*								name,
316 	ImportContext&						context) {
317 
318 	if(m_readState != _READ_ROOT) {
319 		context.reportError("RouteApp import: invalid nested element");
320 		return;
321 	}
322 
323 	if(!strcmp(name, s_routeWindowElement)) {
324 		m_readState = _READ_ROUTE_WINDOW;
325 	}
326 	else if(!strcmp(name, s_mediaRoutingViewElement)) {
327 		m_readState = _READ_MEDIA_ROUTING_VIEW;
328 	}
329 	else {
330 		context.reportError("RouteApp import: unknown child element");
331 	}
332 }
333 
334 void RouteApp::xmlImportChildComplete(
335 	const char*								name,
336 	ImportContext&						context) {
337 
338 	if(m_readState == _READ_ROOT) {
339 		context.reportError("RouteApp import: garbled state");
340 		return;
341 	}
342 	m_readState = _READ_ROOT;
343 }
344 
345 // -------------------------------------------------------- //
346 // *** IStateArchivable
347 // -------------------------------------------------------- //
348 
349 status_t RouteApp::importState(
350 	const BMessage*						archive) {
351 
352 	const char* last;
353 	if(archive->FindString("lastDir", &last) == B_OK) {
354 		m_lastIODir.SetTo(last);
355 		m_openPanel.SetPanelDirectory(last);
356 		m_savePanel.SetPanelDirectory(last);
357 	}
358 
359 	return B_OK;
360 }
361 
362 status_t RouteApp::exportState(
363 	BMessage*									archive) const {
364 
365 	if(m_lastIODir.InitCheck() == B_OK)
366 		archive->AddString("lastDir", m_lastIODir.Path());
367 
368 	return B_OK;
369 }
370 
371 // -------------------------------------------------------- //
372 // implementation
373 // -------------------------------------------------------- //
374 
375 XML::DocumentType* RouteApp::_createSettingsDocType() {
376 
377 	XML::DocumentType* docType = new XML::DocumentType(
378 		s_rootElement);
379 	MessageIO::AddTo(docType);
380 
381 	return docType;
382 }
383 
384 XML::DocumentType* RouteApp::_createNodeSetDocType() {
385 
386 	XML::DocumentType* docType = new XML::DocumentType(
387 		_NODE_SET_ELEMENT);
388 	RouteAppNodeManager::AddTo(docType);
389 
390 	return docType;
391 }
392 
393 status_t RouteApp::_readSettings() {
394 
395 	// figure path
396 	BPath path;
397 	status_t err = find_directory(
398 		B_USER_SETTINGS_DIRECTORY,
399 		&path);
400 	ASSERT(err == B_OK);
401 
402 	path.Append(s_settingsDirectory);
403 	BEntry entry(path.Path());
404 	if(!entry.Exists())
405 		return B_ENTRY_NOT_FOUND;
406 
407 	path.Append(s_settingsFile);
408 	entry.SetTo(path.Path());
409 	if(!entry.Exists())
410 		return B_ENTRY_NOT_FOUND;
411 
412 	// open the settings file
413 	BFile file(&entry, B_READ_ONLY);
414 	if(file.InitCheck() != B_OK)
415 		return file.InitCheck();
416 
417 	// read it:
418 	list<BString> errors;
419 	err = XML::Read(
420 		&file,
421 		this,
422 		m_settingsDocType,
423 		&errors);
424 
425 	if(errors.size()) {
426 		fputs("!!! RouteApp::_readSettings():", stderr);
427 		for(list<BString>::iterator it = errors.begin();
428 			it != errors.end(); ++it)
429 			fputs((*it).String(), stderr);
430 	}
431 	return err;
432 }
433 
434 status_t RouteApp::_writeSettings() {
435 	// figure path, creating settings folder if necessary
436 	BPath path;
437 	status_t err = find_directory(
438 		B_USER_SETTINGS_DIRECTORY,
439 		&path);
440 	ASSERT(err == B_OK);
441 
442 	BDirectory baseDirectory, settingsDirectory;
443 
444 	err = baseDirectory.SetTo(path.Path());
445 	if(err < B_OK)
446 		return err;
447 
448 	path.Append(s_settingsDirectory);
449 
450 	BEntry folderEntry(path.Path());
451 	if(!folderEntry.Exists()) {
452 		// create folder
453 		err = baseDirectory.CreateDirectory(s_settingsDirectory, &settingsDirectory);
454 		ASSERT(err == B_OK);
455 	}
456 	else
457 		settingsDirectory.SetTo(&folderEntry);
458 
459 	// open/clobber file
460 	BFile file(
461 		&settingsDirectory,
462 		s_settingsFile,
463 		B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
464 	err = file.InitCheck();
465 	if(err < B_OK)
466 		return err;
467 
468 	// write document header
469 	const char* header = "<?xml version=\"1.0\"?>\n";
470 	file.Write((const void*)header, strlen(header));
471 
472 	// write content
473 	BString errorText;
474 	err = XML::Write(
475 		&file,
476 		this,
477 		&errorText);
478 
479 	if(err < B_OK) {
480 		fprintf(stderr,
481 			"!!! RouteApp::_writeSettings() failed: %s\n",
482 			errorText.String());
483 	}
484 
485 	return err;
486 }
487 
488 // -------------------------------------------------------- //
489 
490 class _RouteAppImportContext :
491 	public	ImportContext,
492 	public	NodeSetIOContext {
493 
494 public:
495 	_RouteAppImportContext(
496 		list<BString>&							errors,
497 		MediaRoutingView*						routingView) :
498 		ImportContext(errors),
499 		m_routingView(routingView) {}
500 
501 public:													// *** hooks
502 	virtual void importUIState(
503 		const BMessage*							archive) {
504 
505 		PRINT((
506 			"### importUIState\n"));
507 
508 		if(m_routingView) {
509 //			m_routingView->LockLooper();
510 			m_routingView->deselectAll();
511 			status_t err = m_routingView->importStateFor(
512 				this,
513 				archive);
514 			if(err < B_OK) {
515 				PRINT((
516 					"!!! _RouteAppImportContext::importStateFor() failed:\n"
517 					"    %s\n", strerror(err)));
518 			}
519 			m_routingView->Invalidate(); // +++++ not particularly clean
520 //			m_routingView->UnlockLooper();
521 		}
522 	}
523 
524 	MediaRoutingView*							m_routingView;
525 };
526 
527 status_t RouteApp::_readNodeSet(
528 	entry_ref*								ref) {
529 
530 	BFile file(ref, B_READ_ONLY);
531 	status_t err = file.InitCheck();
532 	if(err < B_OK)
533 		return err;
534 
535 	routeWindow->Lock();
536 
537 	list<BString> errors;
538 
539 	err = XML::Read(
540 		&file,
541 		manager,
542 		m_nodeSetDocType,
543 		new _RouteAppImportContext(errors, routeWindow->m_routingView));
544 
545 	routeWindow->Unlock();
546 
547 	if(errors.size()) {
548 		fputs("!!! RouteApp::_readNodeSet():", stderr);
549 		for(list<BString>::iterator it = errors.begin();
550 			it != errors.end(); ++it)
551 			fputs((*it).String(), stderr);
552 	}
553 	return err;
554 }
555 
556 // -------------------------------------------------------- //
557 
558 class _RouteAppExportContext :
559 	public	ExportContext,
560 	public	NodeSetIOContext {
561 
562 public:
563 	_RouteAppExportContext(
564 		MediaRoutingView*						routingView) :
565 		m_routingView(routingView) {}
566 
567 public:													// *** hooks
568 	virtual void exportUIState(
569 		BMessage*										archive) {
570 
571 		PRINT((
572 			"### exportUIState\n"));
573 
574 		if(m_routingView) {
575 			m_routingView->LockLooper();
576 			m_routingView->exportStateFor(
577 				this,
578 				archive);
579 			m_routingView->UnlockLooper();
580 		}
581 	}
582 
583 	MediaRoutingView*							m_routingView;
584 };
585 
586 status_t RouteApp::_writeSelectedNodeSet(
587 	entry_ref*								dirRef,
588 	const char*								filename) {
589 
590 	status_t err;
591 
592 
593 	// sanity-check & fetch the selection
594 	routeWindow->Lock();
595 
596 	MediaRoutingView* v = routeWindow->m_routingView;
597 	ASSERT(v);
598 
599 	if(
600 		v->countSelectedItems() < 0 ||
601 		v->selectedType() != DiagramItem::M_BOX) {
602 		PRINT((
603 			"!!! RouteApp::_writeSelectedNodeSet():\n"
604 			"    Invalid selection!\n"));
605 
606 		routeWindow->Unlock();
607 		return B_NOT_ALLOWED;
608 	}
609 
610 	_RouteAppExportContext context(v);
611 
612 	for(uint32 i = 0; i < v->countSelectedItems(); ++i) {
613 		MediaNodePanel* panel = dynamic_cast<MediaNodePanel*>(v->selectedItemAt(i));
614 		if(!panel)
615 			continue;
616 		err = context.addNode(panel->ref->id());
617 		if(err < B_OK) {
618 			PRINT((
619 				"!!! context.addNode() failed: '%s\n", strerror(err)));
620 		}
621 	}
622 	routeWindow->Unlock();
623 
624 	// open/clobber file
625 	BDirectory dir(dirRef);
626 	err = dir.InitCheck();
627 	if(err < B_OK)
628 		return err;
629 
630 	BFile file(
631 		&dir,
632 		filename,
633 		B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
634 	err = file.InitCheck();
635 	if(err < B_OK)
636 		return err;
637 
638 	// write document header
639 	const char* header = "<?xml version=\"1.0\"?>\n";
640 	file.Write((const void*)header, strlen(header));
641 
642 	// export nodes
643 	context.stream = &file;
644 	err = context.writeObject(manager);
645 	if(err < B_OK) {
646 		PRINT((
647 			"!!! RouteApp::_writeSelectedNodeSet(): error:\n"
648 			"    %s\n", context.errorText()));
649 
650 		// +++++ delete the malformed file
651 
652 	}
653 
654 
655 	// write MIME type
656 	BNodeInfo* fileInfo = new BNodeInfo(&file);
657 	fileInfo->SetType(s_nodeSetType.Type());
658 	fileInfo->SetPreferredApp(s_appSignature);
659 	delete fileInfo;
660 
661 	return B_OK;
662 }
663 
664 /*static*/
665 status_t RouteApp::_InitMimeTypes() {
666 
667 	status_t err;
668 
669 	ASSERT(s_nodeSetType.IsValid());
670 
671 	if(!s_nodeSetType.IsInstalled()) {
672 		err = s_nodeSetType.Install();
673 		if(err < B_OK) {
674 			PRINT((
675 				"!!! RouteApp::_InitMimeTypes(): Install():\n"
676 				"    %s\n", strerror(err)));
677 			return err;
678 		}
679 
680 		err = s_nodeSetType.SetPreferredApp(s_appSignature);
681 		if(err < B_OK) {
682 			PRINT((
683 				"!!! RouteApp::_InitMimeTypes(): SetPreferredApp():\n"
684 				"    %s\n", strerror(err)));
685 			return err;
686 		}
687 	}
688 
689 	return B_OK;
690 }
691 
692 // -------------------------------------------------------- //
693 // main() stub
694 // -------------------------------------------------------- //
695 
696 int main(int argc, char** argv) {
697 //	SetNewLeakChecking(true);
698 //	SetMallocLeakChecking(true);
699 
700 	RouteApp app;
701 	app.Run();
702 
703 	return 0;
704 }
705 
706 // END -- RouteApp.cpp --
707