The %xytitles macro explained here is not a recommendation for setting out titles. It is an example client macro, and a fairly complex one at that, which is useful in showing you how to approach the task of writing such a macro. If you can understand how this macro works then you should be well prepared to cope with writing any client titles macro. The person whose job it is to write it may be the Spectre Administrator or it may be given to a programmer or it may be me. As for getting advice on writing this macro as part of the Spectre support contract then this is the advice!! By talking you through this macro, this is all the advice I can give you. However, if I do write a client style macro as part of the support contract, then it will be on the condition that I can web it here. Preference will be given to the client style macros of the major pharmaceutical companies. Your own donations in this regard would be welcome but I might rewrite it. I will build up a collection and web it.
Spectre will allow you to break the 10 titles 10 footnotes limit by calling an alternative to the %pagexofy macro, which you declare in the protocol.txt member, that could be called a "client pagexofy macro" and you can do this unusual adding of extra titles and footnotes in that macro plus anything else you need to do.
Here is the start of the macro and an explanation:
%macro xytitles(program=,label=);
%*- set up global macro variables -;
%*- set up needed local variables -;
%*- Alert flag for when the population in the form &_poplabel_
has -;
|
In the code above, first some global macro variables are set up. _pagescript_ is set to the name of the shell script, pagexofy, that %closerep will call to add the "Page X of Y" titles as this might vary from client to client. Unix is case sensitive so you might have a capitalized version of pagexofy named PAGEXOFY that creates capitalized page labels like "PAGE X OF Y". _figbkmark_ will be set to a bookmark that might be used for figures and it will be made up of titles that will also be written to the global macro variable _tline1_ _tline2_ etc. The value of _figbkmark_ will be displayed in the log as well as all the other. If you set up or possibly change a global macro variable then you must display these in the log so that programmers know they have been set up and can use them if they wish. This is done in all other Spectre macros and you should follow this convention in the client titles macro.
After the global macro variables have been declared, you can see that two local ones have been declared. The "popalert" one is set to "0" and explained.
Now for the next chunk of code.
%*- For tables, make sure the word "TABLE" is in upper case in
first title -;
%*- and that the population label is also in upper case. -; %if "&_reptype_" EQ "TABLE" %then %do; %let _repid_=%casestrmac(&_repid_,TABLE); %let _poplabel_=%upcase(&_poplabel_); %end; %else %if "&_reptype_" EQ "ATTACHMENT" %then %do; %let _repid_=%casestrmac(&_repid_,ATTACHMENT); %let _poplabel_=%upcase(&_poplabel_); %end; %*- For non-tables, make sure the first letter is upper case and the rest lower case -; %else %do; %let _repid_=%casestrmac(&_repid_, %substr(&_reptype_,1,1)%lowcase(%substr(&_reptype_,2))); %end; |
The three global macros variables _reptype_ , _repid_ and _poplabel_ are set in the %proginfo macro that is called by %titles before the client titles macro is called. Here, the type of report defined to _reptype_ will be used to drive this client titles macro. The macro %casestrmac is used to force any mixed case form of a string to the one specified and you will see that for "TABLE" and "ATTACHMENT" (%proginfo sets _reptype_ values to upper case) the case for that string is set to upper case but for others, the first character is capitalized and the rest set to lower case. The value of _poplabel_ is set to upper case for "TABLE" and "ATTACHMENT" but is left as it is for other types of report. This value of _poplabel_ is set in protocol.txt and is not something the user has supplied, so there will be no conflict of case for the population label for other types of reports.
Now for the next piece of code.
*- set up and create the top three header lines of a standard report
-;
data _null_; length text $ &_ls_ datestr $ 32; *- date string for draft report to add to the title -; datestr=put(date(),weekdate32.); datestr=substr(datestr,index(datestr,",")+2); text="&_drugname_"; substr(text,&_ls_,1)='FF'x; call execute("title1"||' "'||text||'";'); text="&_protocol_"; %if "&_reptype_" NE "TABLE" and "&_reptype_" NE "ATTACHMENT" %then %do; substr(text,%eval(&_ls_-%length(&_repid_)+1))="&_repid_"; %end; call execute("title2"||' "'||text||'";'); text="&_report_"; if index(upcase(text),'DRAFT') then text=trim(text)||" "||datestr; %if %length(&_poplabel_) %then %do; %if "&_reptype_" NE "TABLE" and "&_reptype_" NE "ATTACHMENT" %then %do; substr(text,%eval(&_ls_-%length(&_poplabel_)+1))="&_poplabel_"; %end; %end; call execute("title3"||' "'||text||'";'); run; |
The three "header" lines are set up and executed in this next piece of code. These header lines will become title1, title2 and title3 as far as sas knows them. The content of each title line is set up in turn the variable "text".
You will see that for the first header line it is set to the value of _drugname_ and the page label character, which must be "FF"x and nothing else, is put in the rightmost position that will fit on the page width by using the value of _ls_ that also gets set in the %proginfo macro. A call execute follows which will be activated after the data step ends.
The second header line has the value of _protocol_ on the left and if _reptype_ is not set to either "TABLE" or "ATTACHMENT" then the right is set to the value of _repid_ that had its case adjusted in the previous code. Then follows the call execute to activate title2.
The third header line is set to the value of _report_ that is set up in the %protinfo macro called earlier by %titles. If the word "draft" is detected in this string in any case then it will put the current date at the end that was set up in the variable "datestr" to tell you the date of the draft version of the output. If _reptype_ is neither "TABLE" or "ATTACHMENT" then the population label defined to _poplabel_ is put on the far right. Then follows the call execute to activate title3.
We have our three header lines now and they have been activated. They
are done. More follows.
%*- For tables, throw a blank title line, put table title on -;
%*- following line and then throw another blank title line. -; %*- For others, just throw a blank title line to leave a gap. -; %if "&_reptype_" EQ "TABLE" or "&_reptype_" EQ "ATTACHMENT" %then %do; title4 " "; title5 "&_repid_"; title6 " "; %end; %else %do; title4 " "; %end; %*- do a quiet count of titles (i.e. do not display maximum values
yet) -;
|
If _reptype_ is "TABLE" or "ATTACHMENT" we have yet to put out
the report reference or main title. It has already been done on the far
right of the second header line for other types of report. This case of
this has already been adjusted so title4 is set to blank, title5 is set
to the main title and a blank title line put under it to conform with the
client style. For other types of reports, a blank title4 is created. The
number of title lines created is determined using the %maxtitle(quiet)
call and the value assigned to the local macro variable "tcount"
for use in further logic as follows.
*- extract titles -;
data _titles; set der.titles(where=(program="&program" and label="&label")); run; %*- Only process the titles if there is more than one -;
*- process the titles -;
*- generate the extra titles from the extracted titles -;
%end; |
In the first data step, all the titles (and footnotes) are extracted for that program from the titles dataset. If we only find one observation then this will be the table/appendix/whatever reference number and this has already been put out in a header line or in a title so we have nothing further to do. This is why the number of observations is checked using %attrn(_titles,nobs) and the following data step is executed only if the number of observations is greater than one.
Assuming more than one observation was found then the processing continues as follows. In the titles dataset, the title numbers start at 1 and are numbered sequentially. It knows nothing about what titles line you are really going to use. We have already handled the title numbered 1 and so this is dropped. The second, third and fourth title lines are written to global macro variables for information purposes only, in case the programmer needs to use this. Next the real number of the title line must be adjusted by the number of titles put out already that is in the global macro variable _maxtitle_. This is only done for titles and not for footnotes.
Next in the data step comes the translation of special characters. You must use this same code in whatever client titles macro you write. The lines of code you must use are shown in bold. Note the substitution for the ampersand "&". It will not substitute if the ampersand is followed by an underscore. It allows for macro variables starting with an underscore to be substituted in titles and footnotes. If the underscore is not the first character in the macro variable name then it will show the ampersand as it is in the title or footnote. These characters will get substituted back to what they were originally in the script that adds the page labels which in this case is pagexofy.
Note that the local macro variable "popalert" gets set if it sees "&_poplabel_" in any of the titles in any case. This is to tell it not to add the population label later, if this has already been done.
Next comes the activation of these new titles and possibly footnotes. This is done using the %titlegen macro.
Now we move on. For tables, the XY client wants the population label
as the last title line so long as it has not been put out already, as detected
by the "popalert" setting, so this code follows.
%*- For a table, put the population title as last title unless
it is -;
%*- blank or it has been detected in the title lines (i.e. &popalert=1) -; %if "&_reptype_" EQ "TABLE" and %length(&_poplabel_) and (&popalert EQ 0) %then %do; %maxtitle(quiet) %*- Only add this population title if some extra -; %*- titles were generated in the previous step. -; %if &_maxtitle_ GT &tcount %then %do; title%eval(&_maxtitle_+1) "&_poplabel_"; %end; %end; |
Next comes the bookmark that might be used for figures.
I stress "might" because this bookmark will be set up in the global macro
variable _figbkmark_ and will be used by default for figures but
if the programmer doesn't like what is in it then they are free to change
the value of _figbkmark_ to whatever they want. t is calculated
for all types of report but will only be used for figures. There is no
particular reason why it is done for types of report other than figures
but it just seems logical to do it anyway.
%let _figbkmark_=&_repid_;
%if %length(&_tline2_) %then %do; %let _figbkmark_=%superq(_figbkmark_) %superq(_tline2_); %if %length(&_tline3_) %then %do; %let _figbkmark_=%superq(_figbkmark_) %superq(_tline3_); %if %length(&_tline4_) %then %do; %let _figbkmark_=%superq(_figbkmark_) %superq(_tline4_); %end; %end; %end; %let _figbkmark_=%superq(_figbkmark_) &_poplabel_; |
Note that %superq is used above because there is no telling what is in _tline1_, _tline2_ etc. that might mess up the macro parser. With %superq you are always safe.
And now the last part. We display all the global macro variables we
have set up and might have changed (you must do this) and
after that we exit.
%put;
%put MSG: (xytitles) The following global macro variables have been set; %put MSG: (xytitles) and can be used in your code. ; %put _repid_=&_repid_; %put _poplabel_=&_poplabel_; %put _tline2_=%superq(_tline2_); %put _tline3_=%superq(_tline3_); %put _tline4_=%superq(_tline4_); %put _figbkmark_=%superq(_figbkmark_); %put;
*- tidy up -;
|
Use the "Back" button of your browser to return to the previous page