/* tktree.c
*
* XXX tktree.c should become some other name, like tkmisc?
* Was only article tree drawing. Now includes lots more.
* Perhaps later allow much of it to be accessed from pure tcl.
*/
#include "EXTERN.h"
#include "common.h"
#ifdef USE_TK
#include <tcl.h>
#include <tk.h>
#include "hash.h"
#include "cache.h"
#include "rt-select.h"
#include "rt-wumpus.h"
#include "ng.h"
#include "util.h"
#include "tkstuff.h"
#include "term.h"
#ifdef SCORE
#include "ngdata.h" /* absfirst */
#include "score.h"
#endif
#include "INTERN.h"
#include "tktree.h"
#include "tktree.ih"
extern HASHTABLE* msgid_hash;
extern Tcl_Interp* ttcl_interp;
static int ttk_article_counter = 0;
/* linked variable for passing a messageid */
static char* ttk_msgid = 0;
/* variables for tree dimensions (x and y) */
static int ttk_tree_x = 0;
static int ttk_tree_y = 0;
typedef struct {
ARTICLE* ap;
} TTK_ART;
static void
ttk_article_delete(cd)
ClientData cd;
{
safefree(cd);
}
/* this one is named "ttk__art0" */
static TTK_ART* ttk_fastart;
static char ttk_letters[] = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+";
static void
ttk_treeprep_helper(ap)
ARTICLE* ap;
{
for (;;) {
ap->flags2 &= ~(AF2_WASUNREAD|AF2_CHANGED|AF2_NODEDRAWN);
if (ap->flags & AF_UNREAD)
ap->flags2 |= AF2_WASUNREAD;
if (ap->child1)
ttk_treeprep_helper(ap->child1);
if (!(ap = ap->sibling))
break;
}
}
static void
ttk_treechange_helper(ap)
ARTICLE* ap;
{
int changed; /* later indicate num of changes? */
for (;;) {
changed=0;
if (ap->flags & AF_UNREAD) {
if (!(ap->flags2 & AF2_WASUNREAD))
changed++;
} else {
if (ap->flags2 & AF2_WASUNREAD)
changed++;
}
if (ap->flags2 & AF2_CHANGED)
changed++;
if (changed > 0) {
ttk_fastart->ap = ap;
ttcl_eval("eval $tree_nodechanged ttk__art0");
/* consider doing something with result */
}
if (ap->flags & AF_UNREAD)
ap->flags2 |= AF2_WASUNREAD;
else
ap->flags2 &= ~AF2_WASUNREAD;
ap->flags2 &= ~AF2_CHANGED;
if (ap->child1)
ttk_treechange_helper(ap->child1);
if (!(ap = ap->sibling))
break;
}
}
static int
ttk_article_objectcmd(cd, interp, argc, argv)
ClientData cd;
Tcl_Interp* interp;
int argc;
char* argv[];
{
TTK_ART* p = (TTK_ART*)cd;
ARTICLE* ap;
char* cmd;
char* s;
HASHDATUM data;
if (argc < 2) {
interp->result = "not enough args";
return TCL_ERROR;
}
ap = p->ap;
cmd = argv[1];
if (strEQ(cmd,"setid")) {
if (argc != 3) {
interp->result = "setid: needs id argument";
return TCL_ERROR;
}
data = hashfetch(msgid_hash, argv[2], strlen(argv[2]));
if (!(ap = (ARTICLE*)data.dat_ptr) || data.dat_len) {
interp->result = "0";
p->ap = 0;
} else {
interp->result = "1";
p->ap = ap;
}
return TCL_OK;
}
if (strEQ(cmd,"setcurrent")) {
if (argc != 2) {
interp->result = "setcurrent: too many arguments";
return TCL_ERROR;
}
ap = curr_artp;
if (!ap) {
interp->result = "0";
p->ap = 0;
} else {
interp->result = "1";
p->ap = ap;
}
return TCL_OK;
}
if (strEQ(cmd,"header")) {
char* which;
if (argc != 3) {
interp->result = "header: needs header name argument";
return TCL_ERROR;
}
which = argv[2];
if (!ap) {
s = "NO ARTICLE";
/* return error? */
}
else if (strEQ(which,"from")) {
s = "NO AUTHOR";
if (ap->from) {
s = ap->from;
}
Tcl_AppendResult(interp, s, 0);
return TCL_OK;
}
else if (strEQ(which,"subject")) {
s = "NO SUBJECT";
if (ap->subj) {
s = ap->subj->str +((ap->flags & AF_HAS_RE) ? 0 : 4);
}
}
else if (strEQ(which,"msgid")) {
s = "NO ID (***ERROR***)";
if (ap->msgid) {
s = ap->msgid;
}
}
else {
/* not a known header */
Tcl_AppendResult(interp, "unknown header", 0);
return TCL_ERROR;
}
Tcl_AppendResult(interp, s, 0);
return TCL_OK;
}
if (strEQ(cmd,"move")) {
char* dir;
if (argc != 3) {
interp->result = "move: needs direction";
return TCL_ERROR;
}
dir = argv[2];
if (!ap) {
/* error later? */
interp->result = "0";
return TCL_OK;
}
/* later add threadtop and subject directions */
if (strEQ(dir,"parent")) {
ap = ap->parent;
}
else if (strEQ(dir,"sibling")) {
ap = ap->sibling;
}
else if (strEQ(dir,"child")) {
ap = ap->child1;
}
else {
interp->result = "move: invalid direction";
return TCL_ERROR;
}
p->ap = ap;
if (ap) {
interp->result = "1";
}
else {
interp->result = "0";
}
return TCL_OK;
}
if (strEQ(cmd,"flag")) {
char* flag;
int status = 0;
if (argc != 3) {
interp->result = "move: needs direction";
return TCL_ERROR;
}
flag = argv[2];
if (!ap) {
/* error later? (not error if checking existence?) */
interp->result = "0";
return TCL_OK;
}
if (strEQ(flag,"current")) {
if (ap == curr_artp)
status = 1;
}
else if (strEQ(flag,"exists")) {
if (ap->flags & AF_EXISTS)
status = 1;
}
else if (strEQ(flag,"unread")) {
if (ap->flags & AF_UNREAD)
status = 1;
}
else if (strEQ(flag,"selected")) {
if (ap->flags & AF_SEL)
status = 1;
}
else if (strEQ(flag,"wasunread")) {
if (ap->flags2 & AF2_WASUNREAD)
status = 1;
}
else if (strEQ(flag,"changed")) {
if (ap->flags2 & AF2_CHANGED)
status = 1;
}
else if (strEQ(flag,"parent")) {
if (ap->parent)
status = 1;
}
else if (strEQ(flag,"child")) {
if (ap->child1)
status = 1;
}
else if (strEQ(flag,"nextsibling")) {
if (ap->sibling)
status = 1;
}
else if (strEQ(flag,"priorsibling")) {
if ((ap->parent) && (ap->parent->child1)
&& (ap != ap->parent->child1))
status = 1;
}
else if (strEQ(flag,"parentsubject")) {
if ((ap->subj) && (ap->parent) && (ap->parent->subj)
&& (ap->subj == ap->parent->subj))
status = 1;
}
else if (strEQ(flag,"selectedonly")) {
/* XXX Move this out to a trn-globals section */
if (selected_only)
status = 1;
}
else {
interp->result = "unknown flag";
return TCL_ERROR;
}
if (status) {
interp->result = "1";
} else {
interp->result = "0";
}
return TCL_OK;
}
if (strEQ(cmd,"copy")) {
TTK_ART* p2;
if (argc != 2) {
interp->result = "too many arguments";
return TCL_ERROR;
}
p2 = (TTK_ART*)safemalloc(sizeof(TTK_ART));
p2->ap = p->ap;
sprintf(interp->result, "trn_article%d",ttk_article_counter++);
Tcl_CreateCommand(interp,interp->result, ttk_article_objectcmd,
p2,ttk_article_delete);
return TCL_OK;
}
if (strEQ(cmd,"subjectletter")) {
int subj = 0;
ARTICLE* p;
SUBJECT* sp;
if ((!ap) || !(ap->flags & AF_EXISTS) || !(ap->subj)) {
interp->result[0] = ' ';
interp->result[1] = '\0';
return TCL_OK;
}
p = ap;
while (p->parent) {
p = p->parent;
}
sp = p->subj;
while (sp != ap->subj) {
subj++;
sp = sp->thread_link;
}
interp->result[0] = ttk_letters[subj>9+26+26? 9+26+26:subj];
interp->result[1] = '\0';
return TCL_OK;
}
/* prepare article and all children for other tree usage */
if (strEQ(cmd,"treeprepare")) {
ttk_tree_x=0;
ttk_tree_y=0;
if (ap) {
ttk_treeprep_helper(ap);
interp->result = "1";
} else {
interp->result = "0";
}
return TCL_OK;
}
/* call a function for all changed articles */
if (strEQ(cmd,"treechange")) {
if (ap) {
ttk_treechange_helper(ap);
interp->result = "1";
} else {
interp->result = "0";
}
return TCL_OK;
}
/* get the article number for this article, or 0 if non-existant */
if (strEQ(cmd,"artnum")) {
ART_NUM num;
if (ap) {
if (ap->flags & AF_EXISTS) {
num = article_num(ap);
if (num>0) {
sprintf(interp->result,"%d",(int)num);
return TCL_OK;
}
}
}
interp->result = "0";
return TCL_OK;
}
if (strEQ(cmd,"score")) {
#ifdef SCORE
/* Consider a quick query to see if the article is scored... */
ART_NUM num;
int artscore;
if (ap) {
if (ap->flags & AF_EXISTS) {
num = article_num(ap);
if (num>0) {
artscore = sc_score_art(num,TRUE);
sprintf(interp->result,"%d",artscore);
return TCL_OK;
}
}
}
interp->result = "0";
return TCL_OK;
#else
interp->result = "article scoring not compiled in this trn executable";
return TCL_ERROR;
#endif
}
if (strEQ(cmd,"scorequick")) {
#ifdef SCORE
ART_NUM num;
int artscore;
if (ap) {
if (ap->flags & AF_EXISTS) {
num = article_num(ap);
if (num>0) {
if (SCORED(num)) {
artscore = sc_score_art(num,TRUE);
} else {
artscore = 0;
}
sprintf(interp->result,"%d",artscore);
return TCL_OK;
}
}
}
interp->result = "0";
return TCL_OK;
#else
interp->result = "article scoring not compiled in this trn executable";
return TCL_ERROR;
#endif
}
interp->result = "unknown article command";
return TCL_ERROR;
}
/* XXX Cleanup the tree drawing, replace "wipetree" with special procedure */
/* Called from trn to draw an article tree. */
void
ttk_draw_tree(ap,x,y)
ARTICLE* ap;
int x,y; /* starting X and Y positions */
{
static char lbuf[100];
if (!ttk_running)
return; /* no wasted time */
if (!(ap) || (!ap->subj) || (!ap->subj->thread) ||
(!ap->subj->thread->msgid)) {
ttcl_eval("wipetree");
return;
}
ttk_msgid = ap->subj->thread->msgid;
strcpy(lbuf,"trn_draw_article_tree 0 0");
Tcl_Eval(ttcl_interp,lbuf);
}
static int
ttk_article(cd, interp, argc, argv)
ClientData cd;
Tcl_Interp* interp;
int argc;
char* argv[];
{
TTK_ART* p;
/* error checking later */
if (argc != 1) {
interp->result = "wrong # args";
return TCL_ERROR;
}
p = (TTK_ART*)safemalloc(sizeof(TTK_ART));
p->ap = 0;
sprintf(interp->result, "trn_article%d",ttk_article_counter++);
Tcl_CreateCommand(interp,interp->result, ttk_article_objectcmd,
p,ttk_article_delete);
return TCL_OK;
}
/* XXX XXX Move this elsewhere. (general TRN stuff, not just article trees) */
static int
ttk_trn(cd, interp, argc, argv)
ClientData cd;
Tcl_Interp* interp;
int argc;
char* argv[];
{
char* cmd;
static char lbuf[32]; /* long enough for integers... */
cmd = argv[1];
if (strEQ(cmd,"mode")) {
lbuf[0] = mode;
lbuf[1] = '\0';
Tcl_AppendResult(interp, lbuf, 0);
return TCL_OK;
}
if (strEQ(cmd,"pending")) {
ttk_do_waiting_flag = 0; /* do not update TK */
if (finput_pending(1)) {
lbuf[0] = '1';
} else {
lbuf[0] = '0';
}
ttk_do_waiting_flag = 1; /* resume updating TK */
lbuf[1] = '\0';
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, lbuf, 0);
return TCL_OK;
}
interp->result = "unknown trn_misc command";
return TCL_ERROR;
}
int
ttk_tree_init()
{
char lbuf[20];
/* create the fast shared ArticlePointer (ap) ttk__art0 */
ttk_fastart = (TTK_ART*)safemalloc(sizeof(TTK_ART));
ttk_fastart->ap = 0;
strcpy(lbuf,"ttk__art0");
Tcl_CreateCommand(ttcl_interp,lbuf,ttk_article_objectcmd,
ttk_fastart,ttk_article_delete);
Tcl_LinkVar(ttcl_interp, "ttk_msgid", (char*)&ttk_msgid, TCL_LINK_STRING);
Tcl_LinkVar(ttcl_interp, "ttk_tree_x", (char*)&ttk_tree_x, TCL_LINK_INT);
Tcl_LinkVar(ttcl_interp, "ttk_tree_y", (char*)&ttk_tree_y, TCL_LINK_INT);
Tcl_CreateCommand(ttcl_interp, "trn_article", ttk_article, 0, 0);
Tcl_CreateCommand(ttcl_interp, "trn_misc", ttk_trn, 0, 0);
return TCL_OK;
}
#endif /* USE_TK */
syntax highlighted by Code2HTML, v. 0.9.1