1 
2 //  ------------------------------------------------------------------
3 //  GoldED+
4 //  Copyright (C) 1990-1999 Odinn Sorensen
5 //  Copyright (C) 1999-2000 Alexander S. Aganichev
6 //  ------------------------------------------------------------------
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License as
9 //  published by the Free Software Foundation; either version 2 of the
10 //  License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 //  General Public License for more details.
16 //
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20 //  MA 02111-1307 USA
21 //  ------------------------------------------------------------------
22 //  $Id: gealst.cpp,v 1.30 2010/03/28 17:20:21 stas_degteff Exp $
23 //  ------------------------------------------------------------------
24 //  Arealist functions.
25 //  ------------------------------------------------------------------
26 
27 #if defined(_MSC_VER)
28     /* C4786: 'identifier' : identifier was truncated to 'number'
29           characters in the debug information
30     */
31   #pragma warning(disable: 4786)
32 #endif
33 #include <algorithm>
34 #include <limits.h>
35 #include <golded.h>
36 
37 #if defined(__USE_ALLOCA__)
38 #include <malloc.h>
39 #endif
40 
41 
42 //  ------------------------------------------------------------------
43 
44 AreaList AL;
45 
46 int AreaTypeOrder[17] = {
47   0,
48   1,      // GMB_NET
49   2,      // GMB_EMAIL
50   3,      // GMB_NET | GMB_EMAIL
51   4,      // GMB_ECHO
52   5,
53   6,
54   7,
55   8,      // GMB_NEWSGROUP
56   9,
57   10,
58   11,
59   12,     // GMB_ECHO | GMB_NEWSGROUP
60   13,
61   14,
62   15,
63   16      // GMB_LOCAL
64 };
65 
66 
67 //  ------------------------------------------------------------------
68 //  Areagroups compare
69 
compare_groups(int _ga,int _gb)70 int compare_groups(int _ga, int _gb)
71 {
72   register int ga = _ga ? _ga : INT_MAX;
73   register int gb = _gb ? _gb : INT_MAX;
74 
75   const char *gap = NULL;
76   const char *gbp = NULL;
77 
78   const char *g;
79 
80   for(g = CFG->arealistgrouporder; *g != NUL;) {
81 
82     int gr = getgroup(g);
83 
84     if(gr == ga)
85       gap = g;
86 
87     if(gr == gb)
88       gbp = g;
89 
90     if(*g == '#') {
91       do {
92         g++;
93       } while(isdigit(*g));
94     }
95     else
96       g++;
97   }
98 
99   if(gap == NULL) {
100     if(gbp != NULL)
101       return 1;
102     else
103       return compare_two(ga, gb);
104   }
105   else {
106     if(gbp == NULL)
107       return -1;
108     else
109       return compare_two(gap, gbp);
110   }
111 }
112 
113 
114 //  ------------------------------------------------------------------
115 //  Arealist compare
116 
AreaListCmp(const Area ** __a,const Area ** __b)117 extern "C" int AreaListCmp(const Area** __a, const Area** __b) {
118 
119   const Area* a = *__a;
120   const Area* b = *__b;
121   const Area* A = a;
122   const Area* B = b;
123   int cmp = 0;
124 
125   bool sepfirst = false;
126 
127   bool rev = false;
128   char* ptr = AL.sortspec;
129 
130   while(*ptr) {
131     switch(*ptr) {
132       case '-':
133         rev = true;
134         A = b; B = a;
135         break;
136       case '+':
137         rev = false;
138         A = a; B = b;
139         break;
140       case 'A':
141       case 'a':
142         sepfirst = true;
143         if((cmp = A->aka().compare(B->aka())) != 0)
144           return cmp;
145         break;
146       case 'B':
147       case 'b':
148         if(A->isseparator() and B->isseparator())
149           break;
150         else if(A->isseparator()) {
151           if(B->board())
152             return sepfirst?-1:1;
153         }
154         else if(B->isseparator()) {
155           if(A->board())
156             return sepfirst?1:-1;
157         }
158         else if((cmp = compare_two(A->board(), B->board())) != 0)
159           return cmp;
160         break;
161       case 'D':
162       case 'd':
163         if((cmp = stricmp(A->desc(), B->desc())) != 0)
164           return cmp;
165         break;
166       case 'E':
167       case 'e':
168         if((cmp = stricmp(A->echoid(), B->echoid())) != 0)
169           return cmp;
170         break;
171       case 'F':
172       case 'f':
173         if(*area_maybe) {
174           bool amay = make_bool(striinc(area_maybe, A->echoid()));
175           bool bmay = make_bool(striinc(area_maybe, B->echoid()));
176 
177           if((cmp = compare_two(bmay, amay)) != 0)
178             return cmp;
179         }
180         break;
181       case 'G':
182       case 'g':
183         sepfirst = true;
184         if((cmp = compare_groups(A->groupid(), B->groupid())) != 0)
185           return cmp;
186         break;
187       case 'M':
188       case 'm':
189         if(A->isseparator() and B->isseparator())
190           break;
191         else if(A->isseparator()) {
192           if(B->ismarked())
193             return sepfirst?-1:1;
194         }
195         else if(B->isseparator()) {
196           if(A->ismarked())
197             return sepfirst?1:-1;
198         }
199         else if((cmp = compare_two(B->ismarked(), A->ismarked())) != 0)
200           return cmp;
201         break;
202       case 'P':
203         if(A->isseparator() and B->isseparator())
204           break;
205         else if(A->isseparator()) {
206           if(B->PMrk.Count())
207             return sepfirst?-1:1;
208         }
209         else if(B->isseparator()) {
210           if(A->PMrk.Count())
211             return sepfirst?1:-1;
212         }
213         else {
214           register int aunread = A->PMrk.Count();
215           register int bunread = B->PMrk.Count();
216 
217           aunread = (rev or aunread) ? aunread : INT_MAX;
218           bunread = (rev or bunread) ? bunread : INT_MAX;
219           if((cmp = compare_two(aunread, bunread)) != 0)
220             return cmp;
221         }
222         break;
223       case 'p':
224         if(A->isseparator() and B->isseparator())
225           break;
226         else if(A->isseparator()) {
227           if(B->PMrk.Count())
228             return sepfirst?-1:1;
229         }
230         else if(B->isseparator()) {
231           if(A->PMrk.Count())
232             return sepfirst?1:-1;
233         }
234         else if((cmp = compare_two(B->PMrk.Count()?1:0, A->PMrk.Count()?1:0)) != 0)
235           return cmp;
236         break;
237       case 'O':
238       case 'o':
239         if((cmp = compare_two(A->areaid(), B->areaid())) != 0)
240           return cmp;
241         break;
242       case 'T':
243       case 't':
244         sepfirst = true;
245         if((cmp = compare_two(CFG->areatypeorder[A->type()&0xFF], CFG->areatypeorder[B->type()&0xFF])) != 0)
246           return cmp;
247         break;
248       case 'U':
249         if(A->isseparator() and B->isseparator())
250           break;
251         else if(A->isseparator()) {
252           if(B->unread)
253             return sepfirst?-1:1;
254         }
255         else if(B->isseparator()) {
256           if(A->unread)
257             return sepfirst?1:-1;
258         }
259         else {
260           register int aunread = A->unread;
261           register int bunread = B->unread;
262 
263           aunread = (rev or aunread) ? aunread : INT_MAX;
264           bunread = (rev or bunread) ? bunread : INT_MAX;
265           if((cmp = compare_two(aunread, bunread)) != 0)
266             return cmp;
267         }
268         break;
269       case 'u':
270         if(A->isseparator() and B->isseparator())
271           break;
272         else if(A->isseparator()) {
273           if(B->unread)
274             return sepfirst?-1:1;
275         }
276         else if(B->isseparator()) {
277           if(A->unread)
278             return sepfirst?1:-1;
279         }
280         else if((cmp = compare_two(B->unread?1:0, A->unread?1:0)) != 0)
281           return cmp;
282         break;
283       case 'X':
284       case 'x':
285         if(A->isseparator() and B->isseparator())
286           break;
287         else if(A->isseparator()) {
288           return sepfirst?-1:1;
289         }
290         else if(B->isseparator()) {
291           return sepfirst?1:-1;
292         }
293         else if ((cmp = strcmp(A->basetype().c_str(), B->basetype().c_str())) != 0)
294           return cmp;
295         break;
296       case 'Y':
297       case 'y':
298         if(A->isseparator() and B->isseparator())
299           break;
300         else if(A->isseparator()) {
301           if(B->isnewmail())
302             return sepfirst?-1:1;
303         }
304         else if(B->isseparator()) {
305           if(A->isnewmail())
306             return sepfirst?1:-1;
307         }
308         else if((cmp = compare_two(B->isnewmail(), A->isnewmail())) != 0)
309           return cmp;
310         break;
311       case 'Z':
312       case 'z':
313         if(A->isseparator() and B->isseparator())
314           break;
315         else if(A->isseparator())
316           return sepfirst?-1:1;
317         else if(B->isseparator())
318           return sepfirst?1:-1;
319         else if((cmp = stricmp(A->path(), B->path())) != 0)
320           return cmp;
321         break;
322       case 'S':
323       case 's':
324         if((cmp = compare_two(B->isseparator(), A->isseparator())) != 0)
325           return cmp;
326         break;
327     }
328     ptr++;
329   }
330 
331   if(cmp == 0) {
332     cmp = compare_two(B->isseparator(), A->isseparator());
333   }
334 
335   return cmp;
336 }
337 
AreaListCmp2(const Area * a,const Area * b)338 static bool AreaListCmp2(const Area* a, const Area* b) {
339 
340   return AreaListCmp(&a, &b) < 0;
341 }
342 
343 //  ------------------------------------------------------------------
344 //  Arealist sort areas
345 
Sort(const char * specs,int first,int last)346 void AreaList::Sort(const char* specs, int first, int last) {
347 
348   if(specs)
349     strxcpy(sortspec, specs, sizeof(sortspec));
350   else
351     strcpy(sortspec, CFG->arealistsort);
352   if(last == -1)
353     last = idx.size();
354   if(*sortspec) {
355     std::sort(idx.begin()+first, idx.begin()+last, AreaListCmp2);
356   }
357 }
358 
359 
360 //  ------------------------------------------------------------------
361 
AreaEchoToNo(const char * echoid)362 int AreaList::AreaEchoToNo(const char* echoid) {
363 
364   for(uint n=0; n<idx.size(); n++)
365     if(strieql(echoid, idx[n]->echoid()))
366       return n;
367   return -1;
368 }
369 
370 
371 //  ------------------------------------------------------------------
372 
AreaEchoToPtr(const char * echoid)373 Area* AreaList::AreaEchoToPtr(const char* echoid) {
374 
375   for(uint n=0; n<idx.size(); n++)
376     if(strieql(echoid, idx[n]->echoid()))
377       return idx[n];
378   return NULL;
379 }
380 
381 
382 //  ------------------------------------------------------------------
383 
AreaNoToId(int __areano)384 int AreaList::AreaNoToId(int __areano) {
385 
386   if(in_range(__areano, 0, int(idx.size()-1)))
387     return idx[__areano]->areaid();
388   else {
389     LOG.ErrIndex();
390     LOG.printf("! Arealist index out of bounds.");
391     LOG.printf(": Tried to access record %i, but the range was only 0 - %u.", __areano, (uint)(idx.size()-1));
392     LOG.printf("+ Advice: Report this to the author.");
393     IndexErrorExit();
394     return 0;
395   }
396 }
397 
398 
399 //  ------------------------------------------------------------------
400 
AreaNoToPtr(int __areano)401 Area* AreaList::AreaNoToPtr(int __areano) {
402 
403   if(in_range(__areano, 0, int(idx.size()-1)))
404     return idx[__areano];
405   else {
406     LOG.ErrIndex();
407     LOG.printf("! Arealist index out of bounds.");
408     LOG.printf(": Tried to access record %i, but the range was only 0 - %u.", __areano, (uint)(idx.size()-1));
409     LOG.printf("+ Advice: Report this to the author.");
410     IndexErrorExit();
411     return 0;
412   }
413 }
414 
415 
416 //  ------------------------------------------------------------------
417 
AreaIdToNo(int __areaid)418 int AreaList::AreaIdToNo(int __areaid) {
419 
420   uint _areano = 0;
421   while(_areano < idx.size()) {
422     if(idx[_areano]->areaid() == __areaid)
423       return _areano;
424     _areano++;
425   }
426   return -1;
427 }
428 
429 
430 //  ------------------------------------------------------------------
431 
AreaIdToPtr(int __areaid)432 Area* AreaList::AreaIdToPtr(int __areaid) {
433 
434   int _areano = AreaIdToNo(__areaid);
435   if(_areano != -1)
436     return idx[_areano];
437   return NULL;
438 }
439 
440 
441 //  ------------------------------------------------------------------
442 
SetActiveAreaId(int __areaid)443 int AreaList::SetActiveAreaId(int __areaid) {
444 
445   int _areano = AreaIdToNo(__areaid);
446   SetActiveAreaNo(_areano);
447   return _areano;   // Return the areano
448 }
449 
450 
451 //  ------------------------------------------------------------------
452 
453 class SelMaskPick : public gwinpick {
454 
455   gwindow window;
456   int DESC_LEN;
457 
458   void open();                        // Called after window is opened
459   void close();                       // Called after window is closed
460   void print_line(uint idx, uint pos, bool isbar);
461   bool handle_key();                  // Handles keypress
462 
463 public:
464 
465   void Run();
466 
467 };
468 
open()469 void SelMaskPick::open() {
470 
471   window.openxy(ypos, xpos, ylen+2, xlen+2, btype, battr, LGREY_|_BLACK);
472   window.title(title, tattr);
473   window.shadow(C_SHADOW);
474   update();
475 }
476 
close()477 void SelMaskPick::close() {
478 
479   window.close();
480 
481 }
482 
print_line(uint idx,uint pos,bool isbar)483 void SelMaskPick::print_line(uint idx, uint pos, bool isbar) {
484 
485 #if defined(__USE_ALLOCA__)
486   char *buf = (char*)alloca(DESC_LEN+3);
487 #else
488    __extension__ char buf[DESC_LEN+3];
489 #endif
490 
491   *buf = ' '; strxcpy(buf+1, AL.alistselections[idx], DESC_LEN);
492   window.printns(pos, 0, isbar ? sattr : wattr, buf, xlen);
493 }
494 
handle_key()495 bool SelMaskPick::handle_key() {
496 
497   switch(key) {
498     case Key_Esc:
499       aborted = true;
500 
501       /// Drop through
502     case Key_Ent:
503       return false;
504 
505     case Key_Tick:
506       break;
507 
508     default:
509       if(key < KK_Commands) {
510         kbput(key);
511         edit_string(AL.alistselections[index], DESC_LEN-1, LNG->SelectMarksEdit, H_SelectMarks);
512         display_bar();
513       }
514       break;
515   }
516 
517   return true;
518 }
519 
Run()520 void SelMaskPick::Run() {
521 
522   DESC_LEN = (sizeof(Desc) > (MAXCOL-6)) ? MAXCOL-6 : sizeof(Desc);
523 
524   ypos = (MAXROW-18)/2;
525   xpos = (MAXCOL-DESC_LEN-4)/2-1;
526   ylen = 16;
527   xlen = DESC_LEN+2;
528   btype = W_BMENU;
529   battr = C_MENUB;
530   wattr = C_MENUW;
531   tattr = C_MENUT;
532   sattr = C_MENUS;
533   hattr = C_MENUQ;
534 
535   title = LNG->SelectMarks;
536   helpcat = H_SelectMarks;
537   listwrap = true;
538 
539   maximum_index = 15;
540   maximum_position = 16;
541 
542   index = position = AL.mask;
543 
544   run_picker();
545 
546   if(not aborted)
547     AL.mask = (byte)index;
548 
549 }
550 
551 
Select_Mask()552 void AreaList::Select_Mask() {
553 
554   SelMaskPick& SelMask = *new SelMaskPick;
555   throw_new(&SelMask);
556 
557   SelMask.Run();
558 
559   extern bool in_arealist;
560   extern GPickArealist* PickArealist;
561 
562   if(in_arealist) {
563     PickArealist->update();
564     PickArealist->do_delayed();
565   }
566 
567   delete &SelMask;
568 }
569 
570 
571 //  ------------------------------------------------------------------
572 
573