The following commit has been merged in the master branch:
commit 4b06a66467a52311d413e817136ece62c0e9c24a
Author: Andrei Zavada <[email protected]>
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 &lt;a 
href="mailto:aghermann-users@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 &lt;a 
href="mailto:aghermann-users@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 &lt;a 
href="mailto:aghermann-users@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 &lt;a 
href="mailto:aghermann-users@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

_______________________________________________
debian-med-commit mailing list
[email protected]
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/debian-med-commit

Reply via email to