45c6a241b843fe5613d126697a75e00c6a710472
[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         ret->Flags |= WINFLAG_NODECORATE;
97         ret->H = ciMenu_TopPadding + ciMenu_BottomPadding;
98
99 //      _SysDebug("Renderer_Menu_Create: ->MaxItems = %i", info->MaxItems);
100         
101         return ret;
102 }
103
104 void Renderer_Menu_Redraw(tWindow *Window)
105 {
106         tMenuWindowInfo *info = Window->RendererInfo;
107          int    w, h, y, i;
108
109         w = info->CachedW;
110         #if 0
111         h = ciMenu_TopPadding + ciMenu_BottomPadding;
112         for( i = 0; i < info->nItems; i ++ )
113         {
114                 if( !info->Items[i] )   continue;
115                 
116                 if(info->Items[i]->Label)
117                         h += ciMenu_ItemHeight;
118                 else
119                         h += ciMenu_SpacerHeight;
120         }
121         #else
122         h = Window->H;
123         #endif
124
125 //      _SysDebug("w = %i, h = %i", w, h);
126
127         // - Move the window such that it is on screen
128         //  > Make sure to catch if the menu can't fit fully onscreen
129
130         // - Clear
131         WM_Render_FillRect(Window, 0, 0, w, h, cMenu_BackgroundColour);
132         WM_Render_DrawRect(Window, 0, 0, w, h, cMenu_BorderColour);
133
134         // - Render each item
135         y = ciMenu_TopPadding;
136         for( i = 0; i < info->nItems; i ++ )
137         {
138                 tMenuItem       *item = info->Items[i];
139                 
140                 // Unused slot
141                 if(!item)       continue;
142                 
143                 // Spacer
144                 if(!item->Label)
145                 {
146                         WM_Render_FillRect(Window,
147                                 1, y + ciMenu_SpacerHeight/2,
148                                 w-2, 1,
149                                 cMenu_SpacerColour
150                                 );
151                         y += ciMenu_SpacerHeight;
152                         continue ;
153                 }
154         
155                 // Hilight
156                 if( info->HilightedItem == i )
157                 {
158                         WM_Render_FillRect(Window,
159                                 1, y,
160                                 w-2, ciMenu_ItemHeight,
161                                 cMenu_HilightColour
162                                 );
163                 }
164         
165                 // Text
166                 WM_Render_DrawText(Window,
167                         ciMenu_LeftPadding, y,
168                         w, ciMenu_ItemHeight,
169                         gMenu_Font,
170                         cMenu_LabelColour,
171                         item->Label
172                         );
173                 // Underline
174                 if(item->UnderlineW)
175                 {
176                         WM_Render_FillRect(Window,
177                                 ciMenu_LeftPadding + item->UnderlineX, y + ciMenu_FontHeight,
178                                 item->UnderlineW, 1,
179                                 cMenu_LabelColour
180                                 );
181                 }
182                 
183                 // Shortcut key
184                 if(item->Shortcut)
185                 {
186                         WM_Render_DrawText(Window,
187                                 w - item->ShortcutWidth - ciMenu_RightPadding, y,
188                                 w, ciMenu_ItemHeight,
189                                 gMenu_Font,
190                                 cMenu_ShortcutColour,
191                                 item->Shortcut
192                                 );
193                 }
194                 
195                 y += ciMenu_ItemHeight;
196         }
197 }
198
199 int Renderer_Menu_int_AddItem(tWindow *Window, int Length, void *Data)
200 {
201         tMenuWindowInfo *info = Window->RendererInfo;
202         tMenuMsg_AddItem        *req = Data;
203         tMenuItem       *item;
204         
205         // Sanity checking
206         // - Message length
207         if(Length < sizeof(*req) + 1 || req->Label[Length-sizeof(*req)-1] != '\0') {
208                 _SysDebug("Renderer_Menu_int_AddItem: Size checks failed");
209                 return -1;
210         }
211         // - ID Number
212         if(req->ID >= info->MaxItems) {
213                 _SysDebug("Renderer_Menu_int_AddItem: ID (%i) >= MaxItems (%i)",
214                         req->ID, info->MaxItems);
215                 return -1;
216         }
217         
218         // Don't overwrite
219         if(info->Items[req->ID]) {
220                 _SysDebug("- Caught overwrite of %i", req->ID);
221                 return 0;
222         }
223         // Bookkeeping
224         if(req->ID >= info->nItems)     info->nItems = req->ID + 1;
225         // Allocate
226         item = malloc(sizeof(tMenuItem)+strlen(req->Label));
227         info->Items[req->ID] = item;
228         
229         if(req->Label[0] == '\0')
230         {
231                 // Spacer
232                 item->Label = NULL;
233                 WM_ResizeWindow(Window, info->CachedW, Window->H+ciMenu_SpacerHeight);
234                 
235                 return 0;
236         }
237         
238         // Actual item
239         char    *dest = item->Data;
240         char    *src = req->Label;
241          int    ofs = 0;
242
243         // - Main label
244         item->KeyOffset = -1;
245         item->Label = dest;
246         for(ofs = 0; *src && *src != '\t'; ofs ++)
247         {
248                 if(*src == '&') {
249                         *dest = '\0';
250                         item->KeyOffset = ofs;
251                         src ++;
252                 }
253                 else {
254                         *dest++ = *src++;
255                 }
256         }
257         *dest++ = '\0';
258         // - Key combo / Shortcut
259         if(*src)
260         {
261                 src ++;
262                 item->Shortcut = dest;
263                 strcpy(item->Shortcut, src);
264         }
265         else
266         {
267                 item->Shortcut = NULL;
268         }
269         
270         // Get dimensions
271         // - Underline (hotkey)
272         if(item->KeyOffset == -1)
273         {
274                 item->UnderlineX = 0;
275                 item->UnderlineW = 0;
276         }
277         else
278         {
279                 char    tmp = item->Label[item->KeyOffset];
280                 // Get width of preceding substring
281                 item->Label[item->KeyOffset] = '\0';
282                 WM_Render_GetTextDims(NULL, item->Label, &item->UnderlineX, NULL);
283                 // Get the width of the underlined character
284                 // TODO: Fix for high UTF-8 characters
285                 item->Label[item->KeyOffset] = tmp;
286                 tmp = item->Label[item->KeyOffset+1];
287                 item->Label[item->KeyOffset+1] = '\0';
288                 WM_Render_GetTextDims(
289                         NULL, item->Label+item->KeyOffset,
290                         &item->UnderlineW, NULL
291                         );
292                 item->Label[item->KeyOffset+1] = tmp;
293         }
294         // - Labels
295         WM_Render_GetTextDims(NULL, item->Label, &item->LabelWidth, NULL);
296         if(item->Shortcut)
297                 WM_Render_GetTextDims(NULL, item->Shortcut, &item->ShortcutWidth, NULL);
298         else
299                 item->ShortcutWidth = 0;
300         
301         if( item->LabelWidth > info->MaxLabelWidth )
302                 info->MaxLabelWidth = item->LabelWidth;
303         if( item->ShortcutWidth > info->MaxShortcutWidth )
304                 info->MaxShortcutWidth = item->ShortcutWidth;
305         
306         if( info->MaxLabelWidth + info->MaxShortcutWidth + ciMenu_Gap > info->CachedW )
307         {
308                 info->CachedW = ciMenu_LeftPadding + info->MaxLabelWidth
309                         + ciMenu_Gap + info->MaxShortcutWidth
310                         + ciMenu_RightPadding;
311         }
312         WM_ResizeWindow(Window, info->CachedW, Window->H+ciMenu_ItemHeight);
313         
314         return 0;
315 }
316
317 int Renderer_Menu_int_GetItemByPos(tWindow *Window, tMenuWindowInfo *Info, int X, int Y)
318 {
319          int    i;
320
321         if( X < 0 || X >= Window->W )
322                 return -1;
323         
324         for( i = 0; i < Info->nItems; i ++ )
325         {
326                 if( !Info->Items[i] )   continue;
327                         
328                 if( !Info->Items[i]->Label )
329                 {
330                         // Spacer - not selectable
331                         if(Y < ciMenu_SpacerHeight) {
332                                 return -1;
333                         }
334                         Y -= ciMenu_SpacerHeight;
335                 }
336                 else
337                 {
338                         // Normal item, can be selected/hilighted
339                         if(Y < ciMenu_ItemHeight) {
340                                 return i;
341                         }
342                         Y -= ciMenu_ItemHeight;
343                 }
344         }
345         return -1;
346 }
347
348 int Renderer_Menu_HandleMessage(tWindow *Window, int Msg, int Length, void *Data)
349 {
350         tMenuWindowInfo *info = Window->RendererInfo;
351         switch(Msg)
352         {
353         case WNDMSG_SHOW: {
354                 struct sWndMsg_Bool     *msg = Data;
355                 if(Length < sizeof(*msg))       return -1;
356                 if(msg->Val)
357                 {
358 //                      _SysDebug(" - Shown, take focus");
359                         // TODO: This shouldn't really be done, instead focus should be given
360                         //       when the menu is shown.
361 //                      WM_FocusWindow(Window);
362                 }
363                 else
364                 {
365                         // Hide Children
366                         _SysDebug("- Hidden, hide the children!");
367                 }
368                 return 0; }
369         case WNDMSG_FOCUS: {
370                 struct sWndMsg_Bool     *msg = Data;
371                 if(Length < sizeof(*msg))       return -1;
372                 if(!msg->Val) {
373                         // TODO: Catch if focus was given away to a child
374                         _SysDebug("- Lost focus");
375                         WM_ShowWindow(Window, 0);       // Hide!
376                 }
377                 else {
378                         _SysDebug("- Focus gained, TODO: Show accel keys");
379                 }
380                 return 0; }
381
382         case WNDMSG_MOUSEBTN: {
383                 struct sWndMsg_MouseButton      *msg = Data;
384                  int    item;
385                 
386                 if(Length < sizeof(*msg))       return -1;
387
388                 if(msg->Button == 0 && msg->bPressed == 0)
389                 {
390                         item = Renderer_Menu_int_GetItemByPos(Window, info, msg->X, msg->Y);
391                         if(item != -1)
392                         {
393                                 tMenuMsg_Select _msg;
394                                 // TODO: Ignore sub-menus too
395                                 _msg.ID = item;
396                                 WM_SendMessage(Window, Window, MSG_MENU_SELECT, sizeof(_msg), &_msg);
397                                 WM_ShowWindow(Window, 0);
398                         }
399                 }
400                                 
401
402                 return 0; }     
403
404         case WNDMSG_MOUSEMOVE: {
405                 struct sWndMsg_MouseMove        *msg = Data;
406                  int    new_hilight;
407
408                 if(Length < sizeof(*msg))       return -1;
409
410                 new_hilight = Renderer_Menu_int_GetItemByPos(Window, info, msg->X, msg->Y);
411
412                 if( new_hilight != info->HilightedItem )
413                 {
414                         info->HilightedItem = new_hilight;
415                         // TODO: Change sub-menu
416                         WM_Invalidate(Window);
417                 }
418
419                 return 0; }
420
421         // Manipulation messages
422         case MSG_MENU_ADDITEM:
423 //              _SysDebug("MSG_MENU_ADDITEM");
424                 return Renderer_Menu_int_AddItem(Window, Length, Data);
425         
426         // Only message to pass to client
427         case MSG_MENU_SELECT:
428                 return 1;
429         }
430         return 0;
431 }
432

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