[med-svn] [SCM] aghermann branch, master, updated. 4b06a66467a52311d413e817136ece62c0e9c24a

Andrei Zavada johnhommer at gmail.com
Sun Jul 21 19:56:38 UTC 2013


The following commit has been merged in the master branch:
commit 4b06a66467a52311d413e817136ece62c0e9c24a
Author: Andrei Zavada <johnhommer at gmail.com>
Date:   Sun Jul 21 19:01:35 2013 +0300

    support tsv files in aghermann, too

diff --git a/ChangeLog b/ChangeLog
index 2a2df4a..e56dab1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,9 +1,14 @@
-v.1.0 (2013-xx-xx)
+v.0.9.1 (2013-xx-xx)
 	* Reorg source tree around main executable, libs, & tools.
-	* Properly use libsigfile.so.
+	* Properly use our own lib*.so (previously aghermann turned out
+	  static despite libsigfile.so being installed).
 	* Plug a memory leak after early unique_ptr acquisition.
 	* SF: Move selection on montage (with Alt).
+	* SF: Artifact Detection dialog will now not get confused after
+	  clicking outside it on other channels.
 	* New tool agh-profile-gen, a standalone profile generator.
+	* ascii (tsv) format support.
+	* Score assistant now uses delta and theta ranges as defined by user.
 
 v.0.9.0.4 (2013-05-18)
 	* Remove stray AC_CHECK_FUNC(mremap).
diff --git a/data/mw-dialogs.glade b/data/mw-dialogs.glade
index 5e30fe4..c9bbe31 100644
--- a/data/mw-dialogs.glade
+++ b/data/mw-dialogs.glade
@@ -1053,6 +1053,7 @@ With bug reports, either send yours to <a href="mailto:aghermann-users at lists.
     <property name="width_request">500</property>
     <property name="can_focus">False</property>
     <property name="border_width">10</property>
+    <property name="title" translatable="yes">Import EEG source</property>
     <property name="icon">aghermann.png</property>
     <property name="type_hint">normal</property>
     <signal name="close" handler="gtk_widget_hide" swapped="no"/>
@@ -1133,7 +1134,6 @@ With bug reports, either send yours to <a href="mailto:aghermann-users at lists.
                 <property name="xalign">0</property>
                 <property name="xpad">5</property>
                 <property name="ypad">8</property>
-                <property name="label" translatable="yes">(Identify new EDF source)</property>
                 <property name="use_markup">True</property>
                 <property name="ellipsize">middle</property>
               </object>
@@ -1439,7 +1439,7 @@ With bug reports, either send yours to <a href="mailto:aghermann-users at lists.
                   <object class="GtkLabel" id="label14">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
-                    <property name="label" translatable="yes">EDF info</property>
+                    <property name="label" translatable="yes">Details</property>
                   </object>
                   <packing>
                     <property name="position">1</property>
@@ -2524,6 +2524,7 @@ With bug reports, either send yours to <a href="mailto:aghermann-users at lists.
                 <property name="margin_top">5</property>
                 <property name="margin_bottom">5</property>
                 <property name="editable">False</property>
+                <property name="input_hints">GTK_INPUT_HINT_NO_SPELLCHECK | GTK_INPUT_HINT_NONE</property>
               </object>
             </child>
           </object>
