| | @@ -60,28 +60,38 @@ |
| 60 | 60 | ** |
| 61 | 61 | ** The following static variable holds the name of the alternative skin, |
| 62 | 62 | ** or NULL if the skin should be as configured. |
| 63 | 63 | */ |
| 64 | 64 | static struct BuiltinSkin *pAltSkin = 0; |
| 65 | +static char *zAltSkinDir = 0; |
| 65 | 66 | |
| 66 | 67 | /* |
| 67 | 68 | ** Invoke this routine to set the alternative skin. Return NULL if the |
| 68 | 69 | ** alternative was successfully installed. Return a string listing all |
| 69 | 70 | ** available skins if zName does not match an available skin. Memory |
| 70 | 71 | ** for the returned string comes from fossil_malloc() and should be freed |
| 71 | 72 | ** by the caller. |
| 73 | +** |
| 74 | +** If the alternative skin name contains one or more '/' characters, then |
| 75 | +** it is assumed to be a directory on disk that holds override css.txt, |
| 76 | +** footer.txt, and header.txt. This mode can be used for interactive |
| 77 | +** development of new skins. |
| 72 | 78 | */ |
| 73 | 79 | char *skin_use_alternative(const char *zName){ |
| 74 | 80 | int i; |
| 75 | | - Blob err; |
| 81 | + Blob err = BLOB_INITIALIZER; |
| 82 | + if( strchr(zName, '/')!=0 ){ |
| 83 | + zAltSkinDir = fossil_strdup(zName); |
| 84 | + return 0; |
| 85 | + } |
| 76 | 86 | for(i=0; i<ArraySize(aBuiltinSkin); i++){ |
| 77 | 87 | if( fossil_strcmp(aBuiltinSkin[i].zLabel, zName)==0 ){ |
| 78 | 88 | pAltSkin = &aBuiltinSkin[i]; |
| 79 | 89 | return 0; |
| 80 | 90 | } |
| 81 | 91 | } |
| 82 | | - blob_init(&err, aBuiltinSkin[0].zLabel, -1); |
| 92 | + blob_appendf(&err, "available skins: %s", aBuiltinSkin[0].zLabel); |
| 83 | 93 | for(i=1; i<ArraySize(aBuiltinSkin); i++){ |
| 84 | 94 | blob_append(&err, " ", 1); |
| 85 | 95 | blob_append(&err, aBuiltinSkin[i].zLabel, -1); |
| 86 | 96 | } |
| 87 | 97 | return blob_str(&err); |
| | @@ -93,11 +103,11 @@ |
| 93 | 103 | */ |
| 94 | 104 | void skin_override(void){ |
| 95 | 105 | const char *zSkin = find_option("skin",0,1); |
| 96 | 106 | if( zSkin ){ |
| 97 | 107 | char *zErr = skin_use_alternative(zSkin); |
| 98 | | - if( zErr ) fossil_fatal("available skins: %s", zErr); |
| 108 | + if( zErr ) fossil_fatal("%s", zErr); |
| 99 | 109 | } |
| 100 | 110 | } |
| 101 | 111 | |
| 102 | 112 | /* |
| 103 | 113 | ** The following routines return the various components of the skin |
| | @@ -104,10 +114,20 @@ |
| 104 | 114 | ** that should be used for the current run. |
| 105 | 115 | */ |
| 106 | 116 | const char *skin_get(const char *zWhat){ |
| 107 | 117 | const char *zOut; |
| 108 | 118 | char *z; |
| 119 | + if( zAltSkinDir ){ |
| 120 | + char *z = mprintf("%s/%s.txt", zAltSkinDir, zWhat); |
| 121 | + if( file_isfile(z) ){ |
| 122 | + Blob x; |
| 123 | + blob_read_from_file(&x, z); |
| 124 | + fossil_free(z); |
| 125 | + return blob_str(&x); |
| 126 | + } |
| 127 | + fossil_free(z); |
| 128 | + } |
| 109 | 129 | if( pAltSkin ){ |
| 110 | 130 | z = mprintf("skins/%s/%s.txt", pAltSkin->zLabel, zWhat); |
| 111 | 131 | zOut = builtin_text(z); |
| 112 | 132 | fossil_free(z); |
| 113 | 133 | }else{ |
| 114 | 134 | |