1314cec3405ed5036567110474189ddedec47414
[tpg/acess2.git] / Usermode / Applications / irc_src / window.c
1 /*
2  */
3 #include "window.h"
4 #include <stddef.h>
5 #include <string.h>
6 #include <stdarg.h>
7 #include <stdio.h>      // TODO: replace with calls into ACurses_*
8 #include <stdlib.h>
9 #include <assert.h>
10
11 struct sMessage
12 {
13         struct sMessage *Next;
14         time_t  Timestamp;
15         enum eMessageClass      Class;
16         char    *Source;        // Pointer to the end of `Data`
17         char    Data[];
18 };
19
20 struct sWindow
21 {
22         struct sWindow  *Next;
23         tMessage        *Messages;
24         tServer *Server;        //!< Canonical server (can be NULL)
25          int    ActivityLevel;
26         char    Name[]; // Channel name / remote user
27 };
28
29 // === PROTOTYPES ===
30 void    Windows_RepaintCurrent(void);
31  int    Windows_int_PaintMessage(tMessage *Message);
32
33 // === GLOBALS ===
34 tWindow gWindow_Status = {
35         NULL, NULL, NULL,       // No next, empty list, no server
36         0, {""} // No activity, empty name (rendered as status)
37 };
38 tWindow *gpWindows = &gWindow_Status;
39 tWindow *gpCurrentWindow = &gWindow_Status;
40
41 // === CODE ===
42 void Windows_RepaintCurrent(void)
43 {
44         tMessage *msg = gpCurrentWindow->Messages;
45         
46         // TODO: Title bar?
47
48         // Note: This renders from the bottom up
49         for( int y = giTerminal_Height - 1; y -- && msg; msg = msg->Next)
50         {
51                 y -= Windows_int_PaintMessage(msg);
52         }
53
54         // Bottom line is rendered by the prompt
55         
56 }
57
58 int Windows_int_PaintMessage(tMessage *Message)
59 {
60         printf("\33[T");        // Scroll down 1 (free space below)
61         SetCursorPos(giTerminal_Height-2, 1);
62         
63         size_t  prefixlen = 0;
64         prefixlen += printf("%02i:%02i:%02i ", (Message->Timestamp/3600)%24, (Message->Timestamp/60)%60, Message->Timestamp%60);
65         switch(Message->Class)
66         {
67         case MSG_CLASS_BARE:    break;
68         case MSG_CLASS_CLIENT:
69                 if(Message->Source)
70                         prefixlen += printf("[%s] ", Message->Source);
71                 prefixlen += printf("-!- ");
72                 break;
73         case MSG_CLASS_WALL:
74                 prefixlen += printf("[%s] ", Message->Source);
75                 break;
76         case MSG_CLASS_MESSAGE:
77                 prefixlen += printf("<%s> ", Message->Source);
78                 break;
79         case MSG_CLASS_ACTION:
80                 prefixlen += printf("* %s ", Message->Source);
81                 break;
82         }
83          int    avail = giTerminal_Width - prefixlen;
84          int    msglen = strlen(Message->Data);
85         
86         int     nLines = 1;
87         printf("%.*s", avail, Message);
88         while( msglen > avail ) {
89                 msglen -= avail;
90                 Message += avail;
91                 printf("\33[T");
92                 SetCursorPos(giTerminal_Height-2, prefixlen+1);
93                 printf("%.*s", avail, Message);
94                 nLines ++;
95         }
96         
97         return nLines;
98 }
99
100 void Windows_SwitchTo(tWindow *Window)
101 {
102         gpCurrentWindow = Window;
103         Redraw_Screen();
104 }
105
106 void Windows_SetStatusServer(tServer *Server)
107 {
108         gWindow_Status.Server = Server;
109 }
110
111 tWindow *Windows_GetByIndex(int Index)
112 {
113         tWindow *win;
114         for( win = gpWindows; win && Index--; win = win->Next )
115                 ;
116         return win;
117 }
118
119 tWindow *Windows_GetByNameEx(tServer *Server, const char *Name, bool CreateAllowed)
120 {
121         tWindow *ret, *prev = NULL;
122          int    num = 1;
123         
124         // Get the end of the list and check for duplicates
125         // TODO: Cache this instead
126         for( ret = &gWindow_Status; ret; prev = ret, ret = ret->Next )
127         {
128                 if( ret->Server == Server && strcmp(ret->Name, Name) == 0 )
129                 {
130                         return ret;
131                 }
132                 num ++;
133         }
134         if( !CreateAllowed ) {
135                 return NULL;
136         }
137         
138         ret = malloc(sizeof(tWindow) + strlen(Name) + 1);
139         ret->Messages = NULL;
140         ret->Server = Server;
141         ret->ActivityLevel = 1;
142         strcpy(ret->Name, Name);
143         
144         if( prev ) {
145                 ret->Next = prev->Next;
146                 prev->Next = ret;
147         }
148         else {  // Shouldn't happen really
149                 ret->Next = gpWindows;
150                 gpWindows = ret;
151         }
152         
153 //      printf("Win %i %s:%s created\n", num, Server->Name, Name);
154         
155         return ret;
156 }
157
158 tWindow *Windows_GetByName(tServer *Server, const char *Name)
159 {
160         return Windows_GetByNameEx(Server, Name, false);
161 }
162
163 tWindow *Window_Create(tServer *Server, const char *Name)
164 {
165         return Windows_GetByNameEx(Server, Name, true);
166 }
167
168
169 tWindow *Window_int_ParseSpecial(const tWindow *Window)
170 {
171         if( Window == NULL )
172                 return gpCurrentWindow;
173         if( Window == WINDOW_STATUS )
174                 return &gWindow_Status;
175         return (tWindow*)Window;
176 }
177
178 const char *Window_GetName(const tWindow *Window) {
179         return Window_int_ParseSpecial(Window)->Name;
180 }
181 tServer *Window_GetServer(const tWindow *Window) {
182         return Window_int_ParseSpecial(Window)->Server;
183 }
184 bool Window_IsChat(const tWindow *Window) {
185         return Window_int_ParseSpecial(Window) != &gWindow_Status;
186 }
187
188 // -----------------------------
189 //  Messages
190 // -----------------------------
191 void Window_AppendMessage(tWindow *Window, enum eMessageClass Class, const char *Source, const char *Message, ...)
192 {
193         Window = Window_int_ParseSpecial(Window);
194
195         va_list args;
196         
197         va_start(args, Message);
198         size_t len = vsnprintf(NULL, 0, Message, args);
199         va_end(args);
200         
201         tMessage *msg = malloc( sizeof(tMessage) + len+1 + (Source?strlen(Source)+1:0) );
202         assert(msg);
203         
204         msg->Class = Class;
205         msg->Source = (Source ? msg->Data + len+1 : NULL);
206         va_start(args, Message);
207         vsnprintf(msg->Data, len+1, Message, args);
208         va_end(args);
209         
210         msg->Next = Window->Messages;
211         Window->Messages = msg;
212         
213         if( Window == gpCurrentWindow )
214         {
215                 // Scroll if needed, and redraw?
216                 // - Lazy option of draw at bottom of screen
217                 printf("\33[s");        // Save cursor
218                 Windows_int_PaintMessage(msg);
219                 printf("\x1b[u");       // Restore cursor
220         }
221 }
222
223 void Window_AppendMsg_Join(tWindow *Window, const char *Usermask)
224 {
225         Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "%s has joined %s", Usermask, Window->Name);
226 }
227 void Window_AppendMsg_Quit(tWindow *Window, const char *Usermask, const char *Reason)
228 {
229         Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "%s has joined quit (%s)", Usermask, Reason);
230 }
231 void Window_AppendMsg_Part(tWindow *Window, const char *Usermask, const char *Reason)
232 {
233         Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "%s has joined left %s (%s)", Usermask, Window->Name, Reason);
234 }
235 void Window_AppendMsg_Topic(tWindow *Window, const char *Topic)
236 {
237         Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "Topic of %s is %s", Window->Name, Topic);
238 }
239 void Window_AppendMsg_TopicTime(tWindow *Window, const char *User, const char *Timestamp)
240 {
241         Window_AppendMessage(Window, MSG_CLASS_CLIENT, NULL, "Topic set by %s at %s", User, Timestamp);
242 }
243

UCC git Repository :: git.ucc.asn.au