Usermode/AxWin3 - Added menu render code (with hilights)
[tpg/acess2.git] / Usermode / Applications / axwin3_src / WM / renderer_menu.c
1 /*
2  * Acess2 Window Manager v3
3  * - By John Hodge (thePowersGang)
4  *
5  * render_menu.c
6  * - Pop-up menu window class/renderer
7  */
8 #include <common.h>
9 #include <wm_renderer.h>
10 #include <menu_messages.h>
11 #include <wm_messages.h>
12 #include <stdlib.h>
13 #include <string.h>
14
15 // === STRUCTURES ===
16 typedef struct sMenuItem
17 {
18         // Settings
19         char    *Label;
20         char    *Shortcut;
21          int    KeyOffset;
22          int    Flags;
23         
24         // Cached values
25          int    LabelWidth;     
26          int    ShortcutWidth;
27          int    UnderlineX;
28          int    UnderlineW;
29
30         char    Data[];
31 } tMenuItem;
32
33 typedef struct sMenuWindowInfo
34 {
35          int    MaxLabelWidth;
36          int    MaxShortcutWidth;
37          int    CachedW;
38
39          int    HilightedItem;  
40
41          int    MaxItems;
42          int    nItems;
43         tMenuItem       *Items[];
44 } tMenuWindowInfo;
45
46 // === PROTOTYPES ===
47 void    Renderer_Menu_Init(void);
48 tWindow *Renderer_Menu_Create(int Argument);
49 void    Renderer_Menu_Redraw(tWindow *Window);
50  int    Renderer_Menu_HandleMessage(tWindow *Window, int Msg, int Length, void *Data);
51
52 // === CONSTANTS ===
53 const int       ciMenu_Gap = 10;        // Gap between label and shortcut
54 const int       ciMenu_TopPadding = 2;
55 const int       ciMenu_BottomPadding = 2;
56 const int       ciMenu_LeftPadding = 2;
57 const int       ciMenu_RightPadding = 2;
58 const int       ciMenu_FontHeight = 16;
59 const int       ciMenu_ItemHeight = 20;
60 const int       ciMenu_SpacerHeight = 5;
61 const tColour   cMenu_BackgroundColour = 0xCCCCCC;
62 const tColour   cMenu_BorderColour   = 0x000000;
63 const tColour   cMenu_SpacerColour   = 0x404040;
64 const tColour   cMenu_LabelColour    = 0x000000;
65 const tColour   cMenu_ShortcutColour = 0x404040;
66 const tColour   cMenu_HilightColour  = 0xE0E0E0;
67
68 // === GLOBALS ===
69 tWMRenderer     gRenderer_Menu = {
70         .Name = "Menu",
71         .CreateWindow = Renderer_Menu_Create,
72         .Redraw = Renderer_Menu_Redraw,
73         .HandleMessage = Renderer_Menu_HandleMessage
74 };
75 tFont   *gMenu_Font = NULL;     // System monospace
76
77 // === CODE ===
78 void Renderer_Menu_Init(void)
79 {
80         WM_RegisterRenderer(&gRenderer_Menu);
81 }
82
83 tWindow *Renderer_Menu_Create(int Argument)
84 {
85         tWindow *ret;
86         tMenuWindowInfo *info;
87
88         if(Argument < 5)        Argument = 5;
89         if(Argument > 200)      Argument = 200; 
90
91         ret = WM_CreateWindowStruct(sizeof(*info) + Argument*sizeof(info->Items[0]));
92         info = ret->RendererInfo;
93         info->MaxItems = Argument;
94         info->HilightedItem = -1;
95
96         _SysDebug("Renderer_Menu_Create: ->MaxItems = %i", info->MaxItems);
97         
98         return ret;
99 }
100
101 void Renderer_Menu_Redraw(tWindow *Window)
102 {
103         tMenuWindowInfo *info = Window->RendererInfo;
104          int    w, h, y, i;
105         _SysDebug("TODO: Implement Renderer_Menu_Redraw");
106
107 //      _SysDebug("info->nItems = %i", info->nItems);
108
109         w = info->CachedW;
110         h = ciMenu_TopPadding + ciMenu_BottomPadding;
111         for( i = 0; i < info->nItems; i ++ )
112         {
113                 if( !info->Items[i] )   continue;
114                 
115                 if(info->Items[i]->Label)
116                         h += ciMenu_ItemHeight;
117                 else
118                         h += ciMenu_SpacerHeight;
119         }
120
121 //      _SysDebug("w = %i, h = %i", w, h);
122
123         // - Resize window to contain all items
124         WM_ResizeWindow(Window, w, h);
125
126         // - Move the window such that it is on screen
127         //  > Make sure to catch if the menu can't fit fully onscreen
128
129         // - Clear
130         WM_Render_FillRect(Window, 0, 0, w, h, cMenu_BackgroundColour);
131         WM_Render_DrawRect(Window, 0, 0, w, h, cMenu_BorderColour);
132
133         // - Render each item
134         y = ciMenu_TopPadding;
135         for( i = 0; i < info->nItems; i ++ )
136         {
137                 tMenuItem       *item = info->Items[i];
138                 
139                 // Unused slot
140                 if(!item)       continue;
141                 
142                 // Spacer
143                 if(!item->Label)
144                 {
145                         WM_Render_FillRect(Window,
146                                 1, y + ciMenu_SpacerHeight/2,
147                                 w-2, 1,
148                                 cMenu_SpacerColour
149                                 );
150                         y += ciMenu_SpacerHeight;
151                         continue ;
152                 }
153         
154                 // Hilight
155                 if( info->HilightedItem == i )
156                 {
157                         WM_Render_FillRect(Window,
158                                 1, y,
159                                 w-2, ciMenu_ItemHeight,
160                                 cMenu_HilightColour
161                                 );
162                 }
163         
164                 // Text
165                 WM_Render_DrawText(Window,
166                         ciMenu_LeftPadding, y,
167                         w, ciMenu_ItemHeight,
168                         gMenu_Font,
169                         cMenu_LabelColour,
170                         item->Label
171                         );
172                 // Underline
173                 if(item->UnderlineW)
174                 {
175                         WM_Render_FillRect(Window,
176                                 ciMenu_LeftPadding + item->UnderlineX, y + ciMenu_FontHeight + 1,
177                                 item->UnderlineW, 1,
178                                 cMenu_LabelColour
179                                 );
180                 }
181                 
182                 // Shortcut key
183                 if(item->Shortcut)
184                 {
185                         WM_Render_DrawText(Window,
186                                 w - item->ShortcutWidth - ciMenu_RightPadding, y,
187                                 w, ciMenu_ItemHeight,
188                                 gMenu_Font,
189                                 cMenu_ShortcutColour,
190                                 item->Shortcut
191                                 );
192                 }
193                 
194                 y += ciMenu_ItemHeight;
195         }
196 }
197
198 int Renderer_Menu_int_AddItem(tWindow *Window, int Length, void *Data)
199 {
200         tMenuWindowInfo *info = Window->RendererInfo;
201         tMenuMsg_AddItem        *req = Data;
202         tMenuItem       *item;
203         
204         // Sanity checking
205         // - Message length
206         if(Length < sizeof(*req) + 1 || req->Label[Length-sizeof(*req)-1] != '\0') {
207                 _SysDebug("Renderer_Menu_int_AddItem: Size checks failed");
208                 return -1;
209         }
210         // - ID Number
211         if(req->ID >= info->MaxItems) {
212                 _SysDebug("Renderer_Menu_int_AddItem: ID (%i) >= MaxItems (%i)",
213                         req->ID, info->MaxItems);
214                 return -1;
215         }
216         
217         // Don't overwrite
218         if(info->Items[req->ID])        return 0;
219         // Bookkeeping
220         if(req->ID >= info->nItems)     info->nItems = req->ID + 1;
221         // Allocate
222         item = malloc(sizeof(tMenuItem)+strlen(req->Label));
223         info->Items[req->ID] = item;
224         
225         if(req->Label[0] == '\0')
226         {
227                 // Spacer
228                 item->Label = NULL;
229                 
230                 return 0;
231         }
232         
233         // Actual item
234         char    *dest = item->Data;
235         char    *src = req->Label;
236          int    ofs = 0;
237
238         // - Main label
239         item->KeyOffset = -1;
240         item->Label = dest;
241         for(ofs = 0; *src && *src != '\t'; ofs ++)
242         {
243                 if(*src == '&') {
244                         *dest = '\0';
245                         item->KeyOffset = ofs;
246                         src ++;
247                 }
248                 else {
249                         *dest++ = *src++;
250                 }
251         }
252         *dest++ = '\0';
253         // - Key combo / Shortcut
254         if(*src)
255         {
256                 src ++;
257                 item->Shortcut = dest;
258                 strcpy(item->Shortcut, src);
259         }
260         else
261         {
262                 item->Shortcut = NULL;
263         }
264         
265         // Get dimensions
266         // - Underline (hotkey)
267         if(item->KeyOffset == -1)
268         {
269                 item->UnderlineX = 0;
270                 item->UnderlineW = 0;
271         }
272         else
273         {
274                 char    tmp = item->Label[item->KeyOffset];
275                 // Get width of preceding substring
276                 item->Label[item->KeyOffset] = '\0';
277                 WM_Render_GetTextDims(NULL, item->Label, &item->UnderlineX, NULL);
278                 // Get the width of the underlined character
279                 // TODO: Fix for high UTF-8 characters
280                 item->Label[item->KeyOffset] = tmp;
281                 tmp = item->Label[item->KeyOffset+1];
282                 item->Label[item->KeyOffset+1] = '\0';
283                 WM_Render_GetTextDims(
284                         NULL, item->Label+item->KeyOffset,
285                         &item->UnderlineW, NULL
286                         );
287                 item->Label[item->KeyOffset+1] = tmp;
288         }
289         // - Labels
290         WM_Render_GetTextDims(NULL, item->Label, &item->LabelWidth, NULL);
291         if(item->Shortcut)
292                 WM_Render_GetTextDims(NULL, item->Shortcut, &item->ShortcutWidth, NULL);
293         else
294                 item->ShortcutWidth = 0;
295         
296         if( item->LabelWidth > info->MaxLabelWidth )
297                 info->MaxLabelWidth = item->LabelWidth;
298         if( item->ShortcutWidth > info->MaxShortcutWidth )
299                 info->MaxShortcutWidth = item->ShortcutWidth;
300         
301         if( info->MaxLabelWidth + info->MaxShortcutWidth + ciMenu_Gap > info->CachedW )
302         {
303                 info->CachedW = ciMenu_LeftPadding + info->MaxLabelWidth
304                         + ciMenu_Gap + info->MaxShortcutWidth
305                         + ciMenu_RightPadding;
306                 // TODO: Smarter height?
307                 WM_ResizeWindow(Window, info->CachedW, info->nItems*ciMenu_ItemHeight);
308         }
309         
310         return 0;
311 }
312
313 int Renderer_Menu_HandleMessage(tWindow *Window, int Msg, int Length, void *Data)
314 {
315         switch(Msg)
316         {
317         case WNDMSG_MOUSEMOVE: {
318                 tMenuWindowInfo *info = Window->RendererInfo;
319                 struct sWndMsg_MouseMove        *msg = Data;
320                  int    new_hilight;
321
322                 if(Length < sizeof(*msg))       return -1;
323
324                 if( msg->X < 0 || msg->X >= Window->W )
325                 {
326                         new_hilight = -1;
327                 }
328                 else
329                 {
330                          int    i, y;
331                         y = msg->Y;
332                         new_hilight = -1;
333                         for( i = 0; i < info->nItems; i ++ )
334                         {
335                                 if( !info->Items[i] )   continue;
336                                         
337                                 if( !info->Items[i]->Label )
338                                 {
339                                         // Spacer - doesn't hilight
340                                         if(y < ciMenu_SpacerHeight) {
341                                                 new_hilight = -1;
342                                                 break;
343                                         }
344                                         y -= ciMenu_SpacerHeight;
345                                 }
346                                 else
347                                 {
348                                         // Normal item, set the hilight
349                                         if(y < ciMenu_ItemHeight) {
350                                                 new_hilight = i;
351                                                 break;
352                                         }
353                                         y -= ciMenu_ItemHeight;
354                                 }
355                         }
356                 }
357
358                 if( new_hilight != info->HilightedItem )
359                 {
360                         info->HilightedItem = new_hilight;
361                         WM_Invalidate(Window);
362                 }
363
364                 return 0; }
365
366         // Manipulation messages
367         case MSG_MENU_ADDITEM:
368                 _SysDebug("MSG_MENU_ADDITEM");
369                 return Renderer_Menu_int_AddItem(Window, Length, Data);
370         
371         // Only message to pass to client
372         case MSG_MENU_SELECT:
373                 return 1;
374         }
375         return 0;
376 }
377

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