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