591 lines
16 KiB
C++
591 lines
16 KiB
C++
|
||
// ------------------------------------------------------------------
|
||
// GoldED+
|
||
// Copyright (C) 1990-1999 Odinn Sorensen
|
||
// Copyright (C) 1999-2000 Alexander S. Aganichev
|
||
// ------------------------------------------------------------------
|
||
// This program is free software; you can redistribute it and/or
|
||
// modify it under the terms of the GNU General Public License as
|
||
// published by the Free Software Foundation; either version 2 of the
|
||
// License, or (at your option) any later version.
|
||
//
|
||
// This program is distributed in the hope that it will be useful,
|
||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
// General Public License for more details.
|
||
//
|
||
// You should have received a copy of the GNU General Public License
|
||
// along with this program; if not, write to the Free Software
|
||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
||
// MA 02111-1307 USA
|
||
// ------------------------------------------------------------------
|
||
// $Id$
|
||
// ------------------------------------------------------------------
|
||
// Copy, Move, Forward and Delete.
|
||
// ------------------------------------------------------------------
|
||
|
||
#include <golded.h>
|
||
|
||
|
||
// ------------------------------------------------------------------
|
||
// Global data from GEREAD
|
||
|
||
extern GMsg* reader_msg;
|
||
|
||
|
||
// ------------------------------------------------------------------
|
||
|
||
void Area::DeleteMsg(GMsg* msg, int direction) {
|
||
|
||
ulong replyto=0, reply1st=0, lread;
|
||
|
||
GMsg* uplink = (GMsg*)throw_calloc(1, sizeof(GMsg));
|
||
GMsg* downlink = (GMsg*)throw_calloc(1, sizeof(GMsg));
|
||
|
||
if(msg->msgno) {
|
||
|
||
// Fix the lastread pointer
|
||
lread = Msgn.CvtReln(lastread());
|
||
if(msg->msgno == lread) {
|
||
uint l = lastread();
|
||
if(direction == DIR_PREV) {
|
||
if(l-1) {
|
||
l--;
|
||
}
|
||
else if((l+1) <= Msgn.Count())
|
||
l++;
|
||
else
|
||
l = 0;
|
||
}
|
||
else {
|
||
if((l+1) <= Msgn.Count())
|
||
l++;
|
||
else if(l-1)
|
||
l--;
|
||
else
|
||
l = 0;
|
||
}
|
||
set_lastread(l);
|
||
lread = Msgn.CvtReln(lastread());
|
||
}
|
||
|
||
// Get reply links
|
||
if(Msgn.ToReln(msg->link.to())) {
|
||
replyto = msg->link.to();
|
||
}
|
||
if(Msgn.ToReln(msg->link.first())) {
|
||
reply1st = msg->link.first();
|
||
}
|
||
if(replyto) {
|
||
if(not LoadHdr(downlink, replyto))
|
||
downlink->link.first_set(0);
|
||
}
|
||
if(reply1st) {
|
||
if(not LoadHdr(uplink, reply1st))
|
||
uplink->link.to_set(0);
|
||
}
|
||
|
||
// --------------------------------------------------------------
|
||
// Diagram of splicing the links of a deleted msg
|
||
// --------------------------------------------------------------
|
||
// Reply1st Replyto
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ
|
||
// Downlink --> Deleted --> Uplink
|
||
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
||
// Reply1st Replyto Confused ? :-)
|
||
// --------------------------------------------------------------
|
||
|
||
// Only update the up/downlinks if they are pointing to this msg
|
||
if(replyto) {
|
||
if(downlink->link.first() == msg->msgno) {
|
||
downlink->link.first_set(reply1st);
|
||
SaveHdr(GMSG_UPDATE, downlink);
|
||
}
|
||
else {
|
||
replyto = 0;
|
||
}
|
||
}
|
||
if(reply1st) {
|
||
if(uplink->link.to() == msg->msgno) {
|
||
uplink->link.to_set(replyto);
|
||
SaveHdr(GMSG_UPDATE, uplink);
|
||
}
|
||
}
|
||
|
||
// Delete the message
|
||
msg->attr.del1();
|
||
DelMsg(msg);
|
||
|
||
// Update scanning files
|
||
if(isnet() and isfido())
|
||
TouchNetscan(false);
|
||
|
||
// Remove message from internal table
|
||
Msgn.Del(msg->msgno);
|
||
|
||
// Update lastreads
|
||
if(Msgn.Count())
|
||
set_lastread(Msgn.ToReln(lread));
|
||
else
|
||
set_lastread(0);
|
||
msg->msgno = lread;
|
||
UpdateAreadata();
|
||
}
|
||
|
||
ResetMsg(downlink);
|
||
throw_free(downlink);
|
||
|
||
ResetMsg(uplink);
|
||
throw_free(uplink);
|
||
}
|
||
|
||
|
||
// ------------------------------------------------------------------
|
||
|
||
void Area::DelMsgs(GMsg* msg) {
|
||
|
||
GFTRK("DelMsgs");
|
||
|
||
uint n, x;
|
||
int topline=0;
|
||
bool delask=true, dellocked=false;
|
||
|
||
n = 0;
|
||
if(Mark.Count()) {
|
||
GMenuDomarks MenuDomarks;
|
||
n = MenuDomarks.Run(LNG->Delete);
|
||
if(n == 1) {
|
||
HandleGEvent(EVTT_MSGDELETING);
|
||
w_progress(MODE_NEW, C_INFOW, 0, Mark.Count(), LNG->Deleting);
|
||
Lock();
|
||
int escaped = false;
|
||
for(n=0; n<Mark.Count(); n++) {
|
||
x = n;
|
||
if(kbxhit()) {
|
||
if(kbxget() == Key_Esc) {
|
||
HandleGEvent(EVTT_JOBFAILED);
|
||
escaped = true;
|
||
break;
|
||
}
|
||
}
|
||
update_statuslinef(LNG->DeletingMsg, x+1, Mark.Count());
|
||
w_progress(MODE_UPDATE, C_INFOW, x+1, Mark.Count(), LNG->Deleting);
|
||
ulong msgno = Mark[x];
|
||
if(Msgn.ToReln(msgno)) {
|
||
if(LoadHdr(msg, msgno)) {
|
||
bool deletethis = false;
|
||
if(delask) {
|
||
if(msg->attr.uns() and not (msg->attr.rcv() or msg->attr.del())) {
|
||
AA->LoadMsg(msg, msgno, CFG->dispmargin-(int)CFG->switches.get(disppagebar));
|
||
w_progress(MODE_QUIT, 0, 0, 0, NULL);
|
||
HeaderView->Use(AA, msg);
|
||
HeaderView->Paint();
|
||
BodyView->Use(AA, msg, topline);
|
||
BodyView->Paint();
|
||
GMenuDelete MenuDelete;
|
||
switch(MenuDelete.Run(YES, msg)) {
|
||
case YES: // Yes, delete
|
||
deletethis = true;
|
||
break;
|
||
case NO: // No, dont delete
|
||
continue;
|
||
default: // Delete without asking
|
||
delask = false;
|
||
if(msg->attr.lok())
|
||
dellocked = true;
|
||
}
|
||
}
|
||
}
|
||
if(msg->attr.lok() and not dellocked and not deletethis) {
|
||
AA->LoadMsg(msg, msgno, CFG->dispmargin-(int)CFG->switches.get(disppagebar));
|
||
w_progress(MODE_QUIT, 0, 0, 0, NULL);
|
||
HeaderView->Use(AA, msg);
|
||
HeaderView->Paint();
|
||
BodyView->Use(AA, msg, topline);
|
||
BodyView->Paint();
|
||
GMenuDelete MenuDelete;
|
||
switch(MenuDelete.Run(YES, msg)) {
|
||
case YES: // Yes, delete
|
||
break;
|
||
case NO: // No, dont delete
|
||
continue;
|
||
default: // Delete without asking
|
||
dellocked = true;
|
||
}
|
||
}
|
||
DeleteMsg(msg, DIR_PREV);
|
||
PMrk.Del(msg->msgno);
|
||
}
|
||
}
|
||
}
|
||
Unlock();
|
||
if(not escaped) {
|
||
isreadmark = false;
|
||
Mark.ResetAll();
|
||
}
|
||
w_progress(MODE_QUIT, 0, 0, 0, NULL);
|
||
}
|
||
}
|
||
if(n == 0) {
|
||
Mark.Del(msg->msgno);
|
||
PMrk.Del(msg->msgno);
|
||
GMenuDelete MenuDelete;
|
||
if(Mark.Count() or MenuDelete.Run(NO, msg)) {
|
||
HandleGEvent(EVTT_MSGDELETING);
|
||
DeleteMsg(msg, reader_direction);
|
||
}
|
||
}
|
||
|
||
HandleGEvent(EVTT_BREAKLOOP);
|
||
|
||
GFTRK(NULL);
|
||
}
|
||
|
||
|
||
// ------------------------------------------------------------------
|
||
|
||
void TwitDeleteMsg(GMsg* msg) {
|
||
|
||
GFTRK("TwitDeleteMsg");
|
||
|
||
HandleGEvent(EVTT_MSGDELETING);
|
||
AA->Mark.Del(msg->msgno);
|
||
AA->PMrk.Del(msg->msgno);
|
||
AA->DeleteMsg(msg, reader_direction);
|
||
HandleGEvent(EVTT_BREAKLOOP);
|
||
|
||
GFTRK(NULL);
|
||
}
|
||
|
||
|
||
// ------------------------------------------------------------------
|
||
|
||
void Area::DelMsg() {
|
||
|
||
if(Msgn.Count())
|
||
DelMsgs(reader_msg);
|
||
}
|
||
|
||
|
||
// ------------------------------------------------------------------
|
||
|
||
void CmfMsgs(GMsg* msg) {
|
||
|
||
// Select action
|
||
GMenuCMF MenuCMF;
|
||
int cmf = MenuCMF.Run();
|
||
if(cmf == -1)
|
||
return;
|
||
|
||
// Set language strings
|
||
char* pickstr = NULL;
|
||
char* markstr = NULL;
|
||
char* progstr = NULL;
|
||
char* statstr = NULL;
|
||
int loadmode = GMSG_UNS_NOT_RCV;
|
||
switch(cmf) {
|
||
case MODE_COPY:
|
||
GFTRK("CopyMsgs");
|
||
pickstr = LNG->CopyArea;
|
||
markstr = LNG->Copy;
|
||
progstr = LNG->Copying;
|
||
statstr = LNG->CopyingMsg;
|
||
loadmode |= GMSG_COPY;
|
||
break;
|
||
case MODE_MOVE:
|
||
GFTRK("MoveMsgs");
|
||
pickstr = LNG->MoveArea;
|
||
markstr = LNG->Move;
|
||
progstr = LNG->Moving;
|
||
statstr = LNG->MovingMsg;
|
||
loadmode |= GMSG_MOVE;
|
||
break;
|
||
case MODE_FORWARD:
|
||
GFTRK("ForwardMsgs");
|
||
pickstr = LNG->ForwardArea;
|
||
break;
|
||
}
|
||
|
||
// Do with current or marked msgs?
|
||
int do_mode = MODE_CURRENT;
|
||
if(cmf != MODE_FORWARD) {
|
||
if(AA->Mark.Count()) {
|
||
GMenuDomarks MenuDomarks;
|
||
do_mode = MenuDomarks.Run(markstr);
|
||
if(do_mode == MODE_DONT) {
|
||
GFTRK(NULL);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Pick the destination area
|
||
int destarea = CurrArea;
|
||
const char* cmfptr = cmf == MODE_FORWARD ? AA->Areareplyto() : AA->Areacopyto();
|
||
if(*cmfptr) {
|
||
int a = AL.AreaEchoToNo(cmfptr);
|
||
if(a != -1)
|
||
destarea = AL.AreaNoToId(a);
|
||
}
|
||
if(cmf == MODE_FORWARD ? not AA->Areaforwarddirect() : not AA->Areacopydirect())
|
||
destarea = AreaPick(pickstr, 6, &destarea);
|
||
if(destarea == -1) {
|
||
GFTRK(NULL);
|
||
return;
|
||
}
|
||
|
||
AL.SetActiveAreaId(OrigArea);
|
||
AreaData* orig_adat = AA->adat;
|
||
AA->adat = (AreaData*)throw_calloc(1, sizeof(AreaData));
|
||
memcpy(AA->adat, orig_adat, sizeof(AreaData));
|
||
AL.SetActiveAreaId(destarea);
|
||
Area* AAdest = AA;
|
||
|
||
// Is it readonly?
|
||
if(AA->attr().r_o()) {
|
||
GMenuReadonly MenuReadonly;
|
||
if(not MenuReadonly.Run()) {
|
||
AL.SetActiveAreaId(OrigArea);
|
||
throw_free(AA->adat);
|
||
AA->adat = orig_adat;
|
||
GFTRK(NULL);
|
||
return;
|
||
}
|
||
}
|
||
|
||
// Handle a forward
|
||
if(cmf == MODE_FORWARD) {
|
||
extern int _use_fwd;
|
||
_use_fwd = orig_adat->usefwd;
|
||
if(_use_fwd == ASK) {
|
||
GMenuForward MenuForward;
|
||
_use_fwd = MenuForward.Run();
|
||
}
|
||
if(CurrArea != OrigArea)
|
||
AA->Open();
|
||
MakeMsg(MODE_FORWARD, msg);
|
||
AL.SetActiveAreaId(OrigArea);
|
||
throw_free(AA->adat);
|
||
AA->adat = orig_adat;
|
||
GFTRK(NULL);
|
||
return;
|
||
}
|
||
|
||
// Popup wait window
|
||
w_info(progstr);
|
||
|
||
// Open destination area and lock it
|
||
AA->Open();
|
||
AA->Lock();
|
||
AA->RandomizeData();
|
||
|
||
// Re-activeate original area and lock that too
|
||
AL.SetActiveAreaId(OrigArea);
|
||
AA->Lock();
|
||
|
||
// Setup some variables for the loop
|
||
Area* AAorig = AA;
|
||
const char* echoid = AAdest->echoid();
|
||
ulong loadmsgno = msg->msgno;
|
||
ulong* mrkp = AA->Mark.tag;
|
||
int mrks = AA->Mark.Count();
|
||
int mrk = 0;
|
||
|
||
// Copy/move loop
|
||
do {
|
||
|
||
// Check for escape key
|
||
if(kbxhit()) {
|
||
if(kbxget() == Key_Esc) {
|
||
HandleGEvent(EVTT_JOBFAILED);
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Show progress and load the marked msg
|
||
if(do_mode == MODE_MARKED) {
|
||
update_statuslinef(statstr, mrk+1, mrks, echoid);
|
||
loadmsgno = *mrkp++;
|
||
}
|
||
|
||
int mode = 0;
|
||
ulong msgno = 0;
|
||
if(AA->LoadMsg(msg, loadmsgno, CFG->dispmargin-(int)CFG->switches.get(disppagebar), loadmode)) {
|
||
|
||
// Handle unsent msgs
|
||
mode = cmf;
|
||
if((cmf == MODE_MOVE) and (loadmode & GMSG_UNS_NOT_RCV) and (msg->attr.uns() and not msg->attr.rcv())) {
|
||
|
||
// Axe the popup wait window
|
||
w_info(NULL);
|
||
|
||
// Display header and message
|
||
int top = 0;
|
||
HeaderView->Use(AA, msg);
|
||
HeaderView->Paint();
|
||
BodyView->Use(AA, msg, top);
|
||
BodyView->Paint();
|
||
|
||
// Ask if it should be deleted
|
||
GMenuDelete MenuDelete;
|
||
switch(MenuDelete.Run(YES, msg)) {
|
||
case YES: break;
|
||
case NO: mode = MODE_COPY; break;
|
||
default: loadmode &= ~GMSG_UNS_NOT_RCV;
|
||
}
|
||
|
||
// Put up the wait window again
|
||
w_info(progstr);
|
||
}
|
||
|
||
// Switch to destination area
|
||
AA = AAdest;
|
||
|
||
// Change things in the header to match the destination area
|
||
msg->attr.del0(); // Allows deleted msgs to be undeleted
|
||
msg->board = AA->board();
|
||
msg->link.reset();
|
||
|
||
// Fake Scn-Attribute if copied to squisharea
|
||
if(AA->issquish() and not AAorig->issquish())
|
||
if(msg->attr.loc() and msg->attr.snt())
|
||
msg->attr.scn1();
|
||
|
||
if(AA->Areacopyaddid() and not AA->isnet()) {
|
||
char* ptr = msg->txt + (*msg->txt == CTRL_A);
|
||
if(not strnieql(ptr, "AREA:", 5)) {
|
||
uint elen = 6 + strlen(AAorig->echoid()) + 1;
|
||
uint mlen = strlen(msg->txt)+1;
|
||
msg->txt = (char*)throw_realloc(msg->txt, elen+mlen);
|
||
memmove(msg->txt+elen, msg->txt, mlen);
|
||
sprintf(msg->txt, "\001AREA:%s", AAorig->echoid());
|
||
msg->txt[elen-1] = CR;
|
||
}
|
||
}
|
||
|
||
bool need_netmail_kludges = not AAorig->isnet() and AAdest->isnet();
|
||
bool need_fmpt = true;
|
||
bool need_topt = true;
|
||
|
||
if(not need_netmail_kludges and AAorig->issquish() and not AAdest->issquish() and AAdest->isnet() and AAorig->isnet()) {
|
||
if(msg->orig.point and not strstr(msg->txt, "\001FMPT"))
|
||
need_netmail_kludges = true;
|
||
else
|
||
need_fmpt = false;
|
||
if(msg->dest.point and not strstr(msg->txt, "\001TOPT"))
|
||
need_netmail_kludges = true;
|
||
else
|
||
need_topt = false;
|
||
}
|
||
|
||
if(need_netmail_kludges) {
|
||
char buf[256] = "";
|
||
|
||
if(not strstr(msg->txt, "\001INTL"))
|
||
// The INTL kludge for zone crossing
|
||
if(CFG->useintl and (CFG->useintl == YES or (msg->dest.zone != msg->orig.zone))) {
|
||
sprintf(buf, "\001INTL %u:%u/%u %u:%u/%u%c",
|
||
msg->dest.zone ? msg->dest.zone : AA->Aka().addr.zone,
|
||
msg->dest.net, msg->dest.node,
|
||
msg->orig.zone ? msg->orig.zone : AA->Aka().addr.zone,
|
||
msg->orig.net, msg->orig.node, CR
|
||
);
|
||
}
|
||
|
||
if(not strstr(msg->txt, "\001TOPT") or not strstr(msg->txt, "\001FMPT")) {
|
||
// The FMPT and TOPT kludges for point addressing
|
||
if(msg->dest.point and need_topt)
|
||
sprintf(buf+strlen(buf), "\001TOPT %u%c", msg->dest.point, CR);
|
||
if(msg->orig.point and msg->orig.net == msg->oorig.net and msg->orig.node == msg->oorig.node and need_fmpt)
|
||
sprintf(buf+strlen(buf), "\001FMPT %u%c", msg->orig.point, CR);
|
||
}
|
||
|
||
uint alen = strlen(buf);
|
||
if(alen) {
|
||
uint mlen = strlen(msg->txt)+1;
|
||
msg->txt = (char*)throw_realloc(msg->txt, alen+mlen);
|
||
memmove(msg->txt+alen, msg->txt, mlen);
|
||
memmove(msg->txt, buf, alen);
|
||
}
|
||
}
|
||
else if(AA->isecho() and not AAorig->isecho()) {
|
||
|
||
char* kl = strstr(msg->txt, "\001INTL");
|
||
char* p;
|
||
if(kl and (p = strchr(kl, CR)) != NULL)
|
||
memmove(kl, p, strlen(p)+1);
|
||
kl = strstr(msg->txt, "\001TOPT");
|
||
if(kl and (p = strchr(kl, CR)) != NULL)
|
||
memmove(kl, p, strlen(p)+1);
|
||
kl = strstr(msg->txt, "\001FMPT");
|
||
if(kl and (p = strchr(kl, CR)) != NULL)
|
||
memmove(kl, p, strlen(p)+1);
|
||
}
|
||
|
||
// Save the new msg to the destination area
|
||
msgno = msg->msgno;
|
||
AA->SaveMsg(GMSG_NEW|GMSG_NOLSTUPD, msg);
|
||
|
||
// Switch back to original area
|
||
AA = AAorig;
|
||
}
|
||
|
||
// Delete original msg if moved
|
||
if(mode == MODE_MOVE) {
|
||
msg->msgno = msgno;
|
||
AA->DeleteMsg(msg, DIR_PREV);
|
||
AA->PMrk.Del(msg->msgno);
|
||
}
|
||
|
||
} while((do_mode == MODE_MARKED) and ((++mrk) < mrks));
|
||
|
||
kbput(Key_Tick);
|
||
|
||
// Unlock and close destination area
|
||
AL.SetActiveAreaId(destarea);
|
||
AA->UpdateAreadata();
|
||
AA->Unlock();
|
||
AA->Close();
|
||
|
||
// Return to original area and unlock it
|
||
AL.SetActiveAreaId(OrigArea);
|
||
throw_free(AA->adat);
|
||
AA->adat = orig_adat;
|
||
AA->Unlock();
|
||
|
||
if(do_mode == MODE_MARKED) {
|
||
if(cmf == MODE_MOVE)
|
||
AA->Mark.ResetAll();
|
||
}
|
||
else {
|
||
if(cmf == MODE_MOVE) {
|
||
AA->Mark.Del(msg->msgno);
|
||
AA->PMrk.Del(msg->msgno);
|
||
}
|
||
}
|
||
|
||
w_info(NULL);
|
||
|
||
GFTRK(NULL);
|
||
}
|
||
|
||
|
||
// ------------------------------------------------------------------
|
||
|
||
void CopyMoveForward() {
|
||
|
||
ulong lastread = reader_msg->msgno;
|
||
|
||
AA->set_lastread(AA->Msgn.ToReln(lastread, AA->lastread()));
|
||
|
||
AA->attr().hex0();
|
||
if(AA->Msgn.Count())
|
||
CmfMsgs(reader_msg);
|
||
|
||
AA->set_lastread(AA->Msgn.ToReln(lastread, AA->lastread()));
|
||
}
|
||
|
||
|
||
// ------------------------------------------------------------------
|
||
|