// // Copyright 2000 Perforce Software. All rights reserved. // // This file is part of Perforce - the FAST SCM System. // // p4wFilePane: // Draws the Path Browser page #include <p4wp4.h> #include "p4wStrBuf.h" #include "p4wHtml.h" #include "p4wI18n.h" #include "p4wAllCommands.h" #include "p4wPane.h" #include "p4wWorkspacePane.h" #include "p4wFilePane.h" #include "base64.h" // // Options for the depot mode filter select box static char *const dFilterText[] = { "Files in current path or opened in subfolders", "Files in current path", "Opened files", "Opened files - all clients", "Opened unchanged files", "Changed unopened files", "Files missing from workspace", "Unsynced files", "Unresolved files" }; static AllCommands dFilterVal[] = { AC_PATHOPENED, AC_PATHNOOPEN, AC_PATHNOCURRENT, AC_OPENEDFILESALL, AC_OPENEDUNCHANGED, AC_CHANGESUNOPENED, AC_MISSING, AC_UNSYNCED, AC_UNRESOLVED }; static const int nDFilterOpts = 9; static const int dFilterBitSupp[] = { 1, 1, 1, 0, 0, 0, 1, 1, 0 }; // // Options for the workspace mode filter select box static char *const wFilterText[] = { "Files in current path or opened in subfolders", "Files in current path", "Opened files", "Opened files - all clients", "Opened unchanged files", "Changed unopened files", "Files missing from workspace", "Unsynced files", "Unresolved files" }; static AllCommands wFilterVal[] = { AC_PATHOPENED, AC_PATHNOOPEN, AC_PATHNOCURRENT, AC_OPENEDFILESALL, AC_OPENEDUNCHANGED, AC_CHANGESUNOPENED, AC_MISSING, AC_UNSYNCED, AC_UNRESOLVED }; static const int nWFilterOpts = 9; p4wFilePane::p4wFilePane( p4wView & ParentView, p4wRequest & Request, AllCommands command ) : p4wPane( ParentView, Request ), fSeenBegin(0), fSeenData(0), fFileOffset(0), fFileOffsetBad(0), fAbortNow(0), fSeenThumbs(0), fRowctr(0), fNoEndNow(0), fShowHideCols(0), fstatRun(1), fFileCtr(0), fDirCtr(0) { // // Determine initial state based on command const StrPtr *pattern = fRequest.GetStateArg( "pat" ); const StrPtr *pb = fRequest.GetStateArg( "pb" ); const StrPtr *hd = fRequest.GetStateArg( "hd" ); // // Set Show Hide Columns flags if (hd) { fShowHideCols = atoi(hd->Text()); if (fRequest.GetBrowseMode()) { if ((fShowHideCols & HIDE_OTHERACTION) && (fShowHideCols & HIDE_OTHERUSER) && (fShowHideCols & HIDE_OTHERCLIENT)) fShowHideCols |= HIDE_OTHERCHG; } int coMingle = fRequest.GetBrowseMode(); if (coMingle) { if (fShowHideCols & HIDE_OTHERCHG) fShowHideCols |= HIDE_YOURCHG; else if (fShowHideCols & HIDE_YOURCHG) fShowHideCols |= HIDE_OTHERCHG; if (fShowHideCols & HIDE_OTHERACTION) fShowHideCols |= HIDE_YOURACTION; else if (fShowHideCols & HIDE_YOURACTION) fShowHideCols |= HIDE_OTHERACTION; } } // // State "pat" argument implies that we should match // this pattern if( pattern ) fCommand = AC_FILESMATCHING; // // State argument "pb" specifies a filter on the // path browser view, so use the ac code corresponding to // the filter else if( pb ) fCommand = (AllCommands)atoi( pb->Text() ); else fCommand = command; switch( fCommand ) { case AC_DEPOT: case AC_PATHBROWSER: case AC_CLIENT: fFullBrowseMode = 1; break; default: fFullBrowseMode = 0; break; } fState = gettingDirectories; fFirstErrorOnly = 0; DirList = &DirectoryList; fSubfolders.Clear(); fSubpath.Clear(); } p4wFilePane::~p4wFilePane() { } // ------------------------------------- // Render functions. // void p4wFilePane::AddDetails( StrPtr *action, StrPtr *owner, StrDict *varList, p4wHtml & suffix ) { StrPtr * change = varList->GetVar( "change" ); StrPtr * fileSize = varList->GetVar( "fileSize" ); StrPtr * headTime = varList->GetVar( "headTime" ); StrPtr * headChange = varList->GetVar( "headChange" ); StrPtr * headAction = varList->GetVar( "headAction" ); StrPtr * depotFile = varList->GetVar( "depotFile" ); int isRemote = 0; if (RemoteList.Count() && depotFile) // are there remote depots to worry about? { StrBuf directory; directory << depotFile; char *p = directory.Text(); while (*p == '/') strcpy(p, p+1); p = strchr(p, '/'); if (p) *++p = '\0'; directory.SetLength(); const StrPtr *depot; int id = -1; while( depot = RemoteList.Get( ++id ) ) { if (*depot == directory) { isRemote = 1; break; } } } int coMingle = fRequest.GetBrowseMode(); p4wURL urlMaker; StrBuf changeURL; StrBuf newBase; StrBuf baseNoPath; fRequest.UseNewBase( baseNoPath, NULL, "path", NULL ); suffix.beginCol(); /* hdr will generate spacing */ suffix.endCol(); if (!(fShowHideCols & HIDE_SIZE) && fstatRun) { suffix.beginCol(0,0,0,0, "#dfe3e8"); /* hdr will generate spacing */ suffix.endCol(); suffix.beginCol(); /* hdr will generate spacing */ suffix.endCol(); suffix.beginCol(NULL, "right"); if (fileSize) { char buf[64]; float f; unsigned u = (unsigned)atoi(fileSize->Text()); if (u < 1000) { if (!u) strcpy(buf, "0.00 <span class=\"filestat\">KB</span>"); else if (u <= 14) strcpy(buf, "0.01 <span class=\"filestat\">KB</span>"); else { f = u/1000.0; sprintf(buf, "%.2f <span class=\"filestat\">KB</span>", f); } suffix << buf; } else if (u < 1000000) { f = u/1000.0; sprintf(buf, "%.2f <span class=\"filestat\">KB</span>", f); suffix << buf; } else if (u < 1000000000) { f = u/1000000.0; sprintf(buf, "%.2f <span class=\"fSmaller\">MB</span>", f); suffix << buf; } else { f = u/1000000000.0; sprintf(buf, "%.2f <span class=\"fSmaller\"><b>GB</b></span>", f); suffix << buf; } } suffix.endCol(); } if ((!(fShowHideCols & HIDE_CHG) || !(fShowHideCols & HIDE_ACTION) || !(fShowHideCols & HIDE_DATE)) && fstatRun) { suffix.beginCol(); /* hdr will generate spacing */ suffix.endCol(); suffix.beginCol(0,0,0,0, "#dfe3e8"); /* hdr will generate spacing */ suffix.endCol(); suffix.beginCol(); /* hdr will generate spacing */ suffix.endCol(); suffix.beginCol(0, "right"); if (headChange && strcmp(headChange->Text(), "default") && !(fShowHideCols & HIDE_CHG)) { newBase.Set( baseNoPath ); newBase << headChange->Text(); urlMaker.ConstructURL( changeURL, newBase.Text(), AC_DESCRIBE, NULL, fRequest.GetUnicode() ); if (!isRemote) suffix.beginLink(changeURL.Text()); suffix.text( p4wStrBuf().EscapeHTML( *headChange, Unicode() ).Text() ); if (!isRemote) suffix.endLink(); } suffix.endCol(); suffix.beginCol(); suffix << " "; suffix.endCol(); suffix.beginCol(); if (headAction && !(fShowHideCols & HIDE_ACTION)) suffix << headAction->Text(); suffix.endCol(); suffix.beginCol(); suffix << " "; suffix.endCol(); suffix.beginCol(); if (headTime && !(fShowHideCols & HIDE_DATE)) { suffix << "<nobr> "; if (!(fShowHideCols & HIDE_TIME)) suffix.text( p4wStrBuf().Expand( StrRef( "%headTime@*Y/*m/*d *H:*M:*S%" ), *varList, Unicode() ).Text() ); else suffix.text( p4wStrBuf().Expand( StrRef( "%headTime@*Y/*m/*d%" ), *varList, Unicode() ).Text() ); suffix << "</nobr>"; } suffix.endCol(); } suffix.beginCol(); /* hdr will generate spacing */ suffix.endCol(); char key[16]; int colskipped = 0; if (((!(fShowHideCols & HIDE_YOURCHG) || !(fShowHideCols & HIDE_YOURACTION))) && fstatRun) { suffix.beginCol(0,0,0,0, "#dfe3e8"); /* hdr will generate spacing */ suffix.endCol(); suffix.beginCol(); /* hdr will generate spacing */ suffix.endCol(); if (!fRequest.GetBrowseMode()) { if (change) { suffix.beginCol(0, "right"); if (!(fShowHideCols & HIDE_YOURCHG)) { int b = NotOpenedByOwner( owner ); int d = !strcmp(change->Text(), "default"); if( b && d ) { StrBuf newBase; StrBufDict cmdArgs; newBase << owner << "@" << fRequest.GetClient(); cmdArgs.SetVar( "arg", newBase.Text() ); fRequest.UseNewBase( newBase, NULL, "path", NULL ); urlMaker.ConstructURL( changeURL, newBase.Text(), AC_DESCRIBEDEAFULT, &cmdArgs, fRequest.GetUnicode() ); } else { StrBufDict cmdArgs; cmdArgs.SetVar( "arg", change ); urlMaker.ConstructURL( changeURL, newBase.Text(), AC_CHANGEPENDINGEDIT, d ? NULL : &cmdArgs, fRequest.GetUnicode() ); } suffix.beginLink( changeURL.Text() ); if (d) suffix << "Default"; else suffix << p4wStrBuf().EscapeHTML( *change, Unicode() ).Text(); suffix.endLink(); } suffix.endCol(); } else { if (action) { suffix.beginCol(); if (!(fShowHideCols & HIDE_YOURCHG)) suffix << "will be"; suffix.endCol(); } else colskipped++; } } } const StrPtr *pb = fRequest.GetStateArg( "pb" ); if (fRequest.GetBrowseMode() && !pb) return; if ((!(fShowHideCols & HIDE_YOURCHG) || !(fShowHideCols & HIDE_YOURACTION)) && fstatRun) { if (!coMingle) { suffix.beginCol(); suffix << " "; suffix.endCol(); if (action) { suffix.beginCol(); if (!(fShowHideCols & HIDE_YOURACTION)) suffix << action->Text(); suffix.endCol(); } else colskipped++; if (colskipped) { sprintf(key, "%d", colskipped); suffix.beginCol(0, 0, key); suffix.endCol(); colskipped = 0; } suffix.beginCol(); /* hdr will generate spacing */ suffix.endCol(); } } if (((fShowHideCols & HIDE_OTHERCHG) && (fShowHideCols & HIDE_OTHERACTION) && (fShowHideCols & HIDE_OTHERUSER) && (fShowHideCols & HIDE_OTHERCLIENT)) || !fstatRun) { suffix.beginCol(); /* hdr will generate spacing */ suffix.endCol(); return; } suffix.beginCol(0,0,0,0, "#dfe3e8"); /* hdr will generate spacing */ suffix.endCol(); suffix.beginCol(); /* hdr will generate spacing */ suffix.endCol(); StrBufDict cmdArgs; char oo[] = "otherOpen"; char oc[] = "otherChange"; char oa[] = "otherAction"; int i; for (i = 0; ; i++ ) { sprintf(key, "%s%d", oc, i); StrPtr * otherChange = varList->GetVar(key); if (!otherChange) break; if ((!fRequest.GetBrowseMode())) { if (i) suffix << "<br>"; else if (coMingle && change) suffix << "<br>"; else suffix.beginCol(0, "right"); } if (!fRequest.GetBrowseMode() && !(fShowHideCols & HIDE_OTHERCHG)) { char *p = otherChange->Text(); if (*p == 'd') *p = 'D'; if (isdigit(*(otherChange->Text()))) { cmdArgs.SetVar( "arg", otherChange ); urlMaker.ConstructURL( changeURL, newBase.Text(), AC_CHANGEPENDINGEDIT, &cmdArgs, fRequest.GetUnicode() ); } else { char *p = otherChange->Text(); if (*p == 'd') *p = 'D'; sprintf(key, "%s%d", oo, i); StrPtr * otherOpen = varList->GetVar(key); StrBuf newBase; StrBufDict cmdArgs; cmdArgs.SetVar( "arg", otherOpen->Text() ); fRequest.UseNewBase( newBase, NULL, "path", NULL ); urlMaker.ConstructURL( changeURL, newBase.Text(), AC_DESCRIBEDEAFULT, &cmdArgs, fRequest.GetUnicode() ); } if (!isRemote) suffix.beginLink( changeURL.Text() ); suffix << p4wStrBuf().EscapeHTML( *otherChange, Unicode() ).Text(); if (!isRemote) suffix.endLink(); } } if (!i) { if (coMingle && action) { suffix.beginCol(); suffix << " "; suffix.endCol(); suffix.beginCol(); if (!(fShowHideCols & HIDE_OTHERACTION)) suffix << action->Text(); suffix.endCol(); suffix.beginCol(); suffix << " "; suffix.endCol(); if (!(fShowHideCols & HIDE_OTHERUSER)) { StrPtr * actionOwner = varList->GetVar( "actionOwner" ); suffix.beginCol(0,0,0,0,0,0,0,strchr(actionOwner->Text(), '-') ? 1 : 0); suffix << actionOwner->Text(); } else suffix.beginCol(); suffix.endCol(); suffix.beginCol(); suffix << " "; suffix.endCol(); if (!(fShowHideCols & HIDE_OTHERCLIENT)) { suffix.beginCol(0,0,0,0,0,0,0,strchr(fRequest.GetClient().Text(), '-') ? 1 : 0); suffix << fRequest.GetClient().Text(); } else suffix.beginCol(); suffix.endCol(); suffix.beginCol(0, 0, "3"); suffix.endCol(); } else { suffix.beginCol(0, 0, "9"); suffix.endCol(); } } else { StrBuf temp; char *p; if (!fRequest.GetBrowseMode()) suffix.endCol(); suffix.beginCol(); suffix << " "; suffix.endCol(); if (coMingle && action) { suffix.beginCol(); if (!(fShowHideCols & HIDE_OTHERACTION)) suffix << action->Text(); } for (i = 0; ; i++ ) { sprintf(key, "%s%d", oa, i); StrPtr * otherAction = varList->GetVar(key); if (!otherAction) break; if (!i && (!coMingle || !action)) suffix.beginCol(0,0,0,0,0,0,0,strchr(otherAction->Text(), '-') ? 1 : 0); else suffix << "<br>"; if (!(fShowHideCols & HIDE_OTHERACTION)) suffix << otherAction->Text(); } if (i || (coMingle && action)) { suffix.endCol(); if (coMingle && action) { suffix.beginCol(); suffix << " "; suffix.endCol(); } } if (coMingle && action) { if (!(fShowHideCols & HIDE_OTHERUSER)) { StrPtr * actionOwner = varList->GetVar( "actionOwner" ); suffix.beginCol(0,0,0,0,0,0,0,strchr(actionOwner->Text(), '-') ? 1 : 0); suffix << actionOwner->Text(); } else suffix.beginCol(); } for (i = 0; ; i++ ) { sprintf(key, "%s%d", oo, i); StrPtr * otherOpen = varList->GetVar(key); if (!otherOpen) break; if (!i && (!coMingle || !action)) { suffix.beginCol(); suffix << " "; suffix.endCol(); suffix.beginCol(0,0,0,0,0,0,0,strchr(otherOpen->Text(), '-') ? 1 : 0); } else suffix << "<br>"; temp.Set(otherOpen->Text()); p = strchr(temp.Text(), '@'); if (p) { *p = '\0'; temp.SetLength(); } if (!(fShowHideCols & HIDE_OTHERUSER)) suffix << temp; } if (i || (coMingle && action)) suffix.endCol(); if (coMingle && action) { suffix.beginCol(); suffix << " "; suffix.endCol(); if (!(fShowHideCols & HIDE_OTHERCLIENT)) { suffix.beginCol(0,0,0,0,0,0,0,strchr(fRequest.GetClient().Text(), '-') ? 1 : 0); suffix << fRequest.GetClient().Text(); } else suffix.beginCol(); } for (i = 0; ; i++ ) { sprintf(key, "%s%d", oo, i); StrPtr * otherOpen = varList->GetVar(key); if (!otherOpen) break; if (!i && (!coMingle || !action)) { suffix.beginCol(); suffix << " "; suffix.endCol(); suffix.beginCol(0,0,0,0,0,0,0,strchr(otherOpen->Text(), '-') ? 1 : 0); } else suffix << "<br>"; temp.Set(otherOpen->Text()); p = strchr(temp.Text(), '@'); if (p) p++; if (!(fShowHideCols & HIDE_OTHERCLIENT)) suffix << p; } if (i || (coMingle && action)) suffix.endCol(); suffix.beginCol(0, 0, coMingle ? "3" : 0); suffix.endCol(); } } void p4wFilePane::Begin() { if( fSeenBegin ) return; const StrPtr *pb = fRequest.GetStateArg( "pb" ); if (pb) { switch(atoi(pb->Text())) { case 56: case 58: case 59: case 60: fstatRun = 0; } } // // Output the title, the directory header with links, // and a file filter form const StrPtr *pattern; const StrPtr *srev = fRequest.GetDynArg( "sr", HE_Page ); const StrPtr *srevState = fRequest.GetStateArg( "sr", HE_Page ); // If both dynamic and state variables for sr are set, dynamic // is used. Set srev to the value to be used. Dynamic sr is // used for ala mode browsing. if( !srev && srevState ) srev = srevState; // // Additional title info for back in time browsing StrBuf bitTitle; StrBuf returnURL; if ( srev ) { bitTitle.Set( " (as of @" ); bitTitle << srev; bitTitle << ")"; } p4wHtml htm(1); p4wURL urlMaker; StrBuf grayIcon; StrBuf clearIcon; StrBuf newBase; StrBuf actionURL; // // Construct the url used as the action for the form if( fRequest.GetViewMode() == VM_WORKSPACE ) { StrBuf path; fRequest.UseNewBase( path, NULL, "path", fRequest.GetPath().Text() ); urlMaker.ConstructURL( actionURL, path.Text(), AC_DEPOTPROCESSOR, NULL, fRequest.GetUnicode() ); } else { urlMaker.ConstructURL( actionURL, NULL, AC_DEPOTPROCESSOR, NULL, fRequest.GetUnicode() ); } // // Used to generate a line underneath the title, and to gnerate // vertical whitespace urlMaker.ConstructURL( grayIcon, "/grayPixelIcon", AC_ICON, NULL ); urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); if (fRequest.GetScreenChunks() & SCRN_TITLE) { // Output the title htm.beginTRow(); htm.beginCol(); htm.beginTable(0, 0, "0", "0"); // put the whole line in a table to prevent wrapping htm.beginTRow(); htm.beginCol(); htm.beginSpan( "title" ); if( fRequest.GetViewMode() == VM_WORKSPACE ) { if (!fRequest.isLocalRequest()) htm << "Remote "; htm << "Workspace Tree:"; } else htm << "Depot Tree:"; htm.endSpan(); htm.endCol(); htm.endTRow(); htm.endTable(); htm.endCol(); htm.endTRow(); // // Generate line underneath the title htm.beginTRow(); htm.beginCol( "top" ); htm.icon( clearIcon.Text(), "2", "1", "", 1 ); htm.linebreak(); htm.icon( grayIcon.Text(), "1", "100%", "", 1, "0", "0" ); htm.endCol(); htm.endTRow(); } if (fRequest.GetScreenChunks() & SCRN_FILTER) { htm.beginTRow(); htm.beginCol(); fRequest << htm; htm.Clear(); StrBuf filtermsg; doFilterMessage(&filtermsg); fRequest.doShowHideBlock(SH_FILES, "file filter", "File Filter", filtermsg.Text()); htm.endCol(); htm.endTRow(); // // Start a DIV for showing/hiding the filter htm.beginTRow(); htm.beginCol(); htm << "<script language=javascript>" << crlf; htm << "document.write(\"<div id='showhideBlock'>\")" << crlf; htm << "</script>" << crlf; htm.icon( clearIcon.Text(), "7", "100%", "", 1 ); htm.beginTable(0, "100%", "0", "0"); // // Generate the File Filter form htm.beginTRow(); htm.beginCol(); htm.beginTable( "0", 0, "5", "0", "#FFFFFF" ); htm.beginCol(0,0,0,"100%"); htm.icon( clearIcon.Text(), "1", "6", "", 1 ); htm.endCol(); htm.beginTRow(); htm.beginCol(0, 0, "2"); htm.beginForm( actionURL.Text(), "filterForm" ); if( fRequest.GetStateArg( "pat" ) ) htm.radio( "fileFilter", "menu", 0 ); else htm.radio( "fileFilter", "menu", 1 ); htm.beginSelect( "filter", 0, 0, 0, "setCheckedValue(document.forms['filterForm'].elements['fileFilter'], 'menu')", "selectFilter" ); // // Generate the menu int sel; int act; AllCommands curr = fCommand; // // PATHOPEN, PATHNOCURRENT & PATHOPENED are private codes // that are immediately remapped to PATHBROWSER. Since we // want to highlight the correct title, figure // out which one the current p4web configuration matches. if( curr == AC_PATHBROWSER || curr == AC_FILESMATCHING || curr == AC_DEPOT || curr == AC_CLIENT ) { if( fRequest.GetStateArg( "po" ) ) curr = AC_PATHNOOPEN; else if( fRequest.GetStateArg( "cf" ) ) curr = AC_PATHNOCURRENT; else curr = AC_PATHOPENED; } if( fRequest.GetViewMode() == VM_WORKSPACE ) { for( int i = 0; i < nWFilterOpts; i++ ) { // // Is this the current command? Then it will // be highlighted. sel = ( curr == wFilterVal[i] ); // // Get the current action to pass back act = wFilterVal[i]; htm.selectOpt( sel, StrNum( act ).Text() ); htm.text( wFilterText[i] ); } } else { for( int i = 0; i < nDFilterOpts; i++ ) { // Don't present some options if this // is browse-only or back-in-time mode if( fRequest.GetBrowseMode() && !p4wAllCommands::CanBrowse( dFilterVal[i] ) ) continue; if( fRequest.GetBITBRev().Length() && !dFilterBitSupp[i] ) continue; // // Get the current action to pass back act = dFilterVal[i]; // // Is this the current command? Then it will // be highlighted. sel = ( curr == dFilterVal[i] ); htm.selectOpt( sel, StrNum( act ).Text() ); htm.text( dFilterText[i] ); } } htm.endSelect(); htm.endCol(); // // Show/hide deleted files htm.beginCol(0,0,0,0,0,0,0,1); if( fRequest.GetViewMode() != VM_WORKSPACE ) { sel = !fRequest.HideDeletedFiles(); htm.checkbox( "showDeleted", "showDeleted", sel ); htm.label( "Include deleted files ", "showDeleted" ); } htm.endCol(); htm.endTRow(); htm.beginTRow(); // // Show files matching this pattern htm.beginCol(); htm.beginNobreak(); if( fRequest.GetStateArg( "pat" ) ) { htm.radio( "fileFilter", "matching", 1 ); htm.label( "File pattern: ", "matching" ); htm.textField( "pattern", fRequest.GetStateArg( "pat", HE_Page )->Text(), 30, 0, "setCheckedValue(document.forms['filterForm'].elements['fileFilter'], 'matching')" ); } else { htm.radio( "fileFilter", "matching", 0 ); htm.label( "File pattern: ", "matching" ); htm.textField( "pattern", NULL, 30, 0, "setCheckedValue(document.forms['filterForm'].elements['fileFilter'], 'matching')" ); } htm.endNobreak(); htm.endCol(); // // Include subfolders in Files Matching? htm.beginCol(); htm.beginNobreak(); htm.checkbox( "incSubfolders", "subfolders", fRequest.GetStateArg( "is" ) ? 1 : 0, "setCheckedValue(document.forms['filterForm'].elements['fileFilter'], 'matching')" ); htm.label( "search subfolders for pattern ", "incSubfolders" ); htm.endNobreak(); htm.endCol(); // // Client/Depot view htm.beginCol(0,0,0,0,0,0,0,1); if( fRequest.GetViewMode() != VM_WORKSPACE ) { sel = fRequest.GetViewMode() == VM_CLIENT; htm.checkbox( "showClient", "showClient", sel ); htm.label( "Include only files in client view ", "showClient" ); } htm.endCol(); htm.endTRow(); htm.beginTRow(); // // Filter button htm.beginCol(); htm.button( "Filter", "Filter" ); // // Clear button htm.button( "Clear", "Reset Defaults" ); htm.endCol(); htm.endTRow(); htm.endTable(); htm.icon( clearIcon.Text(), "5", "8", "", 1 ); htm.icon( grayIcon.Text(), "1", "100%", "", 1, "0", "0" ); htm.icon( clearIcon.Text(), "2", "8", "", 1 ); htm.endCol(); htm.endTRow(); htm.endCol(); htm.endTRow(); htm.endTable(); htm << "<script language=javascript>" << crlf; htm << "document.write(\"</div>\")" << crlf; htm << "</script>" << crlf; htm.endTable(); htm.endCol(); htm.beginCol( NULL, NULL, NULL, NULL, NULL, "8" ); htm.icon( clearIcon.Text(), "1", "8", "", 1 ); htm.endCol(); htm.endTRow(); htm.endTable(); htm.endCol(); htm.endTRow(); htm.beginTRow(); htm.beginCol(); htm.beginTable( "0", "100%", "0", "0" ); htm.beginTRow(); htm.beginCol( NULL, NULL, NULL, NULL, NULL, "10" ); htm.icon( clearIcon.Text(), "1", "10", "", 1 ); htm.endCol(); htm.beginCol(); htm.beginTable( "0", "100%", "0", "0" ); fRequest << htm; htm.Clear(); } // // If back in time, display a link to return to present if( fRequest.GetBITBRev().Length() > 0 ) { StrBuf returnURL; fRequest.UseNewBase( returnURL, NULL, "sr", NULL ); fRequest.UseNewBase( returnURL, returnURL.Text(), "path", fRequest.GetPath().Text()); OutputHREF( "Back-in-Time Browsing® is <i>on</i> -- click ", returnURL.Text(), AC_PATHBROWSER, NULL, "here", " to turn it off." ); htm.endCol(); htm.endTRow(); } // // Path with links htm.beginTRow(); htm.beginCol(); htm.icon( clearIcon.Text(), "3", "1", "", 1 ); htm.linebreak(); fRequest << htm; htm.Clear(); int useDepot = ( fRequest.GetViewMode() != VM_WORKSPACE ); int status = OutputDirectoryHeader( fRequest.GetPath().Text(), 0, 1, useDepot ); if( bitTitle.Length() ) htm << bitTitle; if (!fRequest.isLocalRequest() && fRequest.GetViewMode() == VM_WORKSPACE) { htm << " "; htm.beginSpan("remote"); htm << "<nobr>"; htm << "(workspace files are located on remote machine \"" << fRequest.GetHost() << "\")"; htm << "</nobr>"; htm.endSpan(); } htm.endCol(); htm.endTRow(); htm.beginTRow(); htm.beginCol(); if (!fRequest.HideDetails()) { htm.icon( clearIcon.Text(), "4", "1", "", 1 ); htm.endCol(); htm.endTRow(); htm.beginTRow(); htm.beginCol(); htm.beginTable( "0", "100%", "0", "1", "#7F7F7F" ); htm.beginTRow(0, 0, "#FFFFFF"); htm.beginCol(); } htm.beginTable( "0", "100%", "0", "0"); if (!fRequest.HideDetails()) { const StrPtr *pb = fRequest.GetStateArg( "pb" ); htm.beginTRow(0, 0, "#EEEEEE"); htm.beginColHead(0, "left", "2", 0,0,0,0,0,0, "border-right: 1px solid #7F7F7F;"); StrBuf showhideIcon; urlMaker.ConstructURL( showhideIcon, "/showhideIcon", AC_ICON, NULL ); StrBuf shURL; const StrPtr *thx = fRequest.GetStateArg( "thx" ); const StrPtr *X = fRequest.GetStateArg("X"); if ((X && strchr(X->Text(), 'H')) || thx) { urlMaker.ConstructURL( shURL, fRequest.GetBase().Text(), AC_SHOWHIDECOLUMNS, NULL, fRequest.GetUnicode() ); StrBuf jsAction; jsAction << "window.open(this.href+'&jse=1&sho=131','hideshow','dependent,resizable,height=300,width=1024'); return false"; htm.beginLink(shURL.Text(), 0, 0, 0, 0, 0, "onClick", jsAction.Text()); htm.icon( showhideIcon.Text(), "17", "17", "", 1, 0, 0, "absmiddle" ); htm.endLink(); } else { urlMaker.ConstructURL( shURL, fRequest.GetBase().Text(), AC_SHOWHIDEFILECOLS, NULL, fRequest.GetUnicode() ); htm << crlf; htm << "<script language=javascript>" << crlf; htm << "document.write(\"<a href='javascript:showMenu(\\\""; // any filename would go here htm << "\\\",\\\"fcols\\\",\\\"filcols\\\","; htm << -1; htm << ",\\\"\\\")' id='id_fcols' title ='Visible columns'>"; htm << "<img src='showhideIcon?ac=20' height='17' width='17' border='0' alt='' align=absmiddle title=''>"; htm << "</a>\")" << crlf; htm << "</script>" << crlf; htm.beginUrlMenu(&fRequest, "menu_filcols"); fRequest << htm; const StrPtr *M = fRequest.GetStateArg("M"); if (M && strchr(M->Text(), 'A')) generateFineShowHideMenu(); else generateGrossShowHideMenu(); htm.Clear(); htm.endUrlMenu(); } htm.endColHead(); htm.beginColHead(0, "left", "8"); htm.icon( clearIcon.Text(), "2", "2", "", 1 ); htm.linebreak(); htm << " Name"; htm.linebreak(); htm.icon( clearIcon.Text(), "2", "2", "", 1 ); htm.endColHead(); htm.beginColHead(); htm << " "; htm.endColHead(); if (!(fShowHideCols & HIDE_SIZE) && fstatRun) { htm.beginColHead(); htm.icon( clearIcon.Text(), "1", "1", "", 1 ); htm.endColHead(); htm.beginColHead(); htm << " "; htm.endColHead(); htm.beginColHead(); htm << "<nobr>Head Rev Size</nobr>"; htm.endColHead(); } if ((!(fShowHideCols & HIDE_CHG) || !(fShowHideCols & HIDE_ACTION) || !(fShowHideCols & HIDE_DATE)) && fstatRun) { htm.beginColHead(); htm << " "; htm.endColHead(); htm.beginColHead(); htm.icon( clearIcon.Text(), "1", "1", "", 1 ); htm.endColHead(); htm.beginColHead(); htm << " "; htm.endColHead(); htm.beginColHead(0, "left", "5"); htm << "<nobr> Last Submitted Change </nobr>"; htm.endColHead(); } if ((!(fShowHideCols & HIDE_YOURCHG) || !(fShowHideCols & HIDE_YOURACTION)) && fstatRun) { if ( !fRequest.GetBrowseMode() ) { htm.beginColHead(); htm << " "; htm.endColHead(); htm.beginColHead(); htm.icon( clearIcon.Text(), "1", "1", "", 1 ); htm.endColHead(); htm.beginColHead(); htm << " "; htm.endColHead(); htm.beginColHead(0, "left", "3"); htm << " Pending Changes"; htm.endColHead(); } } if ( !fRequest.GetBrowseMode() || pb ) { htm.beginColHead(); htm << " "; htm.endColHead(); if ((!(fShowHideCols & HIDE_OTHERCHG) || !(fShowHideCols & HIDE_OTHERACTION) || !(fShowHideCols & HIDE_OTHERUSER) || !(fShowHideCols & HIDE_OTHERCLIENT)) && fstatRun) { htm.beginColHead(); htm.icon( clearIcon.Text(), "1", "1", "", 1 ); htm.endColHead(); htm.beginColHead(); htm << " "; htm.endColHead(); htm.beginColHead(0, "left", fRequest.GetBrowseMode() ? "10" : "7", 0, 0, 0, 0, 1); if (!fRequest.GetBrowseMode()) htm << "<nobr> Other Clients' Pending Changes </nobr>"; else htm << "<nobr> Pending Changes </nobr>"; htm.endColHead(); } } htm.beginColHead(0, 0, 0, 0, 0, "100%"); htm << " "; htm.endColHead(); htm.endTRow(); htm.beginTRow(); htm.beginCol("top", 0, "40", 0, 0, 0, "1"); htm.icon( grayIcon.Text(), "1", "100%", "", 1, "0", "0" ); htm.endCol(); htm.endTRow(); } fRequest << htm; fSeenBegin = 1; // // If there is something wrong with the path, we don't // want to proceed any further. if( !status ) { ++fAbortNow; if( !fWRootFound ) PromptRetry( "The path entered is not under workspace root." ); else PromptRetry( "The path entered could not be found." ); fHadFatalError = 1; fState = hasFatalError; } else { if ( !fRequest.HideThumbnails() ) { const StrPtr *server; int protocol = 0; server = fRequest.GetProtocol( "server2" ); if( server ) protocol = server->Atoi(); if( protocol > 19 ) { htm.Clear(); htm.beginTRow(); htm.beginCol("top"); htm.beginTable( "0", "200", "0", "0" ); fRequest << htm; } } } } void p4wFilePane::generateGrossShowHideMenu() { SHOWHIDECOLS showhidecols[] = { HIDE_SIZE, " Head Rev Size", HIDE_CHG|HIDE_ACTION|HIDE_DATE|HIDE_TIME, " Last Submitted Change", HIDE_YOURCHG|HIDE_YOURACTION, " Your Pending Changes", HIDE_OTHERCHG|HIDE_OTHERACTION|HIDE_OTHERUSER|HIDE_OTHERCLIENT, " Other Clients' Pending Changes", 0, NULL }; if (fRequest.GetBrowseMode()) { const StrPtr *pb = fRequest.GetStateArg( "pb" ); if (pb && atoi(pb->Text()) == AC_OPENEDFILESALL) { showhidecols[2].mask = HIDE_YOURCHG|HIDE_YOURACTION|HIDE_OTHERCHG|HIDE_OTHERACTION|HIDE_OTHERUSER|HIDE_OTHERCLIENT; showhidecols[2].name = " Pending Changes"; showhidecols[3].mask = 0; } else showhidecols[2].mask = 0; } generateShowHideMenu(&showhidecols); } void p4wFilePane::generateFineShowHideMenu() { SHOWHIDECOLS showhidecols[] = { HIDE_REV, " Revision Numbers", HIDE_TYPE, " File Type", HIDE_NEWTYPE, " New File Type", HIDE_OTHEROPEN, " Also Opend By", -1, NULL, HIDE_SIZE, " Head Rev Size", -1, NULL, HIDE_CHG, " Last Submitted Changelist", HIDE_ACTION, " Last Submitted Action", HIDE_DATE, " Date of Last Submit", HIDE_TIME, " Time of Last Submit", -1, NULL, HIDE_YOURCHG, " Your Pending Changelist", HIDE_YOURACTION, " Your Pending Action", -1, NULL, HIDE_OTHERCHG, " Other Pending Changelists", HIDE_OTHERACTION, " Other Pending Actions", HIDE_OTHERUSER, " Other Users", HIDE_OTHERCLIENT, " Other Clients", 0, NULL }; if (fRequest.GetBrowseMode()) { const StrPtr *pb = fRequest.GetStateArg( "pb" ); if (!pb || atoi(pb->Text()) != AC_OPENEDFILESALL) { int i; for (i = 0; showhidecols[++i].mask; ) { if (showhidecols[i].mask == HIDE_YOURCHG) { showhidecols[i].mask = 0; break; } } } } p4wHtml htm(1); htm.SetRequest(&fRequest); StrBuf url; const StrPtr *hd = fRequest.GetStateArg( "hd" ); url << "?ac=" << AC_SHOWHIDEFILECOLS << "&show=-1"; htm.renderUrlMenuItem( "filcols", 0, AC_SHOWHIDEFILECOLS, url.Text(), " <b>Show All Columns</b>", 0, hd ? NULL : "checkmarkIcon", 0, 0); fRequest << htm; generateShowHideMenu(&showhidecols, 1); } void p4wFilePane::generateShowHideMenu(void *ptrSH, int itemsalreadyinmenu) { int browsewithchgs = 0; if (fRequest.GetBrowseMode()) { const StrPtr *pb = fRequest.GetStateArg( "pb" ); if (pb && atoi(pb->Text()) == AC_OPENEDFILESALL) browsewithchgs = 1; } SHOWHIDECOLS *showhidecols = (SHOWHIDECOLS *)ptrSH; p4wHtml htm(1); htm.SetRequest(&fRequest); htm.SetSepNeeded(itemsalreadyinmenu ? 1 : 0); StrBuf url; const StrPtr *hd = fRequest.GetStateArg( "hd" ); int hid = hd ? atoi(hd->Text()) : 0; int bHidden; int i = itemsalreadyinmenu-1; while (showhidecols[++i].mask) { if (showhidecols[i].mask == -1) { htm.SetSepNeeded(1); continue; } else if (browsewithchgs) { switch(showhidecols[i].mask) { case HIDE_YOURCHG: case HIDE_YOURACTION: case HIDE_OTHERCHG: continue; case HIDE_OTHERACTION: showhidecols[i].name = " Pending Actions"; break; case HIDE_OTHERUSER: showhidecols[i].name = " Users"; break; case HIDE_OTHERCLIENT: showhidecols[i].name = " Clients"; break; default: break; } } url.Set("?ac="); url << AC_SHOWHIDEFILECOLS; bHidden = (hid & showhidecols[i].mask) != 0; if (bHidden) url << "&show="; else url << "&hide="; url << showhidecols[i].mask; htm.renderUrlMenuItem( "filcols", i, AC_SHOWHIDEFILECOLS, url.Text(), showhidecols[i].name, 0, bHidden ? NULL : "checkmarkIcon", 0, 0); } fRequest << htm; } void p4wFilePane::Render( StrDict * varList ) { // // Generate the file url with state arguments with // links to subdirectories in its path. Also, generate // any directory urls that precede this file. // // If more commands were queued after a fatal error, // don't display any more data if( fState == hasFatalError || fState == done ) return; if (fState == gettingDirectories) // have we just run p4 depots? { // maybe StrPtr * depotName = varList->GetVar( "name" ); if (depotName) { // we have just run depots StrBuf *newDir = DepotList.Put(); *newDir << depotName << "/"; fFileOffset = 2; StrPtr *type = varList->GetVar( "type" ); if (type && *type != "local") { StrBuf *newRmt = RemoteList.Put(); *newRmt << depotName << "/"; } return; } } StrPtr * action = varList->GetVar( "action" ); StrPtr * headAction = varList->GetVar( "headAction" ); StrPtr * haveRev = varList->GetVar( "haveRev" ); StrPtr * depotFile = varList->GetVar( "depotFile" ); StrPtr * headRev = varList->GetVar( "headRev" ); StrPtr * dir = varList->GetVar( "dir" ); StrPtr * headType = varList->GetVar( "headType" ); StrPtr * type = varList->GetVar( "type" ); StrPtr * workspace = varList->GetVar( "clientFile" ); StrPtr * owner = varList->GetVar( "actionOwner" ); const StrPtr *srev = fRequest.GetDynArg( "sr" ); StrBuf *newDir; StrBufDict cmdArgs; StrBuf clientFile; StrBuf clientRootDir; StrBuf subDirChain; int nLevels; int tOffset; AllCommands ac; p4wURL urlMaker; // // Determine the action for the file urls. // If dynamic variable sr is set, set this in the file browser href // to browse the file at that revision (ala mode). ac = SEC_DONT_SHOW_REVHIST ? (fRequest.GetViewMode() == VM_WORKSPACE ? AC_WORKSPACEFILE : AC_NONE) : AC_BROWSEFILE; if( srev ) { cmdArgs.SetVar( "sr", srev ); ac = AC_FILETEXTDEPOT; } // // Determine if file has been deleted at head revision int isDeleted = 0; if( ( headAction != NULL ) && ( *headAction == "delete" || *headAction == "move/delete" ) ) { isDeleted = 1; } // // Determine whether to skip files deleted at head revision. Never // skip them if opened on client. Check preference for deletion to // determine whether to skip otherwise. Default is to show deleted // files. int doSkip = 0; if( isDeleted ) { if( haveRev != NULL ) { doSkip = 0; } else if( fRequest.HideDeletedFiles() && !action ) { doSkip = 1; } else { doSkip = 0; } } if( doSkip) return; // // Process this entry according to our state. switch( fState ) { case currentDirectory: // // Display this file and all of its icons unless // we are in "hide current directory mode" and this // file is not open. if( fRequest.HideCurrentDir() && !action ) return; // // If page content limit in bytes has been exceeded, don't // generate this entry if( PageLimitExceeded() ) return; // // Don't ever display files opened for delete if // we are in workspace mode if( fRequest.GetViewMode() == VM_WORKSPACE && action && ( *action == "delete" ) ) return; RenderFileCurrentDir( varList, isDeleted, haveRev, headRev, action, owner, headType, type, ac, &cmdArgs ); break; case gettingDirectories: // // The command to get directories in workspace mode // may not involve using a p4 command. Therefore, // insure that Begin() has been called in case it has // not been called yet. if( !fSeenBegin ) Begin(); // // Add this directory to our list after stripping off // the path prefix. Also calculate the offset from the // beginning of the path to the actual file node. newDir = DirectoryList.Put(); if( !dir && !fRequest.bQuiet() ) printf("Fatal error: dir==NULL: Is your HOME directory writeable? It must be.\n"); if( fRequest.GetViewMode() != VM_WORKSPACE ) { tOffset = p4wI18n::safeStrrchrPos( dir->Text(), '/' ) + 1; // // The file offset to the beginning of the // directory should always be the same. If it // is not, flag this so we can handle it later // in gettingOpened. // This could happen if we are using client mode // syntax. if( fFileOffset > 0 && tOffset != fFileOffset ) fFileOffsetBad = 1; fFileOffset = tOffset; *newDir << dir->Text() + fFileOffset; *newDir << "/"; // // Store the directories again using full path // specification. We need this just in case // the path uses client mode syntax, which could // cause the offsets to differ for each opened // file due to different mappings. In depot // syntax, the mapping is always the same and // therefore the offsets are always the same. FullDirList.Put()->Set( dir->Text() ); } else { *newDir << dir->Text() + fRequest.GetPath().Length(); *newDir << urlMaker.GetSysDirDelim( NULL ); fFileOffset = fRequest.GetPath().Length(); } if( p4debug.GetLevel( DT_NET ) >= 3 ) printf("adding directory \"%s\" to list\n", newDir->Text() ); break; case parsingFiles: // // This state only has work here if the // command is opened files: all clients if( fCommand != AC_OPENEDFILESALL ) return; // // We only want one entry per file. Opened -a gives // one line per person who has the file opened. To // compensate, filter out the duplicate filenames. if( !FileList.Count() ) { FileList.Put()->Set(depotFile); } else if( *( FileList.Get( FileList.Count() - 1 ) ) != depotFile->Text() ) { FileList.Put()->Set( depotFile ); } fSeenData = 1; break; case gettingOpened: // // Look for reasons to bail. // If depotFile is not set, we'll have a world of pain if // we do anything but return. // If page content limit in bytes has been exceeded, // don't generate this entry either. if( !depotFile || PageLimitExceeded() ) return; // // Don't ever display files opened for delete if // we are in workspace mode if( fRequest.GetViewMode() == VM_WORKSPACE && action && ( *action == "delete" ) ) return; // // Get this file's path, stripping off the current // directory path. GetFilePath( depotFile, workspace, clientFile ); // // Get the root directory for this file path. If we are in // the root directory, then output this file now only // if this is not a Path browser command. // In a path browser command, this file will be output at // the end with the rest of the files in the current // directory. nLevels = GetRootDir( &clientFile, clientRootDir ); if( !nLevels ) { if( !fFullBrowseMode ) RenderFileCurrentDir( varList, isDeleted, haveRev, headRev, action, owner, headType, type, ac, &cmdArgs ); return; } // // Clip the root from the beginning of this file's path, // and decrement nLevels to reflect this. clientFile.Set( clientFile.Text() + strlen( clientRootDir.Text() ) ); --nLevels; // // Display links to directories occurring alphabetically // before this file. RenderPrevDirs( &clientRootDir ); // // Render state icons for this file. RenderIcons( varList, 0, isDeleted ); // // Output the set of links for the intermediary // directories of this file. RenderSubDirs( &clientRootDir, nLevels, clientFile, subDirChain ); // // Display the file link itself. RenderFile( &clientRootDir, &subDirChain, &clientFile, haveRev, headRev, headType, type, owner, ac, &cmdArgs, varList ); break; case gettingThumbs: RenderThumbs( varList, depotFile, headRev, haveRev, ac, &cmdArgs ); break; case hasFatalError: case done: break; default: break; } } void p4wFilePane::RenderFileCurrentDir( StrDict *varList, int isDeleted, StrPtr *haveRev, StrPtr *headRev, StrPtr *action, StrPtr *owner, StrPtr *headType, StrPtr *type, AllCommands ac, StrBufDict *cmdArgs ) { // // Generate the filename url and icons when the file // is in the current directory. StrBuf clientFile; StrBuf pathFile; p4wHtml suffix(1); p4wHtml prefix; StrBuf haveRevBuf; // // Insure the have revision is set even if file isn't in // have list if( !haveRev ) { haveRevBuf.Set( "0" ); } else { haveRevBuf.Set( haveRev->Text() ); } // // we have to save filenames in order // to know which thumbnails to disply if filtering if ( !fRequest.HideThumbnails() ) { StrPtr *file = varList->GetVar( "depotFile" ); if (!file) file = varList->GetVar( "clientFile" ); ThumbList.Put()->Set( file->Text() ); } // // Strip off everything from the path except for the file node if( strcmp( fRequest.GetPath().Text(), "//" ) == 0 ) { clientFile.Set( ( varList->GetVar( "depotFile" )->Text() + 2 ) ); pathFile.Set( clientFile ); } else if( fRequest.GetViewMode() != VM_WORKSPACE ) { StrPtr *depotFile = varList->GetVar( "depotFile" ); clientFile.Set( p4wI18n::safeStrrchr( depotFile->Text(), '/' ) + 1 ); pathFile.Set( clientFile ); } else { PathSys *p = PathSys::Create(); StrBuf file; p->Set( varList->GetVar( "clientFile" ) ); p->ToParent( &file ); fRequest.UseNewBase( pathFile, NULL, "path", fRequest.GetPath().Text() ); clientFile.Set( file ); // // If the server supports filenames with special // characters, we need to escape these characters // when in workspace mode. If not in workspace mode, // the special characters have already been escaped. const StrPtr *server = fRequest.GetProtocol( "server2" ); if( server && server->Atoi() > 17 ) file.Set( p4wStrBuf().EscapeP4Chars( file ) ); pathFile << file; delete p; } // // Render the icons for this file RenderIcons(varList, 1, isDeleted); // // Generate the URL, output the data, StrBuf jssuffix; const StrPtr *X = fRequest.GetStateArg("X"); int hoticons = (X && strchr(X->Text(), 'L')) ? 1 : 0; // 1 -> link on icons, 0 -> bare icons const StrPtr *thx = fRequest.GetStateArg( "thx" ); if( (!thx || *thx != "y") && !hoticons ) { // Generate the arguments to pass to javascript:showMenu() StrBuf id; id << "id_" << fFileCtr; StrBuf curFi; curFi << ((fRequest.GetViewMode() == VM_WORKSPACE) ? varList->GetVar( "clientFile" ) : varList->GetVar( "depotFile" )); char *finm = curFi.Text(); *(finm + curFi.Length()) = '\0'; // ensure nul terminated finm += fRequest.GetPath().Length(); if (strchr(finm, '\\')) // if there are backslashes in the path, we need to double them { StrBuf tmp; char buf[2] = { '\0', '\0' }; char *p = finm; while(*p) { if (*p == '\\') { tmp << "\\\\"; p++; } else { buf[0] = *p++; tmp << buf; } } curFi.Set(tmp); finm = curFi.Text(); *(finm + curFi.Length()) = '\0'; // ensure nul terminated } curFi.Set(finm); p4wStrBuf p4wCurFi; p4wCurFi.EscapeURLAllChars(curFi, fRequest.GetUnicode()); if (strstr(p4wCurFi.Text(), "%25")) { p4wStrBuf p4wCurFi2; p4wCurFi2.EscapeURLAllChars(p4wCurFi, fRequest.GetUnicode()); curFi.Set(p4wCurFi2.Text()); } else curFi.Set(p4wCurFi.Text()); finm = curFi.Text(); int show = GetFileMenuShowVals(fstatRun ? varList : NULL); StrBuf args; if (headRev && *headRev > "1") args << "&rev1=" << headRev->Text(); args << "&mu=" << show; StrBuf jsaction; jsaction << "javascript:showMenu(\\\"" << finm << "\\\",\\\"" << fFileCtr << "\\\",\\\"file\\\"," << show << ",\\\"" << args.Text() << "\\\")"; jssuffix << "<script language=javascript>"; jssuffix << "document.write(\" <span class='muaro'>"; jssuffix << "<a href='" << jsaction << "' title='Menu' id='" << id.Text() << "'>"; jssuffix << " </a></span>\")"; jssuffix << "</script>" << crlf; } // // If headRev or headType are missing, do not print revision info. // Skip haveRev if we are in browse-only mode. if( !headRev || !headType ) { if (type && !(fShowHideCols & HIDE_TYPE)) { suffix.beginSpan( "filestat" ); suffix << " " << type->Text(); suffix.endSpan(); } if( !thx || *thx != "y" ) suffix << jssuffix; } else { suffix.beginSpan( "filestat" ); if (!(fShowHideCols & HIDE_REV)) { suffix << " #"; if( !fRequest.GetBrowseMode() ) suffix << haveRevBuf.Text() << "/"; suffix << headRev->Text(); } if (!(fShowHideCols & HIDE_TYPE)) suffix << " " << headType->Text(); suffix.endSpan(); if( !thx || *thx != "y" ) suffix << jssuffix; if (type && strcmp(type->Text(), headType->Text()) && !(fShowHideCols & HIDE_NEWTYPE)) suffix << " <nobr><font color=\"#C08080\">(opened as " << type->Text() << ")</font></nobr>"; if( NotOpenedByOwner( owner ) && !(fShowHideCols & HIDE_OTHEROPEN) ) { suffix << " <nobr><font color=\"#C08080\">(opened by " << owner; suffix << "@" << fRequest.GetClient() << ")</font></nobr>"; } } suffix.endCol(); if ( !fRequest.HideDetails() ) AddDetails(action, owner, varList, suffix); suffix.endTRow(); prefix.beginCol( "top", NULL, "6", 0, 0, fRequest.HideDetails() ? "100%" : "60%" ); // // Only FILETEXTDEPOT has command arguments on the url if( ac == AC_FILETEXTDEPOT ) OutputHREF( prefix.Text(), pathFile.Text(), ac, cmdArgs, clientFile.Text(), suffix.Text() ); //, 0, 0, // id.Text(), "onMouseDown", jsaction.Text() ); else if( ac == AC_WORKSPACEFILE ) OutputHREF( prefix.Text(), RootRelative(&pathFile)->Text(), ac, NULL, clientFile.Text(), suffix.Text() ); //, 0, 0, // id.Text(), "onMouseDown", jsaction.Text() ); else OutputHREF( prefix.Text(), pathFile.Text(), ac, NULL, clientFile.Text(), suffix.Text() ); //, 0, 0, // id.Text(), "onMouseDown", jsaction.Text() ); fSeenData = 1; } void p4wFilePane::RenderInfo( char *data, char level ) { // // Output from the following commands: // resolve -n // sync -n // depots // where // diff -sd/-sr/-se // // If we are in gettingDirectories state, save // the depots output in DepotList. Ignore output from where // as we are only interested in keeping a list of directories // where the command fails. Otherwise, parse the sync & resolve // output and save the filename in FileList. if( fState == gettingDirectories ) { StrBuf depotVal; depotVal.Set( data ); char *depotsVect[7]; int wc = StrOps::Words( depotVal, data, depotsVect, 7 ); StrBuf *newDir = DepotList.Put(); *newDir << depotsVect[1] << "/"; fFileOffset = 2; } else if( fState == gettingWhere ) { return; } else if( fCommand == AC_UNRESOLVED) { char *endChar = strstr( data, " - " ); if( endChar == NULL ) // skip informational lines return; *endChar = '\0'; FileList.Put()->Set( data ); fSeenData = 1; } else if( fCommand == AC_UNSYNCED ) { char *hashChar = strchr( data, '#' ); if( hashChar == NULL ) // skip informational lines return; *hashChar = '\0'; FileList.Put()->Set( data ); fSeenData = 1; } else if( fCommand == AC_MISSING || fCommand == AC_OPENEDUNCHANGED || fCommand == AC_CHANGESUNOPENED ) { FileList.Put()->Set( data ); fSeenData = 1; } } void p4wFilePane::End() { // // Process this call according to our state. StrPtr *noCase = fRequest.GetProtocol( "nocase" ); const StrPtr *pattern = fRequest.GetStateArg( "pat" ); switch( fState ) { case currentDirectory: // // Unless we are getting Thumbnails, // This is the last possible state, so end the tables if ( !fRequest.HideThumbnails() ) { const StrPtr *server; int protocol = 0; server = fRequest.GetProtocol( "server2" ); if( server ) protocol = server->Atoi(); if( protocol > 19 ) fState = gettingThumbs; else doEndPage(); } else doEndPage(); break; case gettingThumbs: { if (fSeenData || fSeenThumbs) { p4wHtml htm; if (!fSeenThumbs) RenderThumbHdr(); RenderThumbEnd(); // end the table of thumbnails htm.endCol(); htm.endCol(); htm.endTRow(); fRequest << htm; } // This is the last possible state, so end the tables doEndPage(); break; } case gettingDirectories: case gettingWhere: { // // If we are at the root in client mode, getting // directories is a 2-state process: depots & // where command. In this case, we just transition to // the gettingWhere state. int ret = 0; if( fRequest.GetPath() == "//" && ( fRequest.GetViewMode() == VM_CLIENT ) && fState == gettingDirectories && DepotList.Count() ) { fState = gettingWhere; return; } // // If we are at the root directory in client or depot // mode, cobble up the directory // list from the depots output. If we are in client mode, we // will filter out depots not mapped to our client. Otherwise // just point to the list of depots. if( fRequest.GetPath() == "//" && ( fRequest.GetViewMode() != VM_WORKSPACE ) ) ret = SetDirsFromDepots(); if( ret ) return; // // Our current directory is our first directory. // Sort DirectoryList so that dirs sorted order matches // the order of results returned by fstat. DirList->Sort( noCase ? 1 : 0 ); fCurDir = -1; // // Suppress the list of opened files under the // following conditions: // 1) Hide Opened Files mode and NOT matching file pattern // 2) Browse-only mode and cmd is not Files matching // or Opened Files all clients // In this case, output the list of directories // as urls and then we're done. if( ( fRequest.HideOpenedFiles() && !pattern ) || ( fRequest.GetBrowseMode() && fCommand != AC_FILESMATCHING && fCommand != AC_OPENEDFILESALL ) ) { RenderDirsInList(); if( fCommand == AC_DEPOT || fCommand == AC_CLIENT || fCommand == AC_PATHBROWSER && ( strcmp( fRequest.GetPath().Text(), "//" ) ) ) { fState = currentDirectory; } else { doEndPage(); // sets fState = done } return; } // // Perform the state transition switch( fCommand ) { case AC_DEPOT: case AC_CLIENT: case AC_WORKSPACE: case AC_PATHBROWSER: case AC_FILESMATCHING: case AC_MISSING: case AC_OPENEDUNCHANGED: case AC_CHANGESUNOPENED: // // Now we need to fetch all of the opened // files rooted at this tree. fState = gettingOpened; break; case AC_UNSYNCED: // // at the call to p4 sync -n we will set fState = gettingOpened; // clear fState here so we don't prematurely call doEndPage() fNoEndNow = 1; fState = gettingOpened; break; case AC_UNRESOLVED: { // // If server supports fstat -Ru, use this instead // of resolved -n followed by fstats of each // individual file. const StrPtr *server = fRequest.GetProtocol( "server2" ); if( server && server->Atoi() > 17 ) fState = gettingOpened; else fState = parsingFiles; break; } default: fState = parsingFiles; break; } } break; case parsingFiles: fState = gettingOpened; // // If we have empty results render a warning message, // depending on which command was requested. Also, // end the pane since we're done. if( !fSeenData ) { RenderEmptyWarning(); doEndPage(); } break; case gettingOpened: { // // Display any remaining directories. if( fFullBrowseMode ) //|| fCommand == AC_FILESMATCHING ) RenderDirsInList(); // // If we have empty results render a warning message, // depending on which command was requested if( !fSeenData ) RenderEmptyWarning(); // // Only generate files in the current directory // when in a path browser type command if( fFullBrowseMode && strcmp( fRequest.GetPath().Text(), "//" ) ) { fState = currentDirectory; } else { // // Unless we are getting Thumbnails, // This is the last possible state, so end the pane if ( !fRequest.HideThumbnails() ) { const StrPtr *server; int protocol = 0; server = fRequest.GetProtocol( "server2" ); if( server ) protocol = server->Atoi(); if( protocol > 19 ) fState = gettingThumbs; else doEndPage(); } else doEndPage(); } } break; case hasFatalError: // // End the pane. doEndPage(); // sets fState = done break; case done: break; default: break; } } void p4wFilePane::RenderThumbs( StrDict * varList, StrPtr * depotFile, StrPtr * headRev, StrPtr * haveRev, AllCommands ac, StrBufDict * cmdArgs ) { StrPtr * attr_thumb = varList->GetVar( "attr-thumb" ); if (!attr_thumb) return; // // make sure this file is in the list of files we have put in the left hand column if (ThumbList.Count()) { int i; const StrBuf *ptr; StrPtr *file = varList->GetVar( "depotFile" ); if (!file) file = varList->GetVar( "clientFile" ); for (i=0; ptr=ThumbList.Get(i++); ) { if (!strcmp(ptr->Text(), file->Text())) break; } if (!ptr) return; } else return; p4wHtml htm; p4wHtml prefix; p4wHtml suffix; p4wURL urlMaker; StrBuf temp; StrBuf clearIcon; urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); StrBuf grayIcon; urlMaker.ConstructURL( grayIcon, "/grayPixelIcon", AC_ICON, NULL ); const StrPtr *thb = fRequest.GetStateArg( "thb" ); const StrPtr *thm = fRequest.GetStateArg( "thm" ); const StrPtr *thw = fRequest.GetStateArg( "thw" ); const StrPtr *thx = fRequest.GetStateArg( "thx" ); const StrPtr *thz = fRequest.GetStateArg( "thz" ); char *pcolstr; int colwidth; int colchars; if (thz) { if (!strcmp(thz->Text(), "s")) { pcolstr = "80"; colwidth = 80; colchars = 12; } else { pcolstr = "120"; colwidth = 120; colchars = 17; } } else { pcolstr = "160"; colwidth = 160; colchars = 20; } if (!fSeenThumbs) RenderThumbHdr(); int b = 0; if ( !InRequestDir(varList, &fLastFolder) ) { int c; int len; StrBuf path; StrBuf filepath; path << fRequest.GetDepotPath(); len = strlen(path.Text()); if (fRequest.GetViewMode() == VM_WORKSPACE) { StrPtr * clientFile = varList->GetVar( "clientFile" ); filepath << clientFile; c = *(path.Text() + len - 1); } else { StrPtr * depotFile = varList->GetVar( "depotFile" ); filepath << depotFile; c = '/'; } char *p = strchr(filepath.Text()+len, c); char *r = strrchr(filepath.Text()+len, c); if (r) { temp.Clear(); temp << filepath.Text()+len; temp.SetLength((r - filepath.Text()) - len + 1); fSubfolders.Clear(); fSubfolders << temp; fLastFolder.Clear(); fLastFolder << path << fSubfolders; } if (p) { temp << filepath.Text()+len; temp.SetLength((p - filepath.Text()) - len + 1); StrBuf subpath; subpath << temp; if (strcmp(subpath.Text(), fSubpath.Text())) { RenderThumbEnd(); htm.beginTable( "0", "100%", "0", "0" ); htm.beginTRow(); htm.beginCol("bottom", 0, "2", 0, 0, 0, "15"); htm.icon( grayIcon.Text(), "1", "100%", "", 1 ); htm.endCol(); htm.beginTRow(0, 0, "#EEEEEE"); htm.beginCol(); htm.icon( clearIcon.Text(), "20", "1", "", 1 ); htm.endCol(); htm.beginCol(0, "left"); fRequest << htm; htm.Clear(); // Output the link for this directory. fSubpath.Set(subpath); fSeenThumbs = 0; temp.Clear(); temp << subpath << "..."; prefix.beginNobreak(); prefix << "<b>Images in "; suffix << "</b>"; suffix.endNobreak(); if( fRequest.GetViewMode() != VM_WORKSPACE ) { OutputHREF( prefix.Text(), subpath.Text(), AC_PATHBROWSER, NULL, temp.Text(), suffix.Text() ); } else { StrBuf newPath; StrBuf newBase; newPath.Set( fRequest.GetPath() ); newPath << subpath; fRequest.UseNewBase( newBase, NULL, "path", newPath.Text() ); OutputHREF( prefix.Text(), newBase.Text(), AC_PATHBROWSER, NULL, temp.Text(), suffix.Text() ); } prefix.Set(""); suffix.Set(""); htm.endCol(); htm.endTRow(); htm.beginTRow(); htm.beginCol("top", 0, "2", 0, 0, 0, "5"); htm.icon( grayIcon.Text(), "1", "100%", "", 1 ); htm.endCol(); htm.endTRow(); htm.endTable(); if (thx || !thw) // if not using CSS for the thumbnail table { htm.beginTable( "0", "100%", "5", "1" ); } else { htm.beginTable( "0", "100%", "0", "0" ); htm.beginTRow(); htm.beginCol(0, 0, 0, 0, 0, 0, "1"); htm.icon( clearIcon.Text(), "1", 0, "", 1, "0", "0", 0, "minwidth" ); htm.endCol(); htm.endTRow(); } htm.beginTRow(); if (!thx && thw) htm.beginCol(); b = 1; } } } if ( !b && (thx || !thw) && fSeenThumbs % fRequest.GetThumbnailCols() == 0) { if (fSeenThumbs) htm.endTRow(); htm.beginTRow(); } if (thx || !thw) // is CSS disabled? { htm.beginCol("bottom", "center", NULL, NULL, NULL, pcolstr, "60"); } else { htm << "<div class=\"thumb\">" << crlf << "<p class=\"img\">"; htm.icon( clearIcon.Text(), pcolstr, "1", "", 1 ); } fRequest << htm; htm.Clear(); fSeenThumbs++; // // Generate the filename url and icons when the file // is in the current directory. StrBuf clientFile; StrBuf pathFile; StrBuf haveRevBuf; // // Set the have revision to the head rev if not on have list // This happens only when a file is not synced; because of // the p4 fstat -Oh, the thumbnail will be the head one if( !haveRev ) { haveRevBuf.Set( headRev->Text() ); } else { haveRevBuf.Set( haveRev->Text() ); } // // Strip off everything from the path except for the file node if( strcmp( fRequest.GetPath().Text(), "//" ) == 0 ) { clientFile.Set( ( varList->GetVar( "depotFile" )->Text() + 2 ) ); pathFile.Set( clientFile ); } else if( fRequest.GetViewMode() != VM_WORKSPACE ) { StrPtr *depotFile = varList->GetVar( "depotFile" ); clientFile.Set( p4wI18n::safeStrrchr( depotFile->Text(), '/' ) + 1 ); pathFile.Set( clientFile ); } else { PathSys *p = PathSys::Create(); StrBuf file; p->Set( varList->GetVar( "clientFile" ) ); p->ToParent( &file ); fRequest.UseNewBase( pathFile, NULL, "path", fRequest.GetPath().Text() ); clientFile.Set( file ); // // If the server supports filenames with special // characters, we need to escape these characters // when in workspace mode. If not in workspace mode, // the special characters have already been escaped. const StrPtr *server = fRequest.GetProtocol( "server2" ); if( server && server->Atoi() > 17 ) file.Set( p4wStrBuf().EscapeP4Chars( file ) ); pathFile << file; delete p; } StrBufDict cmdArgs2; cmdArgs2.SetVar( "rev1", haveRevBuf.Text() ); StrBuf image; StrBuf bindata; char *p = bindata.Alloc((attr_thumb->Length() + 1)/2 + 2); int lgth = DecodeBase16(attr_thumb->Text(), attr_thumb->Length(), p); int h = *((unsigned char *)p+23); int w = *((unsigned char *)p+19); const char *pBorder = thb ? "1" : "0"; if (w >= h && ((colwidth < 160 && w > colwidth) || (thm && w < colwidth))) { image << "<img border=\""; image << pBorder; image << "\" width=\""; image << pcolstr; image << "\" src=\""; } else if (w < h && ((colwidth < 160 && h > colwidth) || (thm && h < colwidth))) { image << "<img border=\""; image << pBorder; image << "\" height=\""; image << pcolstr; image << "\" src=\""; } else { image << "<img border=\""; image << pBorder; image << "\" src=\""; } int l; StrBuf b64data; int ln64; if (lgth != -1) { p = b64data.Alloc(l = ((lgth + 2)/ 3 * 4) + 8); memset(p, '\0', l); ln64 = b64_encode(bindata.Text(), lgth, p, l); if (!ln64) lgth = -1; // failed to convert, set lenght of binary data to -1 } if( fRequest.GetJavascriptMode() == 2 || lgth == -1 ) // MSIE (or error decoding attr_thumb) { p4wURL urlMaker; StrBuf actionURL; StrBuf filePath; char buf[2048*2]; int b = 0; if (!b && (ln64 < 2030) && (lgth != -1)) { sprintf(buf, "http%s//%s/%s?ac=%d", fRequest.IsHTTPS() ? "s:" : ":", fRequest.GetHTTPPort().Text(), b64data.Text(), AC_ECHOURLAsDATA); if (strlen(buf) <= 2040) // can we embed the thumbnail in a URL for MSIE? { image << buf; b = 1; } } if (!b) { sprintf(buf, "http%s//%s%s?ac=%d&rev1=%s", fRequest.IsHTTPS() ? "s:" : ":", fRequest.GetHTTPPort().Text(), depotFile->Text(), AC_GETTHUMBNAIL, haveRevBuf.Text()); image << buf; } } else { image << "data:image/png;base64,"; image << b64data.Text(); } image << "\" alt=\""; image << clientFile.Text(); image << "\" />"; if (*fSubfolders.Text()) { temp.Clear(); if( fRequest.GetViewMode() == VM_WORKSPACE ) temp << fRequest.GetDepotPath() << fSubfolders << clientFile; else temp << fSubfolders << pathFile.Text(); pathFile.Clear(); pathFile << temp.Text(); } StrBuf title; if( fRequest.GetViewMode() == VM_WORKSPACE ) title << fSubfolders << clientFile; else title << pathFile; title << " #" << haveRevBuf; // Some browser truncate long tooltips // so chop off leading folder names // until we are shorter than 73 chars // (or have removed all folder names) while (strlen(title.Text()) > 72 && ((p = strchr(title.Text(), '/')))) { char *q = title.Text(); strcpy(q+3, p+1); *q = *(q+1) = *(q+2) = '.'; title.SetLength(strlen(title.Text())); } OutputHREF( prefix.Text(), pathFile.Text(), AC_TEXTCONTENT, &cmdArgs2, image.Text(), suffix.Text(), 0, title.Text() ); if (thx || !thw) { prefix.linebreak(); suffix.endCol(); } else { prefix << crlf << "<p class=\"txt\">"; suffix << crlf << "</div>"; } // if the current 'directory' is // // we can find up with full paths instead of just the filename // in clientFile. Check for that and clean it up if necessary if( fRequest.GetViewMode() != VM_WORKSPACE ) { char *p = strrchr(clientFile.Text(), '/'); if (p) { temp.Clear(); temp << p+1; clientFile.Clear(); clientFile << temp; } } // truncate clientFile names longer than 20 characters // to first 11 chars, dotdotdot, last 6 chars (whcih gets the extn) int len; if ((len = strlen(clientFile.Text())) > colchars) { temp.Clear(); temp << clientFile.Text() + len - 6; clientFile.SetLength(colchars - 7); clientFile << "…" << temp; } // // Only FILETEXTDEPOT has command arguments on the url if( ac == AC_FILETEXTDEPOT ) OutputHREF( prefix.Text(), pathFile.Text(), ac, cmdArgs, clientFile.Text(), suffix.Text(), 0, title.Text() ); else if( ac == AC_WORKSPACEFILE ) OutputHREF( prefix.Text(), RootRelative(&pathFile)->Text(), ac, NULL, clientFile.Text(), suffix.Text() ); else OutputHREF( prefix.Text(), pathFile.Text(), ac, NULL, clientFile.Text(), suffix.Text(), 0, title.Text() ); } void p4wFilePane::RenderThumbHdr() { StrBuf temp; p4wHtml htm; p4wURL urlMaker; StrBuf clearIcon; urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); StrBuf grayIcon; urlMaker.ConstructURL( grayIcon, "/grayPixelIcon", AC_ICON, NULL ); fLastFolder << fRequest.GetDepotPath(); htm.endTable(); htm.beginCol(0,"center",0,0,0, "5", "100%"); htm.icon( clearIcon.Text(), "100%", "5", "", 1 ); htm.endCol(); htm.beginCol("top", 0, 0, 0, "#FFFFFF", "100%", 0, 0, "thumbnail_pane"); htm.beginTable( "0", "100%", "0", "0" ); htm.beginTRow(0, 0, "#EEEEEE"); htm.beginCol(); htm.icon( clearIcon.Text(), "20", "1", "", 1 ); htm.endCol(); htm.beginCol(0, "left"); htm.beginNobreak(); temp << fLastFolder; char *p = strrchr(temp.Text(), '/'); if (p) { *p = '\0'; p = strrchr(temp.Text(), '/'); p = p ? p+1 : temp.Text(); } else if ((p = strrchr(temp.Text(), '\\'))) { *p = '\0'; p = strrchr(temp.Text(), '\\'); p = p ? p+1 : temp.Text(); } else p = temp.Text(); if (!*p) { temp.Set("//"); p = temp.Text(); } htm << "<b>Images in Folder \"" << p << "\"</b>"; htm.endNobreak(); htm.endCol(); htm.endTRow(); htm.beginTRow(); htm.beginCol("top", 0, "2", 0, 0, 0, "5"); htm.icon( grayIcon.Text(), "1", "100%", "", 1 ); htm.endCol(); htm.endTRow(); htm.endTable(); const StrPtr *thw = fRequest.GetStateArg( "thw" ); const StrPtr *thx = fRequest.GetStateArg( "thx" ); if (!thx && thw) // if using CSS for the thumbnail table { htm.beginTable( "0", "100%", "0", "0" ); htm.beginTRow(); htm.beginCol(0, 0, 0, 0, 0, 0, "1"); htm.icon( clearIcon.Text(), "1", 0, "", 1, "0", "0", 0, "minwidth" ); htm.endCol(); htm.endTRow(); htm.beginTRow(); htm.beginCol(); } else { htm.beginTable( "0", "100%", "5", "1" ); } fRequest << htm; htm.Clear(); } void p4wFilePane::RenderThumbEnd() { p4wHtml htm; const StrPtr *thw = fRequest.GetStateArg( "thw" ); const StrPtr *thx = fRequest.GetStateArg( "thx" ); if (!fSeenThumbs) { if (thx || !thw) // CSS for thumbnails disabled? { htm.beginTRow(); htm.beginCol("top"); } htm << " "; StrBuf helpURL; StrBuf newBase; p4wURL urlMaker; fRequest.UseNewBase( newBase, NULL, "path", "/thumbnails" ); urlMaker.ConstructURL( helpURL, newBase.Text(), AC_HELP, NULL, fRequest.GetUnicode() ); helpURL << "#daemon"; htm.beginLink( helpURL.Text() ); htm << "No thumbnails available"; htm.endLink(); htm.endCol(); } else { if (thx || !thw) // CSS for thumbnails disabled? { char *pcolstr; const StrPtr *thz = fRequest.GetStateArg( "thz" ); if (thz) { if (!strcmp(thz->Text(), "s")) pcolstr = "80"; else pcolstr = "120"; } else pcolstr = "160"; while (fSeenThumbs < fRequest.GetThumbnailCols()) { htm.beginCol(0,0,0,0,0, pcolstr); htm << " "; htm.endCol(); fSeenThumbs++; } } else htm.endCol(); } htm.endTRow(); htm.endTable(); fRequest << htm; htm.Clear(); } // ------------------------------------- // Utility functions. // void p4wFilePane::RenderIcons( StrDict * varList, int inCurrentDirectory, int isDeleted ) { // // Generate the state icons for this file p4wHtml htm; p4wURL urlMaker; StrBuf clearIcon; StrBuf lockIcon; StrBuf lockAlt; StrBuf myActionIcon; StrBuf myActionAlt; StrBuf theirActionIcon; StrBuf theirActionAlt; StrBuf filestateIcon; StrBuf filestateAlt; urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); // // If more commands were queued after a fatal error, // don't display any more data if( fState == hasFatalError || fState == done ) return; // // Fetch this file's state. StrPtr * action = varList->GetVar( "action" ); StrPtr * headRev = varList->GetVar( "headRev" ); StrPtr * haveRev = varList->GetVar( "haveRev" ); StrPtr * otherOpen = varList->GetVar( "otherOpen" ); StrPtr * headAction = varList->GetVar( "headAction" ); StrPtr * otherLock = varList->GetVar( "otherLock" ); StrPtr * ourLock = varList->GetVar( "ourLock" ); StrPtr * unresolved = varList->GetVar( "unresolved" ); StrPtr * depotFile = varList->GetVar( "depotFile" ); StrPtr * clientFile = varList->GetVar( "clientFile" ); StrPtr * headType = varList->GetVar( "headType" ); // if filetype includes +1, set the correct lock flag if (headType) { char * offset = strchr(headType->Text(), '+'); if (offset) { if (strchr(offset, 'l')) { if (action) ourLock = (StrPtr *)1; else if (otherOpen) otherLock = (StrPtr *)1; } } } // // Determine the state of the file int needsSync; int unmapped = 0; int deletedSync = 0; int notInPerforce = 0; int notHad = 0; if( !depotFile ) { notInPerforce = 1; } else if ( action && ( *action == "add" || *action == "branch" || *action == "import" ) ) { notInPerforce = 1; } else if( isDeleted && haveRev ) { needsSync = 1; } else if( isDeleted && !haveRev ) { deletedSync = 1; // File is "had" but not mapped in client, or opened but not // mapped or this is from "unsynced files" filter } else if( (haveRev && !headRev) || (action && !haveRev && !headRev) ) { const StrPtr *pb = fRequest.GetStateArg( "pb" ); if ( pb && atoi(pb->Text()) == AC_UNSYNCED && !haveRev && action ) needsSync = 1; else unmapped = 1; // // File is not on have list and not mapped to a client file } else if( !clientFile && !haveRev ) { unmapped = 1; } else if( !haveRev ) { notHad = 1; } else if( headRev && haveRev && ( strcmp( headRev->Text(), haveRev->Text() ) ) ) { needsSync = 1; } else { needsSync = 0; } // // If this is browse-only mode, erase some client states so that // no client state is displayed. if( fRequest.GetBrowseMode() ) { unresolved = NULL; if( action ) otherOpen = action; action = NULL; } // // Determine which icons to display and construct them // // Locked? By me or others? if( ourLock ) { lockAlt.Set( "Locked by you" ); urlMaker.ConstructURL( lockIcon, "/lockedIcon", AC_ICON, NULL ); } else if( otherLock ) { lockAlt.Set( "Locked by others" ); urlMaker.ConstructURL( lockIcon, "/theirlockIcon", AC_ICON, NULL ); } else { urlMaker.ConstructURL( lockIcon, "/clearpixelIcon", AC_ICON, NULL ); } // // Do I have this file open for edit/add/etc? if( !action ) { urlMaker.ConstructURL( myActionIcon, "/clearpixelIcon", AC_ICON, NULL ); } else if( *action == "add" ) { myActionAlt.Set( "Opened for add" ); urlMaker.ConstructURL( myActionIcon, "/addIcon", AC_ICON, NULL ); } else if( *action == "branch" || *action == "import" ) { myActionAlt.Set( "Opened for branch" ); urlMaker.ConstructURL( myActionIcon, "/branchIcon", AC_ICON, NULL ); } else if( *action == "edit" ) { myActionAlt.Set( "Opened for edit" ); urlMaker.ConstructURL( myActionIcon, "/editIcon", AC_ICON, NULL ); } else if( *action == "delete" ) { myActionAlt.Set( "Opened for delete" ); urlMaker.ConstructURL( myActionIcon, "/deleteIcon", AC_ICON, NULL ); } else if( *action == "integrate" ) { myActionAlt.Set( "Opened for integrate" ); urlMaker.ConstructURL( myActionIcon, "/integIcon", AC_ICON, NULL ); } else { urlMaker.ConstructURL( myActionIcon, "/clearpixelIcon", AC_ICON, NULL ); } // // File state if( notInPerforce ) { filestateAlt.Set( "Not in depot" ); urlMaker.ConstructURL( filestateIcon, "/notdepotIcon", AC_ICON, NULL ); } else if( isDeleted || deletedSync ) { filestateAlt.Set( "Deleted at head revision" ); urlMaker.ConstructURL( filestateIcon, "/headdeletedIcon", AC_ICON, NULL ); } else if( notHad ) { filestateAlt.Set( "Not in workspace" ); urlMaker.ConstructURL( filestateIcon, "/syncednoneIcon", AC_ICON, NULL ); } else if( unmapped ) { filestateAlt.Set( "Not in client view" ); urlMaker.ConstructURL( filestateIcon, "/unmappedIcon", AC_ICON, NULL ); } else if( unresolved ) { filestateAlt.Set( "Needs resolve" ); urlMaker.ConstructURL( filestateIcon, "/resolveIcon", AC_ICON, NULL ); } else if( needsSync ) { filestateAlt.Set( "Needs sync" ); urlMaker.ConstructURL( filestateIcon, "/syncIcon", AC_ICON, NULL ); } else { // in sync filestateAlt.Set( "In sync" ); urlMaker.ConstructURL( filestateIcon, "/syncedIcon", AC_ICON, NULL ); } // // Does someone else have this file open? // If it is opened by other(s): // 1) if anyone else has it opened for delete, show that status // 2) otherwise if anyone else has it opened for add, show that status // 3) otherwise show it as opened for edit. if( !otherOpen ) { urlMaker.ConstructURL( theirActionIcon, "/clearpixelIcon", AC_ICON, NULL ); } else { int tadd = 0; int tdelete = 0; StrPtr *p; for( int i = 0; ( p = varList->GetVar( StrRef( "/otherAction" ), i ) ) != NULL; i++ ) { if( *p == "delete" ) { ++tdelete; break; } if( *p == "add" ) ++tadd; } if( tdelete ) { theirActionAlt.Set( "Opened for delete by other user" ); urlMaker.ConstructURL( theirActionIcon, "/theirdeleteIcon", AC_ICON, NULL ); } else if( tadd ) { theirActionAlt.Set( "Opened for add by other user" ); urlMaker.ConstructURL( theirActionIcon, "/theiraddIcon", AC_ICON, NULL ); } else { theirActionAlt.Set( "Opened for edit by other user" ); urlMaker.ConstructURL( theirActionIcon, "/theiropenIcon", AC_ICON, NULL ); } } // // Display the icons. Leftmost icons show lock (mine or others). // The next column shows my action. The next column shows the // file state. The final column shows their action. Any missing // states will use a blank placeholder so the columns line up // correctly. htm.beginTRow("top", 0, 0, fRequest.HideDetails() ? 0 : (fRowctr++ & 0x01) ? "pathbr_row alt_row" : "pathbr_row"); // // if this file is not in the current directory, we need to // indent it under its directory if( fState == gettingOpened && !InRequestDir(varList, NULL) ) { htm.beginCol( NULL, NULL, "4" ); htm.icon( clearIcon.Text(), "17", "36", "", 1 ); htm.endCol(); } htm.beginCol( NULL, NULL, NULL, NULL, NULL, "7" ); htm.icon( lockIcon.Text(), "17", "7", lockAlt.Text(), 1 ); htm.endCol(); htm.beginCol( NULL, NULL, NULL, NULL, NULL, "8" ); htm.icon( myActionIcon.Text(), "17", "8", myActionAlt.Text(), 1 ); htm.endCol(); htm.beginCol( NULL, NULL, NULL, NULL, NULL, "13" ); fFileCtr++; const StrPtr *X = fRequest.GetStateArg("X"); int hoticons = (X && strchr(X->Text(), 'L')) ? 1 : 0; // 1 -> link on icons, 0 -> bare icons const StrPtr *thx = fRequest.GetStateArg( "thx" ); if( (!thx || *thx != "y") && hoticons ) { // Generate the arguments to pass to javascript:showMenu() StrBuf id; id << "id_" << fFileCtr << "i"; StrBuf curFi; curFi << ((fRequest.GetViewMode() == VM_WORKSPACE) ? varList->GetVar( "clientFile" ) : varList->GetVar( "depotFile" )); char *finm = curFi.Text(); *(finm + curFi.Length()) = '\0'; // ensure nul terminated finm += fRequest.GetPath().Length(); if (strchr(finm, '\\')) // if there are backslashes in the path, we need to double them { StrBuf tmp; char buf[2] = { '\0', '\0' }; char *p = finm; while(*p) { if (*p == '\\') { tmp << "\\\\"; p++; } else { buf[0] = *p++; tmp << buf; } } curFi.Set(tmp); finm = curFi.Text(); *(finm + curFi.Length()) = '\0'; // ensure nul terminated } curFi.Set(finm); p4wStrBuf p4wCurFi; p4wCurFi.EscapeURLAllChars(curFi, fRequest.GetUnicode()); if (strstr(p4wCurFi.Text(), "%25")) { p4wStrBuf p4wCurFi2; p4wCurFi2.EscapeURLAllChars(p4wCurFi, fRequest.GetUnicode()); curFi.Set(p4wCurFi2.Text()); } else curFi.Set(p4wCurFi.Text()); finm = curFi.Text(); int show = GetFileMenuShowVals(fstatRun ? varList : NULL); StrBuf args; if (headRev && *headRev > "1") args << "&rev1=" << headRev->Text(); args << "&mu=" << show; StrBuf jsaction; jsaction << "javascript:showMenu('" << finm << "','" << fFileCtr << "i" << "','file'," << show << ",'" << args.Text() << "')"; // generate the icon and its link htm.beginLink( jsaction.Text(), NULL, NULL, NULL, NULL, id.Text() ); } htm.icon( filestateIcon.Text(), "17", "13", filestateAlt.Text(), 1 ); if( !thx || *thx != "y" ) htm.endLink(); htm.endCol(); htm.beginCol( NULL, NULL, NULL, NULL, NULL, "8" ); htm.icon( theirActionIcon.Text(), "17", "8", theirActionAlt.Text(), 1 ); htm.endCol(); fRequest << htm; } void p4wFilePane::RenderError( char *data, int escapeHTML ) { // // If the last command (fstat) yielded an error, defer printing the // error until after the rest of the screen is drawn. This insures // that all dirs links will be drawn prior to the error message // for the failed fstat. p4wHtml htm; p4wURL urlMaker; StrBuf clearIcon; urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); if( fState == gettingOpened && !strstr( data, "the page content limit was exceeded!" ) && !strstr( data, "Senseless juxtaposition" ) && !fAbortNow ) return; if( fFirstErrorOnly && fPrintedError && !fAbortNow ) return; // // A little vertical space above the error htm.beginTRow(); htm.beginCol(); htm.icon( clearIcon.Text(), "5", "1", "", 1 ); htm.endCol(); htm.endTRow(); // // Span the error over several columns, skipping the // initial columns so that the file state icons don't // stretch across over the page. htm.beginTRow(); htm.beginCol(); htm.endCol(); htm.beginCol(); htm.endCol(); htm.beginCol(); htm.endCol(); htm.beginCol(); htm.endCol(); htm.beginCol( NULL, NULL, "28" ); fRequest << htm; htm.Clear(); if (strstr(data, " has not been enabled by 'p4 protect'")) data = "Access has not been enabled by 'p4 protect'"; if( escapeHTML ) htm.text( p4wStrBuf().EscapeHTML(StrRef(data), Unicode()).Text(), NULL, NULL, "red" ); else htm.text( data, NULL, NULL, "red" ); htm.endCol(); htm.endTRow(); // // A little vertical space underneath htm.beginTRow(); htm.beginCol(); htm.icon( clearIcon.Text(), "5", "0", "", 1 ); htm.endCol(); htm.endTRow(); fRequest << htm; fPrintedError = 1; } void p4wFilePane::IgnoreError( char *data ) { fIgnoredError.Set(data); } void p4wFilePane::OutputError( const char * errBuf ) { // // If more commands were queued after a fatal error, // don't display any more data if( fState == hasFatalError || fState == done ) return; // // Generate a ::Begin() call if necessary. if( !fSeenOutputStat ) { Begin(); fSeenOutputStat = 1; } // // Ignore errors except for incorrect protocol errors StrPtr *server = fRequest.GetProtocol( "server2" ); if( !server || server->Atoi() < 8 ) { // // Don't remove the (char *) cast below: this was // needed for the port to linuxia64 RenderError((char *) "P4web requires Perforce server version of 99.2 or above", 1 ); fHadFatalError = 1; fState = hasFatalError; } } void p4wFilePane::HandleError( Error *err ) { // // If more commands were queued after a fatal error, // don't display any more data if( fState == hasFatalError || fState == done ) return; // // Directories that generate the error "not in client view" // from the where command are put in the ExcludeList so that // we skip showing these directories when // in client mode. if( fState == gettingWhere && err->GetSeverity() == E_WARN && err->GetGeneric() == EV_EMPTY ) { StrBuf em; err->Fmt( &em ); char *cv = strstr( em.Text(), "/... - file(s) not in client view" ); if( cv ) { StrBuf *fname = ExcludeList.Put(); fname->Set( em.Text() + 2, cv - em.Text() - 1 ); } return; } // // Error "no such file" is not a real error if we are in // workspace mode if( fRequest.GetViewMode() == VM_WORKSPACE && fState == currentDirectory && err->GetSeverity() == E_WARN && err->GetGeneric() == EV_EMPTY ) { StrBuf errMsg; err->Fmt(&errMsg); char *e = strstr( errMsg.Text(), "- no such file" ); char *c = strstr( errMsg.Text(), "- file(s) not in client view" ); if( e || c ) { StrBuf filename; if( e ) filename.Set( errMsg.Text(), e - errMsg.Text() - 1 ); else filename.Set( errMsg.Text(), c - errMsg.Text() - 1 ); if( fRequest.HideCurrentDir() ) return; const StrPtr *srev = fRequest.GetDynArg( "sr" ); StrBufDict cmdArgs; AllCommands ac = SEC_DONT_SHOW_REVHIST ? (fRequest.GetViewMode() == VM_WORKSPACE ? AC_WORKSPACEFILE : AC_NONE) : AC_BROWSEFILE; if( srev ) { cmdArgs.SetVar( "sr", srev ); ac = AC_FILETEXTDEPOT; } StrBufDict varList; // // If we escaped any protected characters in // the filename, we need to unescape them now. if( fView.P4CharsProtected() ) { filename.Set( p4wStrBuf().UnescapeP4Chars( filename ) ); } varList.SetVar( "clientFile", filename.Text() ); if( !fSeenBegin ) Begin(); // // If page content limit in bytes has been exceeded, // don't generate this entry if( !PageLimitExceeded() ) RenderFileCurrentDir( &varList, 0, NULL, NULL, NULL, NULL, NULL, NULL, ac, &cmdArgs ); return; } } // // Handle cases where path doesn't exist. This can only be detected // here if the path has only one node (ie //depot/) if( err->GetSeverity() == E_FAILED && err->GetGeneric() == EV_UNKNOWN ) { StrBuf err2; err->Fmt( &err2 ); if ( strstr( err2.Text(), "- must refer to client" ) ) { if( !fSeenBegin ) Begin(); // // Files matching can generate this error, but this // is not a case where we want to prompt the user // to re-enter the path. if( fCommand != AC_FILESMATCHING ) { PromptRetry( "The path entered could not be found." ); fState = hasFatalError; fHadFatalError = 1; return; } } } // // Filter out warnings. Also filter out errors on the // last fstat (they will be printed later). DoHandleError( err, 1 ); // // If we got an error on the fstat to get opened // files, output the rest of the dirs output before printing // out the error message. if( fState == gettingOpened && fHadFatalError ) { if( fFullBrowseMode ) //|| fCommand == AC_FILESMATCHING ) RenderDirsInList(); StrBuf errMsg; err->Fmt( &errMsg ); fState = hasFatalError; // // If we had a login error, generate the error page // we've constructed instead of the original error. if( fPromptedPW ) RenderError( fPasswdErrorPage.Text(), 0 ); else RenderError( p4wStrBuf().EscapeHTML( errMsg, Unicode() ).Text(), 1 ); } // // End is called next. If this error was fatal, set the state // for the End call. if( fHadFatalError ) fState = hasFatalError; } void p4wFilePane::doEndPage() { if (fNoEndNow) { fNoEndNow = 0; } else { // // End this form and the table row p4wHtml htm; if (!fRequest.HideDetails()) { htm.endTable(); htm.endCol(); htm.endTRow(); } htm.endTable(); htm.endCol(); htm.beginCol(); htm.endForm(); htm.endCol(); htm.endTRow(); if( (!fRequest.GetBrowseMode() && fRequest.fUnknownClient) ) { htm.beginTRow(); htm.beginCol(0,0,"100%"); htm << "<font color=\"#FF0000\">Client '"; htm << fRequest.GetClient().Text(); htm << "' does not exist - Click</font> <b>Settings</b> <font color=\"#FF0000\">"; htm << "to change the client.</font>"; htm << "<script language=javascript>" << crlf; htm << "window.location = \""; if (fRequest.IsHTTPS()) htm << "https://"; else htm << "http://"; htm << fRequest.GetHTTPPort().Text() << "?ac=" << AC_MULTIUSER << "&err=1\""; htm << "</script>" << crlf; htm.endCol(); htm.endTRow(); } htm.comment( "END FILE PANE" ); fRequest << htm; } fState = done; } void p4wFilePane::RenderEmptyWarning() { p4wHtml htm; switch( fCommand ) { case AC_UNSYNCED: case AC_UNRESOLVED: case AC_MISSING: case AC_OPENEDUNCHANGED: case AC_CHANGESUNOPENED: case AC_FILESMATCHING: case AC_OPENEDFILESALL: if( !fPrintedError ) { ++fPrintedError; htm.beginTRow(); if( fCommand == AC_FILESMATCHING ) htm.beginCol( NULL, NULL, "5" ); else htm.beginCol( NULL, NULL, "4" ); htm.beginNobreak(); htm << "No files found."; htm.endNobreak(); if (fIgnoredError.Length()) htm << "<p>" << p4wStrBuf().EscapeHTML( fIgnoredError, Unicode() ).Text(); htm.endCol(); htm.endTRow(); fRequest << htm; } break; default: break; } } void p4wFilePane::OutputDirAsUrl( const char *directory ) { // // Output the directory as an url. If we are in workspace // mode, we need to use a full url instead of a relative url if( fRequest.GetViewMode() != VM_WORKSPACE ) { OutputHREF( NULL, directory, AC_PATHBROWSER, NULL, directory, NULL ); } else { StrBuf newBase; StrBuf newPath; newPath.Set( fRequest.GetPath() ); newPath << directory; fRequest.UseNewBase( newBase, NULL, "path", newPath.Text() ); OutputHREF( NULL, newBase.Text(), AC_PATHBROWSER, NULL, directory, NULL ); } } void p4wFilePane::OutputDirMenu( const char *directory ) { const StrPtr *thx = fRequest.GetStateArg( "thx" ); if( thx && *thx == "y" ) return; StrBuf temp; char *p = (char *)directory; char *q; while (q = strchr(p, '\\')) { temp.Append(p, q-p); temp << "\\\\\\\\"; p = q+1; directory = temp.Text(); } // Generate the arguments to pass to javascript:showMenu() StrBuf id; id << "id_d" << ++fDirCtr; StrBuf jsaction; jsaction << "javascript:showMenu(\\\"" << directory << "\\\",\\\"d" << fDirCtr << "\\\",\\\"path\\\","; int show = -1; if (RemoteList.Count()) // are there remote depots to worry about? { const StrPtr *depot; int id = -1; while( depot = RemoteList.Get( ++id ) ) { if (*depot == directory) { show = PM_PATHBROWSER + PM_SYNCCMD + PM_SYNCFRM + PM_INTEGRATEFRM; break; } } } jsaction << show; jsaction << ",\\\"" << "\\\")"; StrBuf jssuffix; jssuffix << "<script language=javascript>"; jssuffix << "document.write(\" <span class='muaro'>"; jssuffix << "<a href='" << jsaction << "' title='Menu' id='" << id.Text() << "'>"; jssuffix << " </a></span>\")"; jssuffix << "</script>" << crlf; fRequest << jssuffix; } void p4wFilePane::GetFilePath( const StrPtr *depotFile, const StrPtr *workspace, StrBuf &clientFile ) { // // Strip the current directory from the file, and // save new path in clientFile. if( fRequest.GetPath() == "//") { clientFile.Set( depotFile->Text() + 2 ); return; } // // Handle client or depot mode if( fRequest.GetViewMode() != VM_WORKSPACE ) { // // Insure we are using the correct file offset // to get the path. if( !fFileOffset ) { fFileOffset = fRequest.GetPath().Length(); // // If we detected more than one type of mapping, // we need to search through the list of dirs to // find the correct offset. This can occur if the // file is unexpectedly in client syntax. } else if( fFileOffsetBad ) { int cOffset; int matched = 0; StrBuf fullDir; StrBuf dFile; char *fd; StrPtr *noCase = fRequest.GetProtocol( "nocase" ); for( int i = 0; i < FullDirList.Count(); i++ ) { fd = FullDirList.Get(i)->Text(); cOffset = p4wI18n::safeStrrchrPos( fd, '/' ) + 1; if( noCase ) { fullDir.Set( fd, cOffset ); dFile.Set( depotFile->Text(), cOffset ); matched = !StrPtr::SCompare( fullDir.Text(), dFile.Text() ); } else { matched = !strncmp( fd, depotFile->Text(), cOffset ); } if( matched ) break; } fFileOffset = cOffset; } clientFile.Set( depotFile->Text() + fFileOffset ); // // Handle workspace mode. } else { if( !fFileOffset ) fFileOffset = fRequest.GetPath().Length(); clientFile.Set( workspace->Text() + fFileOffset ); } } void p4wFilePane::RenderDirsInList() { // // Render an url for each directory up to the current // directory p4wHtml htm; p4wURL urlMaker; StrBuf clearIcon; StrBuf folderIcon; urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); if( fRequest.GetViewMode() == VM_WORKSPACE ) urlMaker.ConstructURL( folderIcon, "/workspacefolderIcon", AC_ICON, NULL ); else urlMaker.ConstructURL( folderIcon, "/depotfolderIcon", AC_ICON, NULL ); int coMingle = fRequest.GetBrowseMode(); const StrPtr *d; while( d = DirList->Get( ++fCurDir ) ) { // // If page content limit in bytes has been // exceeded, don't generate this entry if( PageLimitExceeded() ) break; // // Generate the correct indentation and the folder // icon, then the directory name as an url htm.beginTRow("top", 0, 0, fRequest.HideDetails() ? 0 : (fRowctr++ & 0x01) ? "pathbr_row alt_row" : "pathbr_row"); htm.beginCol( NULL, NULL, "2" ); htm.icon( clearIcon.Text(), "17", "15", "", 1 ); htm.endCol(); htm.beginCol( NULL, NULL, "2" ); htm.icon( folderIcon.Text(), "17", "21", "", 1 ); htm.endCol(); htm.beginCol( NULL, NULL, "6", 0, 0, fRequest.HideDetails() ? "100%" : "60%" ); fRequest << htm; htm.Clear(); OutputDirAsUrl( d->Text() ); OutputDirMenu( d->Text() ); htm.endCol(); if (!fRequest.HideDetails()) { htm.beginCol(); /* hdr will generate spacing */ htm.endCol(); if (!(fShowHideCols & HIDE_SIZE) && fstatRun) { htm.beginCol(0,0,0,0, "#dfe3e8"); /* hdr will generate spacing */ htm.endCol(); htm.beginCol( NULL, NULL, "2" ); htm.endCol(); } htm.beginCol(); /* hdr will generate spacing */ htm.endCol(); if ((!(fShowHideCols & HIDE_CHG) || !(fShowHideCols & HIDE_ACTION) || !(fShowHideCols & HIDE_DATE)) && fstatRun) { htm.beginCol(0,0,0,0, "#dfe3e8"); /* hdr will generate spacing */ htm.endCol(); htm.beginCol( NULL, NULL, "7" ); htm.endCol(); } if ((!(fShowHideCols & HIDE_YOURCHG) || !(fShowHideCols & HIDE_YOURACTION)) && fstatRun) { if (!coMingle) { htm.beginCol(0,0,0,0, "#dfe3e8"); /* hdr will generate spacing */ htm.endCol(); htm.beginCol( NULL, NULL, "5" ); htm.endCol(); } } const StrPtr *pb = fRequest.GetStateArg( "pb" ); if (!fRequest.GetBrowseMode() || pb) { if ((!(fShowHideCols & HIDE_OTHERCHG) || !(fShowHideCols & HIDE_OTHERACTION) || !(fShowHideCols & HIDE_OTHERUSER) || !(fShowHideCols & HIDE_OTHERCLIENT)) && fstatRun) { htm.beginCol(0,0,0,0, "#dfe3e8"); /* hdr will generate spacing */ htm.endCol(); } htm.beginCol( NULL, NULL, "9" ); htm.endCol(); } } htm.endTRow(); fRequest << htm; htm.Clear(); } } int p4wFilePane::GetRootDir( const StrPtr *clientFile, StrBuf &rootDir ) { // // Get the root directory from the clientFile path. In client // or depot mode, return 0 if this has no root dir, or 1 if // it does. If workspace mode, return the level of nesting. char *p; PathSys *pathDir; StrBuf node; int nLevels = 0; p4wURL urlMaker; // // Handle depot and client mode if( fRequest.GetViewMode() != VM_WORKSPACE ) { // // File has subdirectories if( ( p = strchr( clientFile->Text(), '/' ) ) != NULL) { rootDir.Set( clientFile->Text(), p - clientFile->Text() + 1 ); if( p4debug.GetLevel( DT_NET ) >= 3 ) printf("clientRootDir is \"%s\"\n", rootDir.Text() ); return 1; } // // File does not have subdirectories if( p4debug.GetLevel( DT_NET ) >= 3 ) printf( "** clientFile is in the current directory\n"); return 0; } // // Handle workspace mode pathDir = PathSys::Create(); pathDir->Set( clientFile ); // // Get parent nodes until we get the root while( pathDir->ToParent( &node ) ) { if( pathDir->Length() ) { rootDir.Set( pathDir->Text() ); ++nLevels; } } delete pathDir; // // File does not have subdirectories if( !rootDir.Length() ) { if( p4debug.GetLevel( DT_NET ) >= 3 ) printf( "** clientFile is in the current directory\n"); return nLevels; } // // File does have subdirectories: set rootDir & return // # levels of subdirectories. rootDir << urlMaker.GetSysDirDelim( NULL ); return nLevels; } void p4wFilePane::RenderSubDirs( const StrPtr *rootDir, int nLevels, StrBuf &clientFile, StrBuf &subDirChain ) { // // Output the set of links for the subdirectories of the clientFile StrBuf fullP; StrBuf subDir; StrBuf node; StrBuf suffix; StrBuf newPath; StrBuf newBase; PathSys *pathDir; p4wURL urlMaker; p4wHtml htm; char *p; // // Handle filename in depot or client file syntax if (!fRequest.HideDetails() || !fRequest.HideThumbnails()) { char * p = strchr( clientFile.Text(), '/' ); if( p == NULL ) p = clientFile.Text(); int nrap = (strchr(p, '-') && !strchr(p, ' ') && strlen(p) < 40) ? 1 : 0; htm.beginCol(0,0,0,0,0,0,0,nrap); } else htm.beginCol(0,0,0,0,0,"100%"); fRequest << htm; htm.Clear(); if( fRequest.GetViewMode() != VM_WORKSPACE ) { while( 1 ) { // Get the location of the next directory. char * p = strchr( clientFile.Text(), '/' ); if( p == NULL ) break; // Get this directory name and remove it from // the clientFile string. subDir.Set( clientFile.Text(), p - clientFile.Text() ); clientFile.Set( p + 1 ); if( p4debug.GetLevel( DT_NET ) >= 3 ) { printf( "intermediary directory is \"%s\"; ", subDir.Text() ); printf( "clientFile is now \"%s\"\n", clientFile.Text() ); } // Output the link for this directory. fullP.Set( rootDir ); fullP << subDirChain << subDir << "/"; OutputHREF( NULL, fullP.Text(), AC_PATHBROWSER, NULL, subDir.Text(), "/" ); // Add this directory to our subDirChain. subDirChain << subDir << "/"; } return; } // // Handle file in workspace syntax int nDeep = nLevels; node.Set( clientFile ); pathDir = PathSys::Create(); pathDir->Set( clientFile ); suffix << urlMaker.GetSysDirDelim( NULL ); for( int nl = 0; nl < nLevels; nl++ ) { for( int nd = 0; nd < nDeep; nd++ ) { pathDir->ToParent( &node ); } subDir.Set( pathDir->Text() ); fullP.Set( rootDir ); fullP << subDirChain << subDir; fullP << urlMaker.GetSysDirDelim( NULL ); newPath.Set( fRequest.GetPath() ); newPath << fullP; fRequest.UseNewBase( newBase, NULL, "path", newPath.Text() ); OutputHREF( NULL, newBase.Text(), AC_PATHBROWSER, NULL, subDir.Text(), suffix.Text() ); subDirChain << subDir; subDirChain << urlMaker.GetSysDirDelim( NULL ); pathDir->Set( clientFile.Text() + subDirChain.Length() ); --nDeep; } // Last link clientFile.Set( node.Text() ); delete pathDir; } void p4wFilePane::RenderPrevDirs( const StrPtr *rootDir ) { // // Display directories occurring alphabetically before // the current file as path browser links. int iDir; int foundIt = 0; const StrPtr *cDir; StrBuf newBase; StrBuf newPath; p4wHtml rowfix; p4wHtml prefix; p4wHtml suffix; const StrPtr *d; StrPtr *noCase = fRequest.GetProtocol( "nocase" ); StrBuf clearIcon; StrBuf folderIcon; p4wURL urlMaker; urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); if( fRequest.GetViewMode() == VM_WORKSPACE ) urlMaker.ConstructURL( folderIcon, "/workspacefolderIcon", AC_ICON, NULL ); else urlMaker.ConstructURL( folderIcon, "/depotfolderIcon", AC_ICON, NULL ); // // Locate the directory containing this file. for( iDir = 0; cDir = DirList->Get( iDir ); iDir++ ) { // // Case-insensitive comparison if server protocol // is "nocase" (ie case-insensitive server) if( noCase ) { if( cDir->CCompare( *rootDir ) >= 0 ) { if( !cDir->CCompare( *rootDir ) ) foundIt = 1; break; } } else if( *cDir >= *rootDir ) { if( *cDir == *rootDir ) foundIt = 1; break; } } // // If the directory containing this file's root does not // exist, then we should insert the directory into the // DirectoryList. This will occur if you have a // newly-created directory as the result of adding a // file, because all other directories are reported by // 'p4 dirs'. This also may occur if user has // files opened in non-mapped directories. if( !foundIt ) { DirList->Put()->Set( *rootDir ); DirList->Sort( noCase ? 1 : 0 ); } // // We found our match. Have we displayed all of the // other directories up to and including until this one? // If not, do so now for full browse mode type commands // (ie depot mode, etc). // We're now going to display the directories up to and // including the one we just found for the current file. // If we're not in full browse mode, we'll skip straight // to this directory. if( !fFullBrowseMode && fCurDir < iDir ) fCurDir = iDir - 1; prefix.beginCol( NULL, NULL, "2" ); prefix.icon( clearIcon.Text(), "17", "15", "", 1 ); prefix.endCol(); prefix.beginCol( NULL, NULL, "2" ); prefix.icon( folderIcon.Text(), "17", "21", "", 1 ); prefix.endCol(); prefix.beginCol( NULL, NULL, fRequest.HideDetails() ? "6" : "7" ); suffix.endCol(); if (!fRequest.HideDetails()) { int coMingle = fRequest.GetBrowseMode(); if (!(fShowHideCols & HIDE_SIZE) && fstatRun) { suffix.beginCol(0,0,0,0, "#dfe3e8"); /* hdr will generate spacing */ suffix.endCol(); suffix.beginCol( NULL, NULL, "2" ); suffix.endCol(); } suffix.beginCol(); /* hdr will generate spacing */ suffix.endCol(); if ((!(fShowHideCols & HIDE_CHG) || !(fShowHideCols & HIDE_ACTION) || !(fShowHideCols & HIDE_DATE)) && fstatRun) { suffix.beginCol(0,0,0,0, "#dfe3e8"); /* hdr will generate spacing */ suffix.endCol(); suffix.beginCol( NULL, NULL, "7" ); suffix.endCol(); } if ((!(fShowHideCols & HIDE_YOURCHG) || !(fShowHideCols & HIDE_YOURACTION)) && fstatRun) { if (!coMingle) { suffix.beginCol(0,0,0,0, "#dfe3e8"); /* hdr will generate spacing */ suffix.endCol(); suffix.beginCol( NULL, NULL, "5" ); suffix.endCol(); } } const StrPtr *pb = fRequest.GetStateArg( "pb" ); if (!fRequest.GetBrowseMode() || pb) { if ((!(fShowHideCols & HIDE_OTHERCHG) || !(fShowHideCols & HIDE_OTHERACTION) || !(fShowHideCols & HIDE_OTHERUSER) || !(fShowHideCols & HIDE_OTHERCLIENT)) && fstatRun) { suffix.beginCol(0,0,0,0, "#dfe3e8"); /* hdr will generate spacing */ suffix.endCol(); } suffix.beginCol( NULL, NULL, "12" ); suffix.endCol(); } } suffix.endTRow(); while( fCurDir < iDir ) { d = DirList->Get(++fCurDir); rowfix.Clear(); rowfix.beginTRow("top", 0, 0, fRequest.HideDetails() ? 0 : (fRowctr++ & 0x01) ? "pathbr_row alt_row" : "pathbr_row"); fRequest << rowfix; if( fRequest.GetViewMode() != VM_WORKSPACE ) { OutputHREF( prefix.Text(), d->Text(), AC_PATHBROWSER, NULL, d->Text(), 0 ); } else { newPath.Set( fRequest.GetPath() ); newPath << d; fRequest.UseNewBase( newBase, NULL, "path", newPath.Text() ); OutputHREF( prefix.Text(), newBase.Text(), AC_PATHBROWSER, NULL, d->Text(), 0 ); } OutputDirMenu(d->Text()); fRequest << suffix.Text(); } } void p4wFilePane::RenderFile( const StrPtr *rootDir, const StrPtr *sdir, const StrPtr *clientFile, const StrPtr *have, const StrPtr *headRev, const StrPtr *headType, const StrPtr *type, const StrPtr *owner, AllCommands ac, StrBufDict *cmdArgs, StrDict * varList ) { // // Render the file browser url for the file StrBuf url; p4wHtml suffix(1); p4wURL urlMaker; StrBuf clearIcon; urlMaker.ConstructURL( clearIcon, "/clearpixelIcon", AC_ICON, NULL ); // // we have to save filenames in order // to know which thumbnails to disply if filtering if ( !fRequest.HideThumbnails() ) { StrPtr *file = varList->GetVar( "depotFile" ); if (!file) file = varList->GetVar( "clientFile" ); ThumbList.Put()->Set( file->Text() ); } StrBuf jssuffix; const StrPtr *X = fRequest.GetStateArg("X"); int hoticons = (X && strchr(X->Text(), 'L')) ? 1 : 0; // 1 -> link on icons, 0 -> bare icons const StrPtr *thx = fRequest.GetStateArg( "thx" ); if( (!thx || *thx != "y") && !hoticons ) { // Generate the arguments to pass to javascript:showMenu() StrBuf id; id << "id_" << fFileCtr; StrBuf curFi; curFi << ((fRequest.GetViewMode() == VM_WORKSPACE) ? varList->GetVar( "clientFile" ) : varList->GetVar( "depotFile" )); char *finm = curFi.Text(); *(finm + curFi.Length()) = '\0'; // ensure nul terminated finm += fRequest.GetPath().Length(); if (strchr(finm, '\\')) // if there are backslashes in the path, we need to double them { StrBuf tmp; char buf[2] = { '\0', '\0' }; char *p = finm; while(*p) { if (*p == '\\') { tmp << "\\\\\\\\"; p++; } else { buf[0] = *p++; tmp << buf; } } curFi.Set(tmp); finm = curFi.Text(); *(finm + curFi.Length()) = '\0'; // ensure nul terminated } curFi.Set(finm); p4wStrBuf p4wCurFi; p4wCurFi.EscapeURLAllChars(curFi, fRequest.GetUnicode()); if (strstr(p4wCurFi.Text(), "%25")) { p4wStrBuf p4wCurFi2; p4wCurFi2.EscapeURLAllChars(p4wCurFi, fRequest.GetUnicode()); curFi.Set(p4wCurFi2.Text()); } else curFi.Set(p4wCurFi.Text()); finm = curFi.Text(); int show = GetFileMenuShowVals(fstatRun ? varList : NULL); StrBuf args; if (headRev && *headRev > "1") args << "&rev1=" << headRev->Text(); args << "&mu=" << show; StrBuf jsaction; jsaction << "javascript:showMenu(\\\"" << finm << "\\\",\\\"" << fFileCtr << "\\\",\\\"file\\\"," << show << ",\\\"" << args.Text() << "\\\")"; jssuffix << "<script language=javascript>"; jssuffix << "document.write(\" <span class='muaro'>"; jssuffix << "<a href='" << jsaction << "' title='Menu' id='" << id.Text() << "'>"; jssuffix << " </a></span>\")"; jssuffix << "</script>" << crlf; } // // Check the action for the file. Don't show the rev num // and filetype for files opened for add or branch, or in any // circumstance where the headRev & headType are missing. // Don't show have revision if we're in browse-only mode. if( !headRev || !headType ) { if (type && !(fShowHideCols & HIDE_TYPE)) { suffix.beginSpan( "filestat" ); suffix << " " << type->Text(); suffix.endSpan(); } if( !thx || *thx != "y" ) suffix << jssuffix; if ( !fRequest.HideDetails() ) { suffix.endCol(); suffix.beginCol(); } } else { suffix.beginSpan( "filestat" ); if (!(fShowHideCols & HIDE_REV)) { suffix << " #"; if( !fRequest.GetBrowseMode() ) { if( have ) suffix << have->Text() << "/"; else suffix << "0" << "/"; } suffix << headRev->Text(); } if (!(fShowHideCols & HIDE_TYPE)) suffix << " " << headType->Text(); suffix.endSpan(); if( !thx || *thx != "y" ) suffix << jssuffix; if (type && strcmp(type->Text(), headType->Text()) && !(fShowHideCols & HIDE_NEWTYPE)) suffix << " <nobr><font color=\"#C08080\">(opened as " << type->Text() << ")</font></nobr>"; if( NotOpenedByOwner( owner ) && !(fShowHideCols & HIDE_OTHEROPEN) ) { suffix << " <nobr><font color=\"#C08080\">(opened by " << owner; suffix << "@" << fRequest.GetClient() << ")</font></nobr>"; } if ( !fRequest.HideDetails() ) { suffix.endCol(); suffix.beginCol(0,0,0,0,0, fRequest.HideDetails() ? 0 : "40%"); } } suffix.endCol(); if ( !fRequest.HideDetails() ) AddDetails(varList->GetVar( "action" ), varList->GetVar( "actionOwner" ), varList, suffix); suffix.endTRow(); if( fRequest.GetViewMode() == VM_WORKSPACE ) fRequest.UseNewBase( url, NULL, "path", fRequest.GetPath().Text() ); StrBuf rest; rest << rootDir << sdir << clientFile; // // If the server supports filenames with special // characters, we need to escape these characters // when in workspace mode. If not in workspace mode, // the special characters have already been escaped. const StrPtr *server = fRequest.GetProtocol( "server2" ); if( fRequest.GetViewMode() == VM_WORKSPACE && server && server->Atoi() > 17 ) { url << p4wStrBuf().EscapeP4Chars( rest ); } else { url << rest; } // // Only FILETEXTDEPOT has command arguments if( ac == AC_FILETEXTDEPOT ) OutputHREF( NULL, url.Text(), ac, cmdArgs, clientFile->Text(), suffix.Text() ); //, 0, 0, // id.Text(), "onMouseDown", jsaction.Text() ); else if( ac == AC_WORKSPACEFILE ) OutputHREF( NULL, RootRelative(&url)->Text(), ac, NULL, clientFile->Text(), suffix.Text() ); //, 0, 0, // id.Text(), "onMouseDown", jsaction.Text() ); else OutputHREF( NULL, url.Text(), ac, NULL, clientFile->Text(), suffix.Text() ); //, 0, 0, // id.Text(), "onMouseDown", jsaction.Text() ); // // we have to save filenames in order // to know which thumbnails to disply if filtering if ( !fRequest.HideThumbnails() && fRequest.GetCmd() == AC_FILESMATCHING && fRequest.GetStateArg( "is" ) ) { StrPtr *file = varList->GetVar( "depotFile" ); if (!file) file = varList->GetVar( "clientFile" ); ThumbList.Put()->Set( file->Text() ); } fSeenData = 1; } int p4wFilePane::SetDirsFromDepots() { // // Create the list of directories from the list // of depots. This is called only if the path is the // root directory (and not in workspace mode). // Return 1 if caller should return immediately // after this call. // // If no depots are defined, include "depot" // in the list if( !DepotList.Count() ) { StrBuf *depot = DepotList.Put(); *depot << "depot/"; // // Nothing else to do here if this // state is getting directories in // client mode. if( fRequest.GetViewMode() == VM_CLIENT && fState == gettingDirectories ) { fState = gettingWhere; return 1; } } // // In client mode we filter out depots not in user's map // when in the "getting where output" stage. Depot mode // just uses the entire depots list. if( fRequest.GetViewMode() == VM_DEPOT ) { DirList = &DepotList; } else { const StrPtr *depot; const StrPtr *ex; StrBuf *fname; int id = -1; int excludeIt = 0; while( depot = DepotList.Get( ++id ) ) { excludeIt = 0; int ie = -1; while( ex = ExcludeList.Get( ++ie ) ) { if( *depot == *ex ) ++excludeIt; } if( !excludeIt ) { fname = DirectoryList.Put(); fname->Set( depot ); } } } return 0; } int p4wFilePane::NotOpenedByOwner( const StrPtr *owner ) { // // Return 1 if we can prove the current file is // not opened by the current user. This condition // may only be detected in 2002.1 and later servers // which have the actionOwner field in fstat. StrPtr *noCase = fRequest.GetProtocol( "nocase" ); if( !owner ) return 0; // // Use appropriate comparison based on the // case-sensitivity of the server. if( noCase && owner->CCompare( fRequest.GetUser() ) || !noCase && owner->XCompare( fRequest.GetUser() ) ) return 1; return 0; } int p4wFilePane::InRequestDir(StrDict * varList, StrPtr *reqDir) { int c; int len; StrBuf path; StrBuf filepath; if (reqDir) path << reqDir; else path << fRequest.GetDepotPath(); len = strlen(path.Text()); if (fRequest.GetViewMode() == VM_WORKSPACE) { StrPtr * clientFile = varList->GetVar( "clientFile" ); filepath << clientFile; c = *(path.Text() + len - 1); } else { StrPtr * depotFile = varList->GetVar( "depotFile" ); filepath << depotFile; c = '/'; } if (strncmp(path.Text(), filepath.Text(), len) || strchr(filepath.Text()+len, c)) return 0; return 1; } StrBuf *p4wFilePane::RootRelative(StrBuf *pathFile) { char * p = strrchr(pathFile->Text(), '@'); if (p) { p = strstr(p, fRequest.GetClientRoot().Text()); if (p) { strcpy(pathFile->Text(), p + strlen(fRequest.GetClientRoot().Text())); pathFile->SetLength(); #ifdef OS_NT while (p = strchr(pathFile->Text(), '\\')) *p = '/'; #endif } } return pathFile; } int p4wFilePane::findFilterName(AllCommands ac) { int i; for (i = 0; ++i < nDFilterOpts; ) { if (dFilterVal[i] == ac) return i; } return 0; } void p4wFilePane::doFilterMessage(StrBuf *msg) { const StrPtr *pat = fRequest.GetStateArg( "pat", HE_Page ); const StrPtr *md = fRequest.GetStateArg( "md", HE_Page ); const StrPtr *pb = fRequest.GetStateArg( "pb", HE_Page ); const StrPtr *df = fRequest.GetStateArg( "df", HE_Page ); const StrPtr *po = fRequest.GetStateArg( "po", HE_Page ); const StrPtr *cf = fRequest.GetStateArg( "cf", HE_Page ); p4wStrBuf patBuf; const StrPtr *X = fRequest.GetStateArg( "X" ); // undoc eXperimental flags int bQ = (X && strchr(X->Text(), 'm')) ? 1 : 0; if (!pat) { int i = 0; if (po || pb || cf) { if (cf && !strcmp(cf->Text(), "h")) i = findFilterName(AC_PATHNOCURRENT); else if (po && !strcmp(po->Text(), "h")) { if (!fRequest.BypassAuth() && !fRequest.BrowseWithAuth()) i = findFilterName(AC_PATHNOOPEN); else i = bQ ? 0 : findFilterName(fRequest.GetBrowseMode() ? AC_PATHNOOPEN : AC_PATHOPENED); } else { AllCommands ac = AC_NONE; if (po) ac = (AllCommands)atoi(po->Text()); else if (pb) ac = (AllCommands)atoi(pb->Text()); i = findFilterName(ac); } if (i || !bQ) *msg << "(" << dFilterText[i]; } else if (!bQ) { *msg << "(" << dFilterText[0]; } } else // if (pat) { if (*(msg->Text())) *msg << "; Pattern: \\\""; else *msg << "(Pattern: \\\""; patBuf.EscapeURL(*pat); *msg << patBuf << "\\\""; const StrPtr *is = fRequest.GetStateArg( "is" ); if (is) *msg << "; Search subfolders"; } if (df && !strcmp(df->Text(), "s")) //deleted files { if (*(msg->Text())) *msg << "; "; else *msg << "("; *msg << "Include deleted files"; } if (md) { if (!bQ) { if (!strcmp(md->Text(), "c")) *msg << "; Limit to client view"; } else if (!strcmp(md->Text(), "d") && !fRequest.BypassAuth() && !fRequest.BrowseWithAuth()) { if (*(msg->Text())) *msg << ", including "; else *msg << "(Includes "; *msg << "files not in client view"; } else if (strcmp(md->Text(), "d") && (fRequest.BypassAuth() || fRequest.BrowseWithAuth())) { if (*(msg->Text())) *msg << "; "; else *msg << "("; *msg << "Limit to client view"; } } if (*(msg->Text())) *msg << ")"; }
# | 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/p4wFilePane.cpp | |||||
#1 | 8914 | Matt Attaway | Initial add of the P4Web source code |