diff --git a/src/aghermann/expdesign/primaries.hh b/src/aghermann/expdesign/primaries.hh
index 80fbf6c..418b131 100644
--- a/src/aghermann/expdesign/primaries.hh
+++ b/src/aghermann/expdesign/primaries.hh
@@ -322,7 +322,7 @@ class CExpDesign {
 	string error_log_serialize() const;
 	size_t error_log_n_messages() const
 		{ return _error_log.size(); }
-	void log_message( const char* fmt, ...);
+	void log_message( const char* fmt, ...)  __attribute__ (( format (printf, 2, 3) ));
 
       // contains
 	typedef map<string, CJGroup> TJGroups;
diff --git a/src/aghermann/expdesign/tree-scanner.cc b/src/aghermann/expdesign/tree-scanner.cc
index 30e4f62..56cc52d 100644
--- a/src/aghermann/expdesign/tree-scanner.cc
+++ b/src/aghermann/expdesign/tree-scanner.cc
@@ -142,8 +142,12 @@ register_intree_source( sigfile::CTypedSource&& F,
 		list<string>::iterator pe = broken_path.begin();
 		string& g_name = (pe = next(pe), *pe),
 			j_name = (pe = next(pe), *pe),
-			d_name = (pe = next(pe), *pe),
-			e_name = fs::make_fname_base(*next(pe), ".edf", false);
+			d_name = (pe = next(pe), *pe);
+		string	e_name =
+			fs::make_fname_base(
+				*next(pe),
+				sigfile::supported_sigfile_extensions,
+				agh::fs::TMakeFnameOption::normal);
 		// take care of the case of episode-2.edf
 		{
 			auto subf = agh::str::tokens_trimmed(e_name, "-");
@@ -159,15 +163,17 @@ register_intree_source( sigfile::CTypedSource&& F,
 
 		// refuse to register sources of wrong subjects
 		if ( j_name != F().subject().id ) {
-			log_message( "%s: file belongs to subject %s (\"%s\"), is misplaced here under subject \"%s\"",
-				     F().filename(), F().subject().id.c_str(), F().subject().name.c_str(), j_name.c_str());
+			log_message( "$$%s:", F().filename());
+			log_message( "file belongs to subject %s (\"%s\"), is misplaced here under subject \"%s\"",
+				     F().subject().id.c_str(), F().subject().name.c_str(), j_name.c_str());
 			return -1;
 		}
 		try {
 			auto existing_group = group_of( F().subject().id.c_str());
 			if ( g_name != existing_group ) {
-				log_message( "%s: subject %s (\"%s\") belongs to a different group (\"%s\")",
-					     F().filename(), F().subject().id.c_str(), F().subject().name.c_str(), existing_group);
+				log_message( "$$%s:", F().filename());
+				log_message( "subject %s (\"%s\") belongs to a different group (\"%s\")",
+					     F().subject().id.c_str(), F().subject().name.c_str(), existing_group);
 				return -1;
 			}
 		} catch (invalid_argument) {
@@ -176,13 +182,14 @@ register_intree_source( sigfile::CTypedSource&& F,
 
 		// but correct session/episode fields
 		if ( d_name != F().session() ) {
-			log_message( "%s: correcting embedded session \"%s\" to match placement in the tree (\"%s\")",
-				     F().filename(), F().session(), d_name.c_str());
+			log_message( "$$%s:", F().filename());
+			log_message( "correcting embedded session \"%s\" to match placement in the tree (\"%s\")",
+				     F().session(), d_name.c_str());
 			F().set_session( d_name.c_str());
 		}
 		if ( e_name != F().episode() ) {
-			log_message( "%s: correcting embedded episode \"%s\" to match file name",
-				     F().filename(), F().episode());
+			log_message( "correcting embedded episode \"%s\" to match file name",
+				     F().episode());
 			F().set_episode( e_name.c_str());
 		}
 
@@ -225,7 +232,7 @@ register_intree_source( sigfile::CTypedSource&& F,
 		}
 
 	} catch (invalid_argument ex) {
-		log_message( ex.what());
+		log_message( "%s", ex.what());
 		if ( reason_if_failed_p )
 			*reason_if_failed_p = ex.what();
 		return -1;
@@ -252,12 +259,12 @@ is_supported_source( sigfile::CTypedSource& F)
 		CTSVFile::TSubtype t;
 	} u;
 	return (F.type() == CTypedSource::TType::edf and
-		(u.e = static_cast<CEDFFile*>(&F()) -> subtype(),
+		(u.e = F.obj<CEDFFile>().subtype(),
 		 (u.e == CEDFFile::TSubtype::edf ||
 		  u.e == CEDFFile::TSubtype::edfplus_c)))
 		or
 		(F.type() == CTypedSource::TType::ascii and
-		 (u.t = static_cast<CTSVFile*>(&F()) -> subtype(),
+		 (u.t = F.obj<CTSVFile>().subtype(),
 		  (u.t == CTSVFile::TSubtype::csv ||
 		   u.t == CTSVFile::TSubtype::tsv)));
 }
@@ -265,39 +272,39 @@ is_supported_source( sigfile::CTypedSource& F)
 namespace {
 
 size_t
-	__cur_edf_file;
+	current_sigfile_source;
 agh::CExpDesign
-	*__expdesign;
+	*only_expdesign;
 
 agh::CExpDesign::TMsmtCollectProgressIndicatorFun
 	only_progress_fun;
 
 int
-edf_file_processor( const char *fname, const struct stat*, int flag, struct FTW *ftw)
+supported_sigfile_processor( const char *fname, const struct stat*, int flag, struct FTW *ftw)
 {
 	if ( flag == FTW_F && ftw->level == 4 ) {
 		int fnlen = strlen(fname); // - ftw->base;
 		if ( fnlen < 5 )
 			return 0;
-		if ( strcasecmp( &fname[fnlen-4], ".edf") == 0 ) {
-			++__cur_edf_file;
-			only_progress_fun( fname, agh::fs::__n_edf_files, __cur_edf_file);
+		if ( sigfile::is_fname_ext_supported( fname) ) {
+			++current_sigfile_source;
+			only_progress_fun( fname, agh::fs::total_supported_sigfiles, current_sigfile_source);
 			try {
 				using namespace sigfile;
-				CTypedSource F {fname, (size_t)roundf(__expdesign->fft_params.pagesize)};
+				CTypedSource F {fname, (size_t)roundf(only_expdesign->fft_params.pagesize)};
 				string st = F().explain_status();
 				if ( not st.empty() ) {
-					__expdesign->log_message( "$$%s:", fname);
-					__expdesign->log_message( "%s", st.c_str());
+					only_expdesign->log_message( "$$%s:", fname);
+					only_expdesign->log_message( "%s", st.c_str());
 				}
 				// we only support edf and edfplus/edf_c
 				if ( agh::CExpDesign::is_supported_source(F) )
-					__expdesign -> register_intree_source( move(F));
+					only_expdesign -> register_intree_source( move(F));
 				else
-					__expdesign -> log_message( "File %s: unsupported format", fname);
+					only_expdesign -> log_message( "File %s: unsupported format", fname);
 
 			} catch ( invalid_argument ex) {
-				__expdesign->log_message(ex.what());
+				only_expdesign->log_message( "%s", ex.what());
 			}
 		}
 	}
@@ -335,17 +342,17 @@ scan_tree( TMsmtCollectProgressIndicatorFun user_progress_fun)
 	groups.clear();
 
       // glob it!
-	agh::fs::__n_edf_files = 0;
-	nftw( "./", agh::fs::edf_file_counter, 20, 0);
+	agh::fs::total_supported_sigfiles = 0;
+	nftw( "./", agh::fs::supported_sigfile_counter, 20, 0);
 	printf( "CExpDesign::scan_tree(\"%s\"): %zu edf file(s) found\n",
-		session_dir().c_str(), agh::fs::__n_edf_files);
-	if ( agh::fs::__n_edf_files == 0 )
+		session_dir().c_str(), agh::fs::total_supported_sigfiles);
+	if ( agh::fs::total_supported_sigfiles == 0 )
 		return;
 
-	__cur_edf_file = 0;
+	current_sigfile_source = 0;
 	only_progress_fun = user_progress_fun;
-	__expdesign = this;
-	nftw( "./", edf_file_processor, 10, 0);
+	only_expdesign = this;
+	nftw( "./", supported_sigfile_processor, 10, 0);
 	printf( "CExpDesign::scan_tree(): recordings collected\n");
 
 	compute_profiles(); // in an SMP fashion
diff --git a/src/aghermann/ui/mw/admit-one.cc b/src/aghermann/ui/mw/admit-one.cc
index 5b94768..e2f29b9 100644
--- a/src/aghermann/ui/mw/admit-one.cc
+++ b/src/aghermann/ui/mw/admit-one.cc
@@ -22,153 +22,181 @@ int
 aghui::SExpDesignUI::
 dnd_maybe_admit_one( const char* fname)
 {
-	using namespace sigfile;
-	CTypedSource *Fp = nullptr;
-
-	string info;
 	try {
-		Fp = new CTypedSource (fname, ED->fft_params.pagesize);
-		switch ( Fp->type() ) {
-		case CTypedSource::TType::edf:
+		string info;
+		sigfile::CTypedSource F_ (fname, ED->fft_params.pagesize);
+		switch ( F_.type() ) {
+		case sigfile::CTypedSource::TType::edf:
 		{
-			CEDFFile& F = *static_cast<CEDFFile*> (&(*Fp)());
-			if ( F.subtype() == CEDFFile::TSubtype::edfplus_d ) {
+			sigfile::CEDFFile& F = F_.obj<sigfile::CEDFFile>();
+			if ( F.subtype() == sigfile::CEDFFile::TSubtype::edfplus_d ) {
 				pop_ok_message(
-					wMainWindow, "EDF+D Unsupported", "The file <b>%s</b> is in EDF+D format, which is not supported yet",
+					wMainWindow,
+					"EDF+D is unsupported",
+					"The file <b>%s</b> is in EDF+D format, which is not supported yet",
 					fname);
 				return 0;
 			}
-			if ( F.status() & CEDFFile::TStatus::inoperable ) {
+			if ( F.status() & sigfile::CEDFFile::TStatus::inoperable ) {
 				pop_ok_message(
-					wMainWindow, "Bad EDF file", "The file <b>%s</b> cannot be processed due to these issues:\n\n%s",
+					wMainWindow,
+					"Bad EDF file",
+					"The file <b>%s</b> cannot be processed due to these issues:\n\n%s",
 					fname, F.explain_status().c_str());
 				return 0;
 			}
+		}
+		break;
+
+		case sigfile::CTypedSource::TType::ascii:
+		break;
+
+		default:
+			pop_ok_message(
+				wMainWindow,
+				"Unsupported format",
+				"The file <b>%s</b> is in unrecognised format. Sorry.", fname);
+			return 0;
+		}
+
+		auto& F = F_();
+
+		info = F.details( 0|sigfile::CSource::TDetails::with_channels);
 
-			info = (*Fp)().details( 0|sigfile::CEDFFile::with_channels);
+		{
+			char* mike;
+			mike = g_markup_escape_text(
+				agh::str::homedir2tilda( F.filename()).c_str(),
+				-1);
 			gtk_label_set_markup(
 				lEdfImportCaption,
-				(snprintf_buf( "File: <i>%s</i>", fname),
-				 __buf__));
+				snprintf_buf( "File: <i>%s</i>", mike));
+			free( (void*)mike);
+
+			mike = g_markup_escape_text(
+				F.subject().name.empty() ? "<no name>" : F.subject().name.c_str(),
+				-1);
 			gtk_label_set_markup(
 				lEdfImportSubject,
-				(snprintf_buf( "<b>%s</b> (%s)", F.subject().id.c_str(), F.subject().name.c_str()),
-				 __buf__));
-		}
-		break;
-		default:
-			pop_ok_message( wMainWindow, "Unsupported format", "The file <b>%s</b> is in unrecognised format. Sorry.", fname);
-			return 0;
+				snprintf_buf(
+					"<b>%s</b> (%s)",
+					F.subject().id.c_str(),
+					mike));
+			free( (void*)mike);
 		}
 
-	} catch ( exception& ex) {
-		pop_ok_message( wMainWindow, "Corrupted EDF file", "File <b>%s</b> doesn't appear to have a valid header:\n\n%s",
-				fname, (*Fp)().explain_status().c_str());
-		return 0;
-	}
-	gtk_text_buffer_set_text( tEDFFileDetailsReport, info.c_str(), -1);
-
-	GtkTreeIter iter;
-      // populate and attach models
-	GtkListStore
-		*m_groups = gtk_list_store_new( 1, G_TYPE_STRING),
-		*m_episodes = gtk_list_store_new( 1, G_TYPE_STRING),
-		*m_sessions = gtk_list_store_new( 1, G_TYPE_STRING);
-      // when adding a source for an already existing subject, disallow group selection
-	try {
-		gtk_entry_set_text(
-			eEdfImportGroupEntry,
-			ED->group_of( (*Fp)().subject().id.c_str()));
-		gtk_widget_set_sensitive( (GtkWidget*)eEdfImportGroup, FALSE);
-	} catch (invalid_argument ex) {
-		for ( auto &i : AghGG ) {
-			gtk_list_store_append( m_groups, &iter);
-			gtk_list_store_set( m_groups, &iter, 0, i.c_str(), -1);
+		gtk_text_buffer_set_text( tEDFFileDetailsReport, info.c_str(), -1);
+
+		GtkTreeIter iter;
+		// populate and attach models
+		GtkListStore
+			*m_groups = gtk_list_store_new( 1, G_TYPE_STRING),
+			*m_episodes = gtk_list_store_new( 1, G_TYPE_STRING),
+			*m_sessions = gtk_list_store_new( 1, G_TYPE_STRING);
+		// when adding a source for an already existing subject, disallow group selection
+		try {
+			gtk_entry_set_text(
+				eEdfImportGroupEntry,
+				ED->group_of( F.subject().id.c_str()));
+			gtk_widget_set_sensitive( (GtkWidget*)eEdfImportGroup, FALSE);
+		} catch (invalid_argument ex) {
+			for ( auto &i : AghGG ) {
+				gtk_list_store_append( m_groups, &iter);
+				gtk_list_store_set( m_groups, &iter, 0, i.c_str(), -1);
+			}
+			gtk_combo_box_set_model(
+				eEdfImportGroup,
+				(GtkTreeModel*)m_groups);
+			gtk_combo_box_set_entry_text_column( eEdfImportGroup, 0);
+			// gtk_entry_set_text(
+			// 	(GtkEntry*)gtk_bin_get_child( (GtkBin*)eEdfImportGroup),
+			// 	"");
+			gtk_widget_set_sensitive( (GtkWidget*)eEdfImportGroup, TRUE);
 		}
-		gtk_combo_box_set_model( eEdfImportGroup,
-					 (GtkTreeModel*)m_groups);
-		gtk_combo_box_set_entry_text_column( eEdfImportGroup, 0);
-		// gtk_entry_set_text(
-		// 	(GtkEntry*)gtk_bin_get_child( (GtkBin*)eEdfImportGroup),
-		// 	"");
-		gtk_widget_set_sensitive( (GtkWidget*)eEdfImportGroup, TRUE);
-	}
 
-	for ( auto &i : AghEE ) {
-		gtk_list_store_append( m_episodes, &iter);
-		gtk_list_store_set( m_episodes, &iter, 0, i.c_str(), -1);
-	}
-	gtk_combo_box_set_model( eEdfImportEpisode,
-				 (GtkTreeModel*)m_episodes);
-	gtk_combo_box_set_entry_text_column( eEdfImportEpisode, 0);
+		// enumerate known sessions and episodes
+		// suggest those from the file proper
+		for ( auto &i : AghEE ) {
+			gtk_list_store_append( m_episodes, &iter);
+			gtk_list_store_set( m_episodes, &iter, 0, i.c_str(), -1);
+		}
+		gtk_combo_box_set_model(
+			eEdfImportEpisode,
+			(GtkTreeModel*)m_episodes);
+		gtk_combo_box_set_entry_text_column(
+			eEdfImportEpisode, 0);
+		gtk_entry_set_text(
+			(GtkEntry*)gtk_bin_get_child( (GtkBin*)eEdfImportEpisode),
+			F.episode());
 
-	for ( auto &i : AghDD ) {
-		gtk_list_store_append( m_sessions, &iter);
-		gtk_list_store_set( m_sessions, &iter, 0, i.c_str(), -1);
-	}
-	gtk_combo_box_set_model( eEdfImportSession,
-				 (GtkTreeModel*)m_sessions);
-	gtk_combo_box_set_entry_text_column( eEdfImportSession, 0);
-
-      // guess episode from fname
-	char *fname2 = g_strdup( fname), *episode = strrchr( fname2, '/')+1;
-	if ( g_str_has_suffix( episode, ".edf") || g_str_has_suffix( episode, ".EDF") )
-		*strrchr( episode, '.') = '\0';
-	gtk_entry_set_text( (GtkEntry*)gtk_bin_get_child( (GtkBin*)eEdfImportEpisode),
-			    episode);
-
-      // display
-	g_signal_emit_by_name( eEdfImportGroupEntry, "changed");
-
-	gint response = gtk_dialog_run( (GtkDialog*)wEdfImport);
-	const gchar
-		*selected_group   = gtk_entry_get_text( eEdfImportGroupEntry),
-		*selected_session = gtk_entry_get_text( eEdfImportSessionEntry),
-		*selected_episode = gtk_entry_get_text( eEdfImportEpisodeEntry);
-	switch ( response ) {
-	case GTK_RESPONSE_OK: // Admit
-	{
-		char *dest_path, *dest, *cmd;
-		dest_path = g_strdup_printf( "%s/%s/%s/%s",
-					     ED->session_dir().c_str(),
-					     selected_group,
-					     (*Fp)().subject().id.c_str(),
-					     selected_session);
-		dest = g_strdup_printf( "%s/%s.edf",
-					dest_path,
-					selected_episode);
-		if ( gtk_toggle_button_get_active( (GtkToggleButton*)bEdfImportAttachCopy) )
-			cmd = g_strdup_printf( "mkdir -p '%s' && cp -n '%s' '%s'", dest_path, fname, dest);
-		else if ( gtk_toggle_button_get_active( (GtkToggleButton*)bEdfImportAttachMove) )
-			cmd = g_strdup_printf( "mkdir -p '%s' && mv -n '%s' '%s'", dest_path, fname, dest);
-		else
-			cmd = g_strdup_printf( "mkdir -p '%s' && ln -s '%s' '%s'", dest_path, fname, dest);
-		char* cmde = g_markup_escape_text( cmd, -1);
-
-		int cmd_exit = system( cmd);
-		if ( cmd_exit )
-			pop_ok_message( wMainWindow,
+		for ( auto &i : AghDD ) {
+			gtk_list_store_append( m_sessions, &iter);
+			gtk_list_store_set( m_sessions, &iter, 0, i.c_str(), -1);
+		}
+		gtk_combo_box_set_model(
+			eEdfImportSession,
+			(GtkTreeModel*)m_sessions);
+		gtk_combo_box_set_entry_text_column(
+			eEdfImportSession, 0);
+		gtk_entry_set_text(
+			(GtkEntry*)gtk_bin_get_child( (GtkBin*)eEdfImportSession),
+			F.session());
+
+		// display
+		g_signal_emit_by_name( eEdfImportGroupEntry, "changed");
+
+		gint response = gtk_dialog_run( (GtkDialog*)wEdfImport);
+		const gchar
+			*selected_group   = gtk_entry_get_text( eEdfImportGroupEntry),
+			*selected_session = gtk_entry_get_text( eEdfImportSessionEntry),
+			*selected_episode = gtk_entry_get_text( eEdfImportEpisodeEntry);
+		switch ( response ) {
+		case GTK_RESPONSE_OK: // Admit
+		{
+			string dest_path, dest, cmd;
+			using agh::str::sasprintf;
+			dest_path = sasprintf(
+				"%s/%s/%s/%s",
+				ED->session_dir().c_str(), selected_group,
+				F.subject().id.c_str(), selected_session);
+			dest = sasprintf(
+				"%s/%s.edf",
+				dest_path.c_str(), selected_episode);
+			if ( gtk_toggle_button_get_active( (GtkToggleButton*)bEdfImportAttachCopy) )
+				cmd = sasprintf( "mkdir -p '%s' && cp -n '%s' '%s'", dest_path.c_str(), fname, dest.c_str());
+			else if ( gtk_toggle_button_get_active( (GtkToggleButton*)bEdfImportAttachMove) )
+				cmd = sasprintf( "mkdir -p '%s' && mv -n '%s' '%s'", dest_path.c_str(), fname, dest.c_str());
+			else
+				cmd = sasprintf( "mkdir -p '%s' && ln -s '%s' '%s'", dest_path.c_str(), fname, dest.c_str());
+			char* cmde = g_markup_escape_text( cmd.c_str(), -1);
+
+			int cmd_exit = system( cmd.c_str());
+			if ( cmd_exit )
+				pop_ok_message(
+					wMainWindow,
 					"Failed to create recording path in experiment tree",
-					"Command\n <span font=\"monospace\">%s</span>\nexited with code %d", cmde, cmd_exit);
-
-		g_free( cmd);
-		g_free( cmde);
-		g_free( dest);
-		g_free( dest_path);
-	}
-	    break;
-	case GTK_RESPONSE_CANCEL: // Drop
+					"Command\n <span font=\"monospace\">%s</span>\nexited with code %d",
+					cmde, cmd_exit);
+			g_free( cmde);
+		}
 		break;
-	}
+		case GTK_RESPONSE_CANCEL: // Drop
+			break;
+		}
 
-      // finalise
-	g_free( fname2);
+		g_object_unref( m_groups);
+		g_object_unref( m_sessions);
+		g_object_unref( m_episodes);
 
-	g_object_unref( m_groups);
-	g_object_unref( m_sessions);
-	g_object_unref( m_episodes);
+		return 0;
 
-	return 0;
+	} catch ( exception& ex) {
+		pop_ok_message(
+			wMainWindow,
+			"Corrupted source file", "File <b>%s</b> could not be processed.",
+			fname);
+		return 0;
+	}
 }
 
 
diff --git a/src/aghermann/ui/sf/sf.cc b/src/aghermann/ui/sf/sf.cc
index df281df..defd566 100644
--- a/src/aghermann/ui/sf/sf.cc
+++ b/src/aghermann/ui/sf/sf.cc
@@ -671,7 +671,11 @@ aghui::SScoringFacility::
 load_montage()
 {
 	libconfig::Config conf;
-	string montage_file = (agh::fs::make_fname_base( channels.front().crecording.F().filename(), ".edf", true) + ".montage");
+	string montage_file =
+		agh::fs::make_fname_base(
+			channels.front().crecording.F().filename(),
+			sigfile::supported_sigfile_extensions,
+			agh::fs::TMakeFnameOption::hidden) + ".montage";
 	try {
 		conf.readFile (montage_file.c_str());
 	} catch (libconfig::ParseException ex) {
@@ -721,7 +725,12 @@ save_montage()
 		agh::confval::put( h.config_keys_g, conf);
 	}
 	try {
-		conf.writeFile ((agh::fs::make_fname_base( channels.front().crecording.F().filename(), ".edf", true) + ".montage").c_str());
+		conf.writeFile (
+			(agh::fs::make_fname_base(
+				channels.front().crecording.F().filename(),
+				sigfile::supported_sigfile_extensions,
+				agh::fs::TMakeFnameOption::hidden)
+			 + ".montage").c_str() );
 	} catch (...) {
 		;
 	}
diff --git a/src/aghermann/ui/sm/sm.cc b/src/aghermann/ui/sm/sm.cc
index 7aee9f2..74c7949 100644
--- a/src/aghermann/ui/sm/sm.cc
+++ b/src/aghermann/ui/sm/sm.cc
@@ -30,10 +30,10 @@ void
 aghui::SSession::
 get_session_stats()
 {
-	agh::fs::__n_edf_files = 0;
+	agh::fs::total_supported_sigfiles = 0;
 	string path = agh::str::tilda2homedir(c_str());
-	nftw( path.c_str(), agh::fs::edf_file_counter, 20, 0);
-	n_recordings = agh::fs::__n_edf_files;
+	nftw( path.c_str(), agh::fs::supported_sigfile_counter, 20, 0);
+	n_recordings = agh::fs::total_supported_sigfiles;
 
 	{
 		struct stat stat0;
diff --git a/src/common/fs.hh b/src/common/fs.hh
index faa2ca3..979bcfa 100644
--- a/src/common/fs.hh
+++ b/src/common/fs.hh
@@ -27,32 +27,18 @@ using namespace std;
 namespace agh {
 namespace fs {
 
-template<class T>
+enum class TMakeFnameOption { normal, hidden };
 string
-make_fname_base( const T& _filename, const char *suffix, bool hidden)
-{
-	string	fname_ (_filename);
-	auto	slen = strlen( suffix);
-	if ( fname_.size() > slen && strcasecmp( &fname_[fname_.size()-slen], suffix) == 0 )
-		fname_.erase( fname_.size()-slen, slen);
-	if ( hidden ) {
-		size_t slash_at = fname_.rfind('/');
-		if ( slash_at < fname_.size() )
-			fname_.insert( slash_at+1, ".");
-	}
-	return fname_;
-}
+make_fname_base( const string& fname_, const string& suffices, TMakeFnameOption);
 
-template<class T>
-list<string>
-path_elements( const T& _filename)
+inline list<string>
+path_elements( const string& _filename)
 {
 	return agh::str::tokens( _filename, "/");
 }
 
-template<class T>
-string
-dirname( const T& _filename)
+inline string
+dirname( const string& _filename)
 {
 	string pre = (_filename[0] == '/') ? "/" : "";
 	auto ee = agh::str::tokens( _filename, "/");
@@ -64,11 +50,9 @@ dirname( const T& _filename)
 
 
 
-template<class T>
-bool
-exists_and_is_writable( const T& _dir)
+inline bool
+exists_and_is_writable( const string& dir)
 {
-	string dir (_dir);
 	struct stat attr;
 	return stat( dir.c_str(), &attr) == 0 &&
 		S_ISDIR (attr.st_mode) &&
@@ -77,14 +61,13 @@ exists_and_is_writable( const T& _dir)
 }
 
 
-template<class T>
-int
-mkdir_with_parents( const T& dir)
+inline int
+mkdir_with_parents( const string& dir)
 {
 	return system(
 		agh::str::sasprintf(
 			"mkdir -p '%s'",
-			string (dir).c_str())
+			dir.c_str())
 		.c_str());
 }
 
@@ -94,8 +77,8 @@ mkdir_with_parents( const T& dir)
 
 
 // this is another global
-int edf_file_counter( const char *fname, const struct stat*, int flag, struct FTW *ftw);
-extern size_t __n_edf_files;
+int supported_sigfile_counter( const char *fname, const struct stat*, int flag, struct FTW *ftw);
+extern size_t total_supported_sigfiles;
 
 
 
diff --git a/src/common/libcommon.cc b/src/common/libcommon.cc
index 1120884..f499057 100644
--- a/src/common/libcommon.cc
+++ b/src/common/libcommon.cc
@@ -123,6 +123,7 @@ decompose_double( double value, double *mantissa, int *exponent)
 
 
 
+
 string&
 agh::str::
 homedir2tilda( string& inplace)
@@ -296,19 +297,45 @@ from_wstring( const wstring& in, const char* charset)
 
 
 
+
+// fs
+
+string
+agh::fs::
+make_fname_base( const string& fname_, const string& suffices, const TMakeFnameOption option)
+{
+	string	fname (fname_);
+	for ( const auto& X : agh::str::tokens( suffices, ",; ") )
+		if ( fname.size() > X.size() &&
+		     strcasecmp( &fname[fname.size()-X.size()], X.c_str()) == 0 ) {
+			fname.erase( fname.size()-X.size(), X.size());
+			break;
+		}
+
+	if ( option == TMakeFnameOption::hidden ) {
+		size_t slash_at = fname.rfind('/');
+		if ( slash_at < fname.size() )
+			fname.insert( slash_at+1, ".");
+	}
+	return move(fname);
+}
+
+
+
 // found to be of use elsewhere
-size_t	agh::fs::__n_edf_files;
+size_t	agh::fs::total_supported_sigfiles;
+
 int
 agh::fs::
-edf_file_counter( const char *fname, const struct stat*, int flag, struct FTW *ftw)
+supported_sigfile_counter( const char *fname, const struct stat*, int flag, struct FTW *ftw)
 {
 	if ( flag == FTW_F && ftw->level == 4 ) {
 		int fnlen = strlen(fname); // - ftw->base;
 		if ( fnlen < 5 )
 			return 0;
-		if ( strcasecmp( &fname[fnlen-4], ".edf") == 0 ) {
-			++__n_edf_files;
-		}
+		if ( strcasecmp( &fname[fnlen-4], ".edf") == 0 ||
+		     strcasecmp( &fname[fnlen-4], ".tsv") == 0 )
+			++total_supported_sigfiles;
 	}
 	return 0;
 }
diff --git a/src/libmetrics/mc.cc b/src/libmetrics/mc.cc
index 70fa09d..e4ea16e 100644
--- a/src/libmetrics/mc.cc
+++ b/src/libmetrics/mc.cc
@@ -60,7 +60,7 @@ mirror_fname() const
 		  "%s-%s-%lu"
 		  ":%g+%g-%g_%g" "_%g" "_%g_%g" "_%g_%g@%zu"
 		  ".mc",
-		  agh::fs::make_fname_base (_using_F().filename(), "", true).c_str(),
+		  agh::fs::make_fname_base (_using_F().filename(), "", agh::fs::TMakeFnameOption::hidden).c_str(),
 		  _using_F().channel_by_id(_using_sig_no).name(),
 		  _using_F().dirty_signature( _using_sig_no),
 		  Pp.pagesize, Pp.step,
diff --git a/src/libmetrics/psd.cc b/src/libmetrics/psd.cc
index 7eddf6c..21d0531 100644
--- a/src/libmetrics/psd.cc
+++ b/src/libmetrics/psd.cc
@@ -78,7 +78,7 @@ mirror_fname() const
 		  "%s.%s-%lu"
 		  ":%g+%g-%g-%c%c@%zu"
 		  ".psd",
-		  agh::fs::make_fname_base (_using_F().filename(), "", true).c_str(),
+		  agh::fs::make_fname_base (_using_F().filename(), "", agh::fs::TMakeFnameOption::hidden).c_str(),
 		  _using_F().channel_by_id(_using_sig_no).name(),
 		  _using_F().dirty_signature( _using_sig_no),
 		  Pp.pagesize, Pp.step, Pp.binsize,
diff --git a/src/libmetrics/swu.cc b/src/libmetrics/swu.cc
index e1c123c..7be5570 100644
--- a/src/libmetrics/swu.cc
+++ b/src/libmetrics/swu.cc
@@ -57,7 +57,7 @@ mirror_fname() const
 		  "%s.%s-%lu"
 		  ":%g+%g-%g@%zu"
 		  ".swu",
-		  agh::fs::make_fname_base (_using_F().filename(), "", true).c_str(),
+		  agh::fs::make_fname_base (_using_F().filename(), "", agh::fs::TMakeFnameOption::hidden).c_str(),
 		  _using_F().channel_by_id(_using_sig_no).name(),
 		  _using_F().dirty_signature( _using_sig_no),
 		  Pp.pagesize, Pp.step, Pp.min_upswing_duration,
diff --git a/src/libsigfile/edf.cc b/src/libsigfile/edf.cc
index 579b1c3..c966667 100644
--- a/src/libsigfile/edf.cc
+++ b/src/libsigfile/edf.cc
@@ -466,10 +466,10 @@ _parse_header()
 
 		_subtype =
 			(strncasecmp( header.reserved, "edf+c", 5) == 0)
-			? edfplus_c
+			? TSubtype::edfplus_c
 			: (strncasecmp( header.reserved, "edf+d", 5) == 0)
-			? edfplus_d
-			: edf;
+			? TSubtype::edfplus_d
+			: TSubtype::edf;
 
 		size_t	header_length;
 
@@ -771,7 +771,7 @@ details( const int which) const
 			  " Record size\t: %g sec\n"
 			  " # of discontinuities\t: %zu\n"
 			  " # of embedded annotations\t: %zu\n",
-			  filename(),
+			  agh::str::homedir2tilda( filename()).c_str(),
 			  subtype_s(),
 			  patient_id(),
 			  trim( string (header.recording_id, 80)).c_str(),
diff --git a/src/libsigfile/edf.hh b/src/libsigfile/edf.hh
index 8d661ba..33861b1 100644
--- a/src/libsigfile/edf.hh
+++ b/src/libsigfile/edf.hh
@@ -45,7 +45,8 @@ class CEDFFile
 
     public:
 	// subtype
-	enum TSubtype {
+	enum class TSubtype {
+		invalid,
 		edf,
 		edfplus_c,  // continuous
 		edfplus_d   // discontinuous
@@ -56,9 +57,9 @@ class CEDFFile
 	subtype_s( TSubtype t)
 		{
 			switch (t) {
-			case edf:       return "edf";
-			case edfplus_c: return "edf+c";
-			case edfplus_d: return "edf+d";
+			case TSubtype::edf:       return "edf";
+			case TSubtype::edfplus_c: return "edf+c";
+			case TSubtype::edfplus_d: return "edf+d";
 			default:        return "(invalid)";
 			}
 		}
diff --git a/src/libsigfile/source-base.cc b/src/libsigfile/source-base.cc
index e9a7340..baa01c6 100644
--- a/src/libsigfile/source-base.cc
+++ b/src/libsigfile/source-base.cc
@@ -18,6 +18,25 @@
 using namespace std;
 using namespace sigfile;
 
+const char*
+	sigfile::supported_sigfile_extensions = ".edf .tsv .csv";
+
+bool
+sigfile::
+is_fname_ext_supported( const string& fname)
+{
+	for ( const auto& X : agh::str::tokens( supported_sigfile_extensions, " ") )
+		if ( fname.size() < X.size() )
+			continue;
+		else
+			if ( strcasecmp( &fname[fname.size()-4], X.c_str()) == 0 )
+				return true;
+	return false;
+}
+
+
+
+
 void
 SArtifacts::
 mark_artifact( const double aa, const double az)
diff --git a/src/libsigfile/source-base.hh b/src/libsigfile/source-base.hh
index a96dd32..004b840 100644
--- a/src/libsigfile/source-base.hh
+++ b/src/libsigfile/source-base.hh
@@ -28,31 +28,46 @@ using namespace std;
 
 namespace sigfile {
 
+extern const char* supported_sigfile_extensions;
+bool is_fname_ext_supported( const string&);
+
+
 inline string
-make_fname_hypnogram( const string& _filename, size_t pagesize)
+make_fname_hypnogram( const string& filename, size_t pagesize)
 {
-	return agh::fs::make_fname_base( _filename, ".edf", true)
+	return agh::fs::make_fname_base(
+		filename,
+		supported_sigfile_extensions,
+		agh::fs::TMakeFnameOption::hidden)
 		+ "-" + to_string( (long long unsigned)pagesize) + ".hypnogram";
 }
 
 inline string
-make_fname_artifacts( const string& _filename, const SChannel& channel)
+make_fname_artifacts( const string& filename, const SChannel& channel)
 {
-	return agh::fs::make_fname_base( _filename, ".edf", true)
+	return agh::fs::make_fname_base(
+		filename,
+		supported_sigfile_extensions,
+		agh::fs::TMakeFnameOption::hidden)
 		+ "-" + channel.name() + ".af";
 }
 
 inline string
-make_fname_annotations( const string& _filename, const SChannel& channel)
+make_fname_annotations( const string& filename, const SChannel& channel)
 {
-	return agh::fs::make_fname_base( _filename, ".edf", true)
+	return agh::fs::make_fname_base(
+		filename,
+		supported_sigfile_extensions,
+		agh::fs::TMakeFnameOption::hidden)
 		+ "-" + channel.name() + ".annotations";
 }
 
 inline string
 make_fname_filters( const string& _filename)
 {
-	return agh::fs::make_fname_base( _filename, ".edf", true)
+	return agh::fs::make_fname_base( _filename,
+		supported_sigfile_extensions,
+		agh::fs::TMakeFnameOption::hidden)
 		+ ".filters";
 }
 
diff --git a/src/libsigfile/tsv.cc b/src/libsigfile/tsv.cc
index 176cde8..5fe877c 100644
--- a/src/libsigfile/tsv.cc
+++ b/src/libsigfile/tsv.cc
@@ -49,6 +49,11 @@ CTSVFile (const string& fname_, const int flags_)
 	_f = fopen( fname_.c_str(), "r");
 	if ( !_f )
 		throw invalid_argument (explain_status(_status |= sysfail));
+	_subtype =
+		(strcasecmp( &fname_[fname_.size()-4], ".csv") == 0)
+		? TSubtype::csv
+		: (strcasecmp( &fname_[fname_.size()-4], ".tsv") == 0) ? TSubtype::tsv
+		: TSubtype::invalid;
 
       // parse header
 	if ( _parse_header() ) {  // creates channels list
@@ -189,6 +194,8 @@ _parse_header()
 		_status |= CSource::missing_patient_id;;
 		return -1;
 	}
+	_status |=
+		_subject.parse_recording_id_edf_style( metadata["patient_id"]);
 
 	if ( metadata.find( "recording_date") == metadata.end() ||
 	     metadata.find( "recording_time") == metadata.end() ) {
@@ -259,7 +266,7 @@ _read_data()
 	do {
 		for ( r = 0; r < channels.size(); ++r ) {
 			double x;
-			if ( 1 != fscanf( _f, "%lg", &x) )
+			if ( 1 != sscanf( _line0, "%lg%*[,\t]", &x) )
 				goto outer_break;
 			c2[r].push_back( x);
 		}
@@ -335,7 +342,7 @@ details( const int which) const
 		" Duration\t: %s\n"
 		" # of channels\t: %zu\n"
 		" Sample rate\t: %zu\n",
-		filename(),
+		agh::str::homedir2tilda( filename()).c_str(),
 		subtype_s(),
 		patient_id(),
 		recording_id(),
diff --git a/src/libsigfile/tsv.hh b/src/libsigfile/tsv.hh
index 2a8b061..c3fea05 100644
--- a/src/libsigfile/tsv.hh
+++ b/src/libsigfile/tsv.hh
@@ -48,7 +48,8 @@ class CTSVFile
 
     public:
 	// subtype
-	enum TSubtype {
+	enum class TSubtype {
+		invalid,
 		csv,
 		tsv,
 	};
@@ -58,8 +59,8 @@ class CTSVFile
 	subtype_s( TSubtype t)
 		{
 			switch (t) {
-			case csv: return "csv";
-			case tsv: return "tsv";
+			case TSubtype::csv: return "csv";
+			case TSubtype::tsv: return "tsv";
 			default:  return "(invalid)";
 			}
 		}
@@ -331,7 +332,7 @@ class CTSVFile
 
 
 	enum TStatus : int_least32_t {
-		bad_channel_count         = (1 << (COMMON_STATUS_BITS + 1)),
+		bad_channel_count        = (1 << (COMMON_STATUS_BITS + 1)),
 		inoperable		 = (bad_header
 					   | bad_numfld
 					   | bad_datetime
diff --git a/src/libsigfile/typed-source.cc b/src/libsigfile/typed-source.cc
index 26049a2..6600df9 100644
--- a/src/libsigfile/typed-source.cc
+++ b/src/libsigfile/typed-source.cc
@@ -22,6 +22,7 @@ using sigfile::CTSVFile;
 using sigfile::CEDFFile;
 
 
+
 CTypedSource::
 CTypedSource (const string& fname,
 	      const size_t pagesize,
diff --git a/src/libsigfile/typed-source.hh b/src/libsigfile/typed-source.hh
index f372b92..88ca515 100644
--- a/src/libsigfile/typed-source.hh
+++ b/src/libsigfile/typed-source.hh
@@ -13,6 +13,7 @@
 #define AGH_SIGFILE_SOURCE_H_
 
 #include "source-base.hh"
+#include "forward-decls.hh"
 #include "page.hh"
 
 #if HAVE_CONFIG_H && !defined(VERSION)
@@ -61,6 +62,10 @@ class CTypedSource
 	const CSource& operator()() const
 		{ return *_obj; }
 
+	// specialisations for the two known sigfile types
+	template <class T> T& obj();
+	template <class T> const T& obj() const;
+
       // filenames
 	string make_fname_hypnogram() const
 		{
@@ -71,6 +76,13 @@ class CTypedSource
 };
 
 
+template <> inline	 CTSVFile& CTypedSource::obj()       { return *(CTSVFile*)_obj; }
+template <> inline	 CEDFFile& CTypedSource::obj()       { return *(CEDFFile*)_obj; }
+template <> inline const CTSVFile& CTypedSource::obj() const { return *(CTSVFile*)_obj; }
+template <> inline const CEDFFile& CTypedSource::obj() const { return *(CEDFFile*)_obj; }
+
+
+
 template <typename T = int>
 struct SNamedChannel {
 	CSource& source;
diff --git a/src/tools/edfcat.cc b/src/tools/edfcat.cc
index ad72592..3182f10 100644
--- a/src/tools/edfcat.cc
+++ b/src/tools/edfcat.cc
@@ -377,7 +377,7 @@ exec_prune( const SOperation::SObject& obj)
 	}
 	printf( "Keeping %zu channel(s)\n", selected_channels.size());
 
-	sigfile::CEDFFile G ((agh::fs::make_fname_base( obj, ".edf", false) + "-mod.edf").c_str(),
+	sigfile::CEDFFile G ((agh::fs::make_fname_base( obj, ".edf", agh::fs::TMakeFnameOption::normal) + "-mod.edf").c_str(),
 			     sigfile::CEDFFile::TSubtype::edf,
 			     sigfile::CSource::no_ancillary_files,
 			     selected_channels,
diff --git a/src/tools/edfhed.cc b/src/tools/edfhed.cc
index 1c96000..3c89d12 100644
--- a/src/tools/edfhed.cc
+++ b/src/tools/edfhed.cc
@@ -282,7 +282,7 @@ set_session_and_episode_from_tree( sigfile::CEDFFile& F)
 	// filename can be anything, including a symlink
 	bool	is_path_absolute = (F.filename()[0] == '/');
 	list<string> pe = agh::fs::path_elements( string (is_path_absolute ? "" : "./") + F.filename());
-	string	episode = agh::fs::make_fname_base( pe.back(), ".edf", false);
+	string	episode = agh::fs::make_fname_base( pe.back(), ".edf", agh::fs::TMakeFnameOption::normal);
 
 	string	in_dir = string (is_path_absolute ? "/" : "") + agh::str::join( list<string> (pe.begin(), prev(pe.end())), "/") + "/.";
 	// a symlink from ./filename.edf would resolve somewhere else,

-- 
Sleep experiment manager



More information about the debian-med-commit mailing list