From c256bd4c149afd375c243b066e5dafefbe1ae67a Mon Sep 17 00:00:00 2001
From: gb10 <gb10>
Date: Mon, 27 Sep 2010 17:16:11 +0000
Subject: [PATCH] Can now view polyA tails

---
 blxGff3Parser.c        |  14 ++--
 blxview.c              |   9 ++-
 blxwindow.c            | 165 +++++++++++++++++++++++------------------
 blxwindow.h            |  24 +-----
 detailview.c           |  22 ++----
 detailview.h           |   1 -
 detailviewtree.c       |  11 ++-
 sequencecellrenderer.c |  13 ++--
 utilities.c            |  86 +++++++++++++++++----
 utilities.h            |  50 +++++++++++--
 10 files changed, 244 insertions(+), 151 deletions(-)

diff --git a/blxGff3Parser.c b/blxGff3Parser.c
index 228baa1a..db38d4a2 100644
--- a/blxGff3Parser.c
+++ b/blxGff3Parser.c
@@ -232,9 +232,15 @@ static void createBlixemObject(BlxGffData *gffData,
     
   GError *tmpError = NULL;
 
-  if (typeIsExon(gffData->mspType) || gffData->mspType == BLXMSP_MATCH)
+  if (gffData->mspType == BLXMSP_TRANSCRIPT)
     {
-      if (!gffData->sName && !gffData->parentIdTag)
+      /* For transcript types, don't create an MSP, but do create a sequence */
+      addBlxSequence(gffData->sName, gffData->idTag, gffData->qStrand, seqList, gffData->sequence, NULL, &tmpError);
+    }
+  else
+    {
+      /* For all other types, create an MSP */
+      if (!gffData->sName && !gffData->parentIdTag && (typeIsExon(gffData->mspType) || typeIsMatch(gffData->mspType)))
 	{
 	  g_set_error(error, BLX_ERROR, 1, "Target/name/parent-ID must be specified for exons and alignments.\n");
 	  return;
@@ -286,10 +292,6 @@ static void createBlixemObject(BlxGffData *gffData,
 	  parseGapString(gffData->gapString, opts, msp, &tmpError);
 	}    
     }
-  else if (gffData->mspType == BLXMSP_TRANSCRIPT)
-    {
-      addBlxSequence(gffData->sName, gffData->idTag, gffData->qStrand, seqList, gffData->sequence, NULL, &tmpError);
-    }
     
   if (tmpError)
     {
diff --git a/blxview.c b/blxview.c
index faeff3a7..72f6bff5 100644
--- a/blxview.c
+++ b/blxview.c
@@ -88,7 +88,7 @@
 01-10-05	Added getsseqsPfetch to fetch all missing sseqs in one go via socket connection to pfetch [RD]
 
  * Created: Thu Feb 20 10:27:39 1993 (esr)
- * CVS info:   $Id: blxview.c,v 1.68 2010-09-27 11:55:15 gb10 Exp $
+ * CVS info:   $Id: blxview.c,v 1.69 2010-09-27 17:16:11 gb10 Exp $
  *-------------------------------------------------------------------
  */
 
@@ -1415,8 +1415,11 @@ MSP* createNewMsp(MSP **lastMsp,
   sprintf(msp->qframe, "(%c%d)", getStrandAsChar(qStrand), qFrame);
   sprintf(msp->sframe, "(%c%d)", getStrandAsChar(sStrand), 1);
   
-  /* Add/create a BlxSequence for this MSP's sequence name */
-  addBlxSequence(msp->sname, idTag, sStrand, seqList, sequence, msp, error);
+  /* For matches, exons and introns, add (or add to if already exists) a BlxSequence */
+  if (typeIsExon(mspType) || typeIsIntron(mspType) || typeIsMatch(mspType))
+    {
+      addBlxSequence(msp->sname, idTag, sStrand, seqList, sequence, msp, error);
+    }
 
   if (error && *error)
     {
diff --git a/blxwindow.c b/blxwindow.c
index 7016bb98..6dcc1601 100755
--- a/blxwindow.c
+++ b/blxwindow.c
@@ -1664,7 +1664,7 @@ void showInfoDialog(GtkWidget *blxWindow)
   
   /* Compile the message text from the selected sequence(s) */
   GString *resultStr = g_string_new("");
-  const gboolean dataLoaded = blxContextGetFlag(bc, BLXFLAG_EMBL_DATA_LOADED);
+  const gboolean dataLoaded = bc->flags[BLXFLAG_EMBL_DATA_LOADED];
   GList *seqItem = bc->selectedSeqs;
   
   for ( ; seqItem; seqItem = seqItem->next)
@@ -2724,30 +2724,6 @@ static GtkWidget* dialogChildGetBlxWindow(GtkWidget *child)
 }
 
 
-/* Set the given flag to the given value */
-void blxContextSetFlag(BlxViewContext *bc, const BlxFlag flag, const gboolean newValue)
-{
-  gboolean *value = &g_array_index(bc->blxFlags, gboolean, flag);
-  *value = newValue;
-}
-
-
-/* Get the value of the given flag */
-gboolean blxContextGetFlag(const BlxViewContext *bc, const BlxFlag flag)
-{
-  gboolean result = g_array_index(bc->blxFlags, gboolean, flag);
-  
-  /* Special case for the show-unaligned-sequnece option: only allow this to be true if
-   * squash matches is off, because they don't work well together */
-  if (result && flag == BLXFLAG_SHOW_UNALIGNED_SEQ && blxContextGetFlag(bc, BLXFLAG_SQUASH_MATCHES))
-    {
-      result = FALSE;
-    }
-  
-  return result;
-}
-
-
 /* Updates the given flag from the given button. The passed in widget is the toggle button and
  * the data is an enum indicating which flag was toggled. Returns the new value that was set.
  * Returns the new value that was set. */
@@ -2759,7 +2735,7 @@ static gboolean setFlagFromButton(GtkWidget *button, gpointer data)
   BlxFlag flag = GPOINTER_TO_INT(data);
   const gboolean newValue = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
   
-  blxContextSetFlag(bc, flag, newValue);
+  bc->flags[flag] = newValue;
   
   return newValue;
 }
@@ -2888,7 +2864,7 @@ static void onButtonClickedLoadEmblData(GtkWidget *button, gpointer data)
   if (success)
     {
       /* Set the flag to say that the data has now been loaded */
-      blxContextSetFlag(bc, BLXFLAG_EMBL_DATA_LOADED, TRUE);
+      bc->flags[BLXFLAG_EMBL_DATA_LOADED] = TRUE;
       
       /* Disable the button so user can't try to load data again. */
       gtk_widget_set_sensitive(button, FALSE);
@@ -2922,7 +2898,7 @@ static GtkWidget* createColumnLoadDataButton(GtkBox *box, GtkWidget *detailView)
   gtk_box_pack_start(GTK_BOX(box), hbox, FALSE, FALSE, 12);
 
   BlxViewContext *bc = blxWindowGetContext(detailViewGetBlxWindow(detailView));
-  const gboolean dataLoaded = blxContextGetFlag(bc, BLXFLAG_EMBL_DATA_LOADED);
+  const gboolean dataLoaded = bc->flags[BLXFLAG_EMBL_DATA_LOADED];
   
   GtkWidget *button = gtk_button_new_with_label(LOAD_DATA_TEXT);
   gtk_widget_set_sensitive(button, !dataLoaded); /* only enable if data not yet loaded */
@@ -3213,25 +3189,39 @@ static void onTogglePrintColors(GtkWidget *button, gpointer data)
 }
 
 
-/* Callback function called when the 'Show unaligned sequence' button is toggled */
+/* Callback function called when the 'Show unaligned sequence' button is toggled. Could be consolidated
+ * with onShowPolyAToggled (we'd just need to pass both the sub-container and the flag in the user
+ * data)  */
 static void onShowUnalignedSeqToggled(GtkWidget *button, gpointer data)
 {
   /* Get the new value */
-  const gboolean showUnalignedSeq = setFlagFromButton(button, GINT_TO_POINTER(BLXFLAG_SHOW_UNALIGNED_SEQ));
+  const gboolean active = setFlagFromButton(button, GINT_TO_POINTER(BLXFLAG_SHOW_UNALIGNED));
   
   /* Enable/disable the sub-options. Their widgets are all in the container passed as the data. */
   GtkWidget *subComponents = GTK_WIDGET(data);
-  gtk_widget_set_sensitive(subComponents, showUnalignedSeq); 
+  gtk_widget_set_sensitive(subComponents, active); 
   
-  /* Get the detail view from the main window */
   GtkWidget *blxWindow = dialogChildGetBlxWindow(button);
-  GtkWidget *detailView = blxWindowGetDetailView(blxWindow);
-  
-  /* Update the value */
-  detailViewUpdateShowUnalignedSeq(detailView, showUnalignedSeq);
+  blxWindowRedrawAll(blxWindow);
 }
 
 
+/* Callback function called when the 'Show polyA tails' button is toggled. Could be consolidated
+ * with onShowUnalignedSeqToggled (we'd just need to pass both the sub-container and the flag in the user
+ * data) */
+static void onShowPolyAToggled(GtkWidget *button, gpointer data)
+{
+  /* Get the new value */
+  const gboolean active = setFlagFromButton(button, GINT_TO_POINTER(BLXFLAG_SHOW_POLYA));
+  
+  /* Enable/disable the sub-options. Their widgets are all in the container passed as the data. */
+  GtkWidget *subComponents = GTK_WIDGET(data);
+  gtk_widget_set_sensitive(subComponents, active); 
+  
+  GtkWidget *blxWindow = dialogChildGetBlxWindow(button);
+  blxWindowRedrawAll(blxWindow);
+}
+
 /* Callback function called when the 'Limit unaligned bases' button is toggled */
 static void onLimitUnalignedBasesToggled(GtkWidget *button, gpointer data)
 {
@@ -3265,27 +3255,20 @@ static gboolean onSetNumUnalignedBases(GtkWidget *entry, const gint responseId,
 }
 
 
-/* Create option buttons for enabling/disabling display of unaligned sequence */
-static void createUnalignedSeqButtons(GtkWidget *parent, GtkWidget *detailView, BlxViewContext *bc)
+/* Create the check button for the 'limit number of unaligned bases' option on the settings dialog
+ * and pack it into the given container. */
+static void createLimitUnalignedBasesButton(GtkContainer *parent, GtkWidget *detailView, BlxViewContext *bc)
 {
-  const gboolean showUnalignedSeq = blxContextGetFlag(bc, BLXFLAG_SHOW_UNALIGNED_SEQ);
-  
-  GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
-  gtk_box_pack_start(GTK_BOX(parent), vbox, FALSE, FALSE, 0);
-
-  /* Create an hbox for the sub-components. Create it now so we can pass it to the main toggle
-   * button callback, but don't pack it in the container till we've added the other. */
+  /* Create an hbox for the "limit to so-many bases" option, which has a check button, text
+   * entry and some labels. Pack the hbox into the given parent. */
   GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
-  gtk_widget_set_sensitive(hbox, showUnalignedSeq); 
-
-  /* Main check button to enable/disable the option */
-  createCheckButton(GTK_BOX(vbox), "Show _unaligned sequence (only works if Squash Matches is off)", showUnalignedSeq, G_CALLBACK(onShowUnalignedSeqToggled), hbox);
-
-  /* Text entry box to specify the limit */
+  gtk_container_add(parent, hbox);
+  
+  /* Create a text entry box so the user can enter the number of bases */
   GtkWidget *entry = gtk_entry_new();
   gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
   widgetSetCallbackData(entry, onSetNumUnalignedBases, detailView);
-
+  
   DetailViewProperties *properties = detailViewGetProperties(detailView);
   char *numStr = convertIntToString(properties->numUnalignedBases);
   
@@ -3294,19 +3277,52 @@ static void createUnalignedSeqButtons(GtkWidget *parent, GtkWidget *detailView,
   g_free(numStr);
   
   /* Check button to enable/disable setting the limit */
-  GtkWidget *button = gtk_check_button_new_with_mnemonic("_Limit to ");
-  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), blxContextGetFlag(bc, BLXFLAG_LIMIT_UNALIGNED_BASES));
+  GtkWidget *button = gtk_check_button_new_with_mnemonic("Li_mit to ");
+  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), bc->flags[BLXFLAG_LIMIT_UNALIGNED_BASES]);
   g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(onLimitUnalignedBasesToggled), entry);
-
+  
   /* Pack it all in the hbox */
-  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
-  gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new("   "), FALSE, FALSE, 0);
   gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
   gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0);
   gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new(" additional bases"), FALSE, FALSE, 0);
 }
 
 
