Usermode/axwin3 - RichText cursor (hacked)
[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         // HACK!
229         info->DispCols = Window->W / 8; 
230
231         // TODO: Text cursor
232         _SysDebug("Cursor at %i,%i", info->CursorCol, info->CursorRow);
233         _SysDebug(" Range [%i+%i],[%i+%i]", info->FirstVisRow, info->DispLines, info->FirstVisCol, info->DispCols);
234         if( info->CursorRow >= info->FirstVisRow && info->CursorRow < info->FirstVisRow + info->DispLines )
235         {
236                 if( info->CursorCol >= info->FirstVisCol && info->CursorCol < info->FirstVisCol + info->DispCols )
237                 {
238                         // TODO: Kill hardcoded 8 with cached text distance
239                         WM_Render_FillRect(Window,
240                                 (info->CursorCol - info->FirstVisCol) * 8,
241                                 (info->CursorRow - info->FirstVisRow) * info->LineHeight,
242                                 1,
243                                 info->LineHeight,
244                                 info->DefaultFG
245                                 );
246                 }
247         }
248 }
249
250 int Renderer_RichText_HandleIPC_SetAttr(tWindow *Window, size_t Len, const void *Data)
251 {
252         tRichText_Window        *info = Window->RendererInfo;
253         const struct sRichTextIPC_SetAttr *msg = Data;
254         if(Len < sizeof(*msg))  return -1;
255
256         _SysDebug("RichText Attr %i set to %x", msg->Attr, msg->Value);
257         switch(msg->Attr)
258         {
259         case _ATTR_DEFBG:
260                 info->DefaultBG = msg->Value;
261                 break;
262         case _ATTR_DEFFG:
263                 info->DefaultFG = msg->Value;
264                 break;
265         case _ATTR_CURSORPOS:
266                 info->CursorRow = msg->Value >> 12;
267                 info->CursorCol = msg->Value & 0xFFF;
268                 break;
269         case _ATTR_SCROLL:
270                 // TODO: Set scroll flag
271                 break;
272         case _ATTR_LINECOUNT:
273                 info->nLines = msg->Value;
274                 break;
275         }
276         
277         return 0;
278 }
279
280 int Renderer_RichText_HandleIPC_WriteLine(tWindow *Window, size_t Len, const void *Data)
281 {
282         tRichText_Window        *info = Window->RendererInfo;
283         const struct sRichTextIPC_WriteLine     *msg = Data;
284         if( Len < sizeof(*msg) )        return -1;
285         if( msg->Line >= info->nLines ) return 1;       // Bad count
286
287         tRichText_Line  *line = info->FirstLine;
288         tRichText_Line  *prev = NULL;
289         while(line && line->Num < msg->Line)
290                 prev = line, line = line->Next;
291         if( !line || line->Num > msg->Line )
292         {
293                 // New line!
294                 // Round up to 32
295                  int    space = ((Len - sizeof(*msg)) + 32-1) & ~(32-1);
296                 tRichText_Line  *new = malloc(sizeof(*line) + space);
297                 // TODO: Bookkeeping on how much memory each window uses
298                 new->Next = line;
299                 new->Prev = prev;
300                 new->Num = msg->Line;
301                 new->Space = space;
302                 if(new->Prev)   new->Prev->Next = new;
303                 else    info->FirstLine = new;
304                 if(new->Next)   new->Next->Prev = new;
305                 line = new;
306         }
307         else if( line->Space < Len - sizeof(*msg) )
308         {
309                 // Need to allocate more space
310                  int    space = ((Len - sizeof(*msg)) + 32-1) & ~(32-1);
311                 tRichText_Line *new = realloc(line, space);
312                 // TODO: Bookkeeping on how much memory each window uses
313                 new->Space = space;
314
315                 if(new->Prev)   new->Prev->Next = new;
316                 else    info->FirstLine = new;
317                 if(new->Next)   new->Next->Prev = new;
318                 line = new;
319         }
320         else
321         {
322                 // It fits :)
323         }
324         line->ByteLength = Len - sizeof(*msg);
325         memcpy(line->Data, msg->LineData, Len - sizeof(*msg));
326
327         WM_Invalidate( Window );
328
329         return  0;
330 }
331
332 int Renderer_RichText_HandleMessage(tWindow *Target, int Msg, int Len, const void *Data)
333 {
334         tRichText_Window        *info = Target->RendererInfo;
335         switch(Msg)
336         {
337         case WNDMSG_RESIZE: {
338                 const struct sWndMsg_Resize *msg = Data;
339                 if(Len < sizeof(*msg))  return -1;
340                 info->DispLines = msg->H / info->LineHeight;
341                 return 1; }
342         case WNDMSG_KEYDOWN:
343         case WNDMSG_KEYUP:
344         case WNDMSG_KEYFIRE:
345                 return 1;
346         }
347         return 0;
348 }

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