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