+/* Create a "parent" option button that has a vbox container for "sub-components", i.e. more 
+ * option buttons (or other dialog widgets) that will be enabled only when the parent option is
+ * active. Returns the container for the sub-options, which should be packed with the sub-option widgets. */
+static GtkContainer* createParentCheckButton(GtkWidget *parent, 
+                                             GtkWidget *detailView,
+                                             BlxViewContext *bc,
+                                             const char *label,
+                                             const gboolean active,
+                                             GCallback callbackFunc)
+{
+  /* We'll the main button and any sub-components into a vbox */
+  GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(parent), vbox, FALSE, FALSE, 0);
+
+  /* Create a vbox for the sub-components. Create the vbox now so we can pass it to the main toggle
+   * button callback, but don't pack it in the container till we've added the main check button. The sub
+   * components are only active if the main check button is active. */
+  GtkWidget *subContainer = gtk_vbox_new(FALSE, 0);
+  gtk_widget_set_sensitive(subContainer, active); 
+  
+  /* Main check button to enable/disable the option. This call puts it in the vbox. */
+  createCheckButton(GTK_BOX(vbox), label, active, callbackFunc, subContainer);
+
+  /* Now add the subcomponent container to the vbox. Bit of a hack - put it inside an hbox with 
+   * a blank label preceeding it, so that the sub-components appear offset to the right slightly
+   * from the main button. */
+  GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new("   "), FALSE, FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(hbox), subContainer, FALSE, FALSE, 0);
+  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
+  
+  return GTK_CONTAINER(subContainer);
+}
+
+
 /* Refresh the given dialog, if it is open */
 void refreshDialog(const BlxDialogId dialogId, GtkWidget *blxWindow)
 {
@@ -3410,12 +3426,21 @@ void showSettingsDialog(GtkWidget *blxWindow, const gboolean bringToFront)
   /* Display options */
   GtkWidget *vbox1 = createVBoxWithBorder(mainVBox, borderWidth, TRUE, "Display options");
   
-  createCheckButton(GTK_BOX(vbox1), "_Squash matches", blxContextGetFlag(bc, BLXFLAG_SQUASH_MATCHES), G_CALLBACK(onSquashMatches), GINT_TO_POINTER(BLXFLAG_SQUASH_MATCHES));
-  createUnalignedSeqButtons(vbox1, detailView, bc);
-  createCheckButton(GTK_BOX(vbox1), "_Invert sort order", blxContextGetFlag(bc, BLXFLAG_INVERT_SORT), G_CALLBACK(onSortOrderToggled), GINT_TO_POINTER(BLXFLAG_INVERT_SORT));
-  createCheckButton(GTK_BOX(vbox1), "_Highlight differences", blxContextGetFlag(bc, BLXFLAG_HIGHLIGHT_DIFFS), G_CALLBACK(onToggleFlag), GINT_TO_POINTER(BLXFLAG_HIGHLIGHT_DIFFS));
-  createCheckButton(GTK_BOX(vbox1), "Show SN_P track", blxContextGetFlag(bc, BLXFLAG_SHOW_SNP_TRACK), G_CALLBACK(onShowSnpTrackToggled), GINT_TO_POINTER(BLXFLAG_SHOW_SNP_TRACK));
-  createCheckButton(GTK_BOX(vbox1), "Show Sp_lice Sites for selected seqs", blxContextGetFlag(bc, BLXFLAG_SHOW_SPLICE_SITES), G_CALLBACK(onToggleFlag), GINT_TO_POINTER(BLXFLAG_SHOW_SPLICE_SITES));
+  createCheckButton(GTK_BOX(vbox1), "_Squash matches", bc->flags[BLXFLAG_SQUASH_MATCHES], G_CALLBACK(onSquashMatches), GINT_TO_POINTER(BLXFLAG_SQUASH_MATCHES));
+  
+  /* show-polyA-tails option and its sub-options */
+  GtkContainer *polyAContainer = createParentCheckButton(vbox1, detailView, bc, "Show polyA _tails", bc->flags[BLXFLAG_SHOW_POLYA], G_CALLBACK(onShowPolyAToggled));
+  createCheckButton(GTK_BOX(polyAContainer), "Selected sequences only", bc->flags[BLXFLAG_SHOW_POLYA_SELECTED], G_CALLBACK(onToggleFlag), GINT_TO_POINTER(BLXFLAG_SHOW_POLYA_SELECTED));
+
+  /* show-unaligned-sequence option and its sub-options */
+  GtkContainer *unalignContainer = createParentCheckButton(vbox1, detailView, bc, "Show _unaligned sequence (only works if Squash Matches is off)", bc->flags[BLXFLAG_SHOW_UNALIGNED], G_CALLBACK(onShowUnalignedSeqToggled));
+  createLimitUnalignedBasesButton(unalignContainer, detailView, bc);
+  createCheckButton(GTK_BOX(unalignContainer), "Selected sequences only", bc->flags[BLXFLAG_SHOW_UNALIGNED_SELECTED], G_CALLBACK(onToggleFlag), GINT_TO_POINTER(BLXFLAG_SHOW_UNALIGNED_SELECTED));
+
+  createCheckButton(GTK_BOX(vbox1), "_Invert sort order", bc->flags[BLXFLAG_INVERT_SORT], G_CALLBACK(onSortOrderToggled), GINT_TO_POINTER(BLXFLAG_INVERT_SORT));
+  createCheckButton(GTK_BOX(vbox1), "_Highlight differences", bc->flags[BLXFLAG_HIGHLIGHT_DIFFS], G_CALLBACK(onToggleFlag), GINT_TO_POINTER(BLXFLAG_HIGHLIGHT_DIFFS));
+  createCheckButton(GTK_BOX(vbox1), "Show SN_P track", bc->flags[BLXFLAG_SHOW_SNP_TRACK], G_CALLBACK(onShowSnpTrackToggled), GINT_TO_POINTER(BLXFLAG_SHOW_SNP_TRACK));
+  createCheckButton(GTK_BOX(vbox1), "Show Sp_lice Sites for selected seqs", bc->flags[BLXFLAG_SHOW_SPLICE_SITES], G_CALLBACK(onToggleFlag), GINT_TO_POINTER(BLXFLAG_SHOW_SPLICE_SITES));
   
   GtkWidget *pfetchBox = createVBoxWithBorder(mainVBox, borderWidth, TRUE, "Fetch mode");
   createPfetchDropDownBox(GTK_BOX(pfetchBox), blxWindow);
@@ -4211,11 +4236,6 @@ static void destroyBlxContext(BlxViewContext **bc)
 	  (*bc)->defaultColors = NULL;
 	}
     
-      if ((*bc)->blxFlags)
-	{
-	  g_array_free((*bc)->blxFlags, FALSE);
-	}
-      
       destroyMspList(&((*bc)->mspList));
       destroyBlxSequenceList(&((*bc)->matchSeqs));
       blxDestroyGffTypeList(&((*bc)->supportedTypes));
@@ -4498,18 +4518,17 @@ static BlxViewContext* blxWindowCreateContext(CommandLineOptions *options,
   
   createBlxColors(blxContext, widget);
   
-  blxContext->blxFlags = g_array_sized_new(FALSE, TRUE, sizeof(gboolean), BLXFLAG_NUM_FLAGS);
-  
   /* Initialise all the flags to false */
   int flag = BLXFLAG_MIN + 1;
   for ( ; flag < BLXFLAG_NUM_FLAGS; ++flag)
     {
-      blxContextSetFlag(blxContext, flag, FALSE);
+      blxContext->flags[flag] = FALSE;
     }
   
   /* Set any specific flags that we want initialised to TRUE */
-  blxContextSetFlag(blxContext, BLXFLAG_LIMIT_UNALIGNED_BASES, TRUE);
-  blxContextSetFlag(blxContext, BLXFLAG_EMBL_DATA_LOADED, options->parseFullEmblInfo);
+  blxContext->flags[BLXFLAG_LIMIT_UNALIGNED_BASES] = TRUE;
+  blxContext->flags[BLXFLAG_SHOW_POLYA_SELECTED] = TRUE;
+  blxContext->flags[BLXFLAG_EMBL_DATA_LOADED] = options->parseFullEmblInfo;
   
   /* Null out all the entries in the dialogs list */
   int dialogId = 0;
diff --git a/blxwindow.h b/blxwindow.h
index 87510b4e..2c037c4d 100755
--- a/blxwindow.h
+++ b/blxwindow.h
@@ -42,25 +42,6 @@ typedef struct _CommandLineOptions
 } CommandLineOptions;
 
 
-/* This enum contains a list of all the boolean options that the user can toggle on/off */
-typedef enum
-  {
-    BLXFLAG_MIN,		    /* Start index for looping through flags */
-  
-    BLXFLAG_SQUASH_MATCHES,	    /* Puts all MSPs from the same sequence on the same row in the detail view */
-    BLXFLAG_INVERT_SORT,	    /* Inverts the default sort order */
-    BLXFLAG_HIGHLIGHT_DIFFS,	    /* Hides matching bases and highlights mis-matching ones */
-    BLXFLAG_SHOW_SNP_TRACK,	    /* Shows the SNP track */
-    BLXFLAG_SHOW_UNALIGNED_SEQ,	    /* Shows additional bits of the match sequence that are not part of the aligned section */
-    BLXFLAG_LIMIT_UNALIGNED_BASES,  /* If the above option is on, limits how many bases from the unaligned sequence are shown */
-    BLXFLAG_SHOW_SPLICE_SITES,	    /* Highlights splice sites in the reference sequence for the currently-selected MSPs */
-    BLXFLAG_EMBL_DATA_LOADED,       /* Gets set to true if the full EMBL data is parsed and populated in the MSPs */
-    BLXFLAG_SHOW_CDS,               /* True if CDS/UTR regions should be shown; false if plain exons should be shown */
-    
-    BLXFLAG_NUM_FLAGS		    /* Number of flags, for looping through flags or creating an array */
-  } BlxFlag;
-
-
 /* This enum contains IDs for all the persistent dialogs in the application, and should be used
  * to access a stored dialog in the dialogList array in the BlxViewContext. Note that the dialogList
  * array will contain null entries until the dialogs are created for the first time */
@@ -120,7 +101,7 @@ typedef struct _BlxViewContext
   GArray *defaultColors;	    /* Default colors used by Blixem */
   gboolean usePrintColors;	    /* Whether to use print colors (i.e. black and white) */
   
-  GArray *blxFlags;		    /* Array of all the flags the user can toggle. Indexed by the BlxFlags enum. */
+  gboolean flags[BLXFLAG_NUM_FLAGS];              /* Array of all the flags the user can toggle. Indexed by the BlxFlags enum. */
   GtkWidget *dialogList[BLXDIALOG_NUM_DIALOGS];   /* Array of all the persistent dialogs in the application */
 } BlxViewContext;
 
@@ -153,9 +134,6 @@ const char*		  blxWindowGetPaddingSeq(GtkWidget *blxWindow);
 int			  blxWindowGetOffset(GtkWidget *blxWindow);
 BlxStrand		  blxWindowGetActiveStrand(GtkWidget *blxWindow);
 
-void			  blxContextSetFlag(BlxViewContext *bc, const BlxFlag flag, const gboolean newValue);
-gboolean		  blxContextGetFlag(const BlxViewContext *bc, const BlxFlag flag);
-
 GList*			  blxWindowGetSelectedSeqs(GtkWidget *blxWindow);
 void			  blxWindowSelectSeq(GtkWidget *blxWindow, BlxSequence *seq);
 void			  blxWindowSetSelectedSeqList(GtkWidget *blxWindow, GList *seqList);
diff --git a/detailview.c b/detailview.c
index 5b2a522c..aac152b1 100755
--- a/detailview.c
+++ b/detailview.c
@@ -620,7 +620,7 @@ void refreshTextHeader(GtkWidget *header, gpointer data)
 	  /* SNP track */
 	  BlxViewContext *bc = detailViewGetContext(detailView);
 	
-	  if (!blxContextGetFlag(bc, BLXFLAG_SHOW_SNP_TRACK))
+	  if (!bc->flags[BLXFLAG_SHOW_SNP_TRACK])
 	    {
 	      /* SNP track is hidden, so set the height to 0 */
 	      gtk_layout_set_size(GTK_LAYOUT(header), header->allocation.width, 0);
@@ -806,13 +806,13 @@ static char* getFeedbackText(GtkWidget *detailView, const BlxSequence *seq, cons
 	  if (qIdx != UNSET_INT)
 	    {
 	      GList *mspListItem = seq->mspList;
+              const int numUnalignedBases = detailViewGetNumUnalignedBases(detailView);
 	      
 	      for ( ; mspListItem; mspListItem = mspListItem->next)
 		{
 		  MSP *msp = (MSP*)(mspListItem->data);
 		  
-		  sIdx = gapCoord(msp, qIdx, bc->numFrames, mspGetRefFrame(msp, bc->seqType), bc->displayRev,
-				  FALSE, FALSE, UNSET_INT);/* don't show unaligned base coords */
+		  sIdx = gapCoord(msp, qIdx, bc->numFrames, mspGetRefFrame(msp, bc->seqType), bc->displayRev, TRUE, numUnalignedBases, bc->flags, bc->mspList);
 
 		  if (sIdx != UNSET_INT)
 		    {
@@ -959,14 +959,6 @@ void detailViewUpdateShowSnpTrack(GtkWidget *detailView, const gboolean showSnpT
 }
 
 
-/* Set the value of the 'Show Unaligned Sequence' flag */
-void detailViewUpdateShowUnalignedSeq(GtkWidget *detailView, const gboolean showUnalignedSeq)
-{
-  /* Just re-draw */
-  gtk_widget_queue_draw(detailView);
-}
-
-
 /* Set the value of the 'Limit Unaligned Bases' flag */
 void detailViewUpdateLimitUnalignedBases(GtkWidget *detailView, const gboolean limitUnalignedBases)
 {
@@ -1456,7 +1448,7 @@ GHashTable* getIntronBasesToHighlight(GtkWidget *detailView,
   
   /* We only highlight nucleotides, so there is nothing to do if we're showing a peptide sequence,
    * or if the show-splice-sites option is disabled. */
-  if (seqType == BLXSEQ_PEPTIDE || !blxContextGetFlag(bc, BLXFLAG_SHOW_SPLICE_SITES))
+  if (seqType == BLXSEQ_PEPTIDE || !bc->flags[BLXFLAG_SHOW_SPLICE_SITES])
     {
       return result;
     }
@@ -1862,7 +1854,7 @@ static void drawSnpTrack(GtkWidget *snpTrack, GtkWidget *detailView)
   GtkWidget *blxWindow = detailViewGetBlxWindow(detailView);
   BlxViewContext *bc = blxWindowGetContext(blxWindow);
   
-  if (!blxContextGetFlag(bc, BLXFLAG_SHOW_SNP_TRACK))
+  if (!bc->flags[BLXFLAG_SHOW_SNP_TRACK])
     {
       return;
     }
@@ -2936,9 +2928,9 @@ static gboolean onButtonPressSeqColHeader(GtkWidget *header, GdkEventButton *eve
 	  {
 	    /* Double-click: toggle SNP track visibility */
 	    BlxViewContext *bc = detailViewGetContext(detailView);
-            gboolean showSnpTrack = !blxContextGetFlag(bc, BLXFLAG_SHOW_SNP_TRACK);
+            gboolean showSnpTrack = !bc->flags[BLXFLAG_SHOW_SNP_TRACK];
             
-	    blxContextSetFlag(bc, BLXFLAG_SHOW_SNP_TRACK, showSnpTrack);
+	    bc->flags[BLXFLAG_SHOW_SNP_TRACK] = showSnpTrack;
             detailViewUpdateShowSnpTrack(detailView, showSnpTrack);
 	  }
 
diff --git a/detailview.h b/detailview.h
index a99fab43..acf59c23 100644
--- a/detailview.h
+++ b/detailview.h
@@ -163,7 +163,6 @@ void			detailViewRedrawAll(GtkWidget *detailView);
 void			detailViewUpdateSquashMatches(GtkWidget *detailView, const gboolean squash);
 void			detailViewUpdateSortInverted(GtkWidget *detailView, const gboolean invert);
 void			detailViewUpdateShowSnpTrack(GtkWidget *detailView, const gboolean showSnpTrack);
-void			detailViewUpdateShowUnalignedSeq(GtkWidget *detailView, const gboolean showUnalignedSeq);
 void                    detailViewUpdateLimitUnalignedBases(GtkWidget *detailView, const gboolean limitUnalignedBases);
 
 void                    detailViewSetNumUnalignedBases(GtkWidget *detailView, const int numBases);
diff --git a/detailviewtree.c b/detailviewtree.c
index dea62783..4e9d2ab7 100755
--- a/detailviewtree.c
+++ b/detailviewtree.c
@@ -638,7 +638,7 @@ static GtkSortType treeGetColumnSortOrder(GtkWidget *tree, const BlxColumnId col
 	break;
     };
 
-  if (blxContextGetFlag(bc, BLXFLAG_INVERT_SORT))
+  if (bc->flags[BLXFLAG_INVERT_SORT])
     {
       result = (result == GTK_SORT_ASCENDING) ? GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
     }
@@ -687,11 +687,10 @@ static gboolean isMspVisible(const MSP const *msp,
     {
       /* Check the MSP in the current display range. Get the full MSP display range including
        * any portions outside the actual alignment. */
-      const gboolean showUnalignedSeq = blxContextGetFlag(bc, BLXFLAG_SHOW_UNALIGNED_SEQ);
-      const gboolean limitUnalignedBases = blxContextGetFlag(bc, BLXFLAG_LIMIT_UNALIGNED_BASES);
+      const gboolean seqSelected = blxWindowIsSeqSelected(blxWindow, msp->sSequence);
     
       IntRange mspDisplayRange;
-      mspGetFullQRange(msp, showUnalignedSeq, limitUnalignedBases, numUnalignedBases, bc->numFrames, &mspDisplayRange);
+      mspGetFullQRange(msp, seqSelected, bc->flags, numUnalignedBases, bc->mspList, bc->numFrames, &mspDisplayRange);
 
       /* Convert q coords to display coords */
       const int idx1 = convertDnaIdxToDisplayIdx(mspDisplayRange.min, bc->seqType, frame, bc->numFrames, bc->displayRev, &bc->refSeqRange, NULL);
@@ -1212,9 +1211,9 @@ static gboolean onButtonPressTreeHeader(GtkWidget *header, GdkEventButton *event
 	else if (event->type == GDK_2BUTTON_PRESS)
 	  {
 	    BlxViewContext *bc = treeGetContext(tree);
-            gboolean showSnpTrack = !blxContextGetFlag(bc, BLXFLAG_SHOW_SNP_TRACK);
+            gboolean showSnpTrack = !bc->flags[BLXFLAG_SHOW_SNP_TRACK];
             
-	    blxContextSetFlag(bc, BLXFLAG_SHOW_SNP_TRACK, showSnpTrack);
+	    bc->flags[BLXFLAG_SHOW_SNP_TRACK] = showSnpTrack;
             detailViewUpdateShowSnpTrack(detailView, showSnpTrack);
 	  }
 	
diff --git a/sequencecellrenderer.c b/sequencecellrenderer.c
index 99701860..7ec3d355 100755
--- a/sequencecellrenderer.c
+++ b/sequencecellrenderer.c
@@ -64,6 +64,7 @@ typedef struct _RenderData
     GdkLineStyle exonBoundaryStyleStart;
     GdkLineStyle exonBoundaryStyleEnd;
     gboolean showUnalignedSeq;
+    gboolean showUnalignedSelected;
     gboolean limitUnalignedBases;
     int numUnalignedBases;
   } RenderData;
@@ -576,8 +577,7 @@ static void drawBase(MSP *msp,
   const int displayIdx = segmentRange->min + segmentIdx;
   *qIdx = convertDisplayIdxToDnaIdx(displayIdx, data->bc->seqType, data->qFrame, 1, data->bc->numFrames, data->bc->displayRev, &data->bc->refSeqRange);
   
-  *sIdx = gapCoord(msp, *qIdx, data->bc->numFrames, data->qStrand, data->bc->displayRev, 
-		   data->showUnalignedSeq, data->limitUnalignedBases, data->numUnalignedBases);
+  *sIdx = gapCoord(msp, *qIdx, data->bc->numFrames, data->qStrand, data->bc->displayRev, data->seqSelected, data->numUnalignedBases, data->bc->flags, data->bc->mspList);
   
   /* Highlight the base if its base index is selected, or if its sequence is selected.
    * (If it is selected in both, show it in the normal color) */
@@ -814,7 +814,7 @@ static IntRange getVisibleMspRange(MSP *msp, RenderData *data)
 {
   /* Find the full display range of the MSP including any portions of unaligned sequence etc. */
   IntRange result;
-  mspGetFullQRange(msp, data->showUnalignedSeq, data->limitUnalignedBases, data->numUnalignedBases, data->bc->numFrames, &result);
+  mspGetFullQRange(msp, data->seqSelected, data->bc->flags, data->numUnalignedBases, data->bc->mspList, data->bc->numFrames, &result);
   
   /* Convert to display coords */
   const int idx1 = convertDnaIdxToDisplayIdx(result.min, data->bc->seqType, data->qFrame, data->bc->numFrames, data->bc->displayRev, &data->bc->refSeqRange, NULL);
@@ -978,7 +978,7 @@ static void drawMsps(SequenceCellRenderer *renderer,
   DetailViewProperties *detailViewProperties = detailViewGetProperties(treeProperties->detailView);
   BlxViewContext *bc = blxWindowGetContext(detailViewProperties->blxWindow);
   
-  const gboolean highlightDiffs = blxContextGetFlag(bc, BLXFLAG_HIGHLIGHT_DIFFS); /* swap match/mismatch colors if this is true */
+  const gboolean highlightDiffs = bc->flags[BLXFLAG_HIGHLIGHT_DIFFS]; /* swap match/mismatch colors if this is true */
   const MSP *firstMsp = (const MSP*)(renderer->mspGList->data);
   const BlxSequence *seq = firstMsp ? firstMsp->sSequence : NULL;
 
@@ -1029,8 +1029,9 @@ static void drawMsps(SequenceCellRenderer *renderer,
     detailViewProperties->exonBoundaryLineWidth,
     detailViewProperties->exonBoundaryLineStyleStart,
     detailViewProperties->exonBoundaryLineStyleEnd,
-    blxContextGetFlag(bc, BLXFLAG_SHOW_UNALIGNED_SEQ),
-    blxContextGetFlag(bc, BLXFLAG_LIMIT_UNALIGNED_BASES),
+    bc->flags[BLXFLAG_SHOW_UNALIGNED],
+    bc->flags[BLXFLAG_SHOW_UNALIGNED_SELECTED],
+    bc->flags[BLXFLAG_LIMIT_UNALIGNED_BASES],
     detailViewProperties->numUnalignedBases
   };  
   
diff --git a/utilities.c b/utilities.c
index 81b36bae..bd9342d6 100644
--- a/utilities.c
+++ b/utilities.c
@@ -489,6 +489,16 @@ gboolean typeIsExon(const BlxMspType mspType)
   return (mspType == BLXMSP_CDS || mspType == BLXMSP_UTR || mspType == BLXMSP_EXON);
 }
 
+gboolean typeIsIntron(const BlxMspType mspType)
+{
+  return (mspType == BLXMSP_INTRON);
+}
+
+gboolean typeIsMatch(const BlxMspType mspType)
+{
+  return (mspType == BLXMSP_MATCH);
+}
+
 
 gboolean mspIsExon(const MSP const *msp)
 {
@@ -872,9 +882,10 @@ int gapCoord(const MSP *msp,
 	     const int numFrames, 
 	     const BlxStrand strand, 
 	     const gboolean displayRev,
-	     const gboolean showUnalignedSeq,
-	     const gboolean limitUnalignedBases,
-	     const int numUnalignedBases)
+             const gboolean seqSelected,
+             const int numUnalignedBases,
+             gboolean *flags,
+             const MSP const *mspList)
 {
   int result = UNSET_INT;
   
@@ -940,7 +951,7 @@ int gapCoord(const MSP *msp,
           /* Get the full display range of the match sequence. If the result is still out of range
            * then there's nothing to show for this qIdx. */
           IntRange fullSRange;
-          mspGetFullSRange(msp, showUnalignedSeq, limitUnalignedBases, numUnalignedBases, &fullSRange);
+          mspGetFullSRange(msp, seqSelected, flags, numUnalignedBases, mspList, &fullSRange);
 
 	  if (!valueWithinRange(result, &fullSRange))
 	    {
@@ -1049,27 +1060,58 @@ const char *mspGetSName(const MSP const *msp)
 }
 
 
+/* Returns true if there is a polyA site at the 3' end of this MSP's alignment range. */
+static gboolean mspHasPolyATail(const MSP const *msp, const MSP const *mspList)
+{
+  /* For now, loop through all poly A sites and see if the site coord matches the 3' end coord of
+   * the alignment. If speed proves to be an issue we could do some pre-processing to link MSPs 
+   * to relevant polyA signals/sites so that we don't have to loop each time we want to check. */
+  const MSP *curMsp = mspList;
+  gboolean found = FALSE;
+   
+  for ( ; !found && curMsp; curMsp = curMsp->next)
+    {
+      if (mspIsPolyASite(curMsp))
+        {
+          const int qEnd = mspGetQEnd(msp);
+          
+          if (mspGetRefStrand(msp) == BLXSTRAND_FORWARD)
+            {
+              found = (qEnd == curMsp->qRange.min);
+            }
+          else
+            {
+              found = (qEnd == curMsp->qRange.min + 1);
+            }
+        }
+    }
+  
+  return found;
+}
+
+
 /* Get the full range of the given MSP that we want to display, in s coords. This will generally 
  * be the coords of the alignment but could extend outside this range we are displaying unaligned 
  * portions of the match sequence or polyA tails etc. */
 void mspGetFullSRange(const MSP const *msp, 
-                      const gboolean showUnalignedSeq,
-                      const gboolean limitUnalignedBases, 
+                      const gboolean seqSelected,
+                      const gboolean *flags,
                       const int numUnalignedBases, 
+                      const MSP const *mspList,
                       IntRange *result)
 {
   /* Normally we just display the part of the sequence in the alignment */
   result->min = msp->sRange.min;
   result->max = msp->sRange.max;
   
-  if (showUnalignedSeq && mspGetMatchSeq(msp)) /* must have the sequence data to be able to do this... */
+  if (flags[BLXFLAG_SHOW_UNALIGNED] && mspGetMatchSeq(msp) && (!flags[BLXFLAG_SHOW_UNALIGNED_SELECTED] || seqSelected))
     {
       /* We're displaying additional unaligned sequence outside the alignment range. Get 
        * the full range of the match sequence */
       result->min = 1;
       result->max = mspGetMatchSeqLen(msp);
   
-      if (limitUnalignedBases)
+      if (flags[BLXFLAG_LIMIT_UNALIGNED_BASES])
         {
           /* Only include up to 'numUnalignedBases' each side of the MSP range (still limited
            * to the range we found above, though). */
@@ -1077,6 +1119,23 @@ void mspGetFullSRange(const MSP const *msp,
           result->max = min(result->max, msp->sRange.max + numUnalignedBases);
         }
     }
+    
+  if (flags[BLXFLAG_SHOW_POLYA] && mspHasPolyATail(msp, mspList) && (!flags[BLXFLAG_SHOW_POLYA_SELECTED] || seqSelected))
+    {
+      /* We're displaying polyA tails, so override the 3' end coord with the full extent of
+       * the s sequence if there is a polyA site here. The 3' end is the min q coord if the
+       * match is on forward ref seq strand or the max coord if on the reverse. */
+      const gboolean sameDirection = (mspGetRefStrand(msp) == mspGetMatchStrand(msp));
+      
+      if (sameDirection)
+        {
+          result->max = mspGetMatchSeqLen(msp);
+        }
+      else
+        {
+          result->min = 1;
+        }
+    }
 }
 
 
@@ -1084,9 +1143,10 @@ void mspGetFullSRange(const MSP const *msp,
  * be the coords of the alignment but could extend outside this range we are displaying unaligned 
  * portions of the match sequence or polyA tails etc. */
 void mspGetFullQRange(const MSP const *msp, 
-                      const gboolean showUnalignedSeq,
-                      const gboolean limitUnalignedBases, 
+                      const gboolean seqSelected,
+                      const gboolean *flags,
                       const int numUnalignedBases, 
+                      const MSP const *mspList,
                       const int numFrames,
                       IntRange *result)
 {
@@ -1094,11 +1154,11 @@ void mspGetFullQRange(const MSP const *msp,
   result->min = msp->qRange.min;
   result->max = msp->qRange.max;
 
-  if (showUnalignedSeq)
+  if (flags[BLXFLAG_SHOW_UNALIGNED] || flags[BLXFLAG_SHOW_POLYA])
     {
-
+      /* Get the full display range of the MSP including any unaligned portions of the match sequence. */
       IntRange fullSRange;
-      mspGetFullSRange(msp, showUnalignedSeq, limitUnalignedBases, numUnalignedBases, &fullSRange);
+      mspGetFullSRange(msp, seqSelected, flags, numUnalignedBases, mspList, &fullSRange);
       
       /* Find the offset of the start and end of the full range compared to the alignment range and
        * offset the ref seq range by the same amount. We need to multiply by the number of reading
diff --git a/utilities.h b/utilities.h
index 88f0c96b..e9d0bbde 100644
--- a/utilities.h
+++ b/utilities.h
@@ -87,6 +87,30 @@
 #define BLX_BURLYWOOD	      "#deb887"
 #define BLX_TAN		      "#d2b48c"
 
+
+/* This enum contains a list of all the boolean options that the user can toggle on/off */
+typedef enum
+  {
+    BLXFLAG_MIN,		    /* Start index for looping through flags */
+  
+    BLXFLAG_SQUASH_MATCHES,	    /* Puts all MSPs from the same sequence on the same row in the detail view */
+    BLXFLAG_INVERT_SORT,	    /* Inverts the default sort order */
+    BLXFLAG_HIGHLIGHT_DIFFS,	    /* Hides matching bases and highlights mis-matching ones */
+    BLXFLAG_SHOW_SNP_TRACK,	    /* Shows the SNP track */
+    BLXFLAG_SHOW_UNALIGNED,	    /* Shows additional bits of the match sequence that are not part of the aligned section */
+    BLXFLAG_SHOW_UNALIGNED_SELECTED,/* Only show unaligned bits of sequence for the currently-selected sequence(s) */
+    BLXFLAG_LIMIT_UNALIGNED_BASES,  /* If the show-unaligned-sequence option is on, limits how many bases from the unaligned sequence are shown */
+    BLXFLAG_SHOW_POLYA,             /* Show polyA tails */
+    BLXFLAG_SHOW_POLYA_SELECTED,    /* Only show polyA tails for the currently-selected sequence(s) */
+    BLXFLAG_SHOW_SPLICE_SITES,	    /* Highlights splice sites in the reference sequence for the currently-selected MSPs */
+    BLXFLAG_EMBL_DATA_LOADED,       /* Gets set to true if the full EMBL data is parsed and populated in the MSPs */
+    BLXFLAG_SHOW_CDS,               /* True if CDS/UTR regions should be shown; false if plain exons should be shown */
+    
+    BLXFLAG_NUM_FLAGS		    /* Number of flags, for looping through flags or creating an array */
+  } BlxFlag;
+
+
+
 /* Function pointer for callback functions used by widgets on dialog boxes. */
 typedef gboolean (*BlxResponseCallback)(GtkWidget *widget, const gint responseId, gpointer data);
 
@@ -138,6 +162,8 @@ void		      adjustColorBrightness(GdkColor *origColor, const double factor, GdkC
 
 gboolean              mspLayerIsVisible(const MSP const *msp);
 gboolean              typeIsExon(const BlxMspType mspType);
+gboolean              typeIsIntron(const BlxMspType mspType);
+gboolean              typeIsMatch(const BlxMspType mspType);
 gboolean	      mspIsExon(const MSP const *msp);
 gboolean	      mspIsIntron(const MSP const *msp);
 gboolean	      mspIsSnp(const MSP const *msp);
@@ -198,8 +224,21 @@ char*                 mspGetGeneName(const MSP const *msp);
 char*                 mspGetTissueType(const MSP const *msp);
 char*                 mspGetStrain(const MSP const *msp);
 char*                 mspGetCoordsAsString(const MSP const *msp);
-void                  mspGetFullSRange(const MSP const *msp, const gboolean showUnalignedSeq, const gboolean limitUnalignedBases, const int numUnalignedBases, IntRange *sSeqRange);
-void                  mspGetFullQRange(const MSP const *msp, const gboolean showUnalignedSeq, const gboolean limitUnalignedBases, const int numUnalignedBases, const int numFrames, IntRange *sSeqRange);
+
+void                  mspGetFullSRange(const MSP const *msp, 
+                                       const gboolean seqSelected,
+                                       const gboolean *flags,
+                                       const int numUnalignedBases, 
+                                       const MSP const *mspList,
+                                       IntRange *sSeqRange);
+
+void                  mspGetFullQRange(const MSP const *msp, 
+                                       const gboolean seqSelected,
+                                       const gboolean *flags,
+                                       const int numUnalignedBases, 
+                                       const MSP const *mspList,
+                                       const int numFrames, 
+                                       IntRange *sSeqRange);
 
 char                  getStrandAsChar(const BlxStrand strand);
 
@@ -234,9 +273,10 @@ int		      gapCoord(const MSP *msp,
 			       const int numFrames, 
 			       const BlxStrand strand, 
 			       const gboolean displayRev,
-			       const gboolean showUnalignedSeq,
-			       const gboolean limitUnalignedBases,
-			       const gboolean numUnalignedBases);
+                               const gboolean seqSelected,
+                               const int numUnalignedBases,
+                               gboolean *flags,
+                               const MSP const *mspList);
 
 int		      wildcardSearch(const char *textToSearch, const char *searchStr);
 
-- 
GitLab