8f69c9ca7eca1c78910c112c69cdab8e39fc4aac
[tpg/acess2.git] / Usermode / Applications / axwin3_src / WM / renderers / richtext.c
1 /*
2  * Acess2 Window Manager v3
3  * - By John Hodge (thePowersGang)
4  *
5  * render/richtext.c
6  * - Formatted Line Editor
7  */
8 #include <common.h>
9 #include <wm_renderer.h>
10 #include <wm_messages.h>
11 #include <richtext_messages.h>
12 #include <stdio.h>      // sscanf
13 #include <string.h>     // memcpy
14
15 #define LINES_PER_BLOCK 30
16
17 // === TYPES ===
18 typedef struct sRichText_Line
19 {
20         struct sRichText_Line   *Next;
21         struct sRichText_Line   *Prev;
22          int    Num;
23         // TODO: Pre-rendered cache?
24         short   ByteLength;
25         short   Space;
26         char    Data[];
27 } tRichText_Line;
28 typedef struct sRichText_Window
29 {
30          int    DispLines, DispCols;
31          int    FirstVisRow, FirstVisCol;
32          int    nLines, nCols;
33          int    CursorRow, CursorCol;
34         tRichText_Line  *FirstLine;
35         tRichText_Line  *FirstVisLine;
36         tColour DefaultFG;
37         tColour DefaultBG;
38         tFont   *Font;
39         
40         short   LineHeight;
41 } tRichText_Window;
42
43 // === PROTOTYPES ===
44  int    Renderer_RichText_Init(void);
45 tWindow *Renderer_RichText_Create(int Flags);
46 void    Renderer_RichText_Destroy(tWindow *Window);
47 void    Renderer_RichText_Redraw(tWindow *Window);
48  int    Renderer_RichText_HandleIPC_SetAttr(tWindow *Window, size_t Len, const void *Data);
49  int    Renderer_RichText_HandleIPC_WriteLine(tWindow *Window, size_t Len, const void *Data);
50  int    Renderer_RichText_HandleMessage(tWindow *Target, int Msg, int Len, const void *Data);
51
52 // === GLOBALS ===
53 tWMRenderer     gRenderer_RichText = {
54         .Name = "RichText",
55         .CreateWindow   = Renderer_RichText_Create,
56         .DestroyWindow  = Renderer_RichText_Destroy,
57         .Redraw         = Renderer_RichText_Redraw,
58         .HandleMessage  = Renderer_RichText_HandleMessage,
59         .nIPCHandlers = N_IPC_RICHTEXT,
60         .IPCHandlers = {
61                 [IPC_RICHTEXT_SETATTR] = Renderer_RichText_HandleIPC_SetAttr,
62                 [IPC_RICHTEXT_WRITELINE] = Renderer_RichText_HandleIPC_WriteLine
63         }
64 };
65
66 // === CODE ===
67 int Renderer_RichText_Init(void)
68 {
69         WM_RegisterRenderer(&gRenderer_RichText);       
70         return 0;
71 }
72
73 tWindow *Renderer_RichText_Create(int Flags)
74 {
75         tRichText_Window        *info;
76         tWindow *ret = WM_CreateWindowStruct( sizeof(*info) );
77         if(!ret)        return NULL;
78         info = ret->RendererInfo;
79         
80         // Initialise font (get an idea of dimensions)
81         int h;
82         WM_Render_GetTextDims(NULL, "yY!", 3, NULL, &h);
83         info->LineHeight = h;
84         
85         return ret;
86 }
87
88 void Renderer_RichText_Destroy(tWindow *Window)
89 {
90         tRichText_Window        *info = Window->RendererInfo;
91
92         // TODO: Is locking needed? WM_Destroy should have taken us off the render tree
93         while( info->FirstLine )
94         {
95                 tRichText_Line *line = info->FirstLine;
96                 info->FirstLine = line->Next;
97
98                 free(line);
99         }
100
101         if( info->Font )
102                 _SysDebug("RichText_Destroy - TODO: Free font");
103 }
104
105 static inline int Renderer_RichText_RenderText_Act(tWindow *Window, tRichText_Window *info, int X, int Row, const char *Text, int Bytes, tColour FG, tColour BG, int Flags)
106 {
107          int    rwidth;
108         // TODO: Fill only what is needed? What about the rest of the line?
109         WM_Render_DrawRect(Window, X, Row*info->LineHeight,
110                 Window->W - X, info->LineHeight,
111                 BG
112                 );
113         // TODO: Bold, Italic, Underline
114         rwidth = WM_Render_DrawText(Window,
115                 X, Row*info->LineHeight,
116                 Window->W - X, info->LineHeight,
117                 info->Font, FG,
118                 Text, Bytes
119                 );
120         return rwidth;
121 }
122
123 void Renderer_RichText_RenderText(tWindow *Window, int Line, const char *Text)
124 {
125         tRichText_Window        *info = Window->RendererInfo;
126         tColour fg = info->DefaultFG;
127         tColour bg = info->DefaultBG;
128          int    flagset = 0;
129          int    bRender = 0;
130          int    curx = 0;
131         const char      *oldtext = Text;
132         
133         for( int i = 0; curx < Window->W; i ++ )
134         {
135                 char    ch, flags;
136                  int    len;
137
138                 if( i == info->FirstVisCol )
139                         bRender = 1;
140
141                 ch = *Text++;
142                 if( ch == 0 )   break;
143
144                 // Not an escape - move along
145                 if( ch > 4 )
146                         continue ;              
147
148                 if( bRender ) {
149                         // Render previous characters
150                         curx += Renderer_RichText_RenderText_Act(Window, info, curx, Line,
151                                 oldtext, Text - oldtext - 1, fg, bg, flagset);
152                         if( curx >= Window->W )
153                                 break;
154                 }
155                 oldtext = Text;
156                 switch(ch)
157                 {
158                 case 1: // FG Select (\1 RRGGBB)
159                         if( sscanf(Text, "%6x%n", &fg, &len) != 1 || len != 6 ) {
160                                 // Bad client
161                                 _SysDebug("foreground scanf failed - len=%i", len);
162                                 len = 0;
163                         }
164                         Text += len;
165                         oldtext = Text;
166                         _SysDebug("FG update to %x", fg);
167                         break ;
168                 case 2: // BG Select (\2 RRGGBB)
169                         if( sscanf(Text, "%6x%n", &bg, &len) != 1 || len != 6 ) {
170                                 // Bad client
171                                 _SysDebug("background scanf failed - len=%i", len);
172                                 len = 0;
173                         }
174                         Text += len;
175                         oldtext = Text;
176                         _SysDebug("BG update to %x", bg);
177                         break ;
178                 case 3: // Flagset (0,it,uline,bold)
179                         if( sscanf(Text, "%1hhx%n", &flags, &len) != 1 || len != 1 ) {
180                                 // Bad client
181                                 _SysDebug("Flagset scanf failed - len=%i", len);
182                         }
183                         flagset = flags & 7;
184                         Text += len;
185                         oldtext = Text;
186                         break ;
187                 case 4: // Escape (do nothing)
188                         Text ++;
189                         // NOTE: No update to oldtext
190                         break;
191                 default: // Error.
192                         break;
193                 }
194         }
195         curx += Renderer_RichText_RenderText_Act(Window, info, curx,
196                 Line, oldtext, Text - oldtext + 1, fg, bg, flagset);
197         WM_Render_DrawRect(Window, curx, Line * info->LineHeight,
198                 Window->W - curx, info->LineHeight, info->DefaultBG);
199 }
200
201 void Renderer_RichText_Redraw(tWindow *Window)
202 {
203         tRichText_Window        *info = Window->RendererInfo;
204          int    i;
205         tRichText_Line  *line = info->FirstVisLine;
206         
207         if( !line ) {
208                 line = info->FirstLine;
209                 while(line && line->Num < info->FirstVisRow )
210                         line = line->Next;
211                 info->FirstVisLine = line;
212         }
213         while( line && line->Prev && line->Prev->Num > info->FirstVisRow )
214                 line = line->Prev;
215
216         for( i = 0; i < info->DispLines && line; i ++ )
217         {
218                 if( i >= info->nLines - info->FirstVisRow )
219                         break;
220                 // TODO: Dirty rectangles?
221                 WM_Render_FillRect(Window,
222                         0, i*info->LineHeight,
223                         Window->W, info->LineHeight,
224                         info->DefaultBG
225                         );
226                 if( line->Num > info->FirstVisRow + i )
227                         continue ;
228                 // TODO: Horizontal scrolling?
229                 // TODO: Formatting
230                 
231                 // Formatted text out
232                 Renderer_RichText_RenderText(Window, i, line->Data);
233                 _SysDebug("RichText: %p - Render %i '%.*s'", Window,
234                         line->Num, line->ByteLength, line->Data);
235
236                 line = line->Next;
237         }
238         // Clear out i -- info->DispLines
239         _SysDebug("RichText: %p - Clear %i px lines with %06x starting at %i",
240                 Window, (info->DispLines-i)*info->LineHeight, info->DefaultBG, i*info->LineHeight);
241         WM_Render_FillRect(Window,
242                 0, i*info->LineHeight,
243                 Window->W, (info->DispLines-i)*info->LineHeight,
244                 info->DefaultBG
245                 );
246
247         // HACK!
248         info->DispCols = Window->W / 8; 
249
250         // TODO: Text cursor
251         _SysDebug("Cursor at %i,%i", info->CursorCol, info->CursorRow);
252         _SysDebug(" Range [%i+%i],[%i+%i]", info->FirstVisRow, info->DispLines, info->FirstVisCol, info->DispCols);
253         if( info->CursorRow >= info->FirstVisRow && info->CursorRow < info->FirstVisRow + info->DispLines )
254         {
255                 if( info->CursorCol >= info->FirstVisCol && info->CursorCol < info->FirstVisCol + info->DispCols )
256                 {
257                         // TODO: Kill hardcoded 8 with cached text distance
258                         WM_Render_FillRect(Window,
259                                 (info->CursorCol - info->FirstVisCol) * 8,
260                                 (info->CursorRow - info->FirstVisRow) * info->LineHeight,
261                                 1,
262                                 info->LineHeight,
263                                 info->DefaultFG
264                                 );
265                 }
266         }
267 }
268
269 int Renderer_RichText_HandleIPC_SetAttr(tWindow *Window, size_t Len, const void *Data)
270 {
271         tRichText_Window        *info = Window->RendererInfo;
272         const struct sRichTextIPC_SetAttr *msg = Data;
273         if(Len < sizeof(*msg))  return -1;
274
275         _SysDebug("RichText Attr %i set to %x", msg->Attr, msg->Value);
276         switch(msg->Attr)
277         {
278         case _ATTR_DEFBG:
279                 info->DefaultBG = msg->Value;
280                 break;
281         case _ATTR_DEFFG:
282                 info->DefaultFG = msg->Value;
283                 break;
284         case _ATTR_CURSORPOS:
285                 info->CursorRow = msg->Value >> 12;
286                 info->CursorCol = msg->Value & 0xFFF;
287                 break;
288         case _ATTR_SCROLL:
289                 // TODO: Set scroll flag
290                 break;
291         case _ATTR_LINECOUNT:
292                 info->nLines = msg->Value;
293                 break;
294         }
295         
296         return 0;
297 }
298
299 int Renderer_RichText_HandleIPC_WriteLine(tWindow *Window, size_t Len, const void *Data)
300 {
301         tRichText_Window        *info = Window->RendererInfo;
302         const struct sRichTextIPC_WriteLine     *msg = Data;
303         if( Len < sizeof(*msg) )        return -1;
304         if( msg->Line >= info->nLines ) return 1;       // Bad count
305
306         tRichText_Line  *line = info->FirstLine;
307         tRichText_Line  *prev = NULL;
308         while(line && line->Num < msg->Line)
309                 prev = line, line = line->Next;
310         if( !line || line->Num > msg->Line )
311         {
312                 // New line!
313                 // Round up to 32
314                  int    space = ((Len - sizeof(*msg)) + 32-1) & ~(32-1);
315                 tRichText_Line  *new = malloc(sizeof(*line) + space);
316                 // TODO: Bookkeeping on how much memory each window uses
317                 new->Next = line;
318                 new->Prev = prev;
319                 new->Num = msg->Line;
320                 new->Space = space;
321                 if(new->Prev)   new->Prev->Next = new;
322                 else    info->FirstLine = new;
323                 if(new->Next)   new->Next->Prev = new;
324                 line = new;
325         }
326         else if( line->Space < Len - sizeof(*msg) )
327         {
328                 // Need to allocate more space
329                  int    space = ((Len - sizeof(*msg)) + 32-1) & ~(32-1);
330                 tRichText_Line *new = realloc(line, space);
331                 // TODO: Bookkeeping on how much memory each window uses
332                 new->Space = space;
333
334                 if(new->Prev)   new->Prev->Next = new;
335                 else    info->FirstLine = new;
336                 if(new->Next)   new->Next->Prev = new;
337                 line = new;
338         }
339         else
340         {
341                 // It fits :)
342         }
343         line->ByteLength = Len - sizeof(*msg);
344         memcpy(line->Data, msg->LineData, Len - sizeof(*msg));
345
346         WM_Invalidate( Window );
347
348         return  0;
349 }
350
351 int Renderer_RichText_HandleMessage(tWindow *Target, int Msg, int Len, const void *Data)
352 {
353         tRichText_Window        *info = Target->RendererInfo;
354         switch(Msg)
355         {
356         case WNDMSG_RESIZE: {
357                 const struct sWndMsg_Resize *msg = Data;
358                 if(Len < sizeof(*msg))  return -1;
359                 info->DispLines = msg->H / info->LineHeight;
360                 return 1; }
361         case WNDMSG_KEYDOWN:
362         case WNDMSG_KEYUP:
363         case WNDMSG_KEYFIRE:
364                 return 1;
365         }
366         return 0;
367 }

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