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

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