// // Copyright 2002 Perforce Software. All rights reserved. // // This file is part of Perforce - the FAST SCM System. // // p4wAnnotatePane: // Displays the result of the annotate command on a // file browser page #include <p4wp4.h> #include "p4wStrBuf.h" #include "p4wHtml.h" #include "p4wPane.h" #include "p4wAnnotatePane.h" p4wAnnotatePane::p4wAnnotatePane( p4wView & ParentView, p4wRequest & Request, const char *headRev, const char *headChange ) : p4wPane( ParentView, Request ), fSeenData(0), fLineCtr(-1), fShowUser(0), fShowRvNo(0), fShowIntg(0) { fHeadRev.Set( headRev ); fHeadChg.Set( headChange ); fTopChg.Set( headChange ); if (fRequest.GetCmd() == AC_ANNOTATE || fRequest.GetCmd() == AC_FULLANNOTATE) fRangeWidth = fHeadRev.Length(); else fRangeWidth = fHeadChg.Length(); } p4wAnnotatePane::~p4wAnnotatePane() { } void p4wAnnotatePane::Begin() { // // Begin the pane. p4wHtml htm; htm.comment( "BEGIN ANNOTATE PANE" ); fRequest << htm; const StrPtr *M = fRequest.GetStateArg("M"); if (fRequest.GetCmd() == AC_ANNOTATE || fRequest.GetCmd() == AC_FULLANNOTATE) { fRequest.SetCmd((M && strchr(M->Text(), 'd')) ? AC_FULLANNOTATE : AC_ANNOTATE); } else { const StrPtr *fl = fRequest.GetDynArg( "fl" ); // fl=-i for integrated if (fl && strcmp(fl->Text(), "-i")) fl = NULL; fShowIntg = fl ? 1 : 0; fShowRvNo = (M && strchr(M->Text(), 'r')) ? 1 : 0; fShowUser = (M && strchr(M->Text(), 'u')) ? 1 : 0; fRequest.SetCmd((M && strchr(M->Text(), 'd')) ? AC_CHGLISTFULLANNOTATE : AC_CHGLISTANNOTATE); if (fShowRvNo && !fl) { fChgWidth = fHeadRev.Length(); if (fChgWidth < 3) fChgWidth = 3; } fRangeWidth = fChgWidth; int i; int j = fRangeWidth; if (fRequest.GetCmd() == AC_CHGLISTFULLANNOTATE) { j *= 2; fLastSame << " "; } for (i = -1; ++i < j; ) fLastSame << " "; if (fShowUser) { j = fUsrWidth; if (fRequest.GetCmd() == AC_CHGLISTFULLANNOTATE) { j *= 2; fLastSame << " "; } for (i = -1; ++i < j; ) fLastSame << " "; fLastSame << " "; } fLastSame << "│"; int extra = fChgWidth - 3; if (extra < 0) extra = 0; extra /= 2; for (i = -1; ++i < fChgWidth-extra; ) { if (i < 3) fDots << "."; else fDots << " "; } const StrPtr *rev1 = fRequest.GetDynArg( "rev1" ); if (rev1) { StrBuf sea; const StrPtr *rev2 = fRequest.GetDynArg( "rev2" ); if (rev2) sea << "\n" << rev2 << "\t"; else sea << "\n" << rev1 << "\t"; char *p = strstr((*fFileLog).Text(), sea.Text()); if (p) { p += sea.Length(); char *q = strchr(p, '\t'); if (q) { fTopChg.Clear(); fTopChg.Append(p, q-p); } } } } } void p4wAnnotatePane::Render( StrDict *varList ) { p4wHtml htm( 1 ); p4wURL urlMaker; StrBuf clearIcon; urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); if( !fSeenData ) { fSeenData = 1; int chgann = ((fRequest.GetCmd() == AC_CHGLISTANNOTATE) || (fRequest.GetCmd() == AC_CHGLISTFULLANNOTATE)); htm.beginTRow(); htm.beginCol(); StrBuf actionURL; StrBuf path; if( fRequest.GetViewMode() == VM_WORKSPACE ) fRequest.UseNewBase( path, NULL, "path", fRequest.GetPath().Text() ); else path << fRequest.GetURL(); fRequest.ConstructSafeURL( actionURL, path.Text(), AC_CHGLISTANNOTATEPROC, NULL ); StrBuf revRange; const StrPtr *rev1 = fRequest.GetDynArg( "rev1" ); const StrPtr *rev2 = fRequest.GetDynArg( "rev2" ); const StrPtr *srev = fRequest.GetDynArg( "sr" ); const StrPtr *srev2 = fRequest.GetDynArg( "sr2" ); if( rev1 ) { revRange << "#" << rev1; if ( rev2 ) revRange << "," << rev2; } else if( srev ) { revRange << "@" << srev; if ( srev2 ) revRange << "," << srev2; } htm.beginTable(0,"100%","0", "0", "#FFFFFF"); htm.beginTRow(); htm.beginCol(); htm.icon( clearIcon.Text(), "15", "10", "", 1 ); htm.endCol(); htm.endTRow(); htm.beginTRow(); htm.beginCol(); htm.icon( clearIcon.Text(), "5", "10", "", 1 ); htm.endCol(); htm.beginCol(0,0,"100%",0,0,"100%"); if (chgann) { htm.beginForm(actionURL.Text(), "annOpts"); htm.icon( clearIcon.Text(), "5", "10", "", 1 ); htm.text( "Show: " ); if (!fShowIntg) { htm.radio( "showRevNo", "n", fShowRvNo ? 0 : 1, 0, "chgno" ); htm.label("changelists ", "chgno"); htm.radio( "showRevNo", "y", fShowRvNo ? 1 : 0, 0, "revno" ); htm.label("revision numbers", "revno"); htm.icon( clearIcon.Text(), "5", "15", "", 1 ); } else { if (fShowRvNo) htm.hiddenField("showRevNo", "y"); fShowRvNo = 0; } htm.checkbox( "showUser", "y", fShowUser ? 1 : 0 ); htm.label("users", "showUser"); htm.icon( clearIcon.Text(), "5", "15", "", 1 ); htm.checkbox( "showDelLi", "y", fRequest.GetCmd() == AC_CHGLISTFULLANNOTATE ? 1 : 0 ); htm.label("deleted lines", "showDelLi"); htm.icon( clearIcon.Text(), "5", "30", "", 1 ); htm.text( "Revision range: " ); htm.textField( "RevRange", revRange.Text(), 24 ); htm.icon( clearIcon.Text(), "5", "20", "", 1 ); htm.button("Go", "Redisplay"); htm.endForm(); } else { htm.beginForm(actionURL.Text(), "annOpts"); htm.hiddenField("noDetails", "y"); if (fShowRvNo) htm.hiddenField("showRevNo", "y"); if (fShowUser) htm.hiddenField("showUser", "y"); htm.icon( clearIcon.Text(), "5", "5", "", 1 ); htm.checkbox( "showDelLi", "y", fRequest.GetCmd() == AC_FULLANNOTATE ? 1 : 0 ); htm.label("Show deleted lines", "showDelLi"); htm.icon( clearIcon.Text(), "5", "40", "", 1 ); htm.text( "Revision range: " ); htm.textField( "RevRange", revRange.Text(), 24 ); htm.icon( clearIcon.Text(), "5", "20", "", 1 ); htm.button("Go", "Redisplay"); htm.endForm(); } htm.endCol(); htm.endTRow(); if (strstr(fRequest.GetUserAgent().Text(), "Opera")) { htm.beginTRow(); // Opera requires manual vertical spacing after a form htm.beginCol(); htm.icon( clearIcon.Text(), "15", "1", "", 1 ); htm.endCol(); htm.endTRow(); } htm.beginTRow(); htm.beginCol(); htm.endCol(); htm.beginCol(0,0,"100%",0,0,"100%",0,0,0,"border-top: 1px solid #D1D5DA;"); htm.icon( clearIcon.Text(), "1", "100%", "", 1 ); htm.endCol(); htm.beginCol(); htm.icon( clearIcon.Text(), "5", "11", "", 1 ); htm.endCol(); htm.endTRow(); htm.endTable(); htm.endCol(); htm.endTRow(); htm.beginTRow(); htm.beginCol(); htm.beginTable( "0", "100%", "8", "1", "#FFFFFF" ); htm.beginTRow( NULL, NULL, "#FFFFFF" ); htm.beginCol(); htm.beginTable( "0", "100%", "2", "0", "#FFFFFF" ); htm.beginTRow(); htm.beginCol(); htm.beginUnformatted(); fRequest << htm; htm.Clear(); } // // Check whether page content limit has been exceeded if( PageLimitExceeded() ) return; if( (fRequest.GetCmd() == AC_CHGLISTANNOTATE) || (fRequest.GetCmd() == AC_CHGLISTFULLANNOTATE) ) { RenderWithChgs( varList ); return; } // // Get the range values and the line of data StrPtr *lower = varList->GetVar( "lower" ); StrPtr *upper = varList->GetVar( "upper" ); StrPtr *data = varList->GetVar( "data" ); if( !lower && !upper && !data ) return; // // Construct the range field, blankpadding if needed StrBuf range; for( int i = 0; i < fRangeWidth - lower->Length(); i++ ) range << " "; range << lower; if( fRequest.GetCmd() == AC_FULLANNOTATE ) { range << "-"; for( int j = 0; j < fRangeWidth - upper->Length(); j++ ) range << " "; range << upper; } // // Construct the url for generating a subset of the // filelog based on the range StrBufDict args; StrBuf url; int version; if( fRequest.GetCmd() != AC_FULLANNOTATE ) { args.SetVar( "rev1", lower ); version = fHeadRev.Atoi(); } else { // // If this is a deleted line, include the // version where the line was deleted in the // range. int h; if( upper->Atoi() < fHeadRev.Atoi() ) h = upper->Atoi() + 1; else h = upper->Atoi(); args.SetVar( "rev1", h ); args.SetVar( "mx", h - lower->Atoi() + 1 ); version = upper->Atoi(); } fRequest.ConstructSafeURL( url, fRequest.GetURL().Text(), AC_BROWSEFILE, &args ); // // Generate the output. Deleted lines are shown in // red strikethu font. htm.beginLink( url.Text(), NULL, "fixed" ); htm.text( p4wStrBuf().EscapeHTML( StrBuf(range), Unicode() ).Text() ); htm.endLink(); htm.text( ":" ); const StrPtr *rev1 = fRequest.GetDynArg( "rev1" ); int top = rev1 ? rev1->Atoi() : fHeadRev.Atoi(); if( version < top ) { htm.text( p4wStrBuf().EscapeHTML( *data, Unicode() ).Text(), "strike", 0, "FF0000" ); } else { htm.text( p4wStrBuf().EscapeHTML( *data, Unicode() ).Text() ); } if ( *(data->Text() + data->Length() - 1) != '\n') htm << "\n"; fRequest << htm; } void p4wAnnotatePane::RenderWithChgs( StrDict *varList ) { int i; p4wHtml htm( 1 ); // // Get the range values and the line of data StrPtr *chg1 = varList->GetVar( "lower" ); StrPtr *chg2 = varList->GetVar( "upper" ); StrPtr *data = varList->GetVar( "data" ); if( !chg1 && !chg2 && !data ) return; int deleted = (chg2->Atoi() < fTopChg.Atoi()) ? 1 : 0; if ( fLastChg1 == *chg1 && fLastChg2 == *chg2 ) { htm << fLastSame; if( deleted ) { htm.text( p4wStrBuf().EscapeHTML( *data, Unicode() ).Text(), "strike", 0, "FF0000" ); } else { htm.text( p4wStrBuf().EscapeHTML( *data, Unicode() ).Text() ); } fRequest << htm; return; } if (++fLineCtr & 0x01) { htm.beginDiv(0, "alt_row"); } else if (fLineCtr > 1) htm.endDiv(); fLastChg1.Set(chg1); fLastChg2.Set(chg2); // // Construct the chglist fields, blankpadding if needed // Construct the user fields and any actions fields StrBuf rev1; StrBuf rev2; StrBuf user1; StrBuf user2; StrBuf action1; StrBuf action2; StrBuf title1; StrBuf title2; char *p; char *q; // get rev1, user1 and action1 from chg1 StrBuf sea; sea << "\t" << chg1 << "\t"; p = strstr((*fFileLog).Text(), sea.Text()); if (p) { for (q = p; *--q != '\n'; ) ; if (q) rev1.Append(q+1, p-q-1); p += sea.Length(); q = strchr(p, '\t'); if (q) { user1.Append(p, q-p); p = q+1; q = strchr(p, '\t'); if (q) action1.Append(p, q-p); } } title1 << "#" << rev1 << " " << user1; if (action1.Length()) title1 << " " << action1; while(fUsrWidth > user1.Length()) user1 << " "; // if deleted, we actually display the chg where it was deleted // which is one more revision than p4 annotate gives StrBuf onemore; if (deleted) { sea.Clear(); sea << "\t" << chg2 << "\t"; p = strstr((*fFileLog).Text(), sea.Text()); if (p) { for (q = p; *--q != '\n'; ) ; if (q) rev2.Append(q+1, p-q-1); } else rev2 << "-1"; //we're in trouble - this will cause least problems i = rev2.Atoi(); rev2.Clear(); rev2 << ++i; sea.Clear(); sea << "\n" << rev2 << "\t"; p = strstr((*fFileLog).Text(), sea.Text()); if (p) { p += sea.Length(); q = strchr(p, '\t'); if (q) { onemore.Append(p, q-p); chg2 = &onemore; } } } // get rev2, user2 and action2 from chg2 sea.Clear(); sea << "\t" << chg2 << "\t"; p = strstr((*fFileLog).Text(), sea.Text()); if (p) { for (q = p; *--q != '\n'; ) ; if (q) { rev2.Clear(); rev2.Append(q+1, p-q-1); } p += sea.Length(); q = strchr(p, '\t'); if (q) { user2.Append(p, q-p); p = q+1; q = strchr(p, '\t'); if (q) action2.Append(p, q-p); } } title2 << "#" << rev2 << " " << user2; if (action2.Length()) title2 << " " << action1; while(fUsrWidth > user2.Length()) user2 << " "; // // Construct the urls for generating link to the corresponding changelist StrBufDict args; StrBuf url1; StrBuf url2; StrBuf newBase; p4wURL urlMaker; if (fShowRvNo) { newBase.Set(fRequest.GetURL()); if (fRequest.GetCmd() == AC_CHGLISTANNOTATE) { args.SetVar( "rev1", rev1 ); fRequest.ConstructSafeURL( url1, newBase.Text(), AC_BROWSEFILE, &args ); } else { args.SetVar( "rev1", rev2 ); args.SetVar( "mx", rev2.Atoi() - rev1.Atoi() + 1 ); fRequest.ConstructSafeURL( url2, newBase.Text(), AC_BROWSEFILE, &args ); url1 << url2 << "#" << rev1; } } else { fRequest.UseNewBase( newBase, NULL, "path", NULL ); newBase << chg1; urlMaker.ConstructURL( url1, newBase.Text(), AC_DESCRIBE, NULL, fRequest.GetUnicode() ); fRequest.UseNewBase( newBase, NULL, "path", NULL ); newBase << chg2; urlMaker.ConstructURL( url2, newBase.Text(), AC_DESCRIBE, NULL, fRequest.GetUnicode() ); } // // Generate the output. // Deleted lines are shown in red strike thru font. StrPtr *nbr1 = fShowRvNo ? &rev1 : chg1; StrBuf id; id << "id_" << fLineCtr; StrBuf jsAction; jsAction << "showPopup('" << fLineCtr << "','" << chg1->Text() << "',-1,'')"; htm.beginLink( url1.Text(), NULL, "fixed", jsAction.Text(), 0/*title1.Text()*/, id.Text() ); for( i = 0; i < fChgWidth - nbr1->Length(); i++ ) htm << " "; htm.text( nbr1->Text() ); htm.endLink(); if (fShowUser) htm << " " << user1; if (fRequest.GetCmd() == AC_CHGLISTFULLANNOTATE) { htm << " "; if (fShowUser) htm << " "; StrPtr *nbr2 = fShowRvNo ? &rev2 : chg2; int lgth = ( deleted ) ? nbr2->Length() : fDots.Length(); if( deleted ) { id.Clear(); id << "id_b" << fLineCtr; jsAction.Clear(); jsAction << "showPopup('b" << fLineCtr << "','" << chg2->Text() << "',-1,'')"; htm.beginLink( url2.Text(), NULL, "fixed", jsAction.Text(), 0/*title2.Text()*/, id.Text() ); for( i = 0; i < fChgWidth - lgth; i++ ) htm << " "; htm.text( nbr2->Text() ); htm.endLink(); } else { for( i = 0; i < fChgWidth - lgth; i++ ) htm << " "; htm << fDots; } if (fShowUser) { htm << " "; if( deleted ) htm << user2; else { htm << "..."; int j = user2.Length() - 3; while (j-- > 0) htm << " "; } } } htm << "│"; if( deleted ) { htm.text( p4wStrBuf().EscapeHTML( *data, Unicode() ).Text(), "strike", 0, "FF0000" ); } else { htm.text( p4wStrBuf().EscapeHTML( *data, Unicode() ).Text() ); } fRequest << htm; } void p4wAnnotatePane::End() { // // End the pane. p4wHtml htm; if( fSeenData ) { if ((fLineCtr & 0x01) && (fLineCtr > 1)) htm.endDiv(); htm.endUnformatted(); htm.endCol(); htm.endTRow(); htm.endTable(); htm.endCol(); htm.endTRow(); htm.endTable(); } else { htm.beginTRow(); htm.beginCol(); htm.text( "File not found, or file is empty." ); htm.endCol(); htm.endTRow(); } htm.comment( "END ANNOTATE PANE" ); fRequest << htm; }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#1 | 12234 | Matt Attaway |
Rejigger P4Web project in preparation for official sunsetting The bin directory contains the last official builds of P4Web from the Perforce download site. P4Web is soon to be completely sunsetted; these builds are here for folks who don't want to build their own. To better handle the archived builds the source code has been moved into a separate src directory. |
||
//guest/perforce_software/p4web/Panes/p4wAnnotatePane.cpp | |||||
#1 | 8914 | Matt Attaway | Initial add of the P4Web source code |