// // Copyright 2003 Perforce Software. All rights reserved. // // This file is part of Perforce - the FAST SCM System. // // p4wSpecPane: // A mix-in class for spec panes. A spec pane either displays the // current spec or generates a form for editting the spec. #include <p4wp4.h> #include "p4wStrBuf.h" #include "p4wHtml.h" #include "p4wPane.h" #include "p4wSpecPane.h" p4wSpecPane::p4wSpecPane( p4wView & ParentView, p4wRequest & Request, int isForm ) : p4wPane( ParentView, Request ) { fFirstErrorOnly = fExistsNotNew = 0; fIsForm = isForm; fVarList = NULL; // // Initialize the gui hints variables to some reasonable // defaults. fTextALen = 72; fNCols = 2; fIsOpen = 0; fLeaveOpen = 0; fIsMiddle = 0; fIsIndented = 0; } p4wSpecPane::~p4wSpecPane() { } // ------------------------------------- // Render functions. // void p4wSpecPane::Begin() { // // Begin the pane. p4wHtml htm; p4wURL urlMaker; StrBuf clearIcon; StrBuf grayIcon; htm.comment( "BEGIN SPEC PANE" ); // // If this is a form, build the action value and // start the form; StrBuf Action; if (fVarList && !fVarList->GetVar("Update") && !fVarList->GetVar("Access")) Action << "New "; else { Action << "Edit "; if (fRequest.GetCmd() == AC_EDITNEWCLIENT) SetActionOverride(AC_EDITEXISTINGCLIENT); } if( fIsForm ) { StrBuf actionURL; GetAction( actionURL ); htm.beginForm( actionURL.Text(), "spec" ); } switch( fRequest.GetCmd() ) { case AC_BRANCHVIEW: fTitle.Set( "Branch Specification:" ); break; case AC_CLIENTVIEW: fTitle.Set( "Client Specification:" ); break; case AC_JOBVIEW: fTitle.Set( "Job Specification:" ); break; case AC_USERVIEW: fTitle.Set( "User Specification:" ); break; case AC_LABELVIEW: fTitle.Set( "Label Specification:" ); break; case AC_CONFIGURATION: fTitle.Set( "Settings:" ); break; case AC_MULTIUSER: fTitle.Set( "Choose Client:" ); break; case AC_P4CMDPANE: fTitle.Set( "P4 Cmd:" ); break; case AC_EDITLABEL: fTitle.Set( Action ); fTitle << "Label Spec:"; break; case AC_EDITBRANCH: fTitle.Set( Action ); fTitle << "Branch Spec:"; break; case AC_EDITNEWCLIENT: if (!strcmp(Action.Text(), "Edit ")) { Action.Set(""); fTitle << Action; fExistsNotNew = fPrintedError = 1; break; } case AC_EDITCLIENT: fTitle.Set( Action ); fTitle << "Client:"; break; case AC_EDITJOB: fTitle.Set( "Edit Job:" ); break; case AC_EDITUSER: fTitle.Set( Action ); fTitle << "User:"; break; case AC_CHANGEPENDINGEDIT: fTitle.Set( "Pending Changelist - " ); if( fRequest.GetDynArg( "arg" ) ) fTitle << fRequest.GetDynArg( "arg" ) << ":"; else fTitle << "Default:"; break; case AC_FIXESJOB: fTitle.Set( "View Fixes for Job:" ); break; case AC_CREATELABEL: fTitle.Set( "Create Label Spec:" ); break; case AC_CREATELABELTMP: fTitle.Set( "Use Label " ); fTitle << p4wStrBuf().EscapeHTML(fRequest.GetURL(), Unicode()).Text() << " as Template:"; break; case AC_CREATEBRANCH: fTitle.Set( "Create Branch Spec:" ); break; case AC_CREATEJOB: fTitle.Set( "Create Job:" ); break; default: break; } // Output the title urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); urlMaker.ConstructURL( grayIcon, "/grayPixelIcon", AC_ICON, NULL ); if( fTitle.Length() && (fRequest.GetScreenChunks() & SCRN_TITLE)) { htm.beginTRow(); htm.beginCol(); htm.beginSpan( "title" ); htm << fTitle; htm.endSpan(); htm.endCol(); htm.endTRow(); htm.beginTRow(); htm.beginCol( "top" ); htm.icon( clearIcon.Text(), "2", "100%", "", 1, "0", "0" ); htm.icon( grayIcon.Text(), "1", "100%", "", 1, "0", "0" ); htm.endCol(); htm.endTRow(); htm.beginTRow(); htm.beginCol(); htm.beginTable( "0", "100%", "8", "1" ); htm.beginTRow( NULL, NULL, "#FFFFFF" ); htm.beginCol(); htm.beginTable( "0", NULL, "2", "0", "#FFFFFF" ); } fRequest << htm; } void p4wSpecPane::Render( StrDict * varList ) { // // if type name of existing P4 object when // attempting to create a new P4 object, // output a Yes/No confirm page if (fExistsNotNew) RenderExistsNotNew( varList ); // // Generate the data either in a form or not else if( fIsForm ) renderForm( varList ); else renderNoForm( varList ); } void p4wSpecPane::RenderError( char *data, int escapeHTML ) { RenderErrorTbl( data, escapeHTML ); } void p4wSpecPane::End() { // // End the pane. p4wHtml htm; p4wURL urlMaker; StrBuf clearIcon; StrBuf actionURL; urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); // // Generate a submit button only if this is a form and // if no error occurred. if( !fPrintedError && fIsForm ) { htm.beginTRow(); if( fNCols > 2 ) htm.beginCol( NULL, "center", "4" ); else htm.beginCol( NULL, "center", "2" ); htm.button( NULL, "Save" ); htm.endCol(); htm.endTRow(); } // // Generate an Edit button on the client or user // spec page if viewing the current client or current // user if( ( fRequest.GetCmd() == AC_USERVIEW && ( !fRequest.GetURL().Length() || fRequest.GetURL() == fRequest.GetUser() ) ) || ( fRequest.GetCmd() == AC_CLIENTVIEW && ( !fRequest.GetURL().Length() || fRequest.GetURL() == fRequest.GetClient() ) ) ) { if( fRequest.GetCmd() == AC_USERVIEW ) ConstructAction( AC_EDITUSER, NULL, 0, actionURL ); else ConstructAction( AC_EDITCLIENT, NULL, 0, actionURL ); htm.beginTRow(); htm.beginCol(); htm.icon( clearIcon.Text(), "15", "0", "", 1 ); htm.endCol(); htm.endTRow(); htm.beginTRow(); if( fNCols > 2 ) htm.beginCol( NULL, "center", "4" ); else htm.beginCol( NULL, "center", "2" ); if (!fRequest.GetBrowseMode()) { htm.beginForm( actionURL.Text() ); htm.button( NULL, "Edit" ); htm.endCol(); htm.endTRow(); ++fIsForm; } else { htm.endCol(); htm.endTRow(); } } switch( fRequest.GetCmd() ) { case AC_USERVIEW: case AC_CLIENTVIEW: case AC_BRANCHVIEW: case AC_JOBVIEW: case AC_LABELVIEW: case AC_CONFIGURATION: case AC_MULTIUSER: case AC_P4CMDPANE: case AC_EDITLABEL: case AC_EDITBRANCH: case AC_EDITCLIENT: case AC_EDITNEWCLIENT: case AC_EDITUSER: case AC_FIXESJOB: case AC_CREATELABEL: case AC_CREATELABELTMP: case AC_CREATEBRANCH: case AC_CREATEJOB: case AC_EDITJOB: htm.endTable(); htm.endCol(); htm.endTRow(); htm.endTable(); htm.endCol(); htm.endTRow(); break; case AC_JOBFIELDS: htm.endTable(); htm.endCol(); htm.endTRow(); break; default: break; } if( fIsForm ) htm.endForm(); htm.comment( "END SPEC PANE" ); fRequest << htm; } void p4wSpecPane::ConstructAction( AllCommands ac, StrBufDict *args, int usePath, StrBuf & actionURL ) { // // Generate the ACTION value for the form, using the // passed-in AC code & dynamic arguments. p4wURL url; if( usePath ) { // // If the user is in workspace mode, this url // must be absolute not relative in order to work // correctly with some browsers (such as Netscape 4.X). // Also, use an absolute path if the node part of the // url has a leading '/' (as might be the case if the // entity is a label, client, branch, etc ). StrBuf path; if( fRequest.GetViewMode() == VM_WORKSPACE || ( fRequest.GetURL().Length() && *fRequest.GetURL().Text() == '/' ) ) fRequest.UseNewBase( path, NULL, "path", fRequest.GetPath().Text() ); else path << fRequest.GetURL(); fRequest.ConstructSafeURL( actionURL, path.Text(), ac, args ); } else { url.ConstructURL( actionURL, NULL, ac, args, fRequest.GetUnicode() ); } const StrPtr *mu = fRequest.GetDynArg("mu"); if (mu) actionURL << "&mu=" << mu; } void p4wSpecPane::renderNoForm( StrDict * varList ) { p4wHtml htm; StrBuf tagOut; for( int i = 0; i < fSpec->Count(); i++ ) { // Get our spec element. SpecElem *se = fSpec->Get( i ); const char *val = NULL; // Ignore empty fields UNLESS this is a list. If this // is a list // the values are stored in <varname>0, <varname>1, etc. if( !se->IsList() && ( varList->GetVar( se->tag.Text() ) == NULL) ) continue; // Get the value of this field. if( varList->GetVar( se->tag.Text() ) != NULL ) val = varList->GetVar( se->tag.Text() )->Text(); // // Clear the html source string htm.Clear(); // // Append a colon to the field name used as the title tagOut.Set( se->tag ); tagOut << ":"; // // Let the child class tell us what fields to ignore and // which fields // it needs to process directly. if( IgnoreField(se->tag.Text()) ) continue; if( CustomField(se->tag.Text(), varList) ) continue; if( se->IsDate() ) { // Temporary until the server gives us int time: // insure that time is an int char time_buffer[255 + 1]; time_buffer[0] = '\0'; if( val ) { Error e; DateTime t; t.Set(val, &e); time_t iTime = (time_t) t.Value(); // Do our own strftime StrBuf time_format; time_format.Append( "%Y/%m/%d %H:%M" ); strftime( time_buffer, sizeof( time_buffer ), time_format.Text(), localtime( &iTime ) ); } // // Build a buffer that we can expand the time template into. // StrBuf timeTemplate; // timeTemplate.Append("%"); // timeTemplate.Append(se->tag.Text()); // timeTemplate.Append("@*m/*d/*Y *H:*M%"); // // Output the time value. htm.beginTRow(); htm.beginCol(); htm.text( tagOut.Text(), "b" ); htm.endCol(); htm.beginCol(); htm.text( time_buffer ); htm.endCol(); htm.endTRow(); } else if( se->IsList() ) { const StrPtr * p; if( varList->GetVar( se->tag, 0 ) == NULL ) continue; htm.beginTRow(); htm.beginCol( "top" ); htm.text( tagOut.Text(), "b" ); CustomAppendToTag(tagOut, &htm); htm.endCol(); htm.beginCol(); int d; for( d = 0; ( p = varList->GetVar( se->tag, d ) ) != NULL; d++ ) { htm.text( p4wStrBuf().EscapeHTML( *p, Unicode() ).Text() ); htm.linebreak(); } htm.endCol(); htm.endTRow(); } else { htm.beginTRow(); htm.beginCol( "top" ); htm.text( tagOut.Text(), "b" ); CustomAppendToTag(tagOut, &htm); htm.endCol(); htm.beginCol(); htm.text( p4wStrBuf().EscapeHTML( StrRef(val), Unicode() ).Text() ); htm.endCol(); htm.endTRow(); } // // Render this field's data fRequest << htm; } } void p4wSpecPane::renderForm( StrDict * varList ) { // // Loop through the spec, displaying each field. p4wHtml htm; StrBuf tagOut; // // Determine if this server supports gui hints int guiHints = 0; const StrPtr *server = fRequest.GetProtocol( "server2" ); if( server && server->Atoi() > 15 ) guiHints = 1; // // Determine the number of columns in this table // and the maximum text box length. setSpecParams( guiHints ); for( int i = 0; i < fSpec->Count(); i++ ) { // Get our spec element. SpecElem *se = fSpec->Get( i ); // Get a (sane) maxFieldLength value. int maxFieldLength = se->maxLength; if( (maxFieldLength == 0) || (maxFieldLength > 72) ) maxFieldLength = 72; // Get the value of this field. StrPtr * val = varList->GetVar( se->tag.Text() ); // Append a colon to the tag for the tag label tagOut.Set( se->tag ); tagOut << ":"; // // Clear the html source string htm.Clear(); // // Let the child class tell us what fields to ignore and which fields // it needs to process directly. if( IgnoreField( se->tag.Text() ) ) { // Make required fields hidden. if( se->IsRequired() ) { htm.hiddenField( se->tag.Text(), val->Text() ); fRequest << htm; } // Don't display this field. continue; } // // Determine how to display this field using gui // hints if available setFieldParams( guiHints, i ); if( CustomField( se->tag.Text(), varList, maxFieldLength, se ) ) continue; // // Check gui hints to see if we're starting a new row, // or in the middle of a row if( !fIsOpen ) htm.beginTRow(); if( se->IsDate() ) { // Temporary until the server gives us int time: // insure that time is an int char time_buffer[255 + 1]; time_buffer[0] = '\0'; if( val ) { Error e; DateTime t; t.Set( val->Text(), &e ); time_t iTime = (time_t) t.Value(); // Do our own strftime StrBuf time_format; time_format.Append( "%Y/%m/%d %H:%M:%S" ); strftime( time_buffer, sizeof( time_buffer ), time_format.Text(), localtime( &iTime ) ); } // // Output the time field. FormatFields( htm, tagOut.Text() ); if( se->IsReadOnly() ) { htm.text( time_buffer ); htm.endCol(); // Insure required fields are in the form too, // Let's just assume that it is required // and send it. htm.hiddenField( se->tag.Text(), time_buffer ); } else { htm.textField( se->tag.Text(), time_buffer, maxFieldLength ); htm.endCol(); } } else if( se->IsList() ) { int d; const StrPtr * p; FormatFields( htm, tagOut.Text(), "top" ); if( se->IsReadOnly() || MakeFieldReadOnly(se->tag.Text()) ) { for( d = 0; ( p = varList->GetVar( se->tag, d ) ) != NULL; d++ ) { if (d) htm.linebreak(); htm.text( p4wStrBuf().EscapeHTML( *p, Unicode() ).Text() ); } htm.endCol(); // // Insure required fields are in the form too. // We assume that any read-only field might // be required, just in case. StrBuf buf; for( d = 0; ( p = varList->GetVar( se->tag, d ) ) != NULL; d++ ) { buf << *p << "\n"; } if( buf.Length() ) htm.hiddenField( se->tag.Text(), buf.Text() ); } else { htm.beginTextbox( se->tag.Text(), strcmp("AltRoots:", tagOut.Text()) ? 6 : 2, fTextALen ); for( d = 0; ( p = varList->GetVar( se->tag, d ) ) != NULL; d++ ) htm.text( p4wStrBuf().EscapeHTML( *p, Unicode() ).Text() ); htm.endTextbox(); htm.endCol(); } } else if( se->IsText() ) { FormatFields( htm, tagOut.Text(), "top" ); htm.beginTextbox( se->tag.Text(), 10, fTextALen ); if( val != NULL ) htm.text( p4wStrBuf().EscapeHTML( *val, Unicode() ).Text() ); htm.endTextbox(); htm.endCol(); } else if( se->IsSelect() ) { FormatFields( htm, tagOut.Text() ); htm.beginSelect( se->tag.Text() ); if ( val == NULL && !se->IsRequired() ) { htm.selectOpt( 1 ); htm.text( "" ); } char *selectVal = strtok( se->values.Text(), "/" ); while( selectVal != NULL ) { if( ( val != NULL ) && ( strcmp( val->Text(), selectVal ) == 0 ) ) htm.selectOpt( 1 ); else htm.selectOpt( 0 ); htm.text( selectVal ); selectVal = strtok( NULL, "/" ); } htm.endSelect(); htm.endCol(); } else { if( se->IsReadOnly() || MakeFieldReadOnly(se->tag.Text()) ) { FormatFields( htm, tagOut.Text(), "top" ); if( val ) htm.text( p4wStrBuf().EscapeHTML( *val, Unicode() ).Text() ); htm.endCol(); // // Insure required fields are in the form too. // We assume that any read-only field might // be required, just in case. if( val ) htm.hiddenField( se->tag.Text(), val->Text() ); } else { FormatFields( htm, tagOut.Text() ); if( val ) htm.textField( se->tag.Text(), p4wStrBuf().EscapeHTML( *val, Unicode() ).Text(), maxFieldLength ); else htm.textField( se->tag.Text(), NULL, maxFieldLength ); htm.endCol(); } } if( !fLeaveOpen ) htm.endTRow(); // // Render this field's data fRequest << htm; } } void p4wSpecPane::setSpecParams( int guiHints ) { // // Determine the number of columns for this table based // on values in the spec. Also determine the length of // text areas using the max length of all fields. int maxLen = 0; int nCols = 0; SpecElem *se; for( int i = 0; i < fSpec->Count(); i++ ) { se = fSpec->Get( i ); if( se->maxLength > maxLen ) maxLen = se->maxLength; // // If any fields use the format M* or R*, // the number of fields will be 4. Otherwise // use the default field size of 2. if( !nCols && guiHints && se->GetFmt() == SDF_RIGHT ) nCols = 2; } if( maxLen == 0 || maxLen > 72 ) fTextALen = 72; else fTextALen = maxLen; fNCols = 2 + nCols; } void p4wSpecPane::setFieldParams( int guiHints, int specPos ) { // // Check server's gui hints for this field to determine // how to format the table element. If gui hints are // not supported by this server, set all values to default. if( !guiHints || fNCols < 3 ) { fIsOpen = 0; fLeaveOpen = 0; fIsIndented = 0; fIsMiddle = 0; return; } SpecElem *se = fSpec->Get( specPos ); int seq = se->GetSeq(); fIsOpen = se->GetFmt() == SDF_RIGHT; fIsIndented = se->GetFmt() == SDF_INDENT; fIsMiddle = se->GetFmt() == SDF_RIGHT; // // We may need to check the prev & next fields' hints // to determine how to process this table element. SpecElem *pe; SpecElem *ne; if( se->GetFmt() == SDF_NORMAL ) { fLeaveOpen = 0; } else { if( specPos + 1 < fSpec->Count() ) { pe = (specPos && (fRequest.GetCmd() == AC_EDITCLIENT || fRequest.GetCmd() == AC_EDITNEWCLIENT)) ? fSpec->Get( specPos - 1 ) : 0; int prvseq = pe ? pe->GetSeq() : 0; ne = fSpec->Get( specPos + 1 ); int nxtseq = ne->GetSeq(); if( pe && prvseq + 1 < seq && se->GetFmt() == SDF_LEFT && pe->GetFmt() == SDF_LEFT ) { fIsOpen = 1; fLeaveOpen = 0; } else if( pe && seq + 1 < nxtseq && se->GetFmt() == SDF_LEFT && ne->GetFmt() == SDF_LEFT ) { fIsOpen = 0; fLeaveOpen = 1; } else if( ne->GetFmt() == SDF_NORMAL || ne->GetFmt() == SDF_LEFT ) { fLeaveOpen = 0; } else { fLeaveOpen = 1; } } else { fLeaveOpen = 0; } } } void p4wSpecPane::FormatFields( p4wHtml & htm, const char *tag, const char *capTop, const char *fldTop, const char *colspan ) { // // Use the gui hints provided by the server to determine // how to format fields in this row. The "captop" variable // specifies whether the caption is top-aligned (which is // based on the type of input field that follows. Likewise // the fldtop variable specifies the alignment of the field // data. Finally, the optional colspan specifies a colspan // value to override the default processing here (as used // by changePendingEditPane). StrBuf tagOut; // // When the form is divided into halves, try to start // the second set of form elements in the middle by // using column widths. static const char *tWidth = "10%"; static const char *fWidth = "40%"; const char *tw = NULL; const char *fw = NULL; if( fNCols > 2 && fRequest.GetCmd() != AC_SUBMIT && fRequest.GetCmd() != AC_CHANGEPENDINGEDIT ) { tw = tWidth; fw = fWidth; } if( fIsIndented ) { // // Indented field: all data is pushed into the 2nd // field and 1st field is empty htm.beginCol(); htm.endCol(); // // If table > 2 columns and this is the 2nd // column, span over the last columns. if( fNCols > 2 && !fLeaveOpen ) htm.beginCol( capTop, NULL, "3", NULL, NULL, fw ); else htm.beginCol( capTop, NULL, colspan, NULL, NULL, fw ); tagOut << tag << " "; htm.text( tagOut.Text(), "b" ); } else if( fIsMiddle ) { // // Middle half field if( fRequest.GetCmd() == AC_SUBMIT || fRequest.GetCmd() == AC_CHANGEPENDINGEDIT ) { htm.beginCol( capTop, "left", colspan ); tagOut << tag << " "; htm.text( tagOut.Text(), "b" ); return; } htm.beginCol( capTop, "left", NULL, NULL, NULL, tw ); htm.text( tag, "b" ); htm.endCol(); htm.beginCol( fldTop, "left", colspan, NULL, NULL, fw ); } else { htm.beginCol( capTop, NULL, NULL, NULL, NULL, tw ); htm.text( tag, "b" ); htm.endCol(); // // If table > 2 columns and this is the 2nd // column, span over the last columns. This can // be overridden by explicitly setting colspan if( colspan ) htm.beginCol( fldTop, NULL, colspan, NULL, NULL, fw ); else if( fNCols > 2 && !fLeaveOpen ) htm.beginCol( fldTop, NULL, "3", NULL, NULL, fw ); else htm.beginCol( fldTop, NULL, NULL, NULL, NULL, fw ); } } void p4wSpecPane::CustomAppendToTag(StrBuf tagOut, p4wHtml *htm) { } void p4wSpecPane::RenderExistsNotNew( StrDict * varList ) { fRequest << "Use your browser's Back button and enter and different name."; }
# | 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/p4wSpecPane.cpp | |||||
#1 | 8914 | Matt Attaway | Initial add of the P4Web source code |