swell-cocoa: support GWL_USERDATA on combo/popup boxes -- from fee287f5
[wdl/wdl-tale.git] / WDL / swell / swell-wnd.mm
blob178dbe752b1786415412fb19d4b1288184b9eba2
1 /* Cockos SWELL (Simple/Small Win32 Emulation Layer for Linux/OSX)
2    Copyright (C) 2006 and later, Cockos, Inc.
4     This software is provided 'as-is', without any express or implied
5     warranty.  In no event will the authors be held liable for any damages
6     arising from the use of this software.
8     Permission is granted to anyone to use this software for any purpose,
9     including commercial applications, and to alter it and redistribute it
10     freely, subject to the following restrictions:
12     1. The origin of this software must not be misrepresented; you must not
13        claim that you wrote the original software. If you use this software
14        in a product, an acknowledgment in the product documentation would be
15        appreciated but is not required.
16     2. Altered source versions must be plainly marked as such, and must not be
17        misrepresented as being the original software.
18     3. This notice may not be removed or altered from any source distribution.
19   
21     This file provides basic windows APIs for handling windows, as well as the stubs to enable swell-dlggen to work.
23   */
26 #ifndef SWELL_PROVIDED_BY_APP
28 #import <Cocoa/Cocoa.h>
29 #import <objc/objc-runtime.h>
30 #include "swell.h"
31 #include "../mutex.h"
32 #include "../ptrlist.h"
33 #include "../queue.h"
34 #include "../wdlcstring.h"
36 #include "swell-dlggen.h"
38 #define SWELL_INTERNAL_MERGESORT_IMPL
39 #define SWELL_INTERNAL_HTREEITEM_IMPL
40 #include "swell-internal.h"
42 static bool SWELL_NeedModernListViewHacks()
44 #ifdef __LP64__
45   return false;
46 #else
47   // only needed on 32 bit yosemite as of 10.10.3, but who knows when it will be necessary elsewhere
48   return SWELL_GetOSXVersion() >= 0x10a0;
49 #endif
52 static LRESULT sendSwellMessage(id obj, UINT uMsg, WPARAM wParam, LPARAM lParam)
54   if (obj && [obj respondsToSelector:@selector(onSwellMessage:p1:p2:)])
55     return [(SWELL_hwndChild *)obj onSwellMessage:uMsg p1:wParam p2:lParam];
56   return 0;
58 static void InvalidateSuperViews(NSView *view);
59 #define STANDARD_CONTROL_NEEDSDISPLAY_IMPL(classname) \
60   - (const char *)getSwellClass { return ( classname ); } \
61   - (void)setNeedsDisplay:(BOOL)flag \
62   { \
63   [super setNeedsDisplay:flag]; \
64   if (flag) InvalidateSuperViews(self); \
65   } \
66   - (void)setNeedsDisplayInRect:(NSRect)rect \
67   { \
68   [super setNeedsDisplayInRect:rect]; \
69   InvalidateSuperViews(self); \
70   }
73 static WDL_PtrList<char> s_prefix_removals;
75 int g_swell_osx_readonlytext_wndbg = 0;
76 int g_swell_osx_style = 0; // &1 = rounded buttons, &2=big sur styled lists
77 static void *SWELL_CStringToCFString_FilterPrefix(const char *str)
79   int c=0;
80   while (str[c] && str[c] != '&' && c++<1024);
81   if (!str[c] || c>=1024 || strlen(str)>=1024) return SWELL_CStringToCFString(str);
82   char buf[1500];
83   {
84     const char *p=str;
85     char *op=buf;
86     while (*p)
87     {
88       if (*p == '&')  p++;
89       if (!*p) break;
90       *op++=*p++;
91     }
92     *op=0;
93   }
95   // add to recent prefix removal cache for localization
96   if (WDL_NOT_NORMALLY(s_prefix_removals.GetSize() > 256))
97     s_prefix_removals.Delete(0,true,free);
99   {
100     const size_t sz1 = strlen(buf), sz2 = strlen(str);
101     char *p = (char *)malloc(sz1+sz2+2);
102     if (WDL_NORMALLY(p!=NULL))
103     {
104       memcpy(p,buf,sz1+1);
105       memcpy(p+sz1+1,str,sz2+1);
106       s_prefix_removals.Add(p);
107     }
108   }
110   return SWELL_CStringToCFString(buf);
114 static int _nsStringSearchProc(const void *_a, const void *_b)
116   NSString *a=(NSString *)_a;
117   NSString *b = (NSString *)_b;
118   return (int)[a compare:b];
120 static int _nsMenuSearchProc(const void *_a, const void *_b)
122   NSString *a=(NSString *)_a;
123   NSMenuItem *b = (NSMenuItem *)_b;
124   return (int)[a compare:[b title]];
126 static int _listviewrowSearchFunc(const void *_a, const void *_b, const void *ctx)
128   const char *a = (const char *)_a;
129   SWELL_ListView_Row *row = (SWELL_ListView_Row *)_b;
130   const char *b="";
131   if (!row || !(b=row->get_col_txt(0))) b="";
132   return strcmp(a,b);
134 static int _listviewrowSearchFunc2(const void *_a, const void *_b, const void *ctx)
136   const char *a = (const char *)_a;
137   SWELL_ListView_Row *row = (SWELL_ListView_Row *)_b;
138   const char *b="";
139   if (!row || !(b=row->get_col_txt(0))) b="";
140   return strcmp(b,a);
143 // modified bsearch: returns place item SHOULD be in if it's not found
144 static NSInteger arr_bsearch_mod(void *key, NSArray *arr, int (*compar)(const void *, const void *))
146   const NSInteger nmemb = [arr count];
147   NSInteger p,lim,base=0;
148   
149         for (lim = nmemb; lim != 0; lim >>= 1) {
150                 p = base + (lim >> 1);
151                 int cmp = compar(key, [arr objectAtIndex:p]);
152                 if (cmp == 0) return (p);
153                 if (cmp > 0) {  /* key > p: move right */
154       // check to see if key is less than p+1, if it is, we're done
155                         base = p + 1;
156       if (base >= nmemb || compar(key,[arr objectAtIndex:base])<=0) return base;
157                         lim--;
158                 } /* else move left */
159         }
160         return 0;
164 template<class T> static int ptrlist_bsearch_mod(void *key, WDL_PtrList<T> *arr, int (*compar)(const void *, const void *, const void *ctx), void *ctx)
166   const int nmemb = arr->GetSize();
167   int base=0, lim, p;
168   
169         for (lim = nmemb; lim != 0; lim >>= 1) {
170                 p = base + (lim >> 1);
171                 int cmp = compar(key, arr->Get(p),ctx);
172                 if (cmp == 0) return (p);
173                 if (cmp > 0) {  /* key > p: move right */
174       // check to see if key is less than p+1, if it is, we're done
175                         base = p + 1;
176       if (base >= nmemb || compar(key,arr->Get(base),ctx)<=0) return base;
177                         lim--;
178                 } /* else move left */
179         }
180         return 0;
184 @implementation SWELL_TabView
185 STANDARD_CONTROL_NEEDSDISPLAY_IMPL("SysTabControl32")
187 -(void)setNotificationWindow:(id)dest
189   m_dest=dest;
191 -(id)getNotificationWindow
193   return m_dest;
195 -(NSInteger) tag
197   return m_tag;
199 -(void) setTag:(NSInteger)tag
201   m_tag=tag;
203 - (void)tabView:(NSTabView *)tabView didSelectTabViewItem:(NSTabViewItem *)tabViewItem
205   if (m_dest)
206   {
207     NMHDR nm={(HWND)self,(UINT_PTR)[self tag],TCN_SELCHANGE};
208     SendMessage((HWND)m_dest,WM_NOTIFY,nm.idFrom,(LPARAM)&nm);
209   }
211 @end
214 @implementation SWELL_ProgressView
215 STANDARD_CONTROL_NEEDSDISPLAY_IMPL("msctls_progress32")
217 -(NSInteger) tag
219   return m_tag;
221 -(void) setTag:(NSInteger)tag
223   m_tag=tag;
225 -(LRESULT)onSwellMessage:(UINT)msg p1:(WPARAM)wParam p2:(LPARAM)lParam
227   if (msg == PBM_SETRANGE)
228   {
229     [self setMinValue:LOWORD(lParam)];
230     [self setMaxValue:HIWORD(lParam)];
231   }
232   else if (msg==PBM_SETPOS)
233   {
234     [self setDoubleValue:(double)wParam];
235     [self stopAnimation:self];    
236   }
237   else if (msg==PBM_DELTAPOS)
238   {
239     [self incrementBy:(double)wParam];
240   }
241   return 0;
244 @end
246 @implementation SWELL_ListViewCell
247 -(NSColor *)highlightColorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView 
249   if ([controlView isKindOfClass:[SWELL_ListView class]])
250   {
251     if (((SWELL_ListView *)controlView)->m_selColors) return nil;
252   }
253   else if ([controlView isKindOfClass:[SWELL_TreeView class]])
254   {
255     if (((SWELL_TreeView *)controlView)->m_selColors) return nil;
256   }
258   return [super highlightColorWithFrame:cellFrame inView:controlView];
260 - (NSRect)drawingRectForBounds:(NSRect)rect
262   const NSSize sz = [self cellSizeForBounds:rect];
263   rect = [super drawingRectForBounds:rect];
264   const int offs = (int) floor((rect.size.height - sz.height) * .5);
265   if (offs>0)
266   {
267     rect.origin.y += offs;
268     rect.size.height -= offs*2;
269   }
270   return rect;
272 @end
274 @implementation SWELL_StatusCell
275 -(id)initNewCell:(bool)always_indent
277   if ((self=[super initTextCell:@""]))
278   {
279     m_always_indent=always_indent;
280     status=0;
281   }
282   return self;
284 -(void)setStatusImage:(NSImage *)img
286   status=img;
288 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
290   if (status)
291   {
292     const double fr_h = cellFrame.size.height;
293     const NSSize image_sz = [status size];
294     const double use_h = wdl_min(image_sz.height, fr_h);
295     const double yo = (fr_h-use_h)*.5;
296     double use_w,xo;
297     if (use_h > cellFrame.size.width)
298     {
299       use_w = cellFrame.size.width;
300       xo = 0.0;
301     }
302     else
303     {
304       use_w = use_h;
305       xo = yo;
306     }
308     [status drawInRect:NSMakeRect(cellFrame.origin.x + xo,cellFrame.origin.y + yo,use_w,use_h)
309       fromRect:NSZeroRect operation:NSCompositeSourceOver fraction:1.0];
310   }
311   if (m_always_indent || status)
312   {
313     cellFrame.origin.x += cellFrame.size.height + 2.0;
314     cellFrame.size.width -= cellFrame.size.height + 2.0;
315   }
316   [super drawWithFrame:cellFrame inView:controlView];
319 @end
321 static HTREEITEM FindTreeItemByDataHold(const WDL_PtrList<HTREEITEM__> *list, SWELL_DataHold *srch)
323   for (int x = 0; x < list->GetSize(); x ++)
324   {
325     HTREEITEM item = list->Get(x);
326     if (item && item->m_dh == srch) return item;
327   }
328   for (int x = 0; x < list->GetSize(); x ++)
329   {
330     HTREEITEM item = list->Get(x);
331     if (item && item->m_children.GetSize())
332     {
333       item = FindTreeItemByDataHold(&item->m_children,srch);
334       if (item) return item;
335     }
336   }
337   return NULL;
340 @implementation SWELL_TreeView
341 STANDARD_CONTROL_NEEDSDISPLAY_IMPL("SysTreeView32")
343 -(id) init
345   if ((self = [super init]))
346   {
347     m_fakerightmouse=false;
348     m_items=new WDL_PtrList<HTREEITEM__>;
349     m_fgColor=0;
350     m_selColors=0;
351   }
352   return self;
354 -(void) dealloc
356   if (m_items) m_items->Empty(true);
357   delete m_items;
358   m_items=0;
359   [m_fgColor release];
360   [m_selColors release];
361   [super dealloc];
364 -(bool) findItem:(HTREEITEM)item parOut:(HTREEITEM__ **)par idxOut:(int *)idx
366   if (!m_items||!item) return false;
367   int x=m_items->Find((HTREEITEM__*)item);
368   if (x>=0)
369   {
370     *par=NULL;
371     *idx=x;
372     return true;
373   }
374   for (x = 0; x < m_items->GetSize(); x++)
375   {
376     if (m_items->Get(x)->FindItem(item,par,idx)) return true;
377   }
379   return false;
382 -(NSInteger) outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
384   if (item == nil) return m_items ? m_items->GetSize() : 0;
385   return ((HTREEITEM__*)[item getValue])->m_children.GetSize();
388 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
390   if (item==nil) return YES;
391   HTREEITEM__ *it=(HTREEITEM__ *)[item getValue];
392   
393   return it && it->m_haschildren;
396 - (id)outlineView:(NSOutlineView *)outlineView
397             child:(NSInteger)index
398            ofItem:(id)item
400   HTREEITEM__ *row=item ? ((HTREEITEM__*)[item getValue])->m_children.Get(index) : m_items ? m_items->Get(index) : 0;
402   return (id)(row ? row->m_dh : NULL);
405 - (id)outlineView:(NSOutlineView *)outlineView
406     objectValueForTableColumn:(NSTableColumn *)tableColumn
407            byItem:(id)item
409   if (!item) return @"";
410   HTREEITEM__ *it=(HTREEITEM__ *)[item getValue];
411   
412   if (!it || !it->m_value) return @"";
414   NSString *str=(NSString *)SWELL_CStringToCFString(it->m_value);    
415   
416   return [str autorelease];
419 - (BOOL)outlineView:(NSOutlineView *)outlineView
420          writeItems:(NSArray *)items
421        toPasteboard:(NSPasteboard *)pasteboard
423   if (self->style & TVS_DISABLEDRAGDROP) return NO;
424   [pasteboard declareTypes:[NSArray arrayWithObject:@"swell_treeview"] owner:nil];
425   [pasteboard setString:@"" forType:@"swell_treeview"];
426   return YES;
429 - (BOOL)outlineView:(NSOutlineView *)outlineView
430          acceptDrop:(id<NSDraggingInfo>)info
431                item:(id)item
432          childIndex:(NSInteger)index
434   HWND par = GetParent((HWND)self);
435   if (par && GetCapture() == par)
436   {
437     POINT p;
438     GetCursorPos(&p);
439     ScreenToClient(par,&p);
440     SendMessage(par,WM_LBUTTONUP,0,MAKELPARAM(p.x,p.y));
441   }
442   return YES;
446 - (BOOL)outlineView:(NSOutlineView *)outlineView
447    shouldExpandItem:(id)item
449   return NO; // optionally while dragging?
453 - (void)outlineView:(NSOutlineView *)outlineView
454     draggingSession:(NSDraggingSession *)session
455        endedAtPoint:(NSPoint)screenPoint
456           operation:(NSDragOperation)operation
458   [self unregisterDraggedTypes];
459   HWND par = GetParent((HWND)self);
460   if (par && GetCapture() == par)
461   {
462     // usually acceptDrop above will be the one that is called, but if the user ended up elsewhere
463     // this might, let the caller clean up capture
464     POINT p;
465     GetCursorPos(&p);
466     ScreenToClient(par,&p);
467     SendMessage(par,WM_LBUTTONUP,0,MAKELPARAM(p.x,p.y));
468   }
471 - (void)outlineView:(NSOutlineView *)outlineView
472     draggingSession:(NSDraggingSession *)session
473    willBeginAtPoint:(NSPoint)screenPoint
474            forItems:(NSArray *)draggedItems
476   if (self->style & TVS_DISABLEDRAGDROP) return;
477   HWND hwnd = (HWND)self, par = GetParent(hwnd);
478   if (par)
479   {
480     HTREEITEM hit = NULL;
481     if (m_items && [draggedItems count] > 0)
482     {
483       id obj = [draggedItems objectAtIndex:0];
484       if ([obj isKindOfClass:[SWELL_DataHold class]])
485         hit = FindTreeItemByDataHold(m_items, (SWELL_DataHold *)obj);
486     }
487     if (!hit)
488     {
489       TVHITTESTINFO tht;
490       memset(&tht,0,sizeof(tht));
491       GetCursorPos(&tht.pt);
492       ScreenToClient(hwnd, &tht.pt);
493       hit = TreeView_HitTest(hwnd, &tht);
494     }
496     HTREEITEM sel = TreeView_GetSelection(hwnd);
497     if (hit && hit != sel) 
498     {
499       TreeView_SelectItem(hwnd,hit);
500       sel = hit;
501     }
503     NMTREEVIEW nm={{hwnd,(UINT_PTR)[self tag],TVN_BEGINDRAG},};
504     nm.itemNew.hItem = sel;
505     nm.itemNew.lParam = sel ? sel->m_param : 0;
506     SendMessage(par,WM_NOTIFY,nm.hdr.idFrom,(LPARAM)&nm);
507     if (GetCapture() == par)
508       [self registerForDraggedTypes:[NSArray arrayWithObject: @"swell_treeview"]];
509   }
512 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView
513                   validateDrop:(id<NSDraggingInfo>)info
514                   proposedItem:(id)item
515             proposedChildIndex:(NSInteger)index
517   HWND hwnd=(HWND)self, par = GetParent(hwnd);
518   if (par && GetCapture()==par)
519   {
520     POINT p;
521     GetCursorPos(&p);
522     TVHITTESTINFO tht;
523     memset(&tht,0,sizeof(tht));
524     tht.pt = p;
526     ScreenToClient(par,&p);
527     LRESULT move_res = SendMessage(par,WM_MOUSEMOVE,0,MAKELPARAM(p.x,p.y));
528     if (move_res == (LRESULT)-1) return NSDragOperationNone;
529     if (move_res == (LRESULT)-2) // move to end
530     {
531       HTREEITEM par_item = NULL;
532       HTREEITEM li = self->m_items ? self->m_items->Get(self->m_items->GetSize()-1) : NULL;
533       while (li && li->m_children.GetSize())
534       {
535         par_item = li;
536         li = li->m_children.Get(li->m_children.GetSize()-1);
537       }
538       if (par_item && par_item->m_children.GetSize()) [self setDropItem:par_item->m_dh dropChildIndex:par_item->m_children.GetSize()];
539     }
540     else if (move_res >= 65536)
541     {
542       HTREEITEM paritem = NULL;
543       int idx=0;
544       // it is safe (but time consuming!) to call findItem: on a possibly-junk pointer
545       if ([self findItem:(HTREEITEM)(INT_PTR)move_res parOut:&paritem idxOut:&idx] && paritem)
546         [self setDropItem:paritem->m_dh dropChildIndex:idx];
547     }
548     return NSDragOperationPrivate;
549   }
550   return NSDragOperationNone;
554 -(void)mouseDown:(NSEvent *)theEvent
556   if (([theEvent modifierFlags] & NSControlKeyMask) && IsRightClickEmulateEnabled())
557   {
558     m_fakerightmouse=1;  
559   }
560   else 
561   {
562     
563     NMCLICK nmlv={{(HWND)self,(UINT_PTR)[self tag], NM_CLICK},};
564     SendMessage((HWND)[self target],WM_NOTIFY,nmlv.hdr.idFrom,(LPARAM)&nmlv);
565     
566     m_fakerightmouse=0;
567     [super mouseDown:theEvent];
568   }
571 -(void)mouseDragged:(NSEvent *)theEvent
575 -(void)mouseUp:(NSEvent *)theEvent
576 {   
577   if (m_fakerightmouse||([theEvent modifierFlags] & NSControlKeyMask)) [self rightMouseUp:theEvent];
578   else [super mouseUp:theEvent];
580 - (void)rightMouseUp:(NSEvent *)theEvent
582   bool wantContext=true;
584   NMCLICK nmlv={{(HWND)self,(UINT_PTR)[self tag], NM_RCLICK},};
585   if (SendMessage((HWND)[self target],WM_NOTIFY,nmlv.hdr.idFrom,(LPARAM)&nmlv)) wantContext=false;
586   
587   if (wantContext)
588   {
589     POINT p;
590     GetCursorPos(&p);
591     SendMessage((HWND)[self target],WM_CONTEXTMENU,(WPARAM)self,MAKELONG(p.x&0xffff,p.y));
592   }
593   
594   m_fakerightmouse=0;
597 - (void)highlightSelectionInClipRect:(NSRect)theClipRect
599   if (m_selColors)
600   {
601     int a = GetFocus() == (HWND)self ? 0 : 2;
602     if ([m_selColors count] >= a)
603     {
604       NSColor *c=[m_selColors objectAtIndex:a];
605       if (c)
606       {
607         // calculate rect of selected items, combine with theClipRect, and fill these areas with our background (phew!)
609         NSInteger x = [self selectedRow];
610         if (x>=0)
611         {
612           NSRect r = [self rectOfRow:x];
613           r = NSIntersectionRect(r,theClipRect);
614           if (r.size.height>0 && r.size.width>0)
615           {
616             [c setFill];      
617             NSRectFill(r);
618           }
619         }
620         return ;
621       }
622     }
623   }
624   return [super highlightSelectionInClipRect:theClipRect];
630 @end
636 @implementation SWELL_ListView
637 STANDARD_CONTROL_NEEDSDISPLAY_IMPL( m_lbMode ? "SysListView32_LB" : "SysListView32" )
639 -(BOOL)accessibilityPerformShowMenu
641   HWND par = (HWND)[self target];
642   if (par)
643   {
644     int row = (int)ListView_GetNextItem((HWND)self,-1,LVNI_FOCUSED);
645     int col = 0; // todo
646     NMLISTVIEW nmlv={{(HWND)self,(UINT_PTR)[self tag], NM_RCLICK}, row, col, 0, 0, 0, };
647     SendMessage(par,WM_NOTIFY,nmlv.hdr.idFrom,(LPARAM)&nmlv);
648     return YES;
649   }
650   return NO;
653 -(LONG)getSwellStyle { return style; }
655 -(void)setSwellStyle:(LONG)st 
657   bool hdrchg= ((style&LVS_NOCOLUMNHEADER) != (st&LVS_NOCOLUMNHEADER));
658   style=st;
659   if ((style&LVS_REPORT) && hdrchg)
660   {
661     // todo some crap with NSTableView::setHeaderView, but it's complicated
662   }
665 -(id) init
667   if ((self = [super init]))
668   {
669     m_subitem_images = false;
670     m_selColors=0;
671     m_fgColor = 0;
672     ownermode_cnt=0;
673     m_status_imagelist_type=-1;
674     m_status_imagelist=0;
675     m_leftmousemovecnt=0;
676     m_fakerightmouse=false;
677     m_lbMode=0;
678     m_fastClickMask=0;
679     m_last_shift_clicked_item = m_last_plainly_clicked_item=-1;
680     m_start_item=-1;
681     m_start_subitem=-1;
682     m_start_item_clickmode=0; // 0=clicked item, 1=clicked image, &2=sent drag message, &4=quickclick mode
683     m_cols = new WDL_PtrList<NSTableColumn>;
684     m_items=new WDL_PtrList<SWELL_ListView_Row>;
685   }
686   return self;
688 -(void) dealloc
690   if (m_items) m_items->Empty(true);
691   delete m_items;
692   delete m_cols;
693   m_cols=0;
694   m_items=0;
695   [m_fgColor release];
696   [m_selColors release];
697   [super dealloc];
700 -(int)getColumnPos:(int)idx // get current position of column that was originally at idx
702   int pos=idx;
703   if (m_cols)
704   {
705     NSTableColumn* col=m_cols->Get(idx);
706     if (col)
707     {
708       NSArray* arr=[self tableColumns];
709       if (arr)
710       {
711         pos=(int)[arr indexOfObject:col];
712       }
713     }
714   }
715   return pos;
718 - (void)highlightSelectionInClipRect:(NSRect)theClipRect
720   if (m_selColors)
721   {
722     int a = GetFocus() == (HWND)self ? 0 : 2;
723     if ([m_selColors count] >= a)
724     {
725       NSColor *c=[m_selColors objectAtIndex:a];
726       if (c)
727       {
728         // calculate rect of selected items, combine with theClipRect, and fill these areas with our background (phew!)
729         bool needfillset=true;
730         NSInteger x = [self rowAtPoint:NSMakePoint(0,theClipRect.origin.y)];
731         if (x<0)x=0;
732         const NSInteger n = [self numberOfRows];
733         for (;x <n;x++)
734         {
735           NSRect r = [self rectOfRow:x];
736           if (r.origin.y >= theClipRect.origin.y + theClipRect.size.height) break;
737           
738           if ([self isRowSelected:x])
739           {
740             r = NSIntersectionRect(r,theClipRect);
741             if (r.size.height>0 && r.size.width>0)
742             {
743               if (needfillset) { needfillset=false; [c setFill]; }
744               NSRectFill(r);
745             }
746           }
747         }
748         return ;
749       }
750     }
751   }
752   return [super highlightSelectionInClipRect:theClipRect];
754 -(int)getColumnIdx:(int)pos // get original index of column that is currently at position
756   int idx=pos;
757   NSArray* arr=[self tableColumns];
758   if (arr && pos>=0 && pos < [arr count])
759   {
760     NSTableColumn* col=[arr objectAtIndex:pos];
761     if (col && m_cols)
762     {
763       idx=m_cols->Find(col);
764     }
765   }
766   return idx;
769 -(NSInteger)columnAtPoint:(NSPoint)pt
771   int pos=(int)[super columnAtPoint:pt];
772   return (NSInteger) [self getColumnIdx:pos];
776 - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
778   return (!m_lbMode && (style & LVS_OWNERDATA)) ? ownermode_cnt : (m_items ? m_items->GetSize():0);
781 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
783   NSString *str=NULL;
784   int image_idx=0;
785   
786   if (!m_lbMode && (style & LVS_OWNERDATA))
787   {
788     HWND tgt=(HWND)[self target];
790     char buf[1024];
791     NMLVDISPINFO nm={{(HWND)self, (UINT_PTR)[self tag], LVN_GETDISPINFO}};
792     nm.item.mask=LVIF_TEXT;
793     if (m_status_imagelist_type==LVSIL_STATE) nm.item.mask |= LVIF_STATE;
794     else if (m_status_imagelist_type == LVSIL_SMALL) nm.item.mask |= LVIF_IMAGE;
795     nm.item.iImage = -1;
796     nm.item.iItem=(int)rowIndex;
797     nm.item.iSubItem=m_cols->Find(aTableColumn);
798     nm.item.pszText=buf;
799     nm.item.cchTextMax=sizeof(buf)-1;
800     buf[0]=0;
801     SendMessage(tgt,WM_NOTIFY,nm.hdr.idFrom,(LPARAM)&nm);
802     
803     if (m_status_imagelist_type == LVSIL_STATE) image_idx=(nm.item.state>>16)&0xff;
804     else if (m_status_imagelist_type == LVSIL_SMALL) image_idx = nm.item.iImage + 1;
805     str=(NSString *)SWELL_CStringToCFString(nm.item.pszText); 
806   }
807   else
808   {
809     const char *p=NULL;
810     SWELL_ListView_Row *r=0;
811     if (m_items && m_cols && (r=m_items->Get(rowIndex))) 
812     {
813       int col_idx = m_cols->Find(aTableColumn);
814       p=r->get_col_txt(col_idx);
815       if (m_status_imagelist_type == LVSIL_STATE || m_status_imagelist_type == LVSIL_SMALL)
816       {
817         if (col_idx==0 || m_subitem_images)
818           image_idx=r->get_img_idx(col_idx);
819       }
820     }
821     
822     str=(NSString *)SWELL_CStringToCFString(p);    
823     
824     if (style & LBS_OWNERDRAWFIXED)
825     {
826       SWELL_ODListViewCell *cell=[aTableColumn dataCell];
827       if ([cell isKindOfClass:[SWELL_ODListViewCell class]]) [cell setItemIdx:(int)rowIndex];
828     }
829   }
830   
831   if (!m_lbMode && m_status_imagelist)
832   {
833     SWELL_StatusCell *cell=(SWELL_StatusCell*)[aTableColumn dataCell];
834     if ([cell isKindOfClass:[SWELL_StatusCell class]])
835     {
836       HICON icon=m_status_imagelist->Get(image_idx-1);      
837       NSImage *img=NULL;
838       if (icon)  img=(NSImage *)GetNSImageFromHICON(icon);
839       [cell setStatusImage:img];
840     }
841   }
842   
843   return [str autorelease];
848 -(void)mouseDown:(NSEvent *)theEvent
850   if (([theEvent modifierFlags] & NSControlKeyMask) && IsRightClickEmulateEnabled())
851   {
852     m_fakerightmouse=1;  
853     m_start_item=-1;
854     m_start_subitem=-1;
855   }
856   else 
857   {
858     if ([theEvent clickCount]>1 && SWELL_NeedModernListViewHacks())
859     {
860       [super mouseDown:theEvent];
861       return;
862     }
863     m_leftmousemovecnt=0;
864     m_fakerightmouse=0;
865     
866     NSPoint pt=[theEvent locationInWindow];
867     pt=[self convertPoint:pt fromView:nil];
868     m_start_item=(int)[self rowAtPoint:pt];
869     m_start_subitem=(int)[self columnAtPoint:pt];
870     
871     
872     
873     m_start_item_clickmode=0;
874     if (m_start_item >=0 && (m_fastClickMask&(1<<m_start_subitem)))
875     {
876       int msg = [theEvent clickCount] > 1 ? NM_DBLCLK : NM_CLICK;
877       NMLISTVIEW nmlv={{(HWND)self,(UINT_PTR)[self tag], msg}, m_start_item, m_start_subitem, 0, 0, 0, {NSPOINT_TO_INTS(pt)}, };
878       SWELL_ListView_Row *row=m_items->Get(nmlv.iItem);
879       if (row)
880         nmlv.lParam = row->m_param;
881       SendMessage((HWND)[self target],WM_NOTIFY,nmlv.hdr.idFrom,(LPARAM)&nmlv);
882       m_start_item_clickmode=4;
883     }
884     else
885     {
886       if (m_start_item>=0 && m_status_imagelist && LVSIL_STATE == m_status_imagelist_type && pt.x <= [self rowHeight]) // in left area
887       {
888         m_start_item_clickmode=1;
889       }
890     }
891   }
894 -(void)mouseDragged:(NSEvent *)theEvent
896   if (++m_leftmousemovecnt==4)
897   {
898     if (m_start_item>=0 && !(m_start_item_clickmode&3))
899     {
900       if (!m_lbMode)
901       {
902         // if m_start_item isnt selected, change selection to it now
903         if (!(m_start_item_clickmode&4) && ![self isRowSelected:m_start_item]) 
904         {
905           [self selectRowIndexes:[NSIndexSet indexSetWithIndex:m_start_item] byExtendingSelection:!!(GetAsyncKeyState(VK_CONTROL)&0x8000)];
906         }
907         NMLISTVIEW hdr={{(HWND)self,(UINT_PTR)[self tag],LVN_BEGINDRAG},m_start_item,m_start_subitem,0,};
908         SendMessage((HWND)[self target],WM_NOTIFY,hdr.hdr.idFrom, (LPARAM) &hdr);
909         m_start_item_clickmode |= 2;
910       }
911     }
912   }
913   else if (m_leftmousemovecnt > 4 && !(m_start_item_clickmode&1))
914   {
915     HWND tgt=(HWND)[self target];
916     POINT p;
917     GetCursorPos(&p);
918     ScreenToClient(tgt,&p);
919     
920     SendMessage(tgt,WM_MOUSEMOVE,0,MAKELONG(p.x&0xffff,p.y));
921   }
924 -(void)mouseUp:(NSEvent *)theEvent
926   if ((m_fakerightmouse||([theEvent modifierFlags] & NSControlKeyMask)) && IsRightClickEmulateEnabled())
927   {
928     [self rightMouseUp:theEvent];
929   }
930   else 
931   {
932     if ([theEvent clickCount]>1 && SWELL_NeedModernListViewHacks())
933     {
934       [super mouseUp:theEvent];
935       return;
936     }
937     if (!(m_start_item_clickmode&1))
938     {
939       if (m_leftmousemovecnt>=0 && m_leftmousemovecnt<4 && !(m_start_item_clickmode&4))
940       {
941         const bool msel = [self allowsMultipleSelection];
942         if (m_lbMode && !msel) // listboxes --- allow clicking to reset the selection
943         {
944           [self deselectAll:self];
945         }
947         if (SWELL_NeedModernListViewHacks())
948         {
949           if (m_start_item>=0)
950           {
951             NSMutableIndexSet *m = [[NSMutableIndexSet alloc] init];
952             if (GetAsyncKeyState(VK_CONTROL)&0x8000)
953             {
954               [m addIndexes:[self selectedRowIndexes]];
955               if ([m containsIndex:m_start_item]) [m removeIndex:m_start_item];
956               else 
957               {
958                 if (!msel) [m removeAllIndexes];
959                 [m addIndex:m_start_item];
960               }
961               m_last_plainly_clicked_item = m_start_item;
962             }
963             else if (msel && (GetAsyncKeyState(VK_SHIFT)&0x8000))
964             {
965               [m addIndexes:[self selectedRowIndexes]];
966               const int n = ListView_GetItemCount((HWND)self);
967               if (m_last_plainly_clicked_item<0 || m_last_plainly_clicked_item>=n)
968                 m_last_plainly_clicked_item=m_start_item;
969   
970               if (m_last_shift_clicked_item>=0 && 
971                   m_last_shift_clicked_item<n && 
972                   m_last_plainly_clicked_item != m_last_shift_clicked_item)
973               {
974                 int a1 = m_last_shift_clicked_item;
975                 int a2 = m_last_plainly_clicked_item;
976                 if (a2<a1) { int tmp=a1; a1=a2; a2=tmp; }
977                 [m removeIndexesInRange:NSMakeRange(a1,a2-a1 + 1)];
978               }
979               
980               int a1 = m_start_item;
981               int a2 = m_last_plainly_clicked_item;
982               if (a2<a1) { int tmp=a1; a1=a2; a2=tmp; }
983               [m addIndexesInRange:NSMakeRange(a1,a2-a1 + 1)];
985               m_last_shift_clicked_item = m_start_item;
986             }
987             else
988             {
989               m_last_plainly_clicked_item = m_start_item;
990               [m addIndex:m_start_item];
991             }
992   
993             [self selectRowIndexes:m byExtendingSelection:NO];
994   
995             [m release];
996   
997           }
998           else [self deselectAll:self];
999         }
1000         else
1001         {
1002           [super mouseDown:theEvent];
1003           [super mouseUp:theEvent];
1004         }
1005       }
1006       else if (m_leftmousemovecnt>=4)
1007       {
1008         HWND tgt=(HWND)[self target];
1009         POINT p;
1010         GetCursorPos(&p);
1011         ScreenToClient(tgt,&p);      
1012         SendMessage(tgt,WM_LBUTTONUP,0,MAKELONG(p.x&0xffff,p.y));
1013       }
1014     }
1015   }
1016   
1017   if (!m_lbMode && !(m_start_item_clickmode&(2|4)))
1018   {
1019     NSPoint pt=[theEvent locationInWindow];
1020     pt=[self convertPoint:pt fromView:nil];    
1021     int col = (int)[self columnAtPoint:pt];
1022     NMLISTVIEW nmlv={{(HWND)self,(UINT_PTR)[self tag], NM_CLICK}, (int)[self rowAtPoint:pt], col, 0, 0, 0, {NSPOINT_TO_INTS(pt)}, };
1023     SWELL_ListView_Row *row=m_items->Get(nmlv.iItem);
1024     if (row) nmlv.lParam = row->m_param;
1025     SendMessage((HWND)[self target],WM_NOTIFY,nmlv.hdr.idFrom,(LPARAM)&nmlv);
1026   }  
1029 - (void)rightMouseUp:(NSEvent *)theEvent
1031   bool wantContext=true;
1032   
1033   if (!m_lbMode) 
1034   {
1035     NSPoint pt=[theEvent locationInWindow];
1036     pt=[self convertPoint:pt fromView:nil];
1037     
1038     // note, windows selects on right mousedown    
1039     NSInteger row =[self rowAtPoint:pt];
1040     if (row >= 0 && ![self isRowSelected:row])
1041     {
1042       NSIndexSet* rows=[NSIndexSet indexSetWithIndex:row];
1043       [self deselectAll:self];
1044       [self selectRowIndexes:rows byExtendingSelection:NO];
1045     }       
1046     
1047     NMLISTVIEW nmlv={{(HWND)self,(UINT_PTR)[self tag], NM_RCLICK}, (int)row, (int)[self columnAtPoint:pt], 0, 0, 0, {NSPOINT_TO_INTS(pt)}, };
1048     if (SendMessage((HWND)[self target],WM_NOTIFY,nmlv.hdr.idFrom,(LPARAM)&nmlv)) wantContext=false;
1049   }
1050   if (wantContext)
1051   {
1052     POINT p;
1053     GetCursorPos(&p);
1054     SendMessage((HWND)[self target],WM_CONTEXTMENU,(WPARAM)self,MAKELONG(p.x&0xffff,p.y));
1055   }
1056   m_fakerightmouse=0;
1059 -(LRESULT)onSwellMessage:(UINT)msg p1:(WPARAM)wParam p2:(LPARAM)lParam
1061   HWND hwnd=(HWND)self;
1062     switch (msg)
1063     {
1064       case LB_RESETCONTENT:
1065           ownermode_cnt=0;
1066           if (m_items) 
1067           {
1068             m_items->Empty(true);
1069             [self reloadData];
1070           }
1071       return 0;
1072       case LB_ADDSTRING:
1073       case LB_INSERTSTRING:
1074       {
1075         int cnt=ListView_GetItemCount(hwnd);
1076         if (msg == LB_ADDSTRING && (style & LBS_SORT))
1077         {
1078           SWELL_ListView *tv=(SWELL_ListView*)hwnd;
1079           if (tv->m_lbMode && tv->m_items)
1080           {            
1081             cnt=ptrlist_bsearch_mod((char *)lParam,tv->m_items,_listviewrowSearchFunc,NULL);
1082           }
1083         }
1084         if (msg==LB_ADDSTRING) wParam=cnt;
1085         else if (wParam > cnt) wParam=cnt;
1086         LVITEM lvi={LVIF_TEXT,(int)wParam,0,0,0,(char *)lParam};
1087         ListView_InsertItem(hwnd,&lvi);
1088       }
1089       return wParam;
1090       case LB_GETCOUNT: return ListView_GetItemCount(hwnd);
1091       case LB_SETSEL:
1092         ListView_SetItemState(hwnd, (int)lParam,wParam ? LVIS_SELECTED : 0,LVIS_SELECTED);
1093         return 0;
1094       case LB_GETTEXT:
1095         if (lParam)
1096         {
1097           SWELL_ListView_Row *row=self->m_items ? self->m_items->Get(wParam) : NULL;
1098           *(char *)lParam = 0;
1099           const char *p;
1100           if (row && (p=row->get_col_txt(0)))
1101           {
1102             strcpy((char *)lParam, p);
1103             return (LRESULT)strlen(p);
1104           }
1105         }
1106       return LB_ERR;
1107       case LB_GETTEXTLEN:
1108         {
1109           SWELL_ListView_Row *row=self->m_items ? self->m_items->Get(wParam) : NULL;
1110           if (row) 
1111           {
1112             const char *p=row->get_col_txt(0);
1113             return p?strlen(p):0;
1114           }
1115         }
1116       return LB_ERR;
1117       case LB_FINDSTRINGEXACT:
1118         if (lParam)
1119         {
1120           int x = (int) wParam + 1;
1121           if (x < 0) x=0;
1122           const int n = self->m_items ? self->m_items->GetSize() : 0;
1123           for (int i = 0; i < n; i ++)
1124           {
1125             SWELL_ListView_Row *row=self->m_items->Get(x);
1126             if (row)
1127             {
1128               const char *p = row->get_col_txt(0);
1129               if (p && !stricmp(p,(const char *)lParam)) return x;
1130             }
1131             if (++x >= n) x=0;
1132           }
1133         }
1134       return LB_ERR;
1135       case LB_GETSEL:
1136         return !!(ListView_GetItemState(hwnd,(int)wParam,LVIS_SELECTED)&LVIS_SELECTED);
1137       case LB_GETCURSEL:
1138         return [self selectedRow];
1139       case LB_SETCURSEL:
1140       {
1141         if (wParam<ListView_GetItemCount(hwnd))
1142         {
1143           [self selectRowIndexes:[NSIndexSet indexSetWithIndex:wParam] byExtendingSelection:NO];        
1144           [self scrollRowToVisible:wParam];
1145         }
1146         else
1147         {
1148           [self deselectAll:self];
1149         }
1150       }
1151         return 0;
1152       case LB_GETITEMDATA:
1153       {
1154         if (m_items)
1155         {
1156           SWELL_ListView_Row *row=m_items->Get(wParam);
1157           if (row) return row->m_param;
1158         }
1159       }
1160         return 0;
1161       case LB_SETITEMDATA:
1162       {
1163         if (m_items)
1164         {
1165           SWELL_ListView_Row *row=m_items->Get(wParam);
1166           if (row) row->m_param=lParam;
1167         }
1168       }
1169         return 0;
1170       case LB_GETSELCOUNT:
1171         return [[self selectedRowIndexes] count];
1172       case LB_DELETESTRING:
1173         ListView_DeleteItem((HWND)self, (int)wParam);
1174         return 0;
1175       case WM_SETREDRAW:
1176         if (wParam)
1177         {
1178           if (SWELL_GetOSXVersion() >= 0x1070 && [self respondsToSelector:@selector(endUpdates)])
1179           {
1180             [self endUpdates];
1181             // workaround for a weird 10.14.6 bug
1182             // if the caller calls this, then invalidaterect()s the parent window right away,
1183             // appkit will (sometimes) throw an exception unlocking focus on the NSScrollView.
1184             // invalidating our window directly seems to prevent this.
1185             [self setNeedsDisplay:YES];
1186           }
1187         }
1188         else
1189         {
1190           if (SWELL_GetOSXVersion() >= 0x1070 && [self respondsToSelector:@selector(beginUpdates)])
1191             [self beginUpdates];
1192         }
1194         return 0;
1195     }
1196   return 0;
1199 -(int)getSwellNotificationMode
1201   return m_lbMode;
1203 -(void)setSwellNotificationMode:(int)lbMode
1205   m_lbMode=lbMode;
1208 -(void)onSwellCommand:(int)cmd
1210   // ignore commands
1213 @end
1215 @implementation SWELL_ImageButtonCell
1216 - (NSRect)drawTitle:(NSAttributedString *)title withFrame:(NSRect)frame inView:(NSView *)controlView
1218   return NSMakeRect(0,0,0,0);
1220 @end
1222 @implementation SWELL_ODButtonCell
1223 - (BOOL)isTransparent
1225   return YES;
1227 - (BOOL)isOpaque
1229   return NO;
1232 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
1234   NSView *ctl=[self controlView];
1235   if (!ctl) { [super drawWithFrame:cellFrame inView:controlView]; return; }
1236   
1237   HDC hdc=SWELL_CreateGfxContext([NSGraphicsContext currentContext]);
1238   if (hdc)
1239   {
1240     HWND notWnd = GetParent((HWND)ctl);
1241     DRAWITEMSTRUCT dis={ODT_BUTTON,(UINT)[ctl tag],0,0,0,(HWND)ctl,hdc,{0,},0};
1242     NSRECT_TO_RECT(&dis.rcItem,cellFrame);
1243     SendMessage(notWnd,WM_DRAWITEM,dis.CtlID,(LPARAM)&dis);
1244   
1245     SWELL_DeleteGfxContext(hdc);
1246   }
1247   
1249 @end
1251 @implementation SWELL_ODListViewCell
1252 -(void)setOwnerControl:(SWELL_ListView *)t { m_ownctl=t; m_lastidx=0; }
1253 -(void)setItemIdx:(int)idx
1255   m_lastidx=idx;
1257 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
1259   if (!m_ownctl) { [super drawInteriorWithFrame:cellFrame inView:controlView]; return; }
1260   
1261   int itemidx=m_lastidx;
1262   LPARAM itemData=0;
1263   SWELL_ListView_Row *row=m_ownctl->m_items->Get(itemidx);
1264   if (row) itemData=row->m_param;
1266   HDC hdc=SWELL_CreateGfxContext([NSGraphicsContext currentContext]);
1267   if (hdc)
1268   {
1269     HWND notWnd = GetParent((HWND)m_ownctl);
1270     DRAWITEMSTRUCT dis={ODT_LISTBOX,(UINT)[m_ownctl tag],(UINT)itemidx,0,0,(HWND)m_ownctl,hdc,{0,},(DWORD_PTR)itemData};
1271     NSRECT_TO_RECT(&dis.rcItem,cellFrame);
1272     SendMessage(notWnd,WM_DRAWITEM,dis.CtlID,(LPARAM)&dis);
1273   
1274     SWELL_DeleteGfxContext(hdc);
1275   }
1279 @end
1285 HWND GetDlgItem(HWND hwnd, int idx)
1287   if (WDL_NOT_NORMALLY(!hwnd)) return 0;
1289   NSView *v=0;
1290   id pid=(id)hwnd;
1291   if ([pid isKindOfClass:[NSWindow class]]) v=[((NSWindow *)pid) contentView];
1292   else if ([pid isKindOfClass:[NSView class]]) v=(NSView *)pid;
1293   
1294   if (!idx || !v) return (HWND)v;
1295   
1296   SWELL_BEGIN_TRY
1298   NSArray *ar = [v subviews];
1299   const NSInteger n=[ar count];
1300   for (NSInteger x=0;x<n;x++)
1301   {
1302     NSView *sv = [ar objectAtIndex:x];
1303     if (sv)
1304     {
1305       if ([sv respondsToSelector:@selector(tag)] && [sv tag] == idx) return (HWND)sv;
1307       if (sv && [sv isKindOfClass:[NSScrollView class]])
1308       {
1309         sv=[(NSScrollView *)sv documentView];
1310         if (sv && [sv respondsToSelector:@selector(tag)] && [sv tag] == idx) return (HWND)sv;
1311       }
1312       if (sv && [sv isKindOfClass:[NSClipView class]]) 
1313       {
1314         sv = [(NSClipView *)sv documentView];
1315         if (sv && [sv respondsToSelector:@selector(tag)] && [sv tag] == idx) return (HWND)sv;
1316       }
1317     }
1318   }
1319   // we might want to enable this for max compat with old code, but hopefully not:  return [v viewWithTag:idx]; 
1320   SWELL_END_TRY(;)
1321   return NULL;
1325 LONG_PTR SetWindowLong(HWND hwnd, int idx, LONG_PTR val)
1327   if (WDL_NOT_NORMALLY(!hwnd)) return 0;
1329   SWELL_BEGIN_TRY
1330   id pid=(id)hwnd;
1331   if (idx==GWL_EXSTYLE && [pid respondsToSelector:@selector(swellSetExtendedStyle:)])
1332   {
1333     LONG ret=(LONG) [(SWELL_hwndChild*)pid swellGetExtendedStyle];
1334     [(SWELL_hwndChild*)pid swellSetExtendedStyle:(LONG)val];
1335     return ret;
1336   }
1337   if (idx==GWL_USERDATA && [pid respondsToSelector:@selector(setSwellUserData:)])
1338   {
1339     LONG_PTR ret=(LONG_PTR)[(SWELL_hwndChild*)pid getSwellUserData];
1340     [(SWELL_hwndChild*)pid setSwellUserData:(LONG_PTR)val];
1341     return ret;
1342   }
1343     
1344   if (idx==GWL_ID && [pid respondsToSelector:@selector(tag)] && [pid respondsToSelector:@selector(setTag:)])
1345   {
1346     int ret= (int) [pid tag];
1347     [pid setTag:(int)val];
1348     return (LONG_PTR)ret;
1349   }
1350   
1351   if (idx==GWL_WNDPROC && [pid respondsToSelector:@selector(setSwellWindowProc:)])
1352   {
1353     WNDPROC ov=(WNDPROC)[pid getSwellWindowProc];
1354     [pid setSwellWindowProc:(WNDPROC)val];
1355     return (LONG_PTR)ov;
1356   }
1357   if (idx==DWL_DLGPROC && [pid respondsToSelector:@selector(setSwellDialogProc:)])
1358   {
1359     DLGPROC ov=(DLGPROC)[pid getSwellDialogProc];
1360     [pid setSwellDialogProc:(DLGPROC)val];
1361     return (LONG_PTR)ov;
1362   }
1363   
1364   if (idx==GWL_STYLE)
1365   {
1366     if ([pid respondsToSelector:@selector(setSwellStyle:)])
1367     {
1368       LONG ov=[pid getSwellStyle];
1369       [pid setSwellStyle:(LONG)(val & ~WS_VISIBLE)];
1370       return ov;
1371     }
1372     else if ([pid isKindOfClass:[NSButton class]]) 
1373     {
1374       int ret=(int)GetWindowLong(hwnd,idx);
1375       
1376       if ((val&0xf) == BS_AUTO3STATE)
1377       {
1378         [pid setButtonType:NSSwitchButton];
1379         [pid setAllowsMixedState:YES];
1380         if ([pid isKindOfClass:[SWELL_Button class]]) [pid swellSetRadioFlags:0];
1381       }    
1382       else if ((val & 0xf) == BS_AUTOCHECKBOX)
1383       {
1384         [pid setButtonType:NSSwitchButton];
1385         [pid setAllowsMixedState:NO];
1386         if ([pid isKindOfClass:[SWELL_Button class]]) [pid swellSetRadioFlags:0];
1387       }
1388       else if ((val &  0xf) == BS_AUTORADIOBUTTON)
1389       {
1390         [pid setButtonType:NSRadioButton];
1391         if ([pid isKindOfClass:[SWELL_Button class]]) [pid swellSetRadioFlags:(val&WS_GROUP)?3:1];
1392       }               
1393       
1394       return ret;
1395     }
1396     else 
1397     {
1398       if ([[pid window] contentView] == pid)
1399       {
1400         NSView *tv=(NSView *)pid;
1401         NSWindow *oldw = [tv window];
1402         NSUInteger smask = [oldw styleMask];
1403         int mf=0;
1404         if (smask & NSTitledWindowMask)
1405         {
1406           mf|=WS_CAPTION;
1407           if (smask & NSResizableWindowMask) mf|=WS_THICKFRAME;
1408         }
1409         if (mf != (val&(WS_CAPTION|WS_THICKFRAME)))
1410         {
1411           BOOL dovis = IsWindowVisible((HWND)oldw);
1412           NSWindow *oldpar = [oldw parentWindow];
1413           char oldtitle[2048];
1414           oldtitle[0]=0;
1415           GetWindowText(hwnd,oldtitle,sizeof(oldtitle));
1416           NSRect fr=[oldw frame];
1417           HWND oldOwner=NULL;
1418           if ([oldw respondsToSelector:@selector(swellGetOwner)]) oldOwner=(HWND)[(SWELL_ModelessWindow*)oldw swellGetOwner];
1419           NSInteger oldlevel = [oldw level];
1421           
1422           [tv retain];
1423           SWELL_hwndChild *tempview = [[SWELL_hwndChild alloc] initChild:nil Parent:(NSView *)oldw dlgProc:nil Param:0];          
1424           [tempview release];          
1425           
1426           unsigned int mask=0;
1427           
1428           if (val & WS_CAPTION)
1429           {
1430             mask|=NSTitledWindowMask;
1431             if (val & WS_THICKFRAME)
1432               mask|=NSMiniaturizableWindowMask|NSClosableWindowMask|NSResizableWindowMask;
1433           }
1434       
1435           HWND SWELL_CreateModelessFrameForWindow(HWND childW, HWND ownerW, unsigned int);
1436           HWND bla=SWELL_CreateModelessFrameForWindow((HWND)tv,(HWND)oldOwner,mask);
1437           
1438           if (bla)
1439           {
1440             [tv release];
1441             // move owned windows over
1442             if ([oldw respondsToSelector:@selector(swellGetOwnerWindowHead)])
1443             {
1444               void **p=(void **)[(SWELL_ModelessWindow*)oldw swellGetOwnerWindowHead];
1445               if (p && [(id)bla respondsToSelector:@selector(swellGetOwnerWindowHead)])
1446               {
1447                 void **p2=(void **)[(SWELL_ModelessWindow*)bla swellGetOwnerWindowHead];
1448                 if (p && p2) 
1449                 {
1450                   *p2=*p;
1451                   *p=0;
1452                   OwnedWindowListRec *rec = (OwnedWindowListRec *) *p2;
1453                   while (rec)
1454                   {
1455                     if (rec->hwnd && [rec->hwnd respondsToSelector:@selector(swellSetOwner:)])
1456                       [(SWELL_ModelessWindow *)rec->hwnd swellSetOwner:(id)bla];
1457                     rec=rec->_next;
1458                   }
1459                 }
1460               }
1461             }
1462             // move all child and owned windows over to new window
1463             NSArray *ar=[oldw childWindows];
1464             if (ar)
1465             {
1466               int x;
1467               for (x = 0; x < [ar count]; x ++)
1468               {
1469                 NSWindow *cw=[ar objectAtIndex:x];
1470                 if (cw)
1471                 {
1472                   [cw retain];
1473                   [oldw removeChildWindow:cw];
1474                   [(NSWindow *)bla addChildWindow:cw ordered:NSWindowAbove];
1475                   [cw release];
1476                   
1477                   
1478                 }
1479               }
1480             }
1481           
1482             if (oldpar) [oldpar addChildWindow:(NSWindow *)bla ordered:NSWindowAbove];
1483             if (oldtitle[0]) SetWindowText(hwnd,oldtitle);
1484             
1485             [(NSWindow *)bla setFrame:fr display:dovis];
1486             [(NSWindow *)bla setLevel:oldlevel];
1487             if (dovis) ShowWindow(bla,SW_SHOW);
1488       
1489             DestroyWindow((HWND)oldw);
1490           }
1491           else
1492           {
1493             [oldw setContentView:tv];
1494             [tv release];
1495           }  
1496       
1497         }
1498       }
1499     }
1500     return 0;
1501   }
1503   if (idx == GWL_HWNDPARENT)
1504   {
1505     NSWindow *window = [pid window];
1506     if (![window respondsToSelector:@selector(swellGetOwner)]) return 0;
1508     NSWindow *new_owner = val && [(id)(INT_PTR)val isKindOfClass:[NSView class]] ? [(NSView *)(INT_PTR)val window] : NULL;
1509     if (new_owner && ![new_owner respondsToSelector:@selector(swellAddOwnedWindow:)]) new_owner=NULL;
1511     NSWindow *old_owner = [(SWELL_ModelessWindow *)window swellGetOwner];
1512     if (old_owner != new_owner)
1513     {
1514       if (old_owner) [(SWELL_ModelessWindow*)old_owner swellRemoveOwnedWindow:window];
1515       [(SWELL_ModelessWindow *)window swellSetOwner:nil];
1516       if (new_owner) [(SWELL_ModelessWindow *)new_owner swellAddOwnedWindow:window];
1517     }
1518     return (old_owner ? (LONG_PTR)[old_owner contentView] : 0);
1519   }
1520   
1521   if ([pid respondsToSelector:@selector(setSwellExtraData:value:)])
1522   {
1523     LONG_PTR ov=0;
1524     if ([pid respondsToSelector:@selector(getSwellExtraData:)]) ov=(LONG_PTR)[pid getSwellExtraData:idx];
1526     [pid setSwellExtraData:idx value:val];
1527     
1528     return ov;
1529   }
1530    
1531   SWELL_END_TRY(;)
1532   return 0;
1535 LONG_PTR GetWindowLong(HWND hwnd, int idx)
1537   if (WDL_NOT_NORMALLY(!hwnd)) return 0;
1538   id pid=(id)hwnd;
1540   SWELL_BEGIN_TRY
1541   
1542   if (idx==GWL_EXSTYLE && [pid respondsToSelector:@selector(swellGetExtendedStyle)])
1543   {
1544     return (LONG_PTR)[pid swellGetExtendedStyle];
1545   }
1546   
1547   if (idx==GWL_USERDATA && [pid respondsToSelector:@selector(getSwellUserData)])
1548   {
1549     return (LONG_PTR)[pid getSwellUserData];
1550   }
1551   if (idx==GWL_USERDATA && [pid isKindOfClass:[NSText class]])
1552   {
1553     NSView *par = [pid superview];
1554     if (par)
1555     {
1556       if (![par isKindOfClass:[SWELL_TextField class]])
1557         par = [par superview];
1558       if ([par isKindOfClass:[SWELL_TextField class]])
1559         return [(SWELL_TextField*)par getSwellUserData];
1560     }
1562     return 0xdeadf00b;
1563   }
1564   
1565   if (idx==GWL_ID && [pid respondsToSelector:@selector(tag)])
1566     return [pid tag];
1567   
1568   
1569   if (idx==GWL_WNDPROC && [pid respondsToSelector:@selector(getSwellWindowProc)])
1570   {
1571     return (LONG_PTR)[pid getSwellWindowProc];
1572   }
1573   if (idx==DWL_DLGPROC && [pid respondsToSelector:@selector(getSwellDialogProc)])
1574   {
1575     return (LONG_PTR)[pid getSwellDialogProc];
1576   }  
1577   if (idx==GWL_STYLE)
1578   {
1579     int ret=0;
1580     if (![pid isHidden]) ret |= WS_VISIBLE;
1581     if ([pid respondsToSelector:@selector(getSwellStyle)])
1582     {
1583       return (LONG_PTR)(([pid getSwellStyle]&~WS_VISIBLE) | ret);
1584     }    
1585     
1586     if ([pid isKindOfClass:[NSButton class]]) 
1587     {
1588       int tmp;
1589       if ([pid isKindOfClass:[NSPopUpButton class]]) ret |= CBS_DROPDOWNLIST;
1590       else if ([pid allowsMixedState]) ret |= BS_AUTO3STATE;
1591       else if ([pid isKindOfClass:[SWELL_Button class]] && (tmp = (int)[pid swellGetRadioFlags]))
1592       {
1593         if (tmp != 4096)
1594         {
1595           ret |= BS_AUTORADIOBUTTON;
1596           if (tmp&2) ret|=WS_GROUP;
1597         }
1598       }
1599       else ret |= BS_AUTOCHECKBOX; 
1600     }
1601     
1602     if ([pid isKindOfClass:[NSView class]])
1603     {
1604       if ([[pid window] contentView] != pid) ret |= WS_CHILDWINDOW;
1605       else
1606       {
1607         NSUInteger smask  =[[pid window] styleMask];
1608         if (smask & NSTitledWindowMask)
1609         {
1610           ret|=WS_CAPTION;
1611           if (smask & NSResizableWindowMask) ret|=WS_THICKFRAME;
1612         }
1613       }
1614     }
1615     
1616     return ret;
1617   }
1618   if (idx == GWL_HWNDPARENT)
1619   {
1620     NSWindow *window = [pid window];
1621     if (![window respondsToSelector:@selector(swellGetOwner)]) return 0;
1622     NSWindow *old_owner = [(SWELL_ModelessWindow *)window swellGetOwner];
1623     return (old_owner ? (LONG_PTR)[old_owner contentView] : 0);
1624   }
1626   if ([pid respondsToSelector:@selector(getSwellExtraData:)])
1627   {
1628     return (LONG_PTR)[pid getSwellExtraData:idx];
1629   }
1630   
1631   SWELL_END_TRY(;)
1632   return 0;
1635 static bool IsWindowImpl(NSView *ch, NSView *par)
1637   if (!par || ![par isKindOfClass:[NSView class]]) return false;
1639   NSArray *ar = [par subviews];
1640   if (!ar) return false;
1641   [ar retain];
1642   NSInteger x,n=[ar count];
1643   for (x=0;x<n;x++)
1644     if ([ar objectAtIndex:x] == ch) 
1645     {
1646       [ar release];
1647       return true;
1648     }
1650   for (x=0;x<n;x++)
1651     if (IsWindowImpl(ch,[ar objectAtIndex:x])) 
1652     {
1653       [ar release];
1654       return true;
1655     }
1657   [ar release];
1658   return false;
1660 bool IsWindow(HWND hwnd)
1662   if (!hwnd) return false;
1663   // this is very costly, but required
1664   SWELL_BEGIN_TRY
1666   NSArray *ch=[NSApp windows];
1667   [ch retain];
1668   NSInteger x,n=[ch count];
1669   for(x=0;x<n; x ++)
1670   {
1671     @try { 
1672       NSWindow *w = [ch objectAtIndex:x]; 
1673       if (w == (NSWindow *)hwnd || [w contentView] == (NSView *)hwnd) 
1674       {
1675         [ch release];
1676         return true;
1677       }
1678     }
1679     @catch (NSException *ex) { 
1680     }
1681     @catch (id ex) {
1682     }
1683   }
1684   for(x=0;x<n; x ++)
1685   {
1686     @try { 
1687       NSWindow *w = [ch objectAtIndex:x];
1688       if (w && 
1689           // only validate children of our windows (maybe an option for this?)
1690           ([w isKindOfClass:[SWELL_ModelessWindow class]] || [w isKindOfClass:[SWELL_ModalDialog class]]) &&
1691           IsWindowImpl((NSView*)hwnd,[w contentView])) 
1692       {
1693         [ch release];
1694         return true;
1695       }
1696     } 
1697     @catch (NSException *ex) { 
1698     }
1699     @catch (id ex) {
1700     }
1701   }
1702   [ch release];
1704   SWELL_END_TRY(;)
1705   return false;
1708 bool IsWindowVisible(HWND hwnd)
1710   if (!hwnd) return false;
1712   SWELL_BEGIN_TRY
1713   id turd=(id)hwnd;
1714   if ([turd isKindOfClass:[NSView class]])
1715   {
1716     NSWindow *w = [turd window];
1717     if (w && ![w isVisible]) return false;
1718     
1719     return ![turd isHiddenOrHasHiddenAncestor];
1720   }
1721   if ([turd isKindOfClass:[NSWindow class]])
1722   {
1723     return !![turd isVisible];
1724   }
1725   SWELL_END_TRY(;)
1726   return true;
1730 bool IsWindowEnabled(HWND hwnd)
1732   if (WDL_NOT_NORMALLY(!hwnd)) return false;
1734   bool rv = true;
1736   SWELL_BEGIN_TRY
1738   id view = (id)hwnd;
1739   if ([view isKindOfClass:[NSWindow class]]) view = [view contentView];
1741   rv = view && [view respondsToSelector:@selector(isEnabled)] && [view isEnabled];
1743   SWELL_END_TRY(;)
1745   return rv;
1751 static void *__GetNSImageFromHICON(HICON ico) // local copy to not be link dependent on swell-gdi.mm
1753   HGDIOBJ__ *i = (HGDIOBJ__ *)ico;
1754   if (!i || i->type != TYPE_BITMAP) return 0;
1755   return i->bitmapptr;
1759 @implementation SWELL_Button : NSButton
1761 STANDARD_CONTROL_NEEDSDISPLAY_IMPL("Button")
1763 -(id) init {
1764   self = [super init];
1765   if (self != nil) {
1766     m_userdata=0;
1767     m_swellGDIimage=0;
1768     m_radioflags=0; // =4096 if not a checkbox at all
1769   }
1770   return self;
1772 -(int)swellGetRadioFlags { return m_radioflags; }
1773 -(void)swellSetRadioFlags:(int)f { m_radioflags=f; }
1774 -(LONG_PTR)getSwellUserData { return m_userdata; }
1775 -(void)setSwellUserData:(LONG_PTR)val {   m_userdata=val; }
1777 -(void)setSwellGDIImage:(void *)par
1779   m_swellGDIimage=par;
1781 -(void *)getSwellGDIImage
1783   return m_swellGDIimage;
1786 @end
1789 NSFont *SWELL_GetNSFont(HGDIOBJ__ *obj);
1791 LRESULT SendMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1793   if (WDL_NOT_NORMALLY(!hwnd)) return 0;
1795   SWELL_BEGIN_TRY
1796   id obj=(id)hwnd;
1797   if ([obj respondsToSelector:@selector(onSwellMessage:p1:p2:)])
1798   {
1799     return (LRESULT) [obj onSwellMessage:msg p1:wParam p2:lParam];
1800   }
1801   else 
1802   {
1803     if (msg == BM_GETCHECK && [obj isKindOfClass:[NSButton class]])
1804     {
1805       NSInteger a=[(NSButton*)obj state];
1806       if (a==NSMixedState) return BST_INDETERMINATE;
1807       return a!=NSOffState;
1808     }
1809     if (msg == BM_SETCHECK && [obj isKindOfClass:[NSButton class]])
1810     {
1811       [(NSButton*)obj setState:(wParam&BST_INDETERMINATE)?NSMixedState:((wParam&BST_CHECKED)?NSOnState:NSOffState)];
1812       return 0;
1813     }
1814     if ((msg==BM_GETIMAGE || msg == BM_SETIMAGE) && [obj isKindOfClass:[SWELL_Button class]])
1815     {
1816       if (wParam != IMAGE_BITMAP && wParam != IMAGE_ICON) return 0; // ignore unknown types
1817       LONG_PTR ret=(LONG_PTR) (void *)[obj getSwellGDIImage];
1818       if (msg==BM_SETIMAGE)
1819       {
1820         NSImage *img=NULL;
1821         if (lParam) img=(NSImage *)__GetNSImageFromHICON((HICON)lParam);
1822         [obj setImage:img];
1823         [obj setSwellGDIImage:(void *)(img?lParam:0)];
1824       }
1825       return ret;
1826     }
1827     else if (msg >= CB_ADDSTRING && msg <= CB_INITSTORAGE && ([obj isKindOfClass:[NSPopUpButton class]] || [obj isKindOfClass:[NSComboBox class]]))
1828     {
1829         switch (msg)
1830         {
1831           case CB_ADDSTRING: return SWELL_CB_AddString(hwnd,0,(char*)lParam); 
1832           case CB_DELETESTRING: SWELL_CB_DeleteString(hwnd,0,(int)wParam); return 1;
1833           case CB_GETCOUNT: return SWELL_CB_GetNumItems(hwnd,0);
1834           case CB_GETCURSEL: return SWELL_CB_GetCurSel(hwnd,0);
1835           case CB_GETLBTEXT: return SWELL_CB_GetItemText(hwnd,0,(int)wParam,(char *)lParam, 1<<20);
1836           case CB_GETLBTEXTLEN: return SWELL_CB_GetItemText(hwnd,0,(int)wParam,NULL,0);
1837           case CB_INSERTSTRING: return SWELL_CB_InsertString(hwnd,0,(int)wParam,(char *)lParam);
1838           case CB_RESETCONTENT: SWELL_CB_Empty(hwnd,0); return 0;
1839           case CB_SETCURSEL: SWELL_CB_SetCurSel(hwnd,0,(int)wParam); return 0;
1840           case CB_GETITEMDATA: return SWELL_CB_GetItemData(hwnd,0,(int)wParam);
1841           case CB_SETITEMDATA: SWELL_CB_SetItemData(hwnd,0,(int)wParam,lParam); return 0;
1842           case CB_FINDSTRING:
1843           case CB_FINDSTRINGEXACT:
1844             if (lParam) return SWELL_CB_FindString(hwnd,0,(int)wParam,(const char *)lParam,msg==CB_FINDSTRINGEXACT);
1845             return CB_ERR;
1846           case CB_INITSTORAGE: return 0;                                                      
1847         }
1848         return 0;
1849     }
1850     else if (msg >= TBM_GETPOS && msg <= TBM_SETRANGE && ([obj isKindOfClass:[NSSlider class]]))
1851     {
1852         switch (msg)
1853         {
1854           case TBM_GETPOS: return SWELL_TB_GetPos(hwnd,0);
1855           case TBM_SETTIC: SWELL_TB_SetTic(hwnd,0,(int)lParam); return 1;
1856           case TBM_SETPOS: SWELL_TB_SetPos(hwnd,0,(int)lParam); return 1;
1857           case TBM_SETRANGE: SWELL_TB_SetRange(hwnd,0,LOWORD(lParam),HIWORD(lParam)); return 1;
1858         }
1859         return 0;
1860     }
1861     else if ((msg == EM_SETSEL || msg == EM_GETSEL || msg == EM_SETPASSWORDCHAR) && ([obj isKindOfClass:[NSTextField class]]))
1862     { 
1863       if (msg == EM_GETSEL)
1864       {
1865         NSRange range={0,};
1866         NSResponder *rs = [[obj window] firstResponder];
1867         if ([rs isKindOfClass:[NSView class]] && [(NSView *)rs isDescendantOf:obj])
1868         {
1869           NSText* text=[[obj window] fieldEditor:YES forObject:(NSTextField*)obj];  
1870           if (text) range=[text selectedRange];
1871         }
1872         if (wParam) *(int*)wParam=(int)range.location;
1873         if (lParam) *(int*)lParam=(int)(range.location+range.length);
1874       }      
1875       else if (msg == EM_SETSEL)
1876       {        
1877         //        [(NSTextField*)obj selectText:obj]; // Force the window's text field editor onto this control
1878         // don't force it, just ignore EM_GETSEL/EM_SETSEL if not in focus
1879         NSResponder *rs = [[obj window] firstResponder];
1880         if ([rs isKindOfClass:[NSView class]] && [(NSView *)rs isDescendantOf:obj])
1881         {
1882           NSText* text = [[obj window] fieldEditor:YES forObject:(NSTextField*)obj]; // then get it from the window 
1883           NSUInteger sl = [[text string] length];
1884           if (wParam == -1) lParam = wParam = 0;
1885           else if (lParam == -1) lParam = sl;        
1886           if (wParam>sl) wParam=sl;
1887           if (lParam>sl) lParam=sl;      
1888           if (text) [text setSelectedRange:NSMakeRange(wParam, wdl_max(lParam-wParam,0))]; // and set the range
1889         }
1890       }
1891       else if (msg == EM_SETPASSWORDCHAR)
1892       {
1893         // not implemented, because it requires replacing obj within its parent window
1894         // instead caller explicitly destroy the edit control and create a new one with ES_PASSWORD
1895       }
1896       return 0;
1897     }
1898     else if (msg == WM_SETFONT && ([obj isKindOfClass:[NSTextField class]] ||
1899                                    [obj isKindOfClass:[NSTextView class]]))
1900     {
1901       NSFont *font = SWELL_GetNSFont((HGDIOBJ__*)wParam);
1902       if (font) [obj setFont:font];
1903       return 0;
1904     }
1905     else if (msg == WM_SETFONT && ([obj isKindOfClass:[NSScrollView class]]))
1906     {
1907       NSView *cv=[(NSScrollView *)obj documentView];
1908       if (cv && [cv isKindOfClass:[NSTextView class]])
1909       {
1910         NSFont *font = SWELL_GetNSFont((HGDIOBJ__*)wParam);
1911         if (font) [(NSTextView *)cv setFont:font];
1912         return 0;
1913       }
1914     }
1915     else
1916     {
1917       NSWindow *w;
1918       NSView *v;
1919       // if content view gets unhandled message send to window
1920       if ([obj isKindOfClass:[NSView class]] && (w=[obj window]) && [w contentView] == obj && [w respondsToSelector:@selector(onSwellMessage:p1:p2:)])
1921       {
1922         return (LRESULT) [(SWELL_hwndChild *)w onSwellMessage:msg p1:wParam p2:lParam];
1923       }
1924       // if window gets unhandled message send to content view
1925       else if ([obj isKindOfClass:[NSWindow class]] && (v=[obj contentView]) && [v respondsToSelector:@selector(onSwellMessage:p1:p2:)])
1926       {
1927         return (LRESULT) [(SWELL_hwndChild *)v onSwellMessage:msg p1:wParam p2:lParam];
1928       }
1929     }
1930   }
1931   SWELL_END_TRY(;)
1932   return 0;
1935 void DestroyWindow(HWND hwnd)
1937   if (WDL_NOT_NORMALLY(!hwnd)) return;
1938   SWELL_BEGIN_TRY
1939   id pid=(id)hwnd;
1940   if ([pid isKindOfClass:[NSView class]])
1941   {
1942     KillTimer(hwnd,~(UINT_PTR)0);
1943     sendSwellMessage((id)pid,WM_DESTROY,0,0);
1945     NSWindow *pw = [(NSView *)pid window];
1946     if (pw && [pw contentView] == pid) // destroying contentview should destroy top level window
1947     {
1948       DestroyWindow((HWND)pw);
1949     }
1950     else 
1951     {
1952       if (pw)
1953       {
1954         id foc=[pw firstResponder];
1955         if (foc && (foc == pid || IsChild((HWND)pid,(HWND)foc)))
1956         {
1957           [pw makeFirstResponder:nil];
1958         }
1959       }
1960       [(NSView *)pid removeFromSuperview];
1961     }
1962   }
1963   else if ([pid isKindOfClass:[NSWindow class]])
1964   {
1965     KillTimer(hwnd,~(UINT_PTR)0);
1966     sendSwellMessage([(id)pid contentView],WM_DESTROY,0,0);
1967     sendSwellMessage((id)pid,WM_DESTROY,0,0);
1968       
1969     if ([(id)pid respondsToSelector:@selector(swellDoDestroyStuff)])
1970       [(id)pid swellDoDestroyStuff];
1971       
1972     NSWindow *par=[(NSWindow*)pid parentWindow];
1973     if (par)
1974     {
1975       [par removeChildWindow:(NSWindow*)pid];
1976     }
1977     [(NSWindow *)pid close]; // this is probably bad, but close takes too long to close!
1978   }
1979   SWELL_END_TRY(;)
1982 void EnableWindow(HWND hwnd, int enable)
1984   if (WDL_NOT_NORMALLY(!hwnd)) return;
1985   SWELL_BEGIN_TRY
1986   id bla=(id)hwnd;
1987   if ([bla isKindOfClass:[NSWindow class]]) bla = [bla contentView];
1988     
1989   if (bla && [bla respondsToSelector:@selector(setEnabled:)])
1990   {
1991     if (!enable)
1992     {
1993       HWND foc = GetFocus();
1994       if (foc && (foc==hwnd || IsChild(hwnd,foc)))
1995       {
1996         HWND par = GetParent(hwnd);
1997         if (par) SetFocus(par);
1998       }
1999     }
2000     if (enable == -1000 && [bla respondsToSelector:@selector(setEnabledSwellNoFocus)])
2001       [(SWELL_hwndChild *)bla setEnabledSwellNoFocus];
2002     else
2003       [bla setEnabled:(enable?YES:NO)];
2004     if ([bla isKindOfClass:[SWELL_TextField class]])
2005       [(SWELL_TextField*)bla initColors:-1];
2006   }
2007   SWELL_END_TRY(;)
2010 void SetForegroundWindow(HWND hwnd)
2012   WDL_ASSERT(hwnd != NULL);
2013   SetFocus(hwnd);
2016 void SetFocus(HWND hwnd) // these take NSWindow/NSView, and return NSView *
2018   id r=(id) hwnd;
2019   if (!r) return; // on win32 SetFocus(NULL) is allowed, removes focus (maybe we should implement)
2020   
2021   SWELL_BEGIN_TRY
2022   if ([r isKindOfClass:[NSWindow class]])
2023   {
2024     [(NSWindow *)r makeFirstResponder:[(NSWindow *)r contentView]]; 
2025     if ([(NSWindow *)r isVisible]) [(NSWindow *)r makeKeyAndOrderFront:nil];
2026   }
2027   else if (WDL_NORMALLY([r isKindOfClass:[NSView class]]))
2028   {
2029     NSWindow *wnd=[(NSView *)r window];
2030     if (wnd)
2031     {
2032       if ([wnd isVisible])
2033         [wnd makeKeyAndOrderFront:nil];
2034       if ([r acceptsFirstResponder])
2035         [wnd makeFirstResponder:r];
2036     }
2037   }
2038   SWELL_END_TRY(;)
2041 void SWELL_GetViewPort(RECT *r, const RECT *sourcerect, bool wantWork)
2043   SWELL_BEGIN_TRY
2045   NSArray *ar=[NSScreen screens];
2046   
2047   const NSInteger cnt=[ar count];
2048   if (!sourcerect || cnt < 2)
2049   {
2050     NSScreen *sc=[NSScreen mainScreen];
2051     if (sc)
2052     {
2053       NSRect tr=wantWork ? [sc visibleFrame] : [sc frame];
2054       NSRECT_TO_RECT(r,tr);
2055     }
2056     else
2057     {
2058       r->left=r->top=0;
2059       r->right=1600;
2060       r->bottom=1200;
2061     }
2062   }
2063   else
2064   {
2065     double best_score = -1e20;
2066     // find screen of best intersection
2067     RECT sr = *sourcerect;
2068     if (sr.top > sr.bottom) { sr.top = sourcerect->bottom; sr.bottom = sourcerect->top; }
2069     if (sr.left > sr.right) { sr.left = sourcerect->right; sr.right = sourcerect->left; }
2070     for (NSInteger x = 0; x < cnt; x ++)
2071     {
2072       NSScreen *sc=[ar objectAtIndex:x];
2073       if (sc)
2074       {
2075         NSRect tr=wantWork ? [sc visibleFrame] : [sc frame];
2076         RECT tmp;
2077         NSRECT_TO_RECT(&tmp,tr);
2079         double score;
2080         RECT res;
2081         if (IntersectRect(&res, &tmp, &sr))
2082         {
2083           score = wdl_abs((res.right-res.left) * (res.bottom-res.top));
2084         }
2085         else
2086         {
2087           int dx = 0, dy = 0;
2088           if (tmp.left > sr.right) dx = tmp.left - sr.right;
2089           else if (tmp.right < sr.left) dx = sr.left - tmp.right;
2090           if (tmp.bottom < sr.top) dy = tmp.bottom - sr.top;
2091           else if (tmp.top > sr.bottom) dy = tmp.top - sr.bottom;
2092           score = - (dx*dx + dy*dy);
2093         }
2096         if (!x || score > best_score)
2097         {
2098           best_score = score;
2099           *r = tmp;
2100         }
2101       }
2102     }
2103   }
2104   SWELL_END_TRY(;)
2107 void ScreenToClient(HWND hwnd, POINT *p)
2109   if (WDL_NOT_NORMALLY(!hwnd)) return;
2110   // no need to try/catch, this should never have an issue *wince*
2111   
2112   id ch=(id)hwnd;
2113   if ([ch isKindOfClass:[NSWindow class]]) ch=[((NSWindow *)ch) contentView];
2114   if (WDL_NOT_NORMALLY(!ch || ![ch isKindOfClass:[NSView class]])) return;
2115   
2116   NSWindow *window=[ch window];
2117   
2118   NSPoint wndpt = [window convertScreenToBase:NSMakePoint(p->x,p->y)];
2119   
2120   // todo : WM_NCCALCSIZE 
2121   NSPOINT_TO_POINT(p, [ch convertPoint:wndpt fromView:nil]);
2124 void ClientToScreen(HWND hwnd, POINT *p)
2126   if (WDL_NOT_NORMALLY(!hwnd)) return;
2127   
2128   id ch=(id)hwnd;
2129   if ([ch isKindOfClass:[NSWindow class]]) ch=[((NSWindow *)ch) contentView];
2130   if (!ch || ![ch isKindOfClass:[NSView class]]) return;
2131   
2132   NSWindow *window=[ch window];
2133   
2134   NSPoint wndpt = [ch convertPoint:NSMakePoint(p->x,p->y) toView:nil];
2135   
2136   // todo : WM_NCCALCSIZE 
2137   
2138   NSPOINT_TO_POINT(p,[window convertBaseToScreen:wndpt]);
2141 static NSView *NavigateUpScrollClipViews(NSView *ch)
2143   NSView *par=[ch superview];
2144   if (par && [par isKindOfClass:[NSClipView class]]) 
2145   {
2146     par=[par superview];
2147     if (par && [par isKindOfClass:[NSScrollView class]])
2148     {
2149       ch=par;
2150     }
2151   }
2152   return ch;
2155 HWND SWELL_NavigateUpScrollClipViews(HWND h)
2157   NSView *v = 0;
2158   if (h && [(id)h isKindOfClass:[NSView class]]) v = (NSView *)h;
2159   else if (h && [(id)h isKindOfClass:[NSWindow class]]) v = [(NSWindow *)h contentView];
2160   if (v)
2161     return (HWND)NavigateUpScrollClipViews(v);
2162   return 0;
2165 bool GetWindowRect(HWND hwnd, RECT *r)
2167   r->left=r->top=r->right=r->bottom=0;
2168   if (WDL_NOT_NORMALLY(!hwnd)) return false;
2170   SWELL_BEGIN_TRY
2171   
2172   id ch=(id)hwnd;
2173   NSWindow *nswnd;
2174   if ([ch isKindOfClass:[NSView class]] && (nswnd=[(NSView *)ch window]) && [nswnd contentView]==ch)
2175     ch=nswnd;
2176     
2177   if ([ch isKindOfClass:[NSWindow class]]) 
2178   {
2179     NSRECT_TO_RECT(r,[ch frame]);
2180     return true;
2181   }
2182   if (![ch isKindOfClass:[NSView class]]) return false;
2183   ch=NavigateUpScrollClipViews(ch);
2184   NSRECT_TO_RECT(r,[ch bounds]);
2185   ClientToScreen((HWND)ch,(POINT *)r);
2186   ClientToScreen((HWND)ch,((POINT *)r)+1);
2187   SWELL_END_TRY(return false;)
2189   return true;
2192 void GetWindowContentViewRect(HWND hwnd, RECT *r)
2194   SWELL_BEGIN_TRY
2195   NSWindow *nswnd;
2196   if (hwnd && [(id)hwnd isKindOfClass:[NSView class]] && (nswnd=[(NSView *)hwnd window]) && [nswnd contentView]==(id)hwnd)
2197     hwnd=(HWND)nswnd;
2198     
2199   if (hwnd && [(id)hwnd isKindOfClass:[NSWindow class]])
2200   {
2201     NSView *ch=[(id)hwnd contentView];
2202     NSRECT_TO_RECT(r,[ch bounds]);
2203     ClientToScreen(hwnd,(POINT *)r);
2204     ClientToScreen(hwnd,((POINT *)r)+1);
2205   }
2206   else GetWindowRect(hwnd,r);
2207   SWELL_END_TRY(;)
2211 void GetClientRect(HWND hwnd, RECT *r)
2213   r->left=r->top=r->right=r->bottom=0;
2214   if (WDL_NOT_NORMALLY(!hwnd)) return;
2215   
2216   SWELL_BEGIN_TRY
2217   id ch=(id)hwnd;
2218   if ([ch isKindOfClass:[NSWindow class]]) ch=[((NSWindow *)ch) contentView];
2219   if (!ch || ![ch isKindOfClass:[NSView class]]) return;
2220   ch=NavigateUpScrollClipViews(ch);
2221   
2222   NSRECT_TO_RECT(r,[ch bounds]);
2224   // todo this may need more attention
2225   NCCALCSIZE_PARAMS tr={{*r,},};
2226   SendMessage(hwnd,WM_NCCALCSIZE,FALSE,(LPARAM)&tr);
2227   r->right = r->left + (tr.rgrc[0].right-tr.rgrc[0].left);
2228   r->bottom = r->top + (tr.rgrc[0].bottom-tr.rgrc[0].top);
2229   SWELL_END_TRY(;)
2234 void SetWindowPos(HWND hwnd, HWND hwndAfter, int x, int y, int cx, int cy, int flags)
2236   if (WDL_NOT_NORMALLY(!hwnd)) return;
2238   SWELL_BEGIN_TRY
2239   NSWindow *nswnd; // content views = move window
2240   if (hwnd && [(id)hwnd isKindOfClass:[NSView class]] && (nswnd=[(NSView *)hwnd window]) && [nswnd contentView]==(id)hwnd)
2241     hwnd=(HWND)nswnd;
2243  // todo: handle SWP_SHOWWINDOW
2244   id ch=(id)hwnd;
2245   bool isview=false;
2246   if ([ch isKindOfClass:[NSWindow class]] || (isview=[ch isKindOfClass:[NSView class]])) 
2247   {
2248     if (isview)
2249     {
2250       ch=NavigateUpScrollClipViews(ch);
2251       if (isview && !(flags&SWP_NOZORDER))
2252       {
2253         NSView *v = (NSView *)ch;
2254         NSView *par = [v superview];
2255         NSArray *subs = [par subviews];
2256         NSInteger idx = [subs indexOfObjectIdenticalTo:v], cnt=[subs count];
2257         
2258         NSView *viewafter = NULL;            
2259         NSWindowOrderingMode omode = NSWindowAbove;
2260         
2261         if (cnt>1 && hwndAfter != (HWND)ch)
2262         {
2263           if (hwndAfter==HWND_TOPMOST||hwndAfter==HWND_NOTOPMOST)
2264           {
2265           }
2266           else if (hwndAfter == HWND_TOP)
2267           {
2268             if (idx<cnt-1) viewafter = [subs objectAtIndex:cnt-1];
2269           }
2270           else if (hwndAfter == HWND_BOTTOM)
2271           {
2272             if (idx>0) viewafter = [subs objectAtIndex:0];
2273             omode = NSWindowBelow;
2274           }
2275           else 
2276           {
2277             NSInteger a=[subs indexOfObjectIdenticalTo:(NSView *)hwndAfter];
2278             if (a != NSNotFound && a != (idx-1)) viewafter = (NSView *)hwndAfter;
2279           }
2280         }
2281         
2282         if (viewafter)
2283         { 
2284           HWND h = GetCapture();
2285           if (!h || (h!=(HWND)v && !IsChild((HWND)v,h))) // if this window is captured don't reorder!
2286           {
2287             NSView *oldfoc = (NSView*)[[v window] firstResponder];
2288             if (!oldfoc || ![oldfoc isKindOfClass:[NSView class]] || 
2289                 (oldfoc != v && ![oldfoc isDescendantOf:v])) oldfoc=NULL;
2290           
2291             // better way to do this? maybe :/
2292             [v retain];
2293             [v removeFromSuperviewWithoutNeedingDisplay];
2294             [par addSubview:v positioned:omode relativeTo:viewafter];
2295           
2296             if (oldfoc && [oldfoc isKindOfClass:[NSView class]] && [oldfoc window] == [v window])
2297               [[v window] makeFirstResponder:oldfoc];
2299             [v release];
2300           }
2301         }
2302       }
2303     }    
2304     NSRect f=[ch frame];
2305     bool repos=false;
2306     if (!(flags&SWP_NOMOVE))
2307     {
2308       f.origin.x=(float)x;
2309       f.origin.y=(float)y;
2310       repos=true;
2311     }
2312     if (!(flags&SWP_NOSIZE))
2313     {
2314       f.size.width=(float)cx;
2315       f.size.height=(float)cy;
2316       if (f.size.height<0)f.size.height=-f.size.height;
2317       repos=true;
2318     }
2319     if (repos)
2320     {
2321       if (!isview)
2322       {
2323         NSSize mins=[ch minSize];
2324         NSSize maxs=[ch maxSize];
2325         if (f.size.width  < mins.width) f.size.width=mins.width;
2326         else if (f.size.width > maxs.width) f.size.width=maxs.width;
2327         if (f.size.height < mins.height) f.size.height=mins.height;
2328         else if (f.size.height> maxs.height) f.size.height=maxs.height;
2329         [ch setFrame:f display:NO];
2330         [ch display];
2331       }
2332       else
2333       {
2334         // this doesnt seem to actually be a good idea anymore
2335   //      if ([[ch window] contentView] != ch && ![[ch superview] isFlipped])
2336 //          f.origin.y -= f.size.height;
2337         [ch setFrame:f];
2338         if ([ch isKindOfClass:[NSScrollView class]])
2339         {
2340           NSView *cv=[ch documentView];
2341           if (cv && [cv isKindOfClass:[NSTextView class]])
2342           {
2343             NSRect fr=[cv frame];
2344             NSSize sz=[ch contentSize];
2345             int a=0;
2346             if (![ch hasHorizontalScroller]) {a ++; fr.size.width=sz.width; }
2347             if (![ch hasVerticalScroller]) { a++; fr.size.height=sz.height; }
2348             if (a) [cv setFrame:fr];
2349           }
2350         }
2351       }
2352     }    
2353     return;
2354   }  
2355   SWELL_END_TRY(;)  
2358 BOOL EnumWindows(BOOL (*proc)(HWND, LPARAM), LPARAM lp)
2360   NSArray *ch=[NSApp windows];
2361   [ch retain];
2362   const NSInteger n=[ch count];
2363   for(NSInteger x=0;x<n; x ++)
2364   {
2365     NSWindow *w = [ch objectAtIndex:x];
2366     if (!proc((HWND)[w contentView],lp)) 
2367     {
2368       [ch release];
2369       return FALSE;
2370     }
2371   }
2372   [ch release];
2373   return TRUE;
2377 HWND GetWindow(HWND hwnd, int what)
2379   if (WDL_NOT_NORMALLY(!hwnd)) return 0;
2380   SWELL_BEGIN_TRY
2382   if ([(id)hwnd isKindOfClass:[NSWindow class]]) hwnd=(HWND)[(id)hwnd contentView];
2383   if (!hwnd || ![(id)hwnd isKindOfClass:[NSView class]]) return 0;
2384   
2385   NSView *v=(NSView *)hwnd;
2386   if (what == GW_CHILD)
2387   {
2388     NSArray *ar=[v subviews];
2389     if (ar && [ar count]>0)
2390     {
2391       return (HWND)[ar objectAtIndex:0];
2392     }
2393     return 0;
2394   }
2395   if (what == GW_OWNER)
2396   {
2397     v=NavigateUpScrollClipViews(v);
2398     if ([[v window] contentView] == v)
2399     {
2400       if ([[v window] respondsToSelector:@selector(swellGetOwner)])
2401       {
2402         return (HWND)[(SWELL_ModelessWindow*)[v window] swellGetOwner];
2403       }
2404       return 0;
2405     }
2406     return (HWND)[v superview];
2407   }
2408   
2409   if (what >= GW_HWNDFIRST && what <= GW_HWNDPREV)
2410   {
2411     v=NavigateUpScrollClipViews(v);
2412     if ([[v window] contentView] == v)
2413     {
2414       if (what <= GW_HWNDLAST) return (HWND)hwnd; // content view is only window
2415       
2416       return 0; // we're the content view so cant do next/prev
2417     }
2418     NSView *par=[v superview];
2419     if (par)
2420     {
2421       NSArray *ar=[par subviews];
2422       NSInteger cnt;
2423       if (ar && (cnt=[ar count]) > 0)
2424       {
2425         if (what == GW_HWNDFIRST)
2426           return (HWND)[ar objectAtIndex:0];
2427         if (what == GW_HWNDLAST)
2428           return (HWND)[ar objectAtIndex:(cnt-1)];
2429         
2430         NSInteger idx=[ar indexOfObjectIdenticalTo:v];
2431         if (idx == NSNotFound) return 0;
2433         if (what==GW_HWNDNEXT) idx++;
2434         else if (what==GW_HWNDPREV) idx--;
2435         
2436         if (idx<0 || idx>=cnt) return 0;
2437         
2438         return (HWND)[ar objectAtIndex:idx];
2439       }
2440     }
2441     return 0;
2442   }
2443   SWELL_END_TRY(;)
2444   return 0;
2448 HWND GetParent(HWND hwnd)
2449 {  
2450   SWELL_BEGIN_TRY
2451   if (WDL_NORMALLY(hwnd) && [(id)hwnd isKindOfClass:[NSView class]])
2452   {
2453     hwnd=(HWND)NavigateUpScrollClipViews((NSView *)hwnd);
2455     NSView *cv=[[(NSView *)hwnd window] contentView];
2456     if (cv == (NSView *)hwnd) hwnd=(HWND)[(NSView *)hwnd window]; // passthrough to get window parent
2457     else
2458     {
2459       HWND h=(HWND)[(NSView *)hwnd superview];
2460       return h;
2461     }
2462   }
2463   
2464   if (hwnd && [(id)hwnd isKindOfClass:[NSWindow class]]) 
2465   {
2466     HWND h= (HWND)[(NSWindow *)hwnd parentWindow];
2467     if (h) h=(HWND)[(NSWindow *)h contentView];
2468     if (h) return h;
2469   }
2470   
2471   if (hwnd && [(id)hwnd respondsToSelector:@selector(swellGetOwner)])
2472   {
2473     HWND h= (HWND)[(SWELL_ModelessWindow *)hwnd swellGetOwner];
2474     if (h && [(id)h isKindOfClass:[NSWindow class]]) h=(HWND)[(NSWindow *)h contentView];
2475     return h;  
2476   }
2477   
2478   SWELL_END_TRY(;)
2479   return 0;
2482 HWND SetParent(HWND hwnd, HWND newPar)
2484   SWELL_BEGIN_TRY
2485   NSView *v=(NSView *)hwnd;
2486   WDL_ASSERT(hwnd != NULL);
2487   if (!v || ![(id)v isKindOfClass:[NSView class]]) return 0;
2488   v=NavigateUpScrollClipViews(v);
2489   
2490   if ([(id)hwnd isKindOfClass:[NSView class]])
2491   {
2492     NSView *tv=(NSView *)hwnd;
2493     if ([[tv window] contentView] == tv) // if we're reparenting a contentview (aka top level window)
2494     {
2495       if (!newPar) return NULL;
2496     
2497       NSView *npv = (NSView *)newPar;
2498       if ([npv isKindOfClass:[NSWindow class]]) npv=[(NSWindow *)npv contentView];
2499       if (!npv || ![npv isKindOfClass:[NSView class]])
2500         return NULL;
2501     
2502       char oldtitle[2048];
2503       oldtitle[0]=0;
2504       GetWindowText(hwnd,oldtitle,sizeof(oldtitle));
2505     
2506       NSWindow *oldwnd = [tv window];
2507       id oldown = NULL;
2508       if ([oldwnd respondsToSelector:@selector(swellGetOwner)]) oldown=[(SWELL_ModelessWindow*)oldwnd swellGetOwner];
2510       if ([tv isKindOfClass:[SWELL_hwndChild class]]) ((SWELL_hwndChild*)tv)->m_lastTopLevelOwner = oldown;
2511     
2512       [tv retain];
2513       SWELL_hwndChild *tmpview = [[SWELL_hwndChild alloc] initChild:nil Parent:(NSView *)oldwnd dlgProc:nil Param:0];          
2514       [tmpview release];
2515     
2516       [npv addSubview:tv];  
2517       [tv release];
2518     
2519       DestroyWindow((HWND)oldwnd); // close old window since its no longer used
2520       if (oldtitle[0]) SetWindowText(hwnd,oldtitle);
2521       return (HWND)npv;
2522     }
2523     else if (!newPar) // not content view, not parent (so making it a top level modeless dialog)
2524     {
2525       char oldtitle[2048];
2526       oldtitle[0]=0;
2527       GetWindowText(hwnd,oldtitle,sizeof(oldtitle));
2528       
2529       [tv retain];
2530       [tv removeFromSuperview];
2532     
2533       unsigned int wf=(NSTitledWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask|NSResizableWindowMask);
2534       if ([tv respondsToSelector:@selector(swellCreateWindowFlags)])
2535         wf=(unsigned int)[(SWELL_hwndChild *)tv swellCreateWindowFlags];
2537       HWND newOwner=NULL;
2538       if ([tv isKindOfClass:[SWELL_hwndChild class]])
2539       {
2540          id oldown = ((SWELL_hwndChild*)tv)->m_lastTopLevelOwner;
2541          if (oldown)
2542          {
2543            NSArray *ch=[NSApp windows];
2544            const NSInteger n = [ch count];
2545            for(NSInteger x=0;x<n && !newOwner; x ++)
2546            {
2547              NSWindow *w = [ch objectAtIndex:x];
2548              if (w == (NSWindow *)oldown || [w contentView] == (NSView *)oldown) newOwner = (HWND)w;
2549            }
2550          }
2551       }
2553       HWND SWELL_CreateModelessFrameForWindow(HWND childW, HWND ownerW, unsigned int);
2554       HWND bla=SWELL_CreateModelessFrameForWindow((HWND)tv,(HWND)newOwner,wf);
2555       // create a new modeless frame 
2557      
2558       
2559       [(NSWindow *)bla display];
2560       
2561       [tv release];
2562       
2563       if (oldtitle[0]) SetWindowText(hwnd,oldtitle);
2564       
2565       return NULL;
2566       
2567     }
2568   }
2569   HWND ret=(HWND) [v superview];
2570   if (ret) 
2571   {
2572     [v retain];
2573     [v removeFromSuperview];
2574   }
2575   NSView *np=(NSView *)newPar;
2576   if (np && [np isKindOfClass:[NSWindow class]]) np=[(NSWindow *)np contentView];
2577   
2578   if (np && [np isKindOfClass:[NSView class]])
2579   {
2580     [np addSubview:v];
2581     [v release];
2582   }
2583   return ret;
2584   SWELL_END_TRY(;)
2585   return NULL;
2589 int IsChild(HWND hwndParent, HWND hwndChild)
2591   if (!hwndParent || !hwndChild || hwndParent == hwndChild) return 0;
2592   SWELL_BEGIN_TRY
2593   id par=(id)hwndParent;
2594   id ch=(id)hwndChild;
2595   if (![ch isKindOfClass:[NSView class]]) return 0;
2596   if ([par isKindOfClass:[NSWindow class]])
2597   {
2598     return [ch window] == par;
2599   }
2600   else if ([par isKindOfClass:[NSView class]])
2601   {
2602     return !![ch isDescendantOf:par];
2603   }
2604   SWELL_END_TRY(;)
2605   return 0;
2608 HWND GetForegroundWindow()
2610   SWELL_BEGIN_TRY
2611   NSWindow *window=[NSApp keyWindow];
2612   if (!window) return 0;
2613   id ret=[window firstResponder];
2614   if (ret && [ret isKindOfClass:[NSView class]]) 
2615   {
2616 //    if (ret == [window contentView]) return (HWND) window;
2617     return (HWND) ret;
2618   }
2619   return (HWND)window;
2620   SWELL_END_TRY(;)
2621   return NULL;
2624 HWND GetFocus()
2626   SWELL_BEGIN_TRY
2627   NSWindow *window=[NSApp keyWindow];
2628   if (!window) return 0;
2629   id ret=[window firstResponder];
2630   if (ret && [ret isKindOfClass:[NSView class]]) 
2631   {
2632 //    if (ret == [window contentView]) return (HWND) window;
2634     if ([ret isKindOfClass:[NSTextView class]] && [ret superview] && [[ret superview] superview])
2635     {
2636       NSView* v = [[ret superview] superview];
2637       if ([v isKindOfClass:[NSTextField class]]) return (HWND) v;
2638     }
2640     return (HWND) ret;
2641   }
2642   SWELL_END_TRY(;)
2643   return 0;
2646 bool IsEquivalentTextView(HWND h1, HWND h2)
2648   if (!h1 || !h2) return false;
2649   if (h1 == h2) return true;
2650   SWELL_BEGIN_TRY
2651   NSView* v1 = (NSView*)h1;
2652   NSView* v2 = (NSView*)h2;
2653   if ([v1 isKindOfClass:[NSTextField class]] && [v2 isKindOfClass:[NSTextView class]])
2654   {
2655     NSView* t = v1;
2656     v1 = v2;
2657     v2 = t;
2658   }
2659   if ([v1 isKindOfClass: [NSTextView class]] && [v2 isKindOfClass:[NSTextField class]])
2660   {
2661     if ([v1 superview] && [[v1 superview] superview] && [[[v1 superview] superview] superview] == v2) return true;
2662   }
2663   SWELL_END_TRY(;)
2664   return false;
2666   
2669 BOOL SetDlgItemText(HWND hwnd, int idx, const char *text)
2671   NSView *obj=(NSView *)(idx ? GetDlgItem(hwnd,idx) : hwnd);
2672   if (WDL_NOT_NORMALLY(!obj)) return false;
2673   
2674   SWELL_BEGIN_TRY
2675   NSWindow *nswnd;
2676   if ([(id)obj isKindOfClass:[NSView class]] && (nswnd=[(NSView *)obj window]) && [nswnd contentView]==(id)obj)
2677   {
2678     SetDlgItemText((HWND)nswnd,0,text); // also set window if setting content view
2679   }
2680   
2681   if ([obj respondsToSelector:@selector(onSwellSetText:)])
2682   {
2683     [(SWELL_hwndChild*)obj onSwellSetText:text];
2684     return TRUE;
2685   }
2686   
2687   BOOL rv=TRUE;  
2688   NSString *lbl=(NSString *)SWELL_CStringToCFString(text);
2689   if ([obj isKindOfClass:[NSWindow class]] || [obj isKindOfClass:[NSButton class]]) [(NSButton*)obj setTitle:lbl];
2690   else if ([obj isKindOfClass:[NSControl class]]) 
2691   {
2692     [(NSControl*)obj setStringValue:lbl];
2693     if ([obj isKindOfClass:[NSTextField class]] && [(NSTextField *)obj isEditable])
2694     {
2695       if (![obj isKindOfClass:[NSComboBox class]])
2696       {
2697         HWND par = GetParent((HWND)obj);
2698         if (par)
2699           SendMessage(par,WM_COMMAND,MAKELONG([(NSControl *)obj tag],EN_CHANGE),(LPARAM)obj);
2700       }
2701     }
2702   }
2703   else if ([obj isKindOfClass:[NSText class]])  
2704   {
2705     // todo if there is a way to find out that the window's NSTextField is already assigned 
2706     // to another field, restore the assignment afterwards
2707     [(NSText*)obj setString:lbl];
2708     [obj setNeedsDisplay:YES]; // required on Sierra, it seems -- if the parent is hidden (e.g. DialogBox() + WM_INITDIALOG), the view is not drawn
2709   }
2710   else if ([obj isKindOfClass:[NSBox class]])
2711   {
2712     [(NSBox *)obj setTitle:lbl];
2713   }
2714   else
2715   {
2716     rv=FALSE;
2717   }
2718   
2719   [lbl release];
2720   return rv;
2721   SWELL_END_TRY(;)
2722   return FALSE;
2725 int GetWindowTextLength(HWND hwnd)
2727   if (WDL_NOT_NORMALLY(!hwnd)) return 0;
2729   SWELL_BEGIN_TRY
2731   NSView *pvw = (NSView *)hwnd;
2732   if ([(id)pvw isKindOfClass:[NSView class]] && [[(id)pvw window] contentView] == pvw)
2733   {
2734     pvw=(NSView *)[(id)pvw window];
2735   }
2737   if ([(id)pvw respondsToSelector:@selector(onSwellGetText)])
2738   {
2739     const char *p=(const char *)[(SWELL_hwndChild*)pvw onSwellGetText];
2740     return p ? (int)strlen(p) : 0;
2741   }
2743   NSString *s;
2745   if ([pvw isKindOfClass:[NSButton class]]||[pvw isKindOfClass:[NSWindow class]]) s=[((NSButton *)pvw) title];
2746   else if ([pvw isKindOfClass:[NSControl class]]) s=[((NSControl *)pvw) stringValue];
2747   else if ([pvw isKindOfClass:[NSText class]])  s=[(NSText*)pvw string];
2748   else if ([pvw isKindOfClass:[NSBox class]]) s=[(NSBox *)pvw title];
2749   else return 0;
2751   const char *p = s ? [s UTF8String] : NULL;
2752   return p ? (int)strlen(p) : 0;
2754   SWELL_END_TRY(;)
2755   return 0;
2758 BOOL GetDlgItemText(HWND hwnd, int idx, char *text, int textlen)
2760   *text=0;
2761   NSView *pvw=(NSView *)(idx?GetDlgItem(hwnd,idx) : hwnd);
2762   if (WDL_NOT_NORMALLY(!pvw)) return false;
2764   SWELL_BEGIN_TRY
2765   
2766   if ([(id)pvw isKindOfClass:[NSView class]] && [[(id)pvw window] contentView] == pvw)
2767   {
2768     pvw=(NSView *)[(id)pvw window];
2769   }
2770   
2771   if ([(id)pvw respondsToSelector:@selector(onSwellGetText)])
2772   {  
2773     const char *p=(const char *)[(SWELL_hwndChild*)pvw onSwellGetText];
2774     lstrcpyn_safe(text,p?p:"",textlen);
2775     return TRUE;
2776   }
2777   
2778   NSString *s;
2779   
2780   if ([pvw isKindOfClass:[NSButton class]]||[pvw isKindOfClass:[NSWindow class]]) s=[((NSButton *)pvw) title];
2781   else if ([pvw isKindOfClass:[NSControl class]]) s=[((NSControl *)pvw) stringValue];
2782   else if ([pvw isKindOfClass:[NSText class]])  s=[(NSText*)pvw string];
2783   else if ([pvw isKindOfClass:[NSBox class]]) s=[(NSBox *)pvw title];
2784   else return FALSE;
2785   
2786   if (s) SWELL_CFStringToCString(s,text,textlen);
2787 //    [s getCString:text maxLength:textlen];
2788     
2789   return !!s;
2790   SWELL_END_TRY(;)
2791   return FALSE;
2794 void CheckDlgButton(HWND hwnd, int idx, int check)
2796   NSView *pvw=(NSView *)GetDlgItem(hwnd,idx);
2797   if (WDL_NOT_NORMALLY(!pvw)) return;
2798   if ([pvw isKindOfClass:[NSButton class]]) 
2799     [(NSButton*)pvw setState:(check&BST_INDETERMINATE)?NSMixedState:((check&BST_CHECKED)?NSOnState:NSOffState)];
2803 int IsDlgButtonChecked(HWND hwnd, int idx)
2805   NSView *pvw=(NSView *)GetDlgItem(hwnd,idx);
2806   if (WDL_NORMALLY(pvw && [pvw isKindOfClass:[NSButton class]]))
2807   {
2808     NSInteger a=[(NSButton*)pvw state];
2809     if (a==NSMixedState) return BST_INDETERMINATE;
2810     return a!=NSOffState;
2811   }
2812   return 0;
2815 void SWELL_TB_SetPos(HWND hwnd, int idx, int pos)
2817   NSSlider *p=(NSSlider *)GetDlgItem(hwnd,idx);
2818   if (WDL_NORMALLY(p) && [p isKindOfClass:[NSSlider class]]) 
2819   {
2820     [p setDoubleValue:(double)pos];
2821   }
2822   else 
2823   {
2824     sendSwellMessage(p,TBM_SETPOS,1,pos); 
2825   }
2828 void SWELL_TB_SetRange(HWND hwnd, int idx, int low, int hi)
2830   NSSlider *p=(NSSlider *)GetDlgItem(hwnd,idx);
2831   if (WDL_NORMALLY(p) && [p isKindOfClass:[NSSlider class]])
2832   {
2833     [p setMinValue:low];
2834     [p setMaxValue:hi];
2835   }
2836   else 
2837   {
2838     sendSwellMessage(p,TBM_SETRANGE,1,MAKELONG(low&0xffff,hi));
2839   }
2840   
2843 int SWELL_TB_GetPos(HWND hwnd, int idx)
2845   NSSlider *p=(NSSlider *)GetDlgItem(hwnd,idx);
2846   if (WDL_NORMALLY(p) && [p isKindOfClass:[NSSlider class]]) 
2847   {
2848     return (int) ([p doubleValue]+0.5);
2849   }
2850   else 
2851   {
2852     return (int) sendSwellMessage(p,TBM_GETPOS,0,0);
2853   }
2854   return 0;
2857 void SWELL_TB_SetTic(HWND hwnd, int idx, int pos)
2859   NSSlider *p=(NSSlider *)GetDlgItem(hwnd,idx);
2860   WDL_ASSERT(p != NULL);
2861   sendSwellMessage(p,TBM_SETTIC,0,pos);
2864 void SWELL_CB_DeleteString(HWND hwnd, int idx, int wh)
2866   NSComboBox *p=(NSComboBox *)GetDlgItem(hwnd,idx);
2867   if (WDL_NOT_NORMALLY(!p)) return;
2868   if ([p isKindOfClass:[SWELL_ComboBox class]])
2869   {
2870     if (wh>=0 && wh<[p numberOfItems])
2871     {
2872       SWELL_ComboBox *s = (SWELL_ComboBox *)p;
2873       if (s->m_ignore_selchg == wh) s->m_ignore_selchg=-1;
2874       else if (s->m_ignore_selchg >= wh) s->m_ignore_selchg--;
2875       [p removeItemAtIndex:wh];
2876       if (s->m_ids) ((SWELL_ComboBox*)p)->m_ids->Delete(wh);
2877     }
2878   }
2879   else if ( [p isKindOfClass:[NSPopUpButton class]])
2880   {
2881     NSMenu *menu = [p menu];
2882     if (menu)
2883     {
2884       if (wh >= 0 && wh < [menu numberOfItems])
2885         [menu removeItemAtIndex:wh];
2886     }
2887   }
2891 int SWELL_CB_FindString(HWND hwnd, int idx, int startAfter, const char *str, bool exact)
2893   NSComboBox *p=(NSComboBox *)GetDlgItem(hwnd,idx);  
2894   if (WDL_NOT_NORMALLY(!p)) return 0;
2895   
2896   int pos = startAfter;
2897   if (pos<0)pos=0;
2898   else pos++;
2899   
2900   const size_t l1len = strlen(str);
2901   const int ni=(int)[p numberOfItems];
2902   
2903   if ([p isKindOfClass:[NSComboBox class]])
2904   {
2905     for(;pos<ni;pos++)
2906     {
2907       NSString *s=[p itemObjectValueAtIndex:pos];
2908       if (s)
2909       {
2910         char buf[4096];
2911         SWELL_CFStringToCString(s,buf,sizeof(buf));
2912         if (exact ? !stricmp(str,buf) : !strnicmp(str,buf,l1len))
2913           return pos;
2914       }
2915     }
2916   }
2917   else 
2918   {
2919     for(;pos<ni;pos++)
2920     {
2921       NSMenuItem *i=[(NSPopUpButton *)p itemAtIndex:pos];
2922       if (i)
2923       {
2924         NSString *s=[i title];
2925         if (s)          
2926         {
2927           char buf[4096];
2928           SWELL_CFStringToCString(s,buf,sizeof(buf));
2929           if (exact ? !stricmp(str,buf) : !strnicmp(str,buf,l1len))
2930             return pos;
2931         }
2932       }
2933     }
2934   }
2935   return -1;
2938 int SWELL_CB_GetItemText(HWND hwnd, int idx, int item, char *buf, int bufsz)
2940   NSComboBox *p=(NSComboBox *)GetDlgItem(hwnd,idx);
2942   if (buf) *buf=0;
2943   if (WDL_NOT_NORMALLY(!p)) return CB_ERR;
2944   const int ni = (int)[p numberOfItems];
2945   if (item < 0 || item >= ni) return CB_ERR;
2946   
2947   if ([p isKindOfClass:[NSComboBox class]])
2948   {
2949     NSString *s=[p itemObjectValueAtIndex:item];
2950     if (s)
2951     {
2952       if (!buf) return (int) ([s lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 64);
2954       SWELL_CFStringToCString(s,buf,bufsz);
2955       return 1;
2956     }
2957   }
2958   else 
2959   {
2960     NSMenuItem *i=[(NSPopUpButton *)p itemAtIndex:item];
2961     if (i)
2962     {
2963       NSString *s=[i title];
2964       if (s)
2965       {
2966         if (!buf) return (int) ([s lengthOfBytesUsingEncoding:NSUTF8StringEncoding] + 64);
2968         SWELL_CFStringToCString(s,buf,bufsz);
2969         return 1;
2970       }
2971     }
2972   }
2973   return CB_ERR;
2977 int SWELL_CB_InsertString(HWND hwnd, int idx, int pos, const char *str)
2979   NSString *label=(NSString *)SWELL_CStringToCFString(str);
2980   NSComboBox *p=(NSComboBox *)GetDlgItem(hwnd,idx);
2981   if (WDL_NOT_NORMALLY(!p)) return 0;
2982   
2983   bool isAppend=false;
2984   const int ni = (int)[p numberOfItems];
2985   if (pos == -1000) 
2986   {
2987     isAppend=true;
2988     pos=ni;
2989   }
2990   else if (pos < 0) pos=0;
2991   else if (pos > ni) pos=ni;
2992   
2993    
2994   if ([p isKindOfClass:[SWELL_ComboBox class]])
2995   {
2996     SWELL_ComboBox *s = (SWELL_ComboBox *)p;
2997     if (isAppend && (((int)[s getSwellStyle]) & CBS_SORT))
2998     {
2999       pos=(int)arr_bsearch_mod(label,[p objectValues],_nsStringSearchProc);
3000     }
3001     
3002     if (s->m_ignore_selchg >= pos) s->m_ignore_selchg++;
3003     if (pos==ni)
3004       [p addItemWithObjectValue:label];
3005     else
3006       [p insertItemWithObjectValue:label atIndex:pos];
3007   
3008     if (s->m_ids) s->m_ids->Insert(pos,(char*)0);
3009     [p setNumberOfVisibleItems:(ni+1)];
3010   }
3011   else
3012   {
3013     NSMenu *menu = [(NSPopUpButton *)p menu];
3014     if (menu)
3015     {
3016       const bool needclearsel = [p indexOfSelectedItem] < 0;
3017       if (isAppend && [p respondsToSelector:@selector(getSwellStyle)] && (((int)[(SWELL_PopUpButton*)p getSwellStyle]) & CBS_SORT))
3018       {
3019         pos=(int)arr_bsearch_mod(label,[menu itemArray],_nsMenuSearchProc);
3020       }
3021       NSMenuItem *item=[menu insertItemWithTitle:label action:NULL keyEquivalent:@"" atIndex:pos];
3022       [item setEnabled:YES];      
3023       if (needclearsel) [(NSPopUpButton *)p selectItemAtIndex:-1];
3024     }
3025   }
3026   [label release];
3027   return pos;
3028   
3031 int SWELL_CB_AddString(HWND hwnd, int idx, const char *str)
3033   return SWELL_CB_InsertString(hwnd,idx,-1000,str);
3036 int SWELL_CB_GetCurSel(HWND hwnd, int idx)
3038   NSComboBox *p=(NSComboBox *)GetDlgItem(hwnd,idx);
3039   if (WDL_NOT_NORMALLY(!p)) return -1;
3040   return (int)[p indexOfSelectedItem];
3043 void SWELL_CB_SetCurSel(HWND hwnd, int idx, int item)
3045   NSComboBox *cb = (NSComboBox *)GetDlgItem(hwnd,idx);
3046   if (WDL_NOT_NORMALLY(!cb)) return;
3047   const bool is_swell_cb = [cb isKindOfClass:[SWELL_ComboBox class]];
3049   if (item < 0 || item >= [cb numberOfItems])
3050   {
3051     // combo boxes can be NSComboBox or NSPopupButton, NSComboBox needs
3052     // a different deselect method (selectItemAtIndex:-1 throws an exception)
3053     if ([cb isKindOfClass:[NSComboBox class]])
3054     {
3055       const NSInteger sel = [cb indexOfSelectedItem];
3056       if (sel>=0) [cb deselectItemAtIndex:sel];
3057       if (is_swell_cb) ((SWELL_ComboBox *)cb)->m_ignore_selchg = -1;
3058     }
3059     else if ([cb isKindOfClass:[NSPopUpButton class]])
3060       [(NSPopUpButton*)cb selectItemAtIndex:-1];
3061   }
3062   else
3063   {
3064     if (is_swell_cb) ((SWELL_ComboBox *)cb)->m_ignore_selchg = item;
3065     [cb selectItemAtIndex:item];
3066   }
3069 int SWELL_CB_GetNumItems(HWND hwnd, int idx)
3071   NSComboBox *p=(NSComboBox *)GetDlgItem(hwnd,idx);
3072   if (WDL_NOT_NORMALLY(!p)) return 0;
3073   return (int)[p numberOfItems];
3078 void SWELL_CB_SetItemData(HWND hwnd, int idx, int item, LONG_PTR data)
3080   id cb=(id)GetDlgItem(hwnd,idx);
3081   if (WDL_NOT_NORMALLY(!cb)) return;
3083   if ([cb isKindOfClass:[NSPopUpButton class]])
3084   {
3085     if (item < 0 || item >= [cb numberOfItems]) return;
3086     NSMenuItem *it=[(NSPopUpButton*)cb itemAtIndex:item];
3087     if (!it) return;
3088   
3089     SWELL_DataHold *p=[[SWELL_DataHold alloc] initWithVal:(void *)data];  
3090     [it setRepresentedObject:p];
3091     [p release];
3092   }
3093   else if ([cb isKindOfClass:[SWELL_ComboBox class]])
3094   {
3095     if (item < 0 || item >= [cb numberOfItems]) return;
3096     if (((SWELL_ComboBox*)cb)->m_ids) ((SWELL_ComboBox*)cb)->m_ids->Set(item,(char*)data);
3097   }
3100 LONG_PTR SWELL_CB_GetItemData(HWND hwnd, int idx, int item)
3102   id cb=(id)GetDlgItem(hwnd,idx);
3103   if (WDL_NOT_NORMALLY(!cb)) return 0;
3104   if ([cb isKindOfClass:[NSPopUpButton class]])
3105   {
3106     if (item < 0 || item >= [cb numberOfItems]) return 0;
3107     NSMenuItem *it=[(NSPopUpButton*)cb itemAtIndex:item];
3108     if (!it) return 0;
3109     id p= [it representedObject];
3110     if (!p || ![p isKindOfClass:[SWELL_DataHold class]]) return 0;
3111     return (LONG_PTR) (void *)[p getValue];
3112   }
3113   else if ([cb isKindOfClass:[SWELL_ComboBox class]])
3114   {
3115     if (item < 0 || item >= [cb numberOfItems]) return 0;
3116     if (((SWELL_ComboBox*)cb)->m_ids) return (LONG_PTR) ((SWELL_ComboBox*)cb)->m_ids->Get(item);    
3117   }
3118   return 0;
3121 void SWELL_CB_Empty(HWND hwnd, int idx)
3123   id cb=(id)GetDlgItem(hwnd,idx);
3124   if (WDL_NOT_NORMALLY(!cb)) return;  
3125   if ([cb isKindOfClass:[NSPopUpButton class]] ||
3126       [cb isKindOfClass:[NSComboBox class]]) [cb removeAllItems];
3127   
3128   if ([cb isKindOfClass:[SWELL_ComboBox class]])
3129   {
3130     SWELL_ComboBox *p = (SWELL_ComboBox *)cb;
3131     p->m_ignore_selchg = -1;
3132     if (p->m_ids) p->m_ids->Empty();
3133   }
3137 BOOL SetDlgItemInt(HWND hwnd, int idx, int val, int issigned)
3139   char buf[128];
3140   sprintf(buf,issigned?"%d":"%u",val);
3141   return SetDlgItemText(hwnd,idx,buf);
3144 int GetDlgItemInt(HWND hwnd, int idx, BOOL *translated, int issigned)
3146   char buf[128];
3147   if (!GetDlgItemText(hwnd,idx,buf,sizeof(buf)))
3148   {
3149     if (translated) *translated=0;
3150     return 0;
3151   }
3152   char *p=buf;
3153   while (*p == ' ' || *p == '\t') p++;
3154   int a=atoi(p);
3155   if ((a<0 && !issigned) || (!a && p[0] != '0')) { if (translated) *translated=0; return 0; }
3156   if (translated) *translated=1;
3157   return a;
3160 void SWELL_HideApp()
3162   [NSApp hide:NSApp];
3166 BOOL SWELL_GetGestureInfo(LPARAM lParam, GESTUREINFO* gi)
3168   if (!lParam || !gi) return FALSE;
3169   memcpy(gi, (GESTUREINFO*)lParam, sizeof(GESTUREINFO));
3170   return TRUE;
3172   
3174 void ShowWindow(HWND hwnd, int cmd)
3176   id pid=(id)hwnd;
3177   
3178   if (WDL_NORMALLY(pid) && [pid isKindOfClass:[NSWindow class]])
3179   {
3180     if (cmd == SW_SHOWNA && [pid isKindOfClass:[SWELL_ModelessWindow class]])
3181     {
3182       if (((SWELL_ModelessWindow *)pid)->m_wantInitialKeyWindowOnShow)
3183       {
3184         ((SWELL_ModelessWindow *)pid)->m_wantInitialKeyWindowOnShow=false;
3185         cmd = SW_SHOW;
3186       }
3187     }
3188     if (cmd==SW_SHOW)
3189     {
3190       [pid makeKeyAndOrderFront:pid];
3191     }
3192     else if (cmd==SW_SHOWNA)
3193     {
3194       [pid orderFront:pid];
3195     }
3196     else if (cmd==SW_HIDE)
3197     {
3198       [pid orderOut:pid];
3199     }
3200     else if (cmd==SW_SHOWMAXIMIZED)
3201     {
3202       if (![pid isZoomed]) [pid zoom:nil];
3203       [pid orderFront:pid];
3204     }
3205     else if (cmd == SW_RESTORE)
3206     {
3207       if ([pid isZoomed]) [pid zoom:nil];
3208       [pid orderFront:pid];
3209     }
3210     else if (cmd == SW_SHOWMINIMIZED)
3211     {   
3212       // this ought to work
3213       //if ([NSApp mainWindow] == pid)
3214       //{
3215       //  [NSApp hide:pid];
3216       //}
3217       //else
3218       //{
3219         [pid miniaturize:pid];
3220       //}
3221     }
3222     return;
3223   }
3224   if (!pid || ![pid isKindOfClass:[NSView class]]) return;
3225   
3226   pid=NavigateUpScrollClipViews(pid);
3227   
3228   switch (cmd)
3229   {
3230     case SW_RESTORE:
3231     case SW_SHOWMAXIMIZED:
3232     case SW_SHOW:
3233     case SW_SHOWNA:
3234       [((NSView *)pid) setHidden:NO];
3235     break;
3236     case SW_HIDE:
3237       {
3238         NSWindow *pw=[pid window];
3239         if (pw)
3240         {
3241           id foc=[pw firstResponder];
3242           if (foc && (foc == pid || IsChild((HWND)pid,(HWND)foc)))
3243           {
3244             [pw makeFirstResponder:nil];
3245           }
3246         }
3247         if (![((NSView *)pid) isHidden])
3248         {
3249           if ((NSView *)pid != [pw contentView])
3250           {
3251             HWND par = (HWND) [(NSView *)pid superview];
3252             if (par)
3253             {
3254               RECT r;
3255               GetWindowRect((HWND)pid,&r);
3256               ScreenToClient(par,(LPPOINT)&r);
3257               ScreenToClient(par,((LPPOINT)&r)+1);
3258               InvalidateRect(par,&r,FALSE);
3259             }
3260           }
3261           [((NSView *)pid) setHidden:YES];
3262         }
3263     }
3264     break;
3265   }
3266   
3267   NSWindow *nswnd;
3268   if ((nswnd=[(NSView *)pid window]) && [nswnd contentView]==(id)pid)
3269   {
3270     ShowWindow((HWND)nswnd,cmd);
3271   }
3274 void *SWELL_ModalWindowStart(HWND hwnd)
3276   if (hwnd && [(id)hwnd isKindOfClass:[NSView class]]) hwnd=(HWND)[(NSView *)hwnd window];
3277   if (WDL_NOT_NORMALLY(!hwnd)) return 0;
3278   return (void *)[NSApp beginModalSessionForWindow:(NSWindow *)hwnd];
3281 bool SWELL_ModalWindowRun(void *ctx, int *ret) // returns false and puts retval in *ret when done
3283   if (!ctx) return false;
3284   NSInteger r=[NSApp runModalSession:(NSModalSession)ctx];
3285   if (r==NSRunContinuesResponse) return true;
3286   if (ret) *ret=(int)r;
3287   return false;
3290 void SWELL_ModalWindowEnd(void *ctx)
3292   if (ctx) 
3293   {
3294     if ([NSApp runModalSession:(NSModalSession)ctx] == NSRunContinuesResponse)
3295     {    
3296       [NSApp stopModal];
3297       while ([NSApp runModalSession:(NSModalSession)ctx]==NSRunContinuesResponse) Sleep(10);
3298     }
3299     [NSApp endModalSession:(NSModalSession)ctx];
3300   }
3303 void SWELL_CloseWindow(HWND hwnd)
3305   if (WDL_NORMALLY(hwnd) && [(id)hwnd isKindOfClass:[NSWindow class]])
3306   {
3307     [((NSWindow*)hwnd) close];
3308   }
3309   else if (hwnd && [(id)hwnd isKindOfClass:[NSView class]])
3310   {
3311     [[(NSView*)hwnd window] close];
3312   }
3316 #include "swell-dlggen.h"
3318 static id m_make_owner;
3319 static NSRect m_transform;
3320 static float m_parent_h;
3321 static bool m_doautoright;
3322 static NSRect m_lastdoauto;
3323 static bool m_sizetofits;
3324 static int m_make_radiogroupcnt;
3326 #define ACTIONTARGET (m_make_owner)
3328 void SWELL_MakeSetCurParms(float xscale, float yscale, float xtrans, float ytrans, HWND parent, bool doauto, bool dosizetofit)
3330   if (parent) s_prefix_removals.Empty(true,free);
3331   m_make_radiogroupcnt=0;
3332   m_sizetofits=dosizetofit;
3333   m_lastdoauto.origin.x = 0;
3334   m_lastdoauto.origin.y = -100;
3335   m_lastdoauto.size.width = 0;
3336   m_doautoright=doauto;
3337   m_transform.origin.x=xtrans;
3338   m_transform.origin.y=ytrans;
3339   m_transform.size.width=xscale;
3340   m_transform.size.height=yscale;
3341   m_make_owner=(id)parent;
3342   if ([m_make_owner isKindOfClass:[NSWindow class]]) m_make_owner=[(NSWindow *)m_make_owner contentView];
3343   m_parent_h=100.0;
3344   if ([(id)m_make_owner isKindOfClass:[NSView class]])
3345   {
3346     m_parent_h=[(NSView *)m_make_owner bounds].size.height;
3347     if (m_transform.size.height > 0 && [(id)parent isFlipped])
3348       m_transform.size.height*=-1;
3349   }
3352 static void UpdateAutoCoords(NSRect r)
3354   m_lastdoauto.size.width=r.origin.x + r.size.width - m_lastdoauto.origin.x;
3357 static NSRect MakeCoords(int x, int y, int w, int h, bool wantauto, bool ignorevscaleheight=false)
3359   if (w<0&&h<0)
3360   {
3361     return NSMakeRect(-x,-y,-w,-h);
3362   }
3363   float ysc=m_transform.size.height;
3364   float ysc2 = ignorevscaleheight ? 1.0 : ysc;
3365   int newx=(int)((x+m_transform.origin.x)*m_transform.size.width + 0.5);
3366   int newy=(int)((ysc >= 0.0 ? m_parent_h - ((y+m_transform.origin.y) )*ysc + h*ysc2 : 
3367                          ((y+m_transform.origin.y) )*-ysc) + 0.5);
3368                          
3369   NSRect ret= NSMakeRect(newx,  
3370                          newy,                  
3371                         (int) (w*m_transform.size.width+0.5),
3372                         (int) (h*fabs(ysc2)+0.5));
3373                         
3374   NSRect oret=ret;
3375   if (wantauto && m_doautoright)
3376   {
3377     float dx = ret.origin.x - m_lastdoauto.origin.x;
3378     if (fabs(dx)<32 && m_lastdoauto.origin.y >= ret.origin.y && m_lastdoauto.origin.y < ret.origin.y + ret.size.height)
3379     {
3380       ret.origin.x += (int) m_lastdoauto.size.width;
3381     }
3382     
3383     m_lastdoauto.origin.x = oret.origin.x + oret.size.width;
3384     m_lastdoauto.origin.y = ret.origin.y + ret.size.height*0.5;
3385     m_lastdoauto.size.width=0;
3386   }
3387   return ret;
3390 static const double minwidfontadjust=1.81;
3391 #define TRANSFORMFONTSIZE (m_transform.size.width<1?8:m_transform.size.width<2?10:12)
3392 /// these are for swell-dlggen.h
3393 HWND SWELL_MakeButton(int def, const char *label, int idx, int x, int y, int w, int h, int flags)
3394 {  
3395   UINT_PTR a=(UINT_PTR)label;
3396   if (a < 65536) label = "ICONTEMP";
3397   SWELL_Button *button=[[SWELL_Button alloc] init];
3398   [button swellSetRadioFlags:4096];
3399   if (flags & BS_BITMAP)
3400   {
3401     SWELL_ImageButtonCell * cell = [[SWELL_ImageButtonCell alloc] init];
3402     [button setCell:cell];
3403     [cell release];
3404   }
3405   
3406   if (m_transform.size.width < minwidfontadjust)
3407   {
3408     [button setFont:[NSFont systemFontOfSize:TRANSFORMFONTSIZE]];
3409   }
3410   
3411   [button setTag:idx];
3413   NSRect tr=MakeCoords(x,y,w,h,true);
3414   if (g_swell_osx_style&1)
3415   {
3416     [button setBezelStyle:NSRoundedBezelStyle];
3417     if (tr.size.height >= 18 && tr.size.height<24)
3418     {
3419       tr.origin.y -= floor((24-tr.size.height)*0.5);
3420       tr.size.height=24;
3421     }
3422     tr.size.width += 14;
3423     tr.origin.x -= 7;
3424   }
3425   else
3426   {
3427     [button setBezelStyle:NSShadowlessSquareBezelStyle];
3428   }
3429   
3430   [button setFrame:tr];
3431   NSString *labelstr=(NSString *)SWELL_CStringToCFString_FilterPrefix(label);
3432   [button setTitle:labelstr];
3433   [button setTarget:ACTIONTARGET];
3434   [button setAction:@selector(onSwellCommand:)];
3435   if ((flags & BS_XPOSITION_MASK) == BS_LEFT) [button setAlignment:NSLeftTextAlignment];
3436   if (flags&SWELL_NOT_WS_VISIBLE) [button setHidden:YES];
3437   [m_make_owner addSubview:button];
3438   if (m_doautoright) UpdateAutoCoords([button frame]);
3439   if (def) [[m_make_owner window] setDefaultButtonCell:(NSButtonCell*)button];
3440   [labelstr release];
3441   [button release];
3442   return (HWND) button;
3446 @implementation SWELL_TextView
3448 STANDARD_CONTROL_NEEDSDISPLAY_IMPL("Edit")
3450 -(id)init
3452   if (NULL != (self = [super init]))
3453   {
3454     m_disable_menu = false;
3455     m_tag = 0;
3456   }
3457   return self;
3460 -(NSInteger) tag
3462   return m_tag;
3465 -(void) setTag:(NSInteger)tag
3467   m_tag=tag;
3470 -(LRESULT)onSwellMessage:(UINT)msg p1:(WPARAM)wParam p2:(LPARAM)lParam
3472   switch (msg)
3473   {
3474     case EM_SCROLL:
3475       if (wParam == SB_TOP)
3476       {
3477         [self scrollRangeToVisible:NSMakeRange(0, 0)];
3478       }
3479       else if (wParam == SB_BOTTOM)
3480       {
3481         NSUInteger len = [[self string] length];
3482         [self scrollRangeToVisible:NSMakeRange(len, 0)];
3483       }
3484     return 0;
3485     
3486     case EM_SETSEL:    
3487     {
3488       NSUInteger sl =  [[self string] length];
3489       if (wParam == -1) lParam = wParam = 0;
3490       else if (lParam == -1) lParam = sl;
3491       
3492       if (wParam>sl)wParam=sl;
3493       if (lParam>sl)lParam=sl;
3494       [self setSelectedRange:NSMakeRange(wParam, lParam>wParam ? lParam-wParam : 0)];
3495     }
3496     return 0;
3497     
3498     case EM_GETSEL:
3499     {
3500       NSRange r = [self selectedRange];
3501       if (wParam) *(int*)wParam = (int)r.location;
3502       if (lParam) *(int*)lParam = (int)(r.location+r.length);
3503     }
3504     return 0;
3505     case EM_REPLACESEL:
3506       if (lParam)
3507       {
3508         NSTextStorage *ts = [self textStorage];
3509         if (ts)
3510         {
3511           NSRange r = [self selectedRange];
3512           const char *s = (const char *)lParam;
3513           NSString *str = *s ? (NSString*)SWELL_CStringToCFString(s) : NULL;
3515           if (r.length > 0 && !str)
3516             [ts deleteCharactersInRange:r];
3517           else if (str && ![ts length])
3518             [self setString:str]; // dark-mode workaround, need default attributes
3519           else if (str)
3520             [ts replaceCharactersInRange:r withString:str];
3522           if (str) [str release];
3523         }
3524       }
3525     return 0;
3526       
3527     case WM_SETFONT:
3528     {
3529       NSFont *font = SWELL_GetNSFont((HGDIOBJ__*)wParam);
3530       if (font) [self setFont:font];
3531     }
3532     return 0;
3533   }
3534   return 0;
3537 - (BOOL)becomeFirstResponder;
3539   BOOL didBecomeFirstResponder = [super becomeFirstResponder];
3540   if (didBecomeFirstResponder) SendMessage(GetParent((HWND)self),WM_COMMAND,MAKELONG([self tag],EN_SETFOCUS),(LPARAM)self);
3541   return didBecomeFirstResponder;
3544 - (void)swellDisableContextMenu:(bool)dis
3546   m_disable_menu = dis;
3549 - (bool)swellWantsContextMenu
3551   return !m_disable_menu;
3553 @end
3555 @implementation SWELL_TextField
3556 STANDARD_CONTROL_NEEDSDISPLAY_IMPL([self isSelectable] ? "Edit" : "Static")
3558 - (id) init
3560   if (NULL != (self = [super init]))
3561   {
3562     m_disable_menu = false;
3563     m_ctlcolor_set = false;
3564     m_last_dark_mode = false;
3565     m_userdata = 0;
3566   }
3567   return self;
3570 - (BOOL)becomeFirstResponder;
3572   BOOL didBecomeFirstResponder = [super becomeFirstResponder];
3573   if (didBecomeFirstResponder) SendMessage(GetParent((HWND)self),WM_COMMAND,MAKELONG([self tag],EN_SETFOCUS),(LPARAM)self);
3574   return didBecomeFirstResponder;
3576 - (void)initColors:(int)darkmode
3578   if (darkmode >= 0)
3579   {
3580     m_ctlcolor_set = false;
3581     m_last_dark_mode = darkmode ? 1 : 0;
3582   }
3584   if ([self isEditable])
3585   {
3586     if (SWELL_osx_is_dark_mode(1))
3587     {
3588       if (m_last_dark_mode)
3589         [self setBackgroundColor:[NSColor windowBackgroundColor]];
3590       else
3591         [self setBackgroundColor:[NSColor textBackgroundColor]];
3592     }
3593     else
3594     {
3595       if (g_swell_osx_readonlytext_wndbg)
3596         [self setBackgroundColor:[NSColor textBackgroundColor]];
3597     }
3598   }
3599   else if (![self isBordered] && ![self drawsBackground]) // looks like a static text control
3600   {
3601     const float alpha = ([self isEnabled] ? 1.0f : 0.5f);
3602     [self setTextColor:[[self textColor] colorWithAlphaComponent:alpha]];
3603   }
3604   else
3605   {
3606     // not editable
3607     if (g_swell_osx_readonlytext_wndbg)
3608       [self setBackgroundColor:[NSColor windowBackgroundColor]];
3609   }
3612 - (void) drawRect:(NSRect)r
3614   // we could move this to sendSwellMessage if (uMsg == WM_DISPLAYCHANGE), but meh
3615   if (!m_ctlcolor_set && SWELL_osx_is_dark_mode(1))
3616   {
3617     const bool m = SWELL_osx_is_dark_mode(0);
3618     if (m != m_last_dark_mode) [self initColors:m];
3619   }
3620   [super drawRect:r];
3623 - (void)swellDisableContextMenu:(bool)dis
3625   m_disable_menu = dis;
3628 - (NSMenu *)textView:(NSTextView *)view
3629                 menu:(NSMenu *)menu
3630             forEvent:(NSEvent *)event
3631              atIndex:(NSUInteger)charIndex
3633   return m_disable_menu ? nil : menu;
3636 -(LONG_PTR)getSwellUserData
3638   return m_userdata;
3640 -(void)setSwellUserData:(LONG_PTR)val
3642   m_userdata = val;
3645 @end
3649 HWND SWELL_MakeEditField(int idx, int x, int y, int w, int h, int flags)
3650 {  
3651   if ((flags&WS_VSCROLL) || (flags&WS_HSCROLL)) // || (flags & ES_READONLY))
3652   {
3653     SWELL_TextView *obj=[[SWELL_TextView alloc] init];
3654     [obj setAutomaticQuoteSubstitutionEnabled:NO];
3655     [obj setEditable:(flags & ES_READONLY)?NO:YES];
3656     if (m_transform.size.width < minwidfontadjust)
3657       [obj setFont:[NSFont systemFontOfSize:TRANSFORMFONTSIZE]];
3658     [obj setTag:idx];
3659     [obj setDelegate:ACTIONTARGET];
3660     [obj setRichText:NO];
3662     [obj setHorizontallyResizable:NO];
3663     
3664     if (flags & WS_VSCROLL)
3665     {
3666       NSRect fr=MakeCoords(x,y,w,h,true);
3667       
3668       [obj setVerticallyResizable:YES];
3669       NSScrollView *obj2=[[NSScrollView alloc] init];
3670       if (flags&WS_VSCROLL) [obj2 setHasVerticalScroller:YES];
3671       if (flags&WS_HSCROLL) 
3672       {
3673         [obj2 setHasHorizontalScroller:YES];
3674         [obj setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
3675         [obj setHorizontallyResizable:YES];
3676         [[obj textContainer] setWidthTracksTextView:NO];
3677         [[obj textContainer] setContainerSize:NSMakeSize(FLT_MAX, FLT_MAX)];
3678       }
3679       [obj2 setAutohidesScrollers:YES];
3680       [obj2 setDrawsBackground:NO];
3681       [obj2 setDocumentView:obj];
3682       [obj2 setFrame:fr];
3683       [m_make_owner addSubview:obj2];
3684       if (m_doautoright) UpdateAutoCoords([obj2 frame]);
3685       if (flags&SWELL_NOT_WS_VISIBLE) [obj2 setHidden:YES];
3686       [obj2 release];
3687       
3688       NSRect tr;
3689       memset(&tr,0,sizeof(tr));
3690       tr.size = [obj2 contentSize];
3691       [obj setFrame:tr];
3692       [obj release];
3693       
3694       return (HWND)obj2;
3695     }
3696     else
3697     {
3698       [obj setFrame:MakeCoords(x,y,w,h,true)];
3699       [obj setVerticallyResizable:NO];
3700       if (flags&SWELL_NOT_WS_VISIBLE) [obj setHidden:YES];
3701       [m_make_owner addSubview:obj];
3702       if (m_doautoright) UpdateAutoCoords([obj frame]);
3703       [obj release];
3704       return (HWND)obj;
3705     }  
3706   }  
3707   
3708   NSTextField *obj;
3709   
3710   if (flags & ES_PASSWORD) obj=[[NSSecureTextField alloc] init];
3711   else obj=[[SWELL_TextField alloc] init];
3712   [obj setEditable:(flags & ES_READONLY)?NO:YES];
3713   if (flags & ES_READONLY) [obj setSelectable:YES];
3714   if (m_transform.size.width < minwidfontadjust)
3715     [obj setFont:[NSFont systemFontOfSize:TRANSFORMFONTSIZE]];
3716   
3717   if ([obj isKindOfClass:[SWELL_TextField class]])
3718     [(SWELL_TextField *)obj initColors:SWELL_osx_is_dark_mode(0)];
3720   NSCell* cell = [obj cell];  
3721   if (flags&ES_CENTER) [cell setAlignment:NSCenterTextAlignment];
3722   else if (flags&ES_RIGHT) [cell setAlignment:NSRightTextAlignment];
3723   if (abs(h) < 20)
3724   {
3725     [cell setWraps:NO];
3726     [cell setScrollable:YES];
3727   }
3728   [obj setTag:idx];
3729   [obj setTarget:ACTIONTARGET];
3730   [obj setAction:@selector(onSwellCommand:)];
3731   [obj setDelegate:ACTIONTARGET];
3732   
3733   [obj setFrame:MakeCoords(x,y,w,h,true)];
3734   if (flags&SWELL_NOT_WS_VISIBLE) [obj setHidden:YES];
3735   [m_make_owner addSubview:obj];
3736   if (m_doautoright) UpdateAutoCoords([obj frame]);
3737   [obj release];
3739   return (HWND)obj;
3742 HWND SWELL_MakeLabel( int align, const char *label, int idx, int x, int y, int w, int h, int flags)
3744   NSTextField *obj=[[SWELL_TextField alloc] init];
3745   [obj setEditable:NO];
3746   [obj setSelectable:NO];
3747   [obj setBordered:NO];
3748   [obj setBezeled:NO];
3749   [obj setDrawsBackground:NO];
3750   if (m_transform.size.width < minwidfontadjust)
3751     [obj setFont:[NSFont systemFontOfSize:TRANSFORMFONTSIZE]];
3753   if (flags & SS_NOTIFY)
3754   {
3755     [obj setTarget:ACTIONTARGET];
3756     [obj setAction:@selector(onSwellCommand:)];
3757   }
3758   
3759   NSString *labelstr=(NSString *)SWELL_CStringToCFString_FilterPrefix(label);
3760   [obj setStringValue:labelstr];
3761   [obj setAlignment:(align<0?NSLeftTextAlignment:align>0?NSRightTextAlignment:NSCenterTextAlignment)];
3762   
3763   [[obj cell] setWraps:(h>12 ? YES : NO)];
3764   
3765   [obj setTag:idx];
3766   [obj setFrame:MakeCoords(x,y,w,h,true)];
3767   if (flags&SWELL_NOT_WS_VISIBLE) [obj setHidden:YES];
3768   [m_make_owner addSubview:obj];
3769   if (m_sizetofits && strlen(label)>1)[obj sizeToFit];
3770   if (m_doautoright) UpdateAutoCoords([obj frame]);
3771   [obj release];
3772   [labelstr release];
3773   return (HWND)obj;
3777 HWND SWELL_MakeCheckBox(const char *name, int idx, int x, int y, int w, int h, int flags=0)
3779   return SWELL_MakeControl(name,idx,"Button",BS_AUTOCHECKBOX|flags,x,y,w,h,0);
3782 HWND SWELL_MakeListBox(int idx, int x, int y, int w, int h, int styles)
3784   HWND hw=SWELL_MakeControl("",idx,"SysListView32_LB",styles,x,y,w,h,0);
3785 /*  if (hw)
3786   {
3787     LVCOLUMN lvc={0,};
3788     RECT r;
3789     GetClientRect(hw,&r);
3790     lvc.cx=300;//yer.right-r.left;
3791     lvc.pszText="";
3792     ListView_InsertColumn(hw,0,&lvc);
3793   }
3794   */
3795   return hw;
3799 typedef struct ccprocrec
3801   SWELL_ControlCreatorProc proc;
3802   int cnt;
3803   struct ccprocrec *next;
3804 } ccprocrec;
3806 static ccprocrec *m_ccprocs;
3808 void SWELL_RegisterCustomControlCreator(SWELL_ControlCreatorProc proc)
3810   if (!proc) return;
3811   
3812   ccprocrec *p=m_ccprocs;
3813   while (p && p->next)
3814   {
3815     if (p->proc == proc)
3816     {
3817       p->cnt++;
3818       return;
3819     }
3820     p=p->next;
3821   }
3822   ccprocrec *ent = (ccprocrec*)malloc(sizeof(ccprocrec));
3823   ent->proc=proc;
3824   ent->cnt=1;
3825   ent->next=0;
3826   
3827   if (p) p->next=ent;
3828   else m_ccprocs=ent;
3831 void SWELL_UnregisterCustomControlCreator(SWELL_ControlCreatorProc proc)
3833   if (!proc) return;
3834   
3835   ccprocrec *lp=NULL;
3836   ccprocrec *p=m_ccprocs;
3837   while (p)
3838   {
3839     if (p->proc == proc)
3840     {
3841       if (--p->cnt <= 0)
3842       {
3843         if (lp) lp->next=p->next;
3844         else m_ccprocs=p->next;
3845         free(p);
3846       }
3847       return;
3848     }
3849     lp=p;
3850     p=p->next;
3851   }
3854 static void set_listview_bigsur_style(NSTableView *obj)
3856   if (SWELL_GetOSXVersion() < 0x1100) return;
3857 #if defined(MAC_OS_VERSION_11_0) && (!defined(SWELL_COCOA_WILL_HAVE_PREBIGSUR_SDK) || defined(__aarch64__))
3858   // newer SDKs default to NSTableViewStyleAutomatic
3859   int style = (g_swell_osx_style & 2) ? -1 : 4 /* NSTableViewStylePlain */;
3860 #else
3861   // old SDKs default to something similar to NSTableViewStylePlain
3862   int style = (g_swell_osx_style & 2) ? 0 /* NSTableViewStyleAutomatic */ : -1;
3863 #endif
3864   if (style >= 0) [obj setValue:[NSNumber numberWithInt:style] forKey:@"style"];
3868 HWND SWELL_MakeControl(const char *cname, int idx, const char *classname, int style, int x, int y, int w, int h, int exstyle)
3870   if (m_ccprocs)
3871   {
3872     NSRect wcr=MakeCoords(x,y,w,h,false);
3873     ccprocrec *p=m_ccprocs;
3874     while (p)
3875     {
3876       HWND hwnd=p->proc((HWND)m_make_owner,cname,idx,classname,style,
3877           (int)(wcr.origin.x+0.5),(int)(wcr.origin.y+0.5),(int)(wcr.size.width+0.5),(int)(wcr.size.height+0.5));
3878       if (hwnd) 
3879       {
3880         if (exstyle) SetWindowLong(hwnd,GWL_EXSTYLE,exstyle);
3881         return hwnd;
3882       }
3883       p=p->next;
3884     }
3885   }
3886   if (!stricmp(classname,"SysTabControl32"))
3887   {
3888     SWELL_TabView *obj=[[SWELL_TabView alloc] init];
3889     if (1) // todo: only if on 10.4 maybe?
3890     {
3891       y-=1;
3892       h+=6;
3893     }
3894     [obj setTag:idx];
3895     [obj setDelegate:(id)obj];
3896     [obj setAllowsTruncatedLabels:YES];
3897     [obj setNotificationWindow:ACTIONTARGET];
3898     [obj setHidden:NO];
3899     [obj setFrame:MakeCoords(x,y,w,h,false)];
3900     if (style&SWELL_NOT_WS_VISIBLE) [obj setHidden:YES];
3901     [m_make_owner addSubview:obj];
3902     SetAllowNoMiddleManRendering((HWND)m_make_owner,FALSE);
3903     [obj release];
3904     return (HWND)obj;
3905   }
3906   else if (!stricmp(classname, "SysListView32")||!stricmp(classname, "SysListView32_LB"))
3907   {
3908     SWELL_ListView *obj = [[SWELL_ListView alloc] init];
3909     set_listview_bigsur_style(obj);
3910     [obj setColumnAutoresizingStyle:NSTableViewNoColumnAutoresizing];
3911     [obj setFocusRingType:NSFocusRingTypeNone];
3912     [obj setDataSource:(id)obj];
3913     obj->style=style;
3915     BOOL isLB=!stricmp(classname, "SysListView32_LB");
3916     [obj setSwellNotificationMode:isLB];
3917     
3918     if (isLB)
3919     {
3920       [obj setHeaderView:nil];
3921       [obj setAllowsMultipleSelection:!!(style & LBS_EXTENDEDSEL)];
3922     }
3923     else
3924     {
3925       if ((style & LVS_NOCOLUMNHEADER) || !(style & LVS_REPORT))  [obj setHeaderView:nil];
3926       [obj setAllowsMultipleSelection:!(style & LVS_SINGLESEL)];
3927     }
3928     [obj setAllowsColumnReordering:NO];
3929     [obj setAllowsEmptySelection:YES];
3930     [obj setTag:idx];
3931     [obj setHidden:NO];
3932     id target=ACTIONTARGET;
3933     [obj setDelegate:target];
3934     [obj setTarget:target];
3935     [obj setAction:@selector(onSwellCommand:)];
3936     if ([target respondsToSelector:@selector(swellOnControlDoubleClick:)])
3937     {
3938       [obj setDoubleAction:@selector(swellOnControlDoubleClick:)];
3939     }
3940     else
3941     {
3942       [obj setDoubleAction:@selector(onSwellCommand:)];
3943     }
3944     NSScrollView *obj2=[[NSScrollView alloc] init];
3945     NSRect tr=MakeCoords(x,y,w,h,false);
3946     [obj2 setFrame:tr];
3947     [obj2 setDocumentView:obj];
3948     [obj2 setHasVerticalScroller:YES];
3949     if (!isLB) [obj2 setHasHorizontalScroller:YES];
3950     [obj2 setAutohidesScrollers:YES];
3951     [obj2 setDrawsBackground:NO];
3952     [obj release];
3953     if (style&SWELL_NOT_WS_VISIBLE) [obj2 setHidden:YES];
3954     [m_make_owner addSubview:obj2];
3955     [obj2 release];
3956     
3957     if (isLB || !(style & LVS_REPORT))
3958     {
3959       LVCOLUMN lvc={0,};
3960       lvc.mask=LVCF_TEXT|LVCF_WIDTH;
3961       lvc.cx=(int)ceil(wdl_max(tr.size.width - 4.0,isLB ? 1200.0 : 300.0));
3962       lvc.pszText=(char*)"";
3963       ListView_InsertColumn((HWND)obj,0,&lvc);
3964       if (isLB && (style & LBS_OWNERDRAWFIXED))
3965       {
3966         NSArray *ar=[obj tableColumns];
3967         NSTableColumn *c;
3968         if (ar && [ar count] && (c=[ar objectAtIndex:0]))
3969         {
3970           SWELL_ODListViewCell *t=[[SWELL_ODListViewCell alloc] init];
3971           [c setDataCell:t];
3972           [t setOwnerControl:obj];
3973           [t release];
3974         }
3975       }
3976     }
3977     
3978     return (HWND)obj;
3979   }
3980   else if (!stricmp(classname, "SysTreeView32"))
3981   {
3982     SWELL_TreeView *obj = [[SWELL_TreeView alloc] init];
3983     set_listview_bigsur_style(obj);
3984     [obj setFocusRingType:NSFocusRingTypeNone];
3985     [obj setDataSource:(id)obj];
3986     obj->style=style;
3987     id target=ACTIONTARGET;
3988     [obj setHeaderView:nil];    
3989     [obj setDelegate:target];
3990     [obj setAllowsColumnReordering:NO];
3991     [obj setAllowsMultipleSelection:NO];
3992     [obj setAllowsEmptySelection:YES];
3993     [obj setTag:idx];
3994     [obj setHidden:NO];
3995     [obj setTarget:target];
3996     [obj setAction:@selector(onSwellCommand:)];
3997     if ([target respondsToSelector:@selector(swellOnControlDoubleClick:)])
3998       [obj setDoubleAction:@selector(swellOnControlDoubleClick:)];
3999     else
4000       [obj setDoubleAction:@selector(onSwellCommand:)];
4001     NSScrollView *obj2=[[NSScrollView alloc] init];
4002     NSRect tr=MakeCoords(x,y,w,h,false);
4003     [obj2 setFrame:tr];
4004     [obj2 setDocumentView:obj];
4005     [obj2 setHasVerticalScroller:YES];
4006     [obj2 setAutohidesScrollers:YES];
4007     [obj2 setDrawsBackground:NO];
4008     [obj release];
4009     if (style&SWELL_NOT_WS_VISIBLE) [obj2 setHidden:YES];
4010     [m_make_owner addSubview:obj2];
4011     [obj2 release];
4013     {
4014       NSTableColumn *col=[[NSTableColumn alloc] init];
4015       SWELL_ListViewCell *cell = [[SWELL_ListViewCell alloc] initTextCell:@""];
4016       [col setDataCell:cell];
4017       [cell release];
4019       [col setWidth:(int)ceil(wdl_max(tr.size.width,300.0))];
4020       [col setEditable:NO];
4021       [[col dataCell] setWraps:NO];     
4022       [obj addTableColumn:col];
4023       [obj setOutlineTableColumn:col];
4025       [col release];
4026     }
4027 ///    [obj setIndentationPerLevel:10.0];
4028     
4029     return (HWND)obj;
4030   }
4031   else if (!stricmp(classname, "msctls_progress32"))
4032   {
4033     SWELL_ProgressView *obj=[[SWELL_ProgressView alloc] init];
4034     [obj setStyle:NSProgressIndicatorBarStyle];
4035     [obj setIndeterminate:NO];
4036     [obj setTag:idx];
4037     [obj setFrame:MakeCoords(x,y,w,h,false)];
4038     if (style&SWELL_NOT_WS_VISIBLE) [obj setHidden:YES];
4039     [m_make_owner addSubview:obj];
4040     [obj release];
4041     return (HWND)obj;
4042   }
4043   else if (!stricmp(classname,"Edit"))
4044   {
4045     return SWELL_MakeEditField(idx,x,y,w,h,style);
4046   }
4047   else if (!stricmp(classname, "Static"))
4048   {
4049     if ((style&SS_TYPEMASK) == SS_ETCHEDHORZ || (style&SS_TYPEMASK) == SS_ETCHEDVERT || (style&SS_TYPEMASK) == SS_ETCHEDFRAME)
4050     {
4051       SWELL_BoxView *obj=[[SWELL_BoxView alloc] init];
4052       obj->m_etch_mode = style & SS_TYPEMASK;
4053       [obj setTag:idx];
4054       [obj setFrame:MakeCoords(x,y,w,h,false)];
4055       [m_make_owner addSubview:obj];
4056       [obj release];
4057       return (HWND)obj;
4058     }
4059     NSTextField *obj=[[SWELL_TextField alloc] init];
4060     [obj setEditable:NO];
4061     [obj setSelectable:NO];
4062     [obj setBordered:NO];
4063     [obj setBezeled:NO];
4064     [obj setDrawsBackground:NO];
4065     if (m_transform.size.width < minwidfontadjust)
4066       [obj setFont:[NSFont systemFontOfSize:TRANSFORMFONTSIZE]];
4068     if (cname && *cname)
4069     {
4070       NSString *labelstr=(NSString *)SWELL_CStringToCFString_FilterPrefix(cname);
4071       [obj setStringValue:labelstr];
4072       [labelstr release];
4073     }
4074     
4075     if ((style&SS_TYPEMASK) == SS_LEFTNOWORDWRAP) [[obj cell] setWraps:NO];
4076     else if ((style&SS_TYPEMASK) == SS_CENTER) [[obj cell] setAlignment:NSCenterTextAlignment];
4077     else if ((style&SS_TYPEMASK) == SS_RIGHT) [[obj cell] setAlignment:NSRightTextAlignment];
4079     [obj setTag:idx];
4080     [obj setFrame:MakeCoords(x,y,w,h,true)];
4081     if (style&SWELL_NOT_WS_VISIBLE) [obj setHidden:YES];
4082     [m_make_owner addSubview:obj];
4083     if ((style & SS_TYPEMASK) == SS_BLACKRECT)
4084     {
4085       [obj setHidden:YES];
4086     }
4087     [obj release];
4088     return (HWND)obj;
4089   }
4090   else if (!stricmp(classname,"Button"))
4091   {
4092     if (style & BS_GROUPBOX)
4093     {
4094       return SWELL_MakeGroupBox(cname, idx, x, y, w, h, style &~BS_GROUPBOX);
4095     }
4096     if (style & BS_DEFPUSHBUTTON)
4097     {
4098        return SWELL_MakeButton(1, cname, idx, x,y,w,h,style &~BS_DEFPUSHBUTTON);
4099     }
4100     if (style & BS_PUSHBUTTON)
4101     {
4102        return SWELL_MakeButton(0, cname, idx, x,y,w,h,style &~BS_PUSHBUTTON);
4103     }
4104     SWELL_Button *button=[[SWELL_Button alloc] init];
4105     [button setTag:idx];
4106     NSRect fr=MakeCoords(x,y,w,h,true);
4107     SEL actionSel = @selector(onSwellCommand:);
4108     if ((style & 0xf) == BS_AUTO3STATE)
4109     {
4110       [button setButtonType:NSSwitchButton];
4111       [button setAllowsMixedState:YES];
4112     }    
4113     else if ((style & 0xf) == BS_AUTOCHECKBOX)
4114     {
4115       [button setButtonType:NSSwitchButton];
4116       [button setAllowsMixedState:NO];
4117     }
4118     else if ((style & 0xf) == BS_AUTORADIOBUTTON)
4119     {
4120 #ifdef MAC_OS_X_VERSION_10_8
4121       // Compiling with the OSX 10.8+ SDK and running on 10.8+ causes radio buttons with a common action selector to
4122       // be treated as a group. This works around that. if you need more than 8 groups (seriously?!), add the extra 
4123       // functions in swell-dlg.mm and in the switch below
4124       {
4125         NSView *v;
4126         NSArray *sv;
4127         if ((style & WS_GROUP) ||
4128               !(sv = [m_make_owner subviews]) || 
4129               ![sv count] ||
4130               !(v = [sv lastObject]) ||
4131               ![v isKindOfClass:[SWELL_Button class]] ||
4132               ([(SWELL_Button *)v swellGetRadioFlags]&2)) m_make_radiogroupcnt++;
4133       }
4134       switch (m_make_radiogroupcnt & 7)
4135       {
4136         case 0: actionSel = @selector(onSwellCommand0:); break;
4137         case 1: break; // default
4138         case 2: actionSel = @selector(onSwellCommand2:); break;
4139         case 3: actionSel = @selector(onSwellCommand3:); break;
4140         case 4: actionSel = @selector(onSwellCommand4:); break;
4141         case 5: actionSel = @selector(onSwellCommand5:); break;
4142         case 6: actionSel = @selector(onSwellCommand6:); break;
4143         case 7: actionSel = @selector(onSwellCommand7:); break;
4144       }
4145 #endif
4146      
4147       [button setButtonType:NSRadioButton];
4148       [button swellSetRadioFlags:(style & WS_GROUP)?3:1];
4149     }
4150     else if ((style & 0xf) == BS_OWNERDRAW)
4151     {
4152       SWELL_ODButtonCell *cell = [[SWELL_ODButtonCell alloc] init];
4153       [button setCell:cell];
4154       [cell release];
4155       //NSButtonCell
4156       [button swellSetRadioFlags:4096];
4157     }
4158     else // normal button
4159     {
4160       if (style & BS_BITMAP)
4161       {
4162         SWELL_ImageButtonCell * cell = [[SWELL_ImageButtonCell alloc] init];
4163         [button setCell:cell];
4164         [cell release];
4165       }
4166       if ((style & BS_XPOSITION_MASK) == BS_LEFT) [button setAlignment:NSLeftTextAlignment];
4167 //      fr.size.width+=8;
4168       [button swellSetRadioFlags:4096];
4169     }
4170     
4171     if (m_transform.size.width < minwidfontadjust)
4172       [button setFont:[NSFont systemFontOfSize:TRANSFORMFONTSIZE]];
4173     [button setFrame:fr];
4174     NSString *labelstr=(NSString *)SWELL_CStringToCFString_FilterPrefix(cname);
4175     [button setTitle:labelstr];
4176     [button setTarget:ACTIONTARGET];
4177     [button setAction:actionSel];
4178     if (style&BS_LEFTTEXT) [button setImagePosition:NSImageRight];
4179     if (style&SWELL_NOT_WS_VISIBLE) [button setHidden:YES];
4180     [m_make_owner addSubview:button];
4181     if (m_sizetofits && (style & 0xf) != BS_OWNERDRAW) [button sizeToFit];
4182     if (m_doautoright) UpdateAutoCoords([button frame]);
4183     [labelstr release];
4184     [button release];
4185     return (HWND)button;
4186   }
4187   else if (!stricmp(classname,"REAPERhfader")||!stricmp(classname,"msctls_trackbar32"))
4188   {
4189     NSSlider *obj=[[NSSlider alloc] init];
4190     [obj setTag:idx];
4191     [obj setMinValue:0.0];
4192     [obj setMaxValue:1000.0];
4193     [obj setFrame:MakeCoords(x,y,w,h,false)];
4194     if (!stricmp(classname, "msctls_trackbar32"))
4195     {
4196       [[obj cell] setControlSize:NSMiniControlSize];
4197     }
4198     [obj setTarget:ACTIONTARGET];
4199     [obj setAction:@selector(onSwellCommand:)];
4200     if (style&SWELL_NOT_WS_VISIBLE) [obj setHidden:YES];
4201     [m_make_owner addSubview:obj];
4202     [obj release];
4203     return (HWND)obj;
4204   }
4205   else if (!stricmp(classname,"COMBOBOX"))
4206   {
4207     return SWELL_MakeCombo(idx, x, y, w, h, style);
4208   }
4209   return 0;
4212 HWND SWELL_MakeCombo(int idx, int x, int y, int w, int h, int flags)
4214   if ((flags & 0x3) == CBS_DROPDOWNLIST)
4215   {
4216     SWELL_PopUpButton *obj=[[SWELL_PopUpButton alloc] init];
4217     [obj setTag:idx];
4218     [obj setFont:[NSFont systemFontOfSize:10.0f]];
4219     NSRect rc=MakeCoords(x,y,w,(g_swell_osx_style&1) ? 24 : 18,true,true);
4220     if (g_swell_osx_style&1) rc.origin.y -= 2;
4221         
4222     [obj setSwellStyle:flags];
4223     [obj setFrame:rc];
4224     [obj setAutoenablesItems:NO];
4225     [obj setTarget:ACTIONTARGET];
4226     [obj setAction:@selector(onSwellCommand:)];
4228     if (!(g_swell_osx_style&1))
4229     {
4230       [obj setBezelStyle:NSShadowlessSquareBezelStyle ];
4231       [[obj cell] setArrowPosition:NSPopUpArrowAtBottom];
4232     }
4233     if (flags&SWELL_NOT_WS_VISIBLE) [obj setHidden:YES];
4234     [m_make_owner addSubview:obj];
4235     if (m_doautoright) UpdateAutoCoords([obj frame]);
4236     [obj release];
4237     return (HWND)obj;
4238   }
4239   else
4240   {
4241     SWELL_ComboBox *obj=[[SWELL_ComboBox alloc] init];
4242     [obj setFocusRingType:NSFocusRingTypeNone];
4243     [obj setFont:[NSFont systemFontOfSize:10.0f]];
4244     [obj setEditable:(flags & 0x3) == CBS_DROPDOWNLIST?NO:YES];
4245     [obj setSwellStyle:flags];
4246     [obj setTag:idx];
4247     [obj setFrame:MakeCoords(x,y-1,w,22,true,true)];
4248     [obj setTarget:ACTIONTARGET];
4249     [obj setAction:@selector(onSwellCommand:)];
4250     [obj setDelegate:ACTIONTARGET];
4251     if (flags&SWELL_NOT_WS_VISIBLE) [obj setHidden:YES];
4252     [m_make_owner addSubview:obj];
4253     if (m_doautoright) UpdateAutoCoords([obj frame]);
4254     [obj release];
4255     return (HWND)obj;
4256   }
4259 @implementation SWELL_BoxView
4261 STANDARD_CONTROL_NEEDSDISPLAY_IMPL(m_etch_mode ? "Static" : "Button")
4263 -(NSInteger) tag
4265   return m_tag;
4267 -(void) setTag:(NSInteger)tag
4269   m_tag=tag;
4272 -(void) drawRect:(NSRect) r
4274   // m_etch_mode override?
4275   [super drawRect:r];
4277 -(int)swellIsEtchBox
4279   return m_etch_mode;
4282 @end
4284 HWND SWELL_MakeGroupBox(const char *name, int idx, int x, int y, int w, int h, int style)
4286   SWELL_BoxView *obj=[[SWELL_BoxView alloc] init];
4287   obj->m_etch_mode = 0;
4288   
4289   // this just doesn't work, you can't color the border unless it's NSBoxCustom, 
4290   // and I can't get it to show the title text if it's NSBoxCustom
4291   //[obj setBoxType:(NSBoxType)4];   // NSBoxCustom, so we can color the border 
4292   //[obj setTitlePosition:(NSTitlePosition)2];  // NSAtTop, default but NSBoxCustom unsets it
4293   
4294 //  [obj setTag:idx];
4295   if (1) // todo: only if on 10.4 maybe?
4296   {
4297     y-=1;
4298     h+=3;
4299   }
4300   NSString *labelstr=(NSString *)SWELL_CStringToCFString_FilterPrefix(name);
4301   [obj setTitle:labelstr];
4302   [obj setTag:idx];
4303   [labelstr release];
4304   if ((style & BS_XPOSITION_MASK) == BS_CENTER)
4305   {
4306     [[obj titleCell] setAlignment:NSCenterTextAlignment];
4307   }
4308   [obj setFrame:MakeCoords(x,y,w,h,false)];
4309   [m_make_owner addSubview:obj positioned:NSWindowBelow relativeTo:nil];
4310   [obj release];
4311   return (HWND)obj;
4315 int TabCtrl_GetItemCount(HWND hwnd)
4317   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_TabView class]])) return 0;
4318   SWELL_TabView *tv=(SWELL_TabView*)hwnd;
4319   return (int)[tv numberOfTabViewItems];
4322 BOOL TabCtrl_AdjustRect(HWND hwnd, BOOL fLarger, RECT *r)
4324   if (WDL_NOT_NORMALLY(!r || !hwnd || ![(id)hwnd isKindOfClass:[SWELL_TabView class]])) return FALSE;
4325   
4326   int sign=fLarger?-1:1;
4327   r->left+=sign*7; // todo: correct this?
4328   r->right-=sign*7;
4329   r->top+=sign*26;
4330   r->bottom-=sign*3;
4331   return TRUE;
4335 BOOL TabCtrl_DeleteItem(HWND hwnd, int idx)
4337   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_TabView class]])) return 0;
4338   SWELL_TabView *tv=(SWELL_TabView*)hwnd;
4339   if (idx<0 || idx>= [tv numberOfTabViewItems]) return 0;
4340   [tv removeTabViewItem:[tv tabViewItemAtIndex:idx]];
4341   return TRUE;
4344 int TabCtrl_InsertItem(HWND hwnd, int idx, TCITEM *item)
4346   if (WDL_NOT_NORMALLY(!item || !hwnd || ![(id)hwnd isKindOfClass:[SWELL_TabView class]])) return -1;
4347   if (!(item->mask & TCIF_TEXT) || !item->pszText) return -1;
4348   SWELL_TabView *tv=(SWELL_TabView*)hwnd;
4350   const int ni = (int)[tv numberOfTabViewItems];
4351   if (idx<0) idx=0;
4352   else if (idx>ni) idx=ni;
4353   
4354   NSTabViewItem *tabitem=[[NSTabViewItem alloc] init];
4355   NSString *str=(NSString *)SWELL_CStringToCFString(item->pszText);  
4356   [tabitem setLabel:str];
4357   [str release];
4358   id turd=[tv getNotificationWindow];
4359   [tv setNotificationWindow:nil];
4360   [tv insertTabViewItem:tabitem atIndex:idx];
4361   [tv setNotificationWindow:turd];
4362   [tabitem release];
4363   return idx;
4366 int TabCtrl_SetCurSel(HWND hwnd, int idx)
4368   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_TabView class]])) return -1;
4369   SWELL_TabView *tv=(SWELL_TabView*)hwnd;
4370   int ret=TabCtrl_GetCurSel(hwnd);
4371   if (idx>=0 && idx < [tv numberOfTabViewItems])
4372   {
4373     [tv selectTabViewItemAtIndex:idx];
4374   }
4375   return ret;
4378 int TabCtrl_GetCurSel(HWND hwnd)
4380   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_TabView class]])) return 0;
4381   SWELL_TabView *tv=(SWELL_TabView*)hwnd;
4382   NSTabViewItem *item=[tv selectedTabViewItem];
4383   if (!item) return 0;
4384   return (int)[tv indexOfTabViewItem:item];
4387 void ListView_SetExtendedListViewStyleEx(HWND h, int mask, int style)
4389   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return;
4390   SWELL_ListView *tv=(SWELL_ListView*)h;
4392   if (mask & LVS_EX_SUBITEMIMAGES)
4393   {
4394     tv->m_subitem_images = (style & LVS_EX_SUBITEMIMAGES) != 0;
4395   }
4396   
4397   if (mask&LVS_EX_GRIDLINES)
4398   {
4399     int s=0;
4400     if (style&LVS_EX_GRIDLINES) 
4401     {
4402       s=NSTableViewSolidVerticalGridLineMask|NSTableViewSolidHorizontalGridLineMask;
4403     }
4404     [tv setGridStyleMask:s];
4405   }
4406   
4407   if (mask&LVS_EX_HEADERDRAGDROP)
4408   {
4409     [tv setAllowsColumnReordering:!!(style&LVS_EX_HEADERDRAGDROP)];
4410   }
4411   
4412   
4413   // todo LVS_EX_FULLROWSELECT (enabled by default on OSX)
4416 void SWELL_SetListViewFastClickMask(HWND hList, int mask)
4418   if (WDL_NOT_NORMALLY(!hList || ![(id)hList isKindOfClass:[SWELL_ListView class]])) return;
4419   SWELL_ListView *lv = (SWELL_ListView *)hList;
4420   lv->m_fastClickMask=mask;
4425 void ListView_SetImageList(HWND h, HIMAGELIST imagelist, int which)
4427   if (WDL_NOT_NORMALLY(!h)) return;
4428   
4429   SWELL_ListView *v=(SWELL_ListView *)h;
4430   
4431   v->m_status_imagelist_type=which;
4432   v->m_status_imagelist=(WDL_PtrList<HGDIOBJ__> *)imagelist;
4433   if (v->m_cols)
4434   {
4435     for (int x = 0; x < v->m_cols->GetSize(); x ++)
4436     {
4437       NSTableColumn *col=(NSTableColumn*)v->m_cols->Get(x);
4438       if (![col isKindOfClass:[SWELL_StatusCell class]])
4439       {
4440         SWELL_StatusCell *cell=[[SWELL_StatusCell alloc] initNewCell:!x];
4441         NSTextFieldCell *oldcell = [col dataCell];
4442         if (oldcell) [cell setAlignment:[oldcell alignment]];
4444         [cell setWraps:NO];
4445         [col setDataCell:cell];
4446         [cell release];
4447       }
4448       if (!v->m_subitem_images)
4449         break;
4450     }
4451   }  
4454 int ListView_GetColumnWidth(HWND h, int pos)
4456   if (!h || ![(id)h isKindOfClass:[SWELL_ListView class]]) return 0;
4457   SWELL_ListView *v=(SWELL_ListView *)h;
4458   if (!v->m_cols || pos < 0 || pos >= v->m_cols->GetSize()) return 0;
4459   
4460   NSTableColumn *col=v->m_cols->Get(pos);
4461   if (!col) return 0;
4462   
4463   if ([col respondsToSelector:@selector(isHidden)] && [(SWELL_TableColumnExtensions*)col isHidden]) return 0;
4464   return (int) floor(0.5+[col width]);
4467 void ListView_InsertColumn(HWND h, int pos, const LVCOLUMN *lvc)
4469   if (WDL_NOT_NORMALLY(!h || !lvc || ![(id)h isKindOfClass:[SWELL_ListView class]])) return;
4471   SWELL_BEGIN_TRY
4473   SWELL_ListView *v=(SWELL_ListView *)h;
4474   NSTableColumn *col=[[NSTableColumn alloc] init];
4475   // note, not looking at lvc->mask at all
4477   [col setEditable:NO];
4478   // [col setResizingMask:2];  // user resizable, this seems to be the default
4479   
4480   if (lvc->fmt == LVCFMT_CENTER) [[col headerCell] setAlignment:NSCenterTextAlignment];
4481   else if (lvc->fmt == LVCFMT_RIGHT) [[col headerCell] setAlignment:NSRightTextAlignment];
4482   
4483   if (!v->m_lbMode && !(v->style & LVS_NOCOLUMNHEADER))
4484   {
4485     NSString *lbl=(NSString *)SWELL_CStringToCFString(lvc->pszText);  
4486     [[col headerCell] setStringValue:lbl];
4487     [lbl release];
4488   }
4490   SWELL_ListViewCell *cell;
4491   if ((!pos || v->m_subitem_images) && v->m_status_imagelist)
4492   {
4493     cell = [[SWELL_StatusCell alloc] initNewCell:!pos];
4494   }
4495   else
4496   {  
4497     cell = [[SWELL_ListViewCell alloc] initTextCell:@""];
4498   }
4499   [cell setWraps:NO];
4500   if (lvc->fmt == LVCFMT_CENTER) [cell setAlignment:NSCenterTextAlignment];
4501   else if (lvc->fmt == LVCFMT_RIGHT) [cell setAlignment:NSRightTextAlignment];
4502   [col setDataCell:cell];
4503   [cell release];
4505   [v addTableColumn:col];
4506   if (pos >= 0 && pos < v->m_cols->GetSize())
4507   {
4508     [v moveColumn:v->m_cols->GetSize() toColumn:pos];
4509     v->m_cols->Insert(pos,col);
4510   }
4511   else
4512   {
4513     v->m_cols->Add(col);
4514   }
4515   [col release];
4517   if (lvc->mask&LVCF_WIDTH)
4518   {
4519     ListView_SetColumnWidth(h,pos,lvc->cx);
4520   }
4521   SWELL_END_TRY(;)
4524 void ListView_SetColumn(HWND h, int pos, const LVCOLUMN *lvc)
4526   if (WDL_NOT_NORMALLY(!h || !lvc || ![(id)h isKindOfClass:[SWELL_ListView class]])) return;
4527   SWELL_ListView *v=(SWELL_ListView *)h;
4528   if (!v->m_cols || pos < 0 || pos >= v->m_cols->GetSize()) return;
4529   
4530   NSTableColumn *col=v->m_cols->Get(pos);
4531   if (!col) return;
4533   if (lvc->mask&LVCF_FMT)
4534   {
4535     NSTextAlignment align = lvc->fmt == LVCFMT_CENTER ? NSCenterTextAlignment :
4536       lvc->fmt == LVCFMT_RIGHT ? NSRightTextAlignment : NSLeftTextAlignment;
4537     [[col headerCell] setAlignment:align];
4538     [[col dataCell] setAlignment:align];
4539   }
4540   if (lvc->mask&LVCF_WIDTH)
4541   {
4542     if (!lvc->cx)
4543     {
4544       if ([col respondsToSelector:@selector(setHidden:)])  [(SWELL_TableColumnExtensions*)col setHidden:YES];
4545     }
4546     else 
4547     {
4548       if ([col respondsToSelector:@selector(setHidden:)])  [(SWELL_TableColumnExtensions*)col setHidden:NO];
4549       [col setWidth:lvc->cx];
4550     }
4551   }
4552   if (lvc->mask&LVCF_TEXT)
4553   {
4554     if (!v->m_lbMode && !(v->style&LVS_NOCOLUMNHEADER))
4555     {
4556       NSString *lbl=(NSString *)SWELL_CStringToCFString(lvc->pszText);  
4557       [[col headerCell] setStringValue:lbl];
4558       [lbl release]; 
4559     }
4560   }
4563 bool ListView_DeleteColumn(HWND h, int pos)
4565   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return false;
4566   SWELL_ListView *v=(SWELL_ListView *)h;
4567   if (!v->m_cols || pos < 0 || pos >= v->m_cols->GetSize()) return false;
4568   [v removeTableColumn:v->m_cols->Get(pos)];
4569   v->m_cols->Delete(pos);
4570   return true;
4573 void ListView_GetItemText(HWND hwnd, int item, int subitem, char *text, int textmax)
4575   LVITEM it={LVIF_TEXT,item,subitem,0,0,text,textmax,};
4576   ListView_GetItem(hwnd,&it);
4579 int ListView_InsertItem(HWND h, const LVITEM *item)
4581   if (WDL_NOT_NORMALLY(!h || !item || item->iSubItem || ![(id)h isKindOfClass:[SWELL_ListView class]])) return 0;
4582   
4583   SWELL_ListView *tv=(SWELL_ListView*)h;
4584   if (WDL_NOT_NORMALLY(!tv->m_lbMode && (tv->style & LVS_OWNERDATA))) return -1;
4585   if (WDL_NOT_NORMALLY(!tv->m_items)) return -1;
4586     
4587   int a=item->iItem;
4588   if (a<0)a=0;
4589   else if (a > tv->m_items->GetSize()) a=tv->m_items->GetSize();
4590   
4591   if (!tv->m_lbMode && (item->mask & LVIF_TEXT))
4592   {
4593     if (tv->style & LVS_SORTASCENDING)
4594     {
4595        a=ptrlist_bsearch_mod((char *)item->pszText,tv->m_items,_listviewrowSearchFunc,NULL);
4596     }
4597     else if (tv->style & LVS_SORTDESCENDING)
4598     {
4599        a=ptrlist_bsearch_mod((char *)item->pszText,tv->m_items,_listviewrowSearchFunc2,NULL);
4600     }
4601   }
4602   
4603   SWELL_ListView_Row *nr=new SWELL_ListView_Row;
4604   nr->add_col((item->mask & LVIF_TEXT) ? item->pszText : "");
4605   if (item->mask & LVIF_PARAM) nr->m_param = item->lParam;
4606   tv->m_items->Insert(a,nr);
4607   
4609   
4610   if ((item->mask&LVIF_STATE) && (item->stateMask & (0xff<<16)))
4611   {
4612     nr->set_img_idx(0,(item->state>>16)&0xff);
4613   }
4614   
4615   [tv reloadData];
4616   
4617   if (a < tv->m_items->GetSize()-1)
4618   {
4619     NSIndexSet *sel=[tv selectedRowIndexes];
4620     if (sel && [sel count])
4621     {
4622       NSMutableIndexSet *ms = [[NSMutableIndexSet alloc] initWithIndexSet:sel];
4623       [ms shiftIndexesStartingAtIndex:a by:1];
4624       [tv selectRowIndexes:ms byExtendingSelection:NO];
4625       [ms release];
4626     }
4627   }
4628   
4629   if (item->mask & LVIF_STATE)
4630   {
4631     if (item->stateMask & LVIS_SELECTED)
4632     {
4633       if (item->state&LVIS_SELECTED)
4634       {
4635         bool isSingle = tv->m_lbMode ? !(tv->style & LBS_EXTENDEDSEL) : !!(tv->style&LVS_SINGLESEL);
4636         [tv selectRowIndexes:[NSIndexSet indexSetWithIndex:a] byExtendingSelection:isSingle?NO:YES];        
4637       }
4638     }
4639   }
4640   
4641   return a;
4644 void ListView_SetItemText(HWND h, int ipos, int cpos, const char *txt)
4646   if (WDL_NOT_NORMALLY(!h || cpos < 0)) return;
4647   if (WDL_NOT_NORMALLY(![(id)h isKindOfClass:[SWELL_ListView class]])) return;
4648   
4649   SWELL_ListView *tv=(SWELL_ListView*)h;
4650   if (WDL_NOT_NORMALLY(!tv->m_lbMode && (tv->style & LVS_OWNERDATA))) return;
4651   if (WDL_NOT_NORMALLY(!tv->m_items || !tv->m_cols)) return;
4653   if (WDL_NOT_NORMALLY(cpos && cpos >= tv->m_cols->GetSize())) return; // always allow setting the first
4654   
4655   SWELL_ListView_Row *p=tv->m_items->Get(ipos);
4656   if (!p) return;
4657   int x;
4658   for (x = p->get_num_cols(); x < cpos; x ++)
4659   {
4660     p->add_col("");
4661   }
4662   if (cpos < p->get_num_cols())
4663   {
4664     p->set_col_txt(cpos,strdup(txt));
4665   }
4666   else p->add_col(txt);
4667     
4668   [tv setNeedsDisplay];
4671 int ListView_GetNextItem(HWND h, int istart, int flags)
4673   if (WDL_NORMALLY(flags==LVNI_FOCUSED||flags==LVNI_SELECTED))
4674   {
4675     if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return -1;
4676     
4677     SWELL_ListView *tv=(SWELL_ListView*)h;
4678     
4679     if (flags==LVNI_SELECTED)
4680     {
4681       //int orig_start=istart;
4682       if (istart++<0)istart=0;
4683       const int n = (int)[tv numberOfRows];
4684       while (istart < n)
4685       {
4686         if ([tv isRowSelected:istart]) return istart;
4687         istart++;
4688       }
4689       return -1;
4690     }
4691     
4692     return (int)[tv selectedRow];
4693   }
4694   return -1;
4697 bool ListView_SetItem(HWND h, LVITEM *item)
4699   if (WDL_NOT_NORMALLY(!item || !h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return false;
4700     
4701   SWELL_ListView *tv=(SWELL_ListView*)h;
4702   if (tv->m_lbMode || !(tv->style & LVS_OWNERDATA))
4703   {
4704     if (WDL_NOT_NORMALLY(!tv->m_items)) return false;
4705     SWELL_ListView_Row *row=tv->m_items->Get(item->iItem);
4706     if (WDL_NOT_NORMALLY(!row)) return false;  
4707   
4708     if (item->mask & LVIF_PARAM) 
4709     {
4710       row->m_param=item->lParam;
4711     }
4712     if ((item->mask & LVIF_TEXT) && item->pszText) 
4713     {
4714       ListView_SetItemText(h,item->iItem,item->iSubItem,item->pszText);
4715     }
4716     if ((item->mask&LVIF_IMAGE) && item->iImage >= 0)
4717     {
4718       row->set_img_idx(item->iSubItem,item->iImage+1);
4719       ListView_RedrawItems(h, item->iItem, item->iItem);
4720     }
4721   }
4722   if ((item->mask & LVIF_STATE) && item->stateMask)
4723   {
4724     ListView_SetItemState(h,item->iItem,item->state,item->stateMask); 
4725   }
4727   return true;
4730 bool ListView_GetItem(HWND h, LVITEM *item)
4732   if (WDL_NOT_NORMALLY(!item)) return false;
4733   if ((item->mask&LVIF_TEXT)&&item->pszText && item->cchTextMax > 0) item->pszText[0]=0;
4734   item->state=0;
4735   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return false;
4736   
4737   SWELL_ListView *tv=(SWELL_ListView*)h;
4738   if (tv->m_lbMode || !(tv->style & LVS_OWNERDATA))
4739   {
4740     if (!tv->m_items) return false;
4741     
4742     SWELL_ListView_Row *row=tv->m_items->Get(item->iItem);
4743     if (!row) return false;  
4744   
4745     if (item->mask & LVIF_PARAM) item->lParam=row->m_param;
4746     if (item->mask & LVIF_TEXT) if (item->pszText && item->cchTextMax>0)
4747     {
4748       const char *p=row->get_col_txt(item->iSubItem);
4749       lstrcpyn_safe(item->pszText,p?p:"",item->cchTextMax);
4750     }
4751       if (item->mask & LVIF_STATE)
4752       {
4753         if (item->stateMask & (0xff<<16))
4754         {
4755           item->state|=row->get_img_idx(item->iSubItem)<<16;
4756         }
4757       }
4758   }
4759   else
4760   {
4761     if (item->iItem <0 || item->iItem >= tv->ownermode_cnt) return false;
4763     int mask = item->mask & (LVIF_PARAM|LVIF_TEXT);
4764     if (mask & LVIF_TEXT)
4765     {
4766       if (!item->pszText || item->cchTextMax < 1) mask &= ~LVIF_TEXT;
4767       else *item->pszText = 0;
4768     }
4769     if (mask)
4770     {
4771       NMLVDISPINFO nm={{(HWND)tv, (UINT_PTR)[tv tag], LVN_GETDISPINFO}};
4772       nm.item.mask = mask;
4773       nm.item.iItem = item->iItem;
4774       nm.item.iSubItem = item->iSubItem;
4775       nm.item.pszText = item->pszText;
4776       nm.item.cchTextMax = item->cchTextMax;
4777       SendMessage(GetParent(h),WM_NOTIFY,nm.hdr.idFrom,(LPARAM)&nm);
4778       if (mask & LVIF_PARAM) item->lParam = nm.item.lParam;
4779     }
4780   }
4781   if (item->mask & LVIF_STATE)
4782   {
4783      if ((item->stateMask&LVIS_SELECTED) && [tv isRowSelected:item->iItem]) item->state|=LVIS_SELECTED;
4784      if ((item->stateMask&LVIS_FOCUSED) && [tv selectedRow] == item->iItem) item->state|=LVIS_FOCUSED;
4785   }
4787   return true;
4789 int ListView_GetItemState(HWND h, int ipos, UINT mask)
4791   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return 0;
4792   SWELL_ListView *tv=(SWELL_ListView*)h;
4793   UINT flag=0;
4794   if (tv->m_lbMode || !(tv->style & LVS_OWNERDATA))
4795   {
4796     if (!tv->m_items) return 0;
4797     SWELL_ListView_Row *row=tv->m_items->Get(ipos);
4798     if (!row) return 0;  
4799     if (mask & (0xff<<16))
4800     {
4801       flag|=row->get_img_idx(0)<<16;
4802     }
4803   }
4804   else
4805   {
4806     if (ipos<0 || ipos >= tv->ownermode_cnt) return 0;
4807   }
4808   
4809   if ((mask&LVIS_SELECTED) && [tv isRowSelected:ipos]) flag|=LVIS_SELECTED;
4810   if ((mask&LVIS_FOCUSED) && [tv selectedRow]==ipos) flag|=LVIS_FOCUSED;
4811   return flag;  
4814 int swell_ignore_listview_changes;
4816 bool ListView_SetItemState(HWND h, int ipos, UINT state, UINT statemask)
4818   int doref=0;
4819   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return false;
4820   SWELL_ListView *tv=(SWELL_ListView*)h;
4821   static int _is_doing_all;
4822   const bool isSingle = tv->m_lbMode ? !(tv->style & LBS_EXTENDEDSEL) : !!(tv->style&LVS_SINGLESEL);
4823   
4824   if (ipos == -1)
4825   {
4826     int x;
4827     int n=ListView_GetItemCount(h);
4828     NSIndexSet *oldSelection = NULL;
4829     if (statemask & LVIS_SELECTED)
4830     {
4831       oldSelection = [tv selectedRowIndexes];
4832       [oldSelection retain];
4833       if (state & LVIS_SELECTED)
4834       {
4835         if (isSingle)
4836         {
4837           statemask &= ~LVIS_SELECTED; // no-op and don't send LVN_ITEMCHANGED
4838         }
4839         else
4840         {
4841           swell_ignore_listview_changes++;
4842           [tv selectAll:nil];
4843           swell_ignore_listview_changes--;
4844         }
4845       }
4846       else
4847       {
4848         swell_ignore_listview_changes++;
4849         [tv deselectAll:nil];
4850         swell_ignore_listview_changes--;
4851       }
4852     }
4853     _is_doing_all++;
4854     for (x = 0; x < n; x ++)
4855     {
4856       if (statemask & ~LVIS_SELECTED)
4857         ListView_SetItemState(h,x,state,statemask & ~LVIS_SELECTED);
4859       if (statemask & LVIS_SELECTED)
4860       {
4861         if ([oldSelection containsIndex:x] == !(state & LVIS_SELECTED))
4862         {
4863           static int __rent;
4864           if (!__rent)
4865           {
4866             __rent=1;
4867             NMLISTVIEW nm={{(HWND)h,(UINT_PTR)[tv tag],LVN_ITEMCHANGED},x,0,state,};
4868             SendMessage(GetParent(h),WM_NOTIFY,nm.hdr.idFrom,(LPARAM)&nm);
4869             __rent=0;
4870           }
4871         }
4872       }
4873     }
4874     [oldSelection release];
4875     _is_doing_all--;
4876     ListView_RedrawItems(h,0,n-1);
4877     return true;
4878   }
4880   if (tv->m_lbMode || !(tv->style & LVS_OWNERDATA))
4881   {
4882     if (!tv->m_items) return false;
4883     SWELL_ListView_Row *row=tv->m_items->Get(ipos);
4884     if (!row) return false;  
4885     if (statemask & (0xff<<16))
4886     {
4887       if (row->get_img_idx(0)!=((state>>16)&0xff))
4888       {
4889         row->set_img_idx(0,(state>>16)&0xff);
4890         doref=1;
4891       }
4892     }
4893   }
4894   else
4895   {
4896     if (ipos<0 || ipos >= tv->ownermode_cnt) return 0;
4897   }
4898   bool didsel=false;
4899   if (statemask & LVIS_SELECTED)
4900   {
4901     if (state & LVIS_SELECTED)
4902     {
4903       if (![tv isRowSelected:ipos])
4904       {
4905         didsel = true;
4906         swell_ignore_listview_changes++;
4907         [tv selectRowIndexes:[NSIndexSet indexSetWithIndex:ipos] byExtendingSelection:isSingle?NO:YES];
4908         swell_ignore_listview_changes--;
4909       }
4910     }
4911     else
4912     {
4913       if ([tv isRowSelected:ipos])
4914       {
4915         didsel = true;
4916         swell_ignore_listview_changes++;
4917         [tv deselectRow:ipos];
4918         swell_ignore_listview_changes--;
4919       }
4920     }
4921   }
4922   if (statemask & LVIS_FOCUSED)
4923   {
4924     if (state&LVIS_FOCUSED)
4925     {
4926     }
4927     else
4928     {
4929       
4930     }
4931   }
4932   
4933   if (!_is_doing_all)
4934   {
4935     if (didsel)
4936     {
4937       static int __rent;
4938       if (!__rent)
4939       {
4940         __rent=1;
4941         NMLISTVIEW nm={{(HWND)h,(UINT_PTR)[tv tag],LVN_ITEMCHANGED},ipos,0,state,};
4942         SendMessage(GetParent(h),WM_NOTIFY,nm.hdr.idFrom,(LPARAM)&nm);
4943         __rent=0;
4944       }
4945     }
4946     if (doref)
4947       ListView_RedrawItems(h,ipos,ipos);
4948   }
4949   return true;
4952 void ListView_RedrawItems(HWND h, int startitem, int enditem)
4954   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return;
4955   SWELL_ListView *tv=(SWELL_ListView*)h;
4956   if (!tv->m_items) return;
4957   [tv reloadData];
4960 void ListView_DeleteItem(HWND h, int ipos)
4962   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return;
4963   
4964   SWELL_ListView *tv=(SWELL_ListView*)h;
4965   if (!tv->m_items) return;
4966   
4967   if (ipos >=0 && ipos < tv->m_items->GetSize())
4968   {
4969     if (ipos != tv->m_items->GetSize()-1)
4970     {
4971       NSIndexSet *sel=[tv selectedRowIndexes];
4972       if (sel && [sel count])
4973       {
4974         NSMutableIndexSet *ms = [[NSMutableIndexSet alloc] initWithIndexSet:sel];
4975         [ms shiftIndexesStartingAtIndex:ipos+1 by:-1];
4976         [tv selectRowIndexes:ms byExtendingSelection:NO];
4977         [ms release];
4978       }
4979     }
4980     tv->m_items->Delete(ipos,true);
4981     
4982     [tv reloadData];
4983     
4984   }
4987 void ListView_DeleteAllItems(HWND h)
4989   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return;
4990   
4991   SWELL_ListView *tv=(SWELL_ListView*)h;
4992   tv->ownermode_cnt=0;
4993   if (tv->m_items) tv->m_items->Empty(true);
4994   
4995   [tv reloadData];
4998 int ListView_GetSelectedCount(HWND h)
5000   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return 0;
5001   
5002   SWELL_ListView *tv=(SWELL_ListView*)h;
5003   return (int)[tv numberOfSelectedRows];
5006 int ListView_GetItemCount(HWND h)
5008   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return 0;
5009   
5010   SWELL_ListView *tv=(SWELL_ListView*)h;
5011   if (tv->m_lbMode || !(tv->style & LVS_OWNERDATA))
5012   {
5013     if (!tv->m_items) return 0;
5014   
5015     return tv->m_items->GetSize();
5016   }
5017   return tv->ownermode_cnt;
5020 int ListView_GetSelectionMark(HWND h)
5022   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return 0;
5023   
5024   SWELL_ListView *tv=(SWELL_ListView*)h;
5025   return (int)[tv selectedRow];
5028 int SWELL_GetListViewHeaderHeight(HWND h)
5030   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return 0;
5031   
5032   SWELL_ListView* tv=(SWELL_ListView*)h;
5033   NSTableHeaderView* hv=[tv headerView];
5034   NSRect r=[hv bounds];
5035   return (int)(r.size.height+0.5);
5038 void ListView_SetColumnWidth(HWND h, int pos, int wid)
5040   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return;
5041   SWELL_ListView *v=(SWELL_ListView *)h;
5042   if (!v->m_cols || pos < 0 || pos >= v->m_cols->GetSize()) return;
5043   
5044   NSTableColumn *col=v->m_cols->Get(pos);
5045   if (!col) return;
5046   
5047   if (!wid)
5048   {
5049     if ([col respondsToSelector:@selector(setHidden:)])  [(SWELL_TableColumnExtensions*)col setHidden:YES];
5050   }
5051   else 
5052   {
5053     if ([col respondsToSelector:@selector(setHidden:)])  [(SWELL_TableColumnExtensions*)col setHidden:NO];
5054     [col setWidth:wid];
5055   }
5058 BOOL ListView_GetColumnOrderArray(HWND h, int cnt, int* arr)
5060   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return FALSE;
5061   SWELL_ListView* lv=(SWELL_ListView*)h;
5062   if (!lv->m_cols || lv->m_cols->GetSize() != cnt) return FALSE;
5063   
5064   int i;
5065   for (i=0; i < cnt; ++i)
5066   {
5067     arr[i]=[lv getColumnPos:i];
5068   }
5070   return TRUE;
5073 BOOL ListView_SetColumnOrderArray(HWND h, int cnt, int* arr)
5075   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return FALSE;
5076   SWELL_ListView* lv=(SWELL_ListView*)h;
5077   if (!lv->m_cols || lv->m_cols->GetSize() != cnt) return FALSE;
5079   int i, j;
5080   for (i=0; i < cnt; ++i)
5081   {
5082     for (j=0; j < cnt; ++j)
5083     {
5084       if (arr[j] == i) break;
5085     }
5086     if (j < cnt)
5087     {
5088       int pos=[lv getColumnPos:j];
5089       [lv moveColumn:pos toColumn:i];
5090     }
5091   }
5093   return TRUE;
5096 HWND ListView_GetHeader(HWND h)
5098   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return 0;
5099   return h;
5102 int Header_GetItemCount(HWND h)
5104   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return 0;
5105   SWELL_ListView* lv=(SWELL_ListView*)h;
5106   if (lv->m_cols) return lv->m_cols->GetSize();
5107   return 0;
5110 BOOL Header_GetItem(HWND h, int col, HDITEM* hi)
5112   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]] || !hi)) return FALSE;
5113   SWELL_ListView* lv=(SWELL_ListView*)h;
5114   if (!lv->m_cols || col < 0 || col >= lv->m_cols->GetSize()) return FALSE;
5115   NSTableColumn* hcol=lv->m_cols->Get(col);
5116   if (WDL_NOT_NORMALLY(!hcol)) return FALSE;
5117   
5118   if (hi->mask&HDI_FORMAT)
5119   {
5120     hi->fmt=0;
5121     NSImage* img=[lv indicatorImageInTableColumn:hcol];
5122     if (img)
5123     {
5124       NSString* imgname=[img name];
5125       if (imgname)
5126       {
5127         if ([imgname isEqualToString:@"NSAscendingSortIndicator"]) hi->fmt |= HDF_SORTUP;
5128         else if ([imgname isEqualToString:@"NSDescendingSortIndicator"]) hi->fmt |= HDF_SORTDOWN;
5129       }
5130     }
5131   }
5132   // etc todo
5133   
5134   return TRUE;
5137 BOOL Header_SetItem(HWND h, int col, HDITEM* hi)
5139   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]] || !hi)) return FALSE;
5140   SWELL_ListView* lv=(SWELL_ListView*)h;
5141   if (!lv->m_cols || col < 0 || col >= lv->m_cols->GetSize()) return FALSE;
5142   NSTableColumn* hcol=lv->m_cols->Get(col);
5143   if (!hcol) return FALSE;
5144   
5145   if (hi->mask&HDI_FORMAT)
5146   {
5147     NSImage* img=0;
5148     if (hi->fmt&HDF_SORTUP) img=[NSImage imageNamed:@"NSAscendingSortIndicator"];
5149     else if (hi->fmt&HDF_SORTDOWN) img=[NSImage imageNamed:@"NSDescendingSortIndicator"];
5150     [lv setIndicatorImage:img inTableColumn:hcol];
5151   }
5152   // etc todo
5153   
5154   return TRUE;
5157 int ListView_HitTest(HWND h, LVHITTESTINFO *pinf)
5159   if (WDL_NOT_NORMALLY(!h || !pinf)) return -1;
5160   if (WDL_NOT_NORMALLY(![(id)h isKindOfClass:[SWELL_ListView class]])) return -1;
5161   
5162   SWELL_ListView *tv=(SWELL_ListView*)h;
5163   // return index
5164   pinf->flags=0;
5165   pinf->iItem=-1;
5166   
5167   // rowAtPoint will return a row even if it is scrolled out of the clip view
5168   NSScrollView* sv=(NSScrollView *)NavigateUpScrollClipViews(tv);
5169   if (![sv isKindOfClass:[NSScrollView class]] && ![sv isKindOfClass:[NSClipView class]]) sv=NULL;
5170   
5171   NSRect r=[sv documentVisibleRect];
5172   int x=pinf->pt.x-r.origin.x;
5173   int y=pinf->pt.y-r.origin.y;
5175   if (x < 0) pinf->flags |= LVHT_TOLEFT;
5176   if (x >= r.size.width) pinf->flags |= LVHT_TORIGHT;
5177   if (y < 0) pinf->flags |= LVHT_ABOVE;
5178   if (y >= r.size.height) pinf->flags |= LVHT_BELOW;
5179   
5180   if (!pinf->flags)
5181   {
5182     NSPoint pt = NSMakePoint( pinf->pt.x, pinf->pt.y );
5183     pinf->iItem=(int)[(NSTableView *)h rowAtPoint:pt];
5184     if (pinf->iItem >= 0)
5185     {
5186       pinf->flags=LVHT_ONITEMLABEL;
5187       if (tv->m_status_imagelist)
5188       {
5189         NSRect c0r = [tv frameOfCellAtColumn:[tv getColumnPos:0] row:pinf->iItem];
5190         if (pt.x >= c0r.origin.x && pt.x <= c0r.origin.x + [tv rowHeight])
5191         {
5192           pinf->flags=LVHT_ONITEMSTATEICON;
5193         }
5194       }
5195     }
5196     else 
5197     {
5198       pinf->flags = y < 10 && ListView_GetItemCount(h)>0 ? LVHT_ABOVE : LVHT_NOWHERE;
5199     }
5200   }
5201   
5202   return pinf->iItem;
5205 int ListView_SubItemHitTest(HWND h, LVHITTESTINFO *pinf)
5207   int row = ListView_HitTest(h, pinf);
5209   NSPoint pt=NSMakePoint(pinf->pt.x,pinf->pt.y);
5211   if (row < 0 && pt.y < 0)
5212   { // Fake the point in the client area of the listview to get the column # (like win32)
5213     pt.y = 0;
5214   }
5215   pinf->iSubItem=(int)[(NSTableView *)h columnAtPoint:pt];
5216   return row;
5219 void ListView_SetItemCount(HWND h, int cnt)
5221   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return;
5222   
5223   SWELL_ListView *tv=(SWELL_ListView*)h;
5224   if (!tv->m_lbMode && (tv->style & LVS_OWNERDATA))
5225   {
5226     tv->ownermode_cnt=cnt;
5227     [tv noteNumberOfRowsChanged];
5228   }
5231 void ListView_EnsureVisible(HWND h, int i, BOOL pok)
5233   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return;
5234   
5235   SWELL_ListView *tv=(SWELL_ListView*)h;
5236   
5237   if (i<0)i=0;
5238   if (!tv->m_lbMode && (tv->style & LVS_OWNERDATA))
5239   {
5240     if (i >=tv->ownermode_cnt-1) i=tv->ownermode_cnt-1;
5241   }
5242   else
5243   {
5244     if (tv->m_items && i >= tv->m_items->GetSize()) i=tv->m_items->GetSize()-1;
5245   }
5246   if (i>=0)
5247   {
5248     [tv scrollRowToVisible:i];
5249   }  
5252 static bool ListViewGetRectImpl(HWND h, int item, int subitem, RECT* r) // subitem<0 for full item rect
5254   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_ListView class]])) return false;
5255   if (item < 0 || item > ListView_GetItemCount(h)) return false;
5256   SWELL_ListView *tv=(SWELL_ListView*)h;
5257   
5258   if (subitem >= 0 && (!tv->m_cols || subitem >= tv->m_cols->GetSize())) return false;
5259   subitem=[tv getColumnPos:subitem];
5260   
5261   NSRect ar;
5262   if (subitem < 0) ar = [tv rectOfRow:item];
5263   else ar=[tv frameOfCellAtColumn:subitem row:item];
5264   NSSize sp=[tv intercellSpacing];
5265   
5266   ar.size.width += sp.width;
5267   ar.size.height += sp.height;
5268   NSRECT_TO_RECT(r,ar);
5269   
5270   return true;
5273 bool ListView_GetSubItemRect(HWND h, int item, int subitem, int code, RECT *r)
5275   return ListViewGetRectImpl(h, item, subitem, r);
5278 bool ListView_GetItemRect(HWND h, int item, RECT *r, int code)
5280   return ListViewGetRectImpl(h, item, -1, r);
5283 int ListView_GetTopIndex(HWND h)
5285   NSTableView* tv = (NSTableView*)h;
5286   if (WDL_NOT_NORMALLY(!tv)) return -1;
5287   NSScrollView* sv = [tv enclosingScrollView];
5288   if (WDL_NOT_NORMALLY(!sv)) return -1;  
5289   
5290   NSPoint pt = { 0, 0 };
5291   NSView *hdr = [tv headerView];
5292   if (hdr && ![hdr isHidden])
5293   {
5294     NSRect fr=[hdr frame];
5295     if (fr.size.height > 0.0) pt.y = fr.origin.y + fr.size.height;
5296   }
5297   pt.y += [sv documentVisibleRect].origin.y;
5298   return (int)[tv rowAtPoint:pt];      
5301 int ListView_GetCountPerPage(HWND h)
5303   NSTableView* tv = (NSTableView*)h;
5304   if (WDL_NOT_NORMALLY(!tv)) return 0;
5305   NSScrollView* sv = [tv enclosingScrollView];
5306   if (WDL_NOT_NORMALLY(!sv)) return 0;  
5307   
5308   NSRect tvr = [sv documentVisibleRect];
5309   int rowh = [tv rowHeight];
5310   return tvr.size.height/rowh;
5313 bool ListView_Scroll(HWND h, int xscroll, int yscroll)
5315   NSTableView* tv = (NSTableView*)h;
5316   NSScrollView* sv = [tv enclosingScrollView];
5317   if (WDL_NOT_NORMALLY(!sv)) return false;
5318   
5319   NSRect tvr = [sv documentVisibleRect];
5320   NSPoint pt = { tvr.origin.x, tvr.origin.y };
5321   if (xscroll > 0) pt.x += tvr.size.width-1;
5322   if (yscroll > 0) pt.y += tvr.size.height-1;
5323   
5324   const NSInteger nr = [tv numberOfRows];
5325   NSInteger rowidx = [tv rowAtPoint:pt];
5326   if (rowidx < 0) rowidx=0;
5327   else if (rowidx >= nr) rowidx=nr-1;
5328   
5329   const NSInteger nc = [tv numberOfColumns];
5330   NSInteger colidx = [tv columnAtPoint:pt];
5331   if (colidx < 0) colidx=0;
5332   else if (colidx >= nc) colidx = nc-1;
5334   // colidx is our column index, not the display order, convert
5335   if ([tv isKindOfClass:[SWELL_ListView class]]) colidx = [(SWELL_ListView*)tv getColumnPos:(int)colidx];
5337   NSRect ir = [tv frameOfCellAtColumn:colidx row:rowidx];
5339   if (yscroll)
5340   {
5341     if (ir.size.height) rowidx += yscroll / ir.size.height;
5343     if (rowidx < 0) rowidx=0;
5344     else if (rowidx >= nr) rowidx = nr-1;
5345     [tv scrollRowToVisible:rowidx];
5346   }
5347   
5348   if (xscroll)
5349   {
5350     if (ir.size.width) colidx += xscroll / ir.size.width;
5351    
5352     if (colidx < 0) colidx=0;
5353     else if (colidx >= nc) colidx = nc-1;
5355     // scrollColumnToVisible takes display order, which we have here
5356     [tv scrollColumnToVisible:colidx];
5357   }
5358   
5359   
5360   return true;
5363 bool ListView_GetScroll(HWND h, POINT* p)
5365   NSTableView* tv = (NSTableView*)h;
5366   NSScrollView* sv = [tv enclosingScrollView];
5367   if (WDL_NORMALLY(sv))
5368   {
5369     NSRect cr = [sv documentVisibleRect];
5370     p->x = cr.origin.x;
5371     p->y = cr.origin.y;
5372     return true;
5373   }
5374   p->x=p->y=0;
5375   return false;
5378 void ListView_SortItems(HWND hwnd, PFNLVCOMPARE compf, LPARAM parm)
5380   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_ListView class]])) return;
5381   SWELL_ListView *tv=(SWELL_ListView*)hwnd;
5382   if (tv->m_lbMode || (tv->style & LVS_OWNERDATA) || !tv->m_items) return;
5383     
5384   WDL_HeapBuf tmp;
5385   tmp.Resize(tv->m_items->GetSize()*sizeof(void *));
5386   int x;
5387   int sc=0;
5388   for(x=0;x<tv->m_items->GetSize();x++)
5389   {
5390     SWELL_ListView_Row *r = tv->m_items->Get(x);
5391     if (r) 
5392     {
5393       r->m_tmp = !![tv isRowSelected:x];
5394       sc++;
5395     }
5396   }
5397   __listview_mergesort_internal(tv->m_items->GetList(),tv->m_items->GetSize(),sizeof(void *),compf,parm,(char*)tmp.Get());
5398   if (sc)
5399   {
5400     NSMutableIndexSet *indexSet = [[NSMutableIndexSet  alloc] init];
5401     
5402     for(x=0;x<tv->m_items->GetSize();x++)
5403     {
5404       SWELL_ListView_Row *r = tv->m_items->Get(x);
5405       if (r && (r->m_tmp&1)) [indexSet addIndex:x];
5406     }
5407     [tv selectRowIndexes:indexSet byExtendingSelection:NO];
5408     [indexSet release];
5409   }
5410   
5411   [tv reloadData];
5415 HWND WindowFromPoint(POINT p)
5417   NSArray *windows=[NSApp orderedWindows];
5418   const NSInteger cnt=windows ? [windows count] : 0;
5420   NSWindow *kw = [NSApp keyWindow];
5421   if (kw && windows && [windows containsObject:kw]) kw=NULL;
5423   NSWindow *bestwnd=0;
5424   for (NSInteger x = kw ? -1 : 0; x < cnt; x ++)
5425   {
5426     NSWindow *wnd = kw;
5427     if (x>=0) wnd=[windows objectAtIndex:x];
5428     if (wnd && [wnd isVisible] && HTTRANSPARENT != SendMessage((HWND)wnd, WM_NCHITTEST, 0, MAKELPARAM(p.x, p.y)))
5429     {
5430       NSRect fr=[wnd frame];
5431       if (p.x >= fr.origin.x && p.x < fr.origin.x + fr.size.width &&
5432           p.y >= fr.origin.y && p.y < fr.origin.y + fr.size.height)
5433       {
5434         bestwnd=wnd;
5435         break;
5436       }    
5437     }
5438   }
5439   
5440   if (!bestwnd) return 0;
5441   NSPoint pt=NSMakePoint(p.x,p.y);
5442   NSPoint lpt=[bestwnd convertScreenToBase:pt];
5443   NSView *v=[[bestwnd contentView] hitTest:lpt];
5444   if (v) return (HWND)v;
5445   return (HWND)[bestwnd contentView]; 
5448 void UpdateWindow(HWND hwnd)
5450   if (WDL_NORMALLY(hwnd && [(id)hwnd isKindOfClass:[NSView class]]))
5451   {
5452 #ifndef SWELL_NO_METAL
5453     if ([(id)hwnd isKindOfClass:[SWELL_hwndChild class]] && 
5454         ((SWELL_hwndChild *)hwnd)->m_use_metal > 0)
5455     {
5456       // do nothing for metal windows, let the timer catch it
5457     }
5458     else 
5459 #endif
5460     {
5461       if ([(NSView *)hwnd needsDisplay])
5462       {
5463         NSWindow *wnd = [(NSView *)hwnd window];
5464         [wnd displayIfNeeded];
5465       }
5466     }
5467   }
5470 void SWELL_FlushWindow(HWND h)
5472   if (WDL_NORMALLY(h))
5473   {
5474     NSWindow *w=NULL;
5475     if ([(id)h isKindOfClass:[NSView class]]) 
5476     {
5477 #ifndef SWELL_NO_METAL
5478       if ([(id)h isKindOfClass:[SWELL_hwndChild class]] && ((SWELL_hwndChild *)h)->m_use_metal > 0)
5479         return;
5480 #endif
5482       if ([(NSView *)h needsDisplay]) return;
5483       
5484       w = [(NSView *)h window];
5485     }
5486     else if ([(id)h isKindOfClass:[NSWindow class]]) w = (NSWindow *)h;
5487     
5488     if (w && ![w viewsNeedDisplay])
5489     {
5490       [w flushWindow];
5491     }
5492   }
5495 static void InvalidateSuperViews(NSView *view)
5497   if (!view) return;
5498   view = [view superview];
5499   while (view)
5500   {
5501     if ([view isKindOfClass:[SWELL_hwndChild class]]) 
5502     {
5503       if (((SWELL_hwndChild *)view)->m_isdirty&2) break;
5504       ((SWELL_hwndChild *)view)->m_isdirty|=2;
5505     }
5506     view = [view superview];
5507   }
5509            
5510 #ifdef _DEBUG
5511 int g_swell_in_paint;
5512 #endif
5514 BOOL InvalidateRect(HWND hwnd, const RECT *r, int eraseBk)
5516 #ifdef _DEBUG
5517   if (g_swell_in_paint)
5518   {
5519     printf("swell-cocoa: calling InvalidateRect() from within paint, this is allowed but bad form.\n");
5520     // WDL_ASSERT(false);
5521   }
5522 #endif
5524   if (WDL_NOT_NORMALLY(!hwnd)) return FALSE;
5525   id view=(id)hwnd;
5526   if ([view isKindOfClass:[NSWindow class]]) view=[view contentView];
5527   if (WDL_NORMALLY([view isKindOfClass:[NSView class]]))
5528   {
5530     NSView *sv = view;
5531     
5532     bool skip_parent_invalidate=false;
5533     if ([view isKindOfClass:[SWELL_hwndChild class]])
5534     {
5535 #ifndef SWELL_NO_METAL
5536       SWELL_hwndChild *hc = (SWELL_hwndChild*)view;
5537       if (hc->m_use_metal > 0)
5538       {
5539         if (![hc isHiddenOrHasHiddenAncestor]) 
5540         {
5541           swell_addMetalDirty(hc,r);
5542         }
5543         return TRUE;
5544       }
5545 #endif
5546       if (!(((SWELL_hwndChild *)view)->m_isdirty&1))
5547       {
5548         ((SWELL_hwndChild *)view)->m_isdirty|=1;
5549       }
5550       else skip_parent_invalidate=true; // if already dirty, then assume parents are already dirty too
5551     }
5552     if (!skip_parent_invalidate)
5553     {
5554       InvalidateSuperViews(view);
5555     }
5556     if (r)
5557     {
5558       RECT tr=*r;
5559       if (tr.top>tr.bottom)
5560       {
5561         int a = tr.top; tr.top=tr.bottom; tr.bottom=a;
5562       }
5563       [sv setNeedsDisplayInRect:NSMakeRect(tr.left,tr.top,tr.right-tr.left,tr.bottom-tr.top)]; 
5564     }
5565     else [sv setNeedsDisplay:YES];
5566     
5567   }
5568   return TRUE;
5571 static HWND m_fakeCapture;
5572 static BOOL m_capChangeNotify;
5573 HWND GetCapture()
5576   return m_fakeCapture;
5579 HWND SetCapture(HWND hwnd)
5581   HWND oc=m_fakeCapture;
5582   int ocn=m_capChangeNotify;
5583   m_fakeCapture=hwnd;
5584   m_capChangeNotify = hwnd && [(id)hwnd respondsToSelector:@selector(swellCapChangeNotify)] && [(SWELL_hwndChild*)hwnd swellCapChangeNotify];
5586   if (hwnd && WDL_NORMALLY([(id)hwnd isKindOfClass:[NSView class]]))
5587     [[(NSView *)hwnd window] disableCursorRects];
5589   if (ocn && oc && oc != hwnd) SendMessage(oc,WM_CAPTURECHANGED,0,(LPARAM)hwnd);
5590   return oc;
5594 void ReleaseCapture()
5596   HWND h=m_fakeCapture;
5597   m_fakeCapture=NULL;
5598   if (m_capChangeNotify && h)
5599   {
5600     SendMessage(h,WM_CAPTURECHANGED,0,0);
5601   }
5605 HDC BeginPaint(HWND hwnd, PAINTSTRUCT *ps)
5607   if (WDL_NOT_NORMALLY(!ps)) return 0;
5608   memset(ps,0,sizeof(PAINTSTRUCT));
5609   if (WDL_NOT_NORMALLY(!hwnd)) return 0;
5610   id turd = (id)hwnd;
5611   if (![turd respondsToSelector:@selector(getSwellPaintInfo:)]) return 0;
5613   [(SWELL_hwndChild*)turd getSwellPaintInfo:(PAINTSTRUCT *)ps];
5614   return ps->hdc;
5617 BOOL EndPaint(HWND hwnd, PAINTSTRUCT *ps)
5619   WDL_ASSERT(hwnd != NULL && ps != NULL);
5620   return TRUE;
5623 LRESULT DefWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
5625   if (msg==WM_RBUTTONUP||msg==WM_NCRBUTTONUP)
5626   {  
5627     POINT p={GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam)};
5628     HWND hwndDest=hwnd;
5629     if (msg==WM_RBUTTONUP)
5630     {
5631       ClientToScreen(hwnd,&p);
5632       HWND h=WindowFromPoint(p);
5633       if (h && IsChild(hwnd,h)) hwndDest=h;
5634     }
5635     SendMessage(hwnd,WM_CONTEXTMENU,(WPARAM)hwndDest,MAKELONG(p.x&0xffff,p.y));
5636     return 1;
5637   }
5638   else if (msg==WM_CONTEXTMENU || msg == WM_MOUSEWHEEL || msg == WM_MOUSEHWHEEL || msg == WM_GESTURE)
5639   {
5640     if ([(id)hwnd isKindOfClass:[NSView class]])
5641     {
5642       NSView *h=(NSView *)hwnd;
5643       while (h && [[h window] contentView] != h)
5644       {
5645         h=[h superview];
5646         if (h && [h respondsToSelector:@selector(onSwellMessage:p1:p2:)]) 
5647         {
5648            return SendMessage((HWND)h,msg,wParam,lParam);    
5649         }
5650       }
5651     }
5652   }
5653   else if (msg==WM_NCHITTEST) 
5654   {
5655     int rv=HTCLIENT;
5656     SWELL_BEGIN_TRY
5657     RECT r;
5658     GetWindowRect(hwnd,&r);
5659     POINT pt={GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam)};
5661     if (r.top > r.bottom) 
5662     { 
5663       pt.y = r.bottom + (r.top - pt.y); // translate coordinate into flipped-window
5665       int a=r.top; r.top=r.bottom; r.bottom=a; 
5666     }
5667     NCCALCSIZE_PARAMS p={{r,}};
5668     SendMessage(hwnd,WM_NCCALCSIZE,FALSE,(LPARAM)&p);
5669     if (!PtInRect(&p.rgrc[0],pt)) rv=HTNOWHERE;
5670     SWELL_END_TRY(;)
5671     return rv;
5672   }
5673   else if (msg==WM_KEYDOWN || msg==WM_KEYUP)
5674   {
5675     // if not ctrl/cmd/opt modified, and not tab, and listview/treeview, do not bubble it up
5676     // (fixes triggering of menu items when searching for text etc)
5677     if (!(lParam & (FCONTROL|FALT|FLWIN)) && !(wParam == VK_TAB && (lParam&FVIRTKEY)))
5678     {
5679       id fr = [[(NSView *)hwnd window] firstResponder];
5680       if ([fr isKindOfClass:[NSTableView class]] ||
5681           [fr isKindOfClass:[NSOutlineView class]]) return 1;
5683       if (wParam >= VK_LEFT && wParam <= VK_DOWN && [fr isKindOfClass:[NSButton class]])
5684       {
5685         // do not bubble up left/right for any
5686         if (wParam == VK_LEFT || wParam == VK_RIGHT) return 1;
5688         // do not bubble up left/down for non-NSPopUpButton
5689         if (![fr isKindOfClass:[NSPopUpButton class]]) return 1;
5690       }
5691     }
5692     return 69;
5693   }
5694   else if (msg == WM_DISPLAYCHANGE)
5695   {
5696     if ([(id)hwnd isKindOfClass:[NSView class]])
5697     {
5698       NSArray *ch = [(NSView *)hwnd subviews];
5699       if (ch)
5700       {
5701         int x;
5702         for(x=0;x<[ch count]; x ++)
5703         {
5704           NSView *v = [ch objectAtIndex:x];
5705           sendSwellMessage(v,WM_DISPLAYCHANGE,wParam,lParam);
5706         }
5707         if (x)
5708         {
5709           void SWELL_DoDialogColorUpdates(HWND hwnd, DLGPROC d, bool isUpdate);
5710           DLGPROC d = (DLGPROC)GetWindowLong(hwnd,DWL_DLGPROC);
5711           if (d) SWELL_DoDialogColorUpdates(hwnd,d,true);
5712         }
5713       }
5714       InvalidateRect((HWND)hwnd,NULL,FALSE);
5715     }
5716   }
5717   else if (msg == WM_CTLCOLORSTATIC && wParam)
5718   {
5719     if (SWELL_osx_is_dark_mode(0))
5720     {
5721       static HBRUSH br;
5722       if (!br)
5723       {
5724         br = CreateSolidBrush(RGB(0,0,0)); // todo hm
5725         br->color = (CGColorRef) [[NSColor windowBackgroundColor] CGColor];
5726         CFRetain(br->color);
5727       }
5728       SetTextColor((HDC)wParam,RGB(255,255,255));
5729       return (LRESULT)br;
5730     }
5731   }
5732   return 0;
5735 void SWELL_BroadcastMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
5737   NSArray *ch=[NSApp windows];
5738   [ch retain];
5739   for (int x=0;x<[ch count]; x ++)
5740   {
5741     NSView *v = [[ch objectAtIndex:x] contentView];
5742     if (v && [v respondsToSelector:@selector(onSwellMessage:p1:p2:)])
5743     {
5744       [(SWELL_hwndChild *)v onSwellMessage:uMsg p1:wParam p2:lParam];
5745     }
5746   }
5747   [ch release];
5763 ///////////////// clipboard compatability (NOT THREAD SAFE CURRENTLY)
5766 BOOL DragQueryPoint(HDROP hDrop,LPPOINT pt)
5768   if (!hDrop) return 0;
5769   DROPFILES *df=(DROPFILES*)GlobalLock(hDrop);
5770   BOOL rv=!df->fNC;
5771   *pt=df->pt;
5772   GlobalUnlock(hDrop);
5773   return rv;
5776 void DragFinish(HDROP hDrop)
5778 //do nothing for now (caller will free hdrops)
5781 UINT DragQueryFile(HDROP hDrop, UINT wf, char *buf, UINT bufsz)
5783   if (!hDrop) return 0;
5784   DROPFILES *df=(DROPFILES*)GlobalLock(hDrop);
5786   size_t rv=0;
5787   char *p=(char*)df + df->pFiles;
5788   if (wf == 0xFFFFFFFF)
5789   {
5790     while (*p)
5791     {
5792       rv++;
5793       p+=strlen(p)+1;
5794     }
5795   }
5796   else
5797   {
5798     while (*p)
5799     {
5800       if (!wf--)
5801       {
5802         if (buf)
5803         {
5804           lstrcpyn_safe(buf,p,bufsz);
5805           rv=strlen(buf);
5806         }
5807         else rv=strlen(p);
5808           
5809         break;
5810       }
5811       p+=strlen(p)+1;
5812     }
5813   }
5814   GlobalUnlock(hDrop);
5815   return (UINT)rv;
5828 static WDL_PtrList<void> m_clip_recs;
5829 static WDL_PtrList<NSString> m_clip_fmts;
5830 static WDL_PtrList<char> m_clip_curfmts;
5831 struct swell_pendingClipboardStates
5833   UINT type;
5834   HANDLE h;
5835   swell_pendingClipboardStates(UINT _type, HANDLE _h)
5836   {
5837     type = _type;
5838     h = _h;
5839   }
5840   ~swell_pendingClipboardStates()
5841   {
5842     GlobalFree(h);
5843   }
5846 static WDL_PtrList<swell_pendingClipboardStates> m_clipsPending;
5848 bool OpenClipboard(HWND hwndDlg)
5850   m_clipsPending.Empty(true);
5851   RegisterClipboardFormat(NULL);
5853   NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
5854   m_clip_curfmts.Empty();
5855   if (SWELL_GetOSXVersion()>=0x1060)
5856   {
5857     NSArray *list = [pasteboard
5858       readObjectsForClasses:[NSArray arrayWithObject:[NSURL class]]
5859       options:[NSMutableDictionary dictionaryWithCapacity:1]];
5860     if ([list count]) m_clip_curfmts.Add((char*)(INT_PTR)CF_HDROP);
5861   }
5862   NSArray *ar=[pasteboard types];
5863   
5864   if (ar && [ar count])
5865   {
5866     int x;
5867     
5868     for (x = 0; x < [ar count]; x ++)
5869     {
5870       NSString *s=[ar objectAtIndex:x];
5871       if (!s) continue;
5872       int y;
5873       for (y = 0; y < m_clip_fmts.GetSize(); y ++)
5874       {
5875         NSString *cs = m_clip_fmts.Get(y);
5876         if (cs && [s compare:cs]==NSOrderedSame)
5877         {
5878           char *tok = (char*)(INT_PTR)(y+1);
5879           if (m_clip_curfmts.Find(tok)<0) m_clip_curfmts.Add(tok);
5880           break;
5881         }
5882       }
5883       
5884     }
5885   }
5886   return true;
5889 void CloseClipboard() // frees any remaining items in clipboard
5891   m_clip_recs.Empty(GlobalFree);
5892   
5893   if (m_clipsPending.GetSize())
5894   {
5895     int x;
5896     NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
5897     
5898     NSMutableArray *ar = [[NSMutableArray alloc] initWithCapacity:m_clipsPending.GetSize()];
5899     
5900     int hdrop_cnt=0;
5901     for (x=0;x<m_clipsPending.GetSize();x++)
5902     {
5903       swell_pendingClipboardStates *cs=m_clipsPending.Get(x);
5904       if (cs->type == CF_HDROP)
5905       {
5906         hdrop_cnt++;
5907       }
5908       else
5909       {
5910         NSString *fmt=m_clip_fmts.Get(cs->type-1);
5911         if (fmt) [ar addObject:fmt];
5912       }
5913     }
5915     if (hdrop_cnt || [ar count])
5916     {
5917       if ([ar count])
5918         [pasteboard declareTypes:ar owner:nil];
5919       else if (SWELL_GetOSXVersion() >= 0x1060)
5920         [pasteboard clearContents];
5921       for (x=0;x<m_clipsPending.GetSize();x++)
5922       {
5923         swell_pendingClipboardStates *cs=m_clipsPending.Get(x);
5924         
5925         void *buf=GlobalLock(cs->h);
5926         if (buf)
5927         {
5928           int bufsz=GlobalSize(cs->h);
5929           if (cs->type == CF_TEXT)
5930           {
5931             char *t = (char *)malloc(bufsz+1);
5932             if (t)
5933             {
5934               memcpy(t,buf,bufsz);
5935               t[bufsz]=0;
5936               NSString *s = (NSString*)SWELL_CStringToCFString(t);
5937               [pasteboard setString:s forType:NSStringPboardType];
5938               [s release];
5939               free(t);
5940             }
5941           }
5942           else if (cs->type == CF_HDROP)
5943           {
5944             if (WDL_NORMALLY(bufsz > sizeof(DROPFILES)))
5945             {
5946               const DROPFILES *hdr = (const DROPFILES *)buf;
5947               if (
5948                   WDL_NORMALLY(hdr->pFiles < bufsz) &&
5949                   WDL_NORMALLY(!hdr->fWide) // todo deal with UTF-16
5950               )
5951               {
5952                 NSMutableArray *list = [NSMutableArray arrayWithCapacity:20];
5953                 const char *rd = (const char *)buf;
5954                 DWORD rdo = hdr->pFiles;
5955                 while (rdo < bufsz && rd[rdo])
5956                 {
5957                   NSString *fnstr=(NSString *)SWELL_CStringToCFString(rd+rdo);
5958                   NSURL *url = [NSURL fileURLWithPath:fnstr];
5959                   [fnstr release];
5960                   if (url) [list addObject:url];
5961                   rdo += strlen(rd+rdo)+1;
5962                 }
5964                 if ([list count] && SWELL_GetOSXVersion() >= 0x1060)
5965                 {
5966                   [pasteboard writeObjects:list];
5967                 }
5968               }
5969             }
5970           }
5971           else
5972           {
5973             NSString *fmt=m_clip_fmts.Get(cs->type-1);
5974             if (fmt)
5975             {
5976               NSData *data=[NSData dataWithBytes:buf length:bufsz];
5977               [pasteboard setData:data forType:fmt];
5978             }
5979           }
5980           GlobalUnlock(cs->h);
5981         }
5982       }
5983     }
5984     [ar release];
5985     m_clipsPending.Empty(true);
5986   }  
5989 UINT EnumClipboardFormats(UINT lastfmt)
5991   if (lastfmt == 0) return (UINT)(INT_PTR)m_clip_curfmts.Get(0);
5992   const int idx = m_clip_curfmts.Find((char *)(INT_PTR)lastfmt);
5993   return idx >= 0 ? (UINT)(INT_PTR)m_clip_curfmts.Get(idx+1) : 0;
5996 HANDLE GetClipboardData(UINT type)
5998   RegisterClipboardFormat(NULL);
5999   NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
6001   HANDLE h=0;
6002   if (type == CF_TEXT)
6003   {
6004     [pasteboard types];
6005     NSString *str = [pasteboard stringForType:NSStringPboardType];
6006     if (str)
6007     {
6008       int l = (int) ([str length]*4 + 32);
6009       char *buf = (char *)malloc(l);
6010       if (!buf) return 0;
6011       SWELL_CFStringToCString(str,buf,l);
6012       buf[l-1]=0;
6013       l = (int) (strlen(buf)+1);
6014       h=GlobalAlloc(0,l);
6015       memcpy(GlobalLock(h),buf,l);
6016       GlobalUnlock(h);
6017       free(buf);
6018     }
6019   }
6020   else if (type == CF_HDROP)
6021   {
6022     if (SWELL_GetOSXVersion()>=0x1060)
6023     {
6024       [pasteboard types];
6025       NSArray *list = [pasteboard
6026         readObjectsForClasses:[NSArray arrayWithObject:[NSURL class]]
6027         options:[NSMutableDictionary dictionaryWithCapacity:1]
6028       ];
6029       int nf = (int) [list count];
6030       if (nf > 0)
6031       {
6032         WDL_TypedQueue<char> flist;
6033         flist.Add(NULL,sizeof(DROPFILES));
6034         for (int x=0;x<nf;x++)
6035         {
6036           NSURL *url = (NSURL *)[list objectAtIndex:x];
6037           if ([url isFileURL])
6038           {
6039             const char *ptr = [[url path] UTF8String];
6040             if (ptr && *ptr) flist.Add(ptr, (int)strlen(ptr)+1);
6041           }
6042         }
6043         if (flist.GetSize()>sizeof(DROPFILES))
6044         {
6045           flist.Add("",1);
6046           DROPFILES *hdr = (DROPFILES*)flist.Get();
6047           memset(hdr,0,sizeof(*hdr));
6048           hdr->pFiles = sizeof(DROPFILES);
6049           h=GlobalAlloc(0,flist.GetSize());
6050           if (h) memcpy(GlobalLock(h),flist.Get(),flist.GetSize());
6051           GlobalUnlock(h);
6052         }
6053       }
6054     }
6055   }
6056   else
6057   {
6058     NSString *fmt=m_clip_fmts.Get(type-1);
6059     if (fmt)
6060     {
6061       NSData *data=[pasteboard dataForType:fmt];
6062       if (!data) return 0;
6063       int l = (int)[data length];
6064       h=GlobalAlloc(0,l);
6065       if (h) memcpy(GlobalLock(h),[data bytes],l);
6066       GlobalUnlock(h);
6067     }
6068   }
6070   if (h) m_clip_recs.Add(h);
6071   return h;
6074 void EmptyClipboard()
6076   m_clipsPending.Empty(true);
6080 void SetClipboardData(UINT type, HANDLE h)
6082   m_clipsPending.Add(new swell_pendingClipboardStates(type,h));
6085 UINT RegisterClipboardFormat(const char *desc)
6087   if (!m_clip_fmts.GetSize())
6088   {
6089     m_clip_fmts.Add([NSStringPboardType retain]); // CF_TEXT
6090     m_clip_fmts.Add(NULL); // CF_HDROP
6091   }
6092   if (!desc || !*desc) return 0;
6094   if (!strcmp(desc,"SWELL__CF_TEXT")) return CF_TEXT; // for legacy SWELL users
6096   NSString *s=(NSString*)SWELL_CStringToCFString(desc);
6097   int x;
6098   for (x = 0; x < m_clip_fmts.GetSize(); x ++)
6099   {
6100     NSString *ts=m_clip_fmts.Get(x);
6101     if (ts && [ts compare:s]==NSOrderedSame)
6102     {
6103       [s release];
6104       return x+1;
6105     }
6106   }
6107   m_clip_fmts.Add(s);
6108   return m_clip_fmts.GetSize();
6111 int EnumPropsEx(HWND hwnd, PROPENUMPROCEX proc, LPARAM lParam)
6113   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd respondsToSelector:@selector(swellEnumProps:lp:)])) return -1;
6114   return (int)[(SWELL_hwndChild *)hwnd swellEnumProps:proc lp:lParam];
6117 HANDLE GetProp(HWND hwnd, const char *name)
6119   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd respondsToSelector:@selector(swellGetProp:wantRemove:)])) return NULL;
6120   return (HANDLE)[(SWELL_hwndChild *)hwnd swellGetProp:name wantRemove:NO];
6123 BOOL SetProp(HWND hwnd, const char *name, HANDLE val)
6125   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd respondsToSelector:@selector(swellSetProp:value:)])) return FALSE;
6126   return (BOOL)!![(SWELL_hwndChild *)hwnd swellSetProp:name value:val];
6129 HANDLE RemoveProp(HWND hwnd, const char *name)
6131   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd respondsToSelector:@selector(swellGetProp:wantRemove:)])) return NULL;
6132   return (HANDLE)[(SWELL_hwndChild *)hwnd swellGetProp:name wantRemove:YES];
6136 int GetSystemMetrics(int p)
6138   switch (p)
6139   {
6140     case SM_CXSCREEN:
6141     case SM_CYSCREEN:
6142     {
6143       NSScreen *s=[NSScreen mainScreen];
6144       if (!s) return 1024;
6145       return p==SM_CXSCREEN ? [s frame].size.width : [s frame].size.height;
6146     }
6147     case SM_CXHSCROLL: return 16;
6148     case SM_CYHSCROLL: return 16;
6149     case SM_CXVSCROLL: return 16;
6150     case SM_CYVSCROLL: return 16;
6151     case SM_CYMENU: return (int)([[NSApp mainMenu] menuBarHeight] + 0.5);
6152   }
6153   return 0;
6156 BOOL ScrollWindow(HWND hwnd, int xamt, int yamt, const RECT *lpRect, const RECT *lpClipRect)
6158   if (hwnd && [(id)hwnd isKindOfClass:[NSWindow class]]) hwnd=(HWND)[(id)hwnd contentView];
6159   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[NSView class]])) return FALSE;
6161   if (!xamt && !yamt) return FALSE;
6162   
6163   // move child windows only
6164   if (1)
6165   {
6166     if (xamt || yamt)
6167     {
6168       NSArray *ar=[(NSView*)hwnd subviews];
6169       NSInteger i,c=[ar count];
6170       for(i=0;i<c;i++)
6171       {
6172         NSView *v=(NSView *)[ar objectAtIndex:i];
6173         NSRect r=[v frame];
6174         r.origin.x+=xamt;
6175         r.origin.y+=yamt;
6176         [v setFrame:r];
6177       }
6178     }
6179     [(id)hwnd setNeedsDisplay:YES];
6180   }
6181   else
6182   {
6183     NSRect r=[(NSView*)hwnd bounds];
6184     r.origin.x -= xamt;
6185     r.origin.y -= yamt;
6186     [(id)hwnd setBoundsOrigin:r.origin];
6187     [(id)hwnd setNeedsDisplay:YES];
6188   }
6189   return TRUE;
6192 HWND FindWindowEx(HWND par, HWND lastw, const char *classname, const char *title)
6194   // note: this currently is far far far from fully functional, bleh
6195   if (!par)
6196   {
6197     if (!title) return NULL;
6199     // get a list of top level windows, find any that match
6200     // (this does not scan child windows, which is a todo really)
6201     HWND rv=NULL;
6202     NSArray *ch=[NSApp windows];
6203     NSInteger x=0,n=[ch count];
6204     if (lastw)
6205     {
6206       for(;x<n; x ++)
6207       {
6208         NSWindow *w = [ch objectAtIndex:x]; 
6209         if ((HWND)w == lastw || (HWND)[w contentView] == lastw) break;
6210       }
6211       x++;
6212     }
6214     NSString *srch=(NSString*)SWELL_CStringToCFString(title);
6215     for(;x<n && !rv; x ++)
6216     {
6217       NSWindow *w = [ch objectAtIndex:x]; 
6218       if ([[w title] isEqualToString:srch]) 
6219       {
6220         rv=(HWND)[w contentView];
6221         if (classname)
6222         {
6223           char tmp[1024];
6224           if (!GetClassName(rv,tmp,sizeof(tmp)) || strcmp(tmp,classname))
6225             rv = NULL;
6226         }
6227       }
6228     }
6229     [srch release]; 
6231     return rv;
6232   }
6233   HWND h=lastw?GetWindow(lastw,GW_HWNDNEXT):GetWindow(par,GW_CHILD);
6234   while (h)
6235   {
6236     bool isOk=true;
6237     if (title)
6238     {
6239       char buf[512];
6240       buf[0]=0;
6241       GetWindowText(h,buf,sizeof(buf));
6242       if (strcmp(title,buf)) isOk=false;
6243     }
6244     if (isOk && classname)
6245     {
6246       char tmp[1024];
6247       if (!GetClassName(h,tmp,sizeof(tmp)) || strcmp(tmp,classname)) 
6248       {
6249         if (!stricmp(classname,"Static") && [(id)h isKindOfClass:[NSTextField class]])
6250         {
6251           // allow finding "Static" to match a textfield
6252         }
6253         else
6254           isOk = false;
6255       }
6256     }
6257     
6258     if (isOk) return h;
6259     h=GetWindow(h,GW_HWNDNEXT);
6260   }
6261   return h;
6264 BOOL TreeView_SetIndent(HWND hwnd, int indent)
6266   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_TreeView class]])) return 0;
6267   SWELL_TreeView* tv = (SWELL_TreeView*)hwnd;  
6268   [tv setIndentationPerLevel:(float)indent];  
6269   return TRUE;
6272 HTREEITEM TreeView_InsertItem(HWND hwnd, TV_INSERTSTRUCT *ins)
6274   if (WDL_NOT_NORMALLY(!hwnd || !ins || ![(id)hwnd isKindOfClass:[SWELL_TreeView class]])) return 0;
6275   
6276   SWELL_TreeView *tv=(SWELL_TreeView*)hwnd;
6278   HTREEITEM__ *par=NULL;
6279   int inspos=0;
6280   
6281   if (ins->hParent && ins->hParent != TVI_ROOT && ins->hParent != TVI_FIRST && ins->hParent != TVI_LAST && ins->hParent != TVI_SORT)
6282   {
6283     if ([tv findItem:ins->hParent parOut:&par idxOut:&inspos])
6284     {
6285       par = (HTREEITEM__ *)ins->hParent;
6286     }
6287     else return 0;
6288   }
6290   if (ins->hInsertAfter == TVI_FIRST) inspos=0;
6291   else if (ins->hInsertAfter == TVI_LAST || ins->hInsertAfter == TVI_SORT || !ins->hInsertAfter) inspos=par ? par->m_children.GetSize() : tv->m_items ? tv->m_items->GetSize() : 0;
6292   else inspos = par ? par->m_children.Find((HTREEITEM__*)ins->hInsertAfter)+1 : tv->m_items ? tv->m_items->Find((HTREEITEM__*)ins->hInsertAfter)+1 : 0;      
6293   
6294   HTREEITEM__ *item=new HTREEITEM__;
6295   if (ins->item.mask & TVIF_CHILDREN)
6296     item->m_haschildren = !!ins->item.cChildren;
6297   if (ins->item.mask & TVIF_PARAM) item->m_param = ins->item.lParam;
6298   if (ins->item.mask & TVIF_TEXT) item->m_value = strdup(ins->item.pszText);
6299   if (!par)
6300   {
6301     if (!tv->m_items) tv->m_items = new WDL_PtrList<HTREEITEM__>;
6302     tv->m_items->Insert(inspos,item);
6303   }
6304   else par->m_children.Insert(inspos,item);
6306   // [tv reloadData] invalidates the contents and breaks stuff
6307   // if we are in the middle of handling a treeview notification
6308   if (SWELL_GetOSXVersion() >= 0x1070)
6309   {
6310     SWELL_DataHold *dh = par ? par->m_dh : NULL;
6311     NSIndexSet *idxset=[NSIndexSet indexSetWithIndex:inspos];
6312     [tv beginUpdates];
6313     [tv insertItemsAtIndexes:idxset inParent:dh withAnimation:0];
6314     [tv endUpdates];
6315   }
6316   else
6317   {
6318     [tv reloadData];
6319   }
6321   return (HTREEITEM) item;
6324 BOOL TreeView_Expand(HWND hwnd, HTREEITEM item, UINT flag)
6326   if (WDL_NOT_NORMALLY(!hwnd || !item)) return false;
6327   
6328   if (WDL_NOT_NORMALLY(![(id)hwnd isKindOfClass:[SWELL_TreeView class]])) return false;
6329   
6330   SWELL_TreeView *tv=(SWELL_TreeView*)hwnd;
6331   
6332   id itemid=((HTREEITEM__*)item)->m_dh;
6333   bool isExp=!![tv isItemExpanded:itemid];
6334   
6335   if (flag == TVE_EXPAND && !isExp) [tv expandItem:itemid];
6336   else if (flag == TVE_COLLAPSE && isExp) [tv collapseItem:itemid];
6337   else if (flag==TVE_TOGGLE) 
6338   {
6339     if (isExp) [tv collapseItem:itemid];
6340     else [tv expandItem:itemid];
6341   }
6342   else return FALSE;
6344   return TRUE;
6345   
6348 HTREEITEM TreeView_GetSelection(HWND hwnd)
6350   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_TreeView class]])) return NULL;
6351   
6352   SWELL_TreeView *tv=(SWELL_TreeView*)hwnd;
6353   NSInteger idx=[tv selectedRow];
6354   if (idx<0) return NULL;
6355   
6356   SWELL_DataHold *t=[tv itemAtRow:idx];
6357   if (t) return (HTREEITEM)[t getValue];
6358   return NULL;
6359   
6362 void TreeView_DeleteItem(HWND hwnd, HTREEITEM item)
6364   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_TreeView class]])) return;
6365   SWELL_TreeView *tv=(SWELL_TreeView*)hwnd;
6366   
6367   HTREEITEM__ *par=NULL;
6368   int idx=0;
6369   
6370   if ([tv findItem:item parOut:&par idxOut:&idx])
6371   {
6372     id retain_obj = [item->m_dh retain]; // workaround for macOS 10.10.5 bug using a cached pointer to this item in -beginUpdates
6374     if (par)
6375     {
6376       HTREEITEM sel = TreeView_GetSelection(hwnd);
6377       bool is_sel = sel && (sel == item || item->FindItem(sel,NULL,NULL));
6378       par->m_children.Delete(idx,true);
6379       if (is_sel) TreeView_SelectItem(hwnd, par);
6380     }
6381     else if (tv->m_items)
6382     {
6383       tv->m_items->Delete(idx,true);
6384     }
6386     if (SWELL_GetOSXVersion() >= 0x1070 && [tv respondsToSelector:@selector(beginUpdates)])
6387     {
6388       SWELL_DataHold *dh = par ? par->m_dh : NULL;
6389       NSIndexSet *idxset=[NSIndexSet indexSetWithIndex:idx];
6390       [tv beginUpdates];
6391       [tv removeItemsAtIndexes:idxset inParent:dh withAnimation:0];
6392       [tv endUpdates];
6393     }
6394     else
6395     {
6396       [tv reloadData];
6397     }
6398     [retain_obj release];
6399   }
6402 void TreeView_DeleteAllItems(HWND hwnd)
6404   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_TreeView class]])) return;
6405   SWELL_TreeView *tv=(SWELL_TreeView*)hwnd;
6406   
6407   if (tv->m_items) tv->m_items->Empty(true);
6408   [tv reloadData];
6411 void TreeView_EnsureVisible(HWND hwnd, HTREEITEM item)
6413   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_TreeView class]])) return;
6414   if (!item) return;
6415   NSInteger row=[(SWELL_TreeView*)hwnd rowForItem:((HTREEITEM__*)item)->m_dh];
6416   if (row>=0)
6417     [(SWELL_TreeView*)hwnd scrollRowToVisible:row];
6420 void TreeView_SelectItem(HWND hwnd, HTREEITEM item)
6422   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_TreeView class]])) return;
6423   
6424   NSInteger row=[(SWELL_TreeView*)hwnd rowForItem:((HTREEITEM__*)item)->m_dh];
6425   if (row>=0)
6426     [(SWELL_TreeView*)hwnd selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];            
6427   static int __rent;
6428   if (!__rent)
6429   {
6430     __rent=1;
6431     NMTREEVIEW nm={{(HWND)hwnd,(UINT_PTR)[(SWELL_TreeView*)hwnd tag],TVN_SELCHANGED},};
6432     nm.itemNew.hItem = item;
6433     nm.itemNew.lParam = item ? item->m_param : 0;
6434     SendMessage(GetParent(hwnd),WM_NOTIFY,nm.hdr.idFrom,(LPARAM)&nm);
6435     __rent=0;
6436   }
6439 BOOL TreeView_GetItem(HWND hwnd, LPTVITEM pitem)
6441   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_TreeView class]] || !pitem) || 
6442       !(pitem->mask & TVIF_HANDLE) || !(pitem->hItem)) return FALSE;
6443   
6444   HTREEITEM__ *ti = (HTREEITEM__*)pitem->hItem;
6445   pitem->cChildren = ti->m_haschildren ? 1:0;
6446   pitem->lParam = ti->m_param;
6447   if ((pitem->mask&TVIF_TEXT)&&pitem->pszText&&pitem->cchTextMax>0)
6448   {
6449     lstrcpyn_safe(pitem->pszText,ti->m_value?ti->m_value:"",pitem->cchTextMax);
6450   }
6451   pitem->state=0;
6452   
6453   
6454   NSInteger itemRow = [(SWELL_TreeView*)hwnd rowForItem:ti->m_dh];
6455   if (itemRow >= 0 && [(SWELL_TreeView*)hwnd isRowSelected:itemRow])
6456     pitem->state |= TVIS_SELECTED;   
6457   if ([(SWELL_TreeView*)hwnd isItemExpanded:ti->m_dh])
6458     pitem->state |= TVIS_EXPANDED;   
6459   
6460   return TRUE;
6463 BOOL TreeView_SetItem(HWND hwnd, LPTVITEM pitem)
6465   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_TreeView class]] || !pitem) || 
6466     !(pitem->mask & TVIF_HANDLE) || !(pitem->hItem)) return FALSE;
6467   
6468   HTREEITEM__ *par=NULL;
6469   int idx=0;
6470   
6471   if (![(SWELL_TreeView*)hwnd findItem:pitem->hItem parOut:&par idxOut:&idx]) return FALSE;
6472   
6473   HTREEITEM__ *ti = (HTREEITEM__*)pitem->hItem;
6475   bool need_reload=false;
6476   if (pitem->mask & TVIF_CHILDREN)
6477   {
6478     if (!ti->m_haschildren != !pitem->cChildren) need_reload=true;
6479     ti->m_haschildren = pitem->cChildren?1:0;
6480   }
6481   if (pitem->mask & TVIF_PARAM)  ti->m_param =  pitem->lParam;
6483   if ((pitem->mask&TVIF_TEXT)&&pitem->pszText)
6484   {
6485     free(ti->m_value);
6486     ti->m_value=strdup(pitem->pszText);
6487     InvalidateRect(hwnd, 0, FALSE);
6488   }
6490   if (pitem->stateMask & TVIS_SELECTED)
6491   {
6492     NSInteger itemRow = [(SWELL_TreeView*)hwnd rowForItem:ti->m_dh];
6493     if (itemRow >= 0)
6494     {
6495       if (pitem->state&TVIS_SELECTED)
6496       {
6497         [(SWELL_TreeView*)hwnd selectRowIndexes:[NSIndexSet indexSetWithIndex:itemRow] byExtendingSelection:NO];
6498         
6499         static int __rent;
6500         if (!__rent)
6501         {
6502           __rent=1;
6503           NMTREEVIEW nm={{(HWND)hwnd,(UINT_PTR)[(SWELL_TreeView*)hwnd tag],TVN_SELCHANGED},};
6504           nm.itemNew.hItem = ti;
6505           nm.itemNew.lParam = ti ? ti->m_param : 0;
6506           SendMessage(GetParent(hwnd),WM_NOTIFY,nm.hdr.idFrom,(LPARAM)&nm);
6507           __rent=0;
6508         }
6509         
6510       }
6511       else
6512       {
6513         // todo figure out unselect?!
6514 //         [(SWELL_TreeView*)hwnd selectRowIndexes:[NSIndexSet indexSetWithIndex:itemRow] byExtendingSelection:NO];
6515       }
6516     }
6517   }
6518   
6519   if (pitem->stateMask & TVIS_EXPANDED)
6520     TreeView_Expand(hwnd,pitem->hItem,(pitem->state&TVIS_EXPANDED)?TVE_EXPAND:TVE_COLLAPSE);
6522   if (need_reload)
6523   {
6524     [(SWELL_TreeView*)hwnd reloadItem:ti->m_dh];
6525   }
6526   
6527   return TRUE;
6530 HTREEITEM TreeView_HitTest(HWND hwnd, TVHITTESTINFO *hti)
6532   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_TreeView class]] || !hti)) return NULL;
6533   SWELL_TreeView* tv = (SWELL_TreeView*)hwnd;
6534   int x = hti->pt.x;
6535   int y = hti->pt.y;
6537   // treeview might be clipped
6538   POINT wp={x, y};
6539   ClientToScreen(hwnd, &wp);
6540   RECT wr;
6541   GetWindowRect(hwnd, &wr);
6542   if (wp.x < wr.left || wp.x >= wr.right) return NULL;
6543   if (wp.y < wdl_min(wr.top, wr.bottom) || wp.y >= wdl_max(wr.top, wr.bottom)) return NULL;
6545   int i; 
6546   double maxy = 0.0;
6547   for (i = 0; i < [tv numberOfRows]; ++i)
6548   {
6549     NSRect r = [tv rectOfRow:i];
6550     maxy = wdl_max(maxy, r.origin.y + r.size.height);
6551     if (x >= r.origin.x && x < r.origin.x+r.size.width && y >= r.origin.y && y < r.origin.y+r.size.height)
6552     {
6553       SWELL_DataHold* t = [tv itemAtRow:i];
6554       if (t) return (HTREEITEM)[t getValue];
6555       return 0;
6556     }
6557   }
6558   if (y >= maxy)
6559   {
6560     hti->flags |= TVHT_NOWHERE;
6561   }
6562   
6563   return NULL; // not hit
6566 HTREEITEM TreeView_GetRoot(HWND hwnd)
6568   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_TreeView class]])) return NULL;
6569   SWELL_TreeView *tv=(SWELL_TreeView*)hwnd;
6570   
6571   if (!tv->m_items) return 0;
6572   return (HTREEITEM) tv->m_items->Get(0);
6575 HTREEITEM TreeView_GetParent(HWND hwnd, HTREEITEM item)
6577   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_TreeView class]])) return NULL;
6579   if (!item) return TreeView_GetRoot(hwnd);
6581   SWELL_TreeView *tv=(SWELL_TreeView*)hwnd;
6582   HTREEITEM__ *par=NULL;
6583   int idx=0;
6584   [tv findItem:item parOut:&par idxOut:&idx];
6585   return par;
6587 HTREEITEM TreeView_GetChild(HWND hwnd, HTREEITEM item)
6589   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_TreeView class]])) return NULL;
6591   HTREEITEM__ *titem=(HTREEITEM__ *)item;
6592   if (!titem || item == TVI_ROOT) return TreeView_GetRoot(hwnd);
6593   
6594   return (HTREEITEM) titem->m_children.Get(0);
6596 HTREEITEM TreeView_GetNextSibling(HWND hwnd, HTREEITEM item)
6598   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_TreeView class]])) return NULL;
6599   SWELL_TreeView *tv=(SWELL_TreeView*)hwnd;
6601   if (!item) return TreeView_GetRoot(hwnd);
6602   
6603   HTREEITEM__ *par=NULL;
6604   int idx=0;  
6605   if ([tv findItem:item parOut:&par idxOut:&idx])
6606   {
6607     if (par)
6608     {
6609       return par->m_children.Get(idx+1);
6610     }    
6611     if (tv->m_items) return tv->m_items->Get(idx+1);
6612   }
6613   return 0;
6616 void TreeView_SetBkColor(HWND hwnd, int color)
6618   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_TreeView class]])) return;
6619   [(NSOutlineView*)hwnd setBackgroundColor:[NSColor colorWithCalibratedRed:GetRValue(color)/255.0f 
6620               green:GetGValue(color)/255.0f 
6621               blue:GetBValue(color)/255.0f alpha:1.0f]];
6623 void TreeView_SetTextColor(HWND hwnd, int color)
6625   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_TreeView class]])) return;
6627   SWELL_TreeView *f = (SWELL_TreeView *)hwnd;
6628   [f->m_fgColor release];
6629   f->m_fgColor = [NSColor colorWithCalibratedRed:GetRValue(color)/255.0f 
6630               green:GetGValue(color)/255.0f 
6631               blue:GetBValue(color)/255.0f alpha:1.0f];
6632   [f->m_fgColor retain];
6634 void ListView_SetBkColor(HWND hwnd, int color)
6636   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_ListView class]])) return;
6637   [(NSTableView*)hwnd setBackgroundColor:[NSColor colorWithCalibratedRed:GetRValue(color)/255.0f 
6638               green:GetGValue(color)/255.0f 
6639               blue:GetBValue(color)/255.0f alpha:1.0f]];
6642 void ListView_SetSelColors(HWND hwnd, int *colors, int ncolors) // this works for SWELL_ListView as well as SWELL_TreeView
6644   if (WDL_NOT_NORMALLY(!hwnd)) return;
6645   NSMutableArray *ar=[[NSMutableArray alloc] initWithCapacity:ncolors];
6646   
6647   while (ncolors-->0)
6648   {
6649     const int color = colors ? *colors++ : 0;
6650     [ar addObject:[NSColor colorWithCalibratedRed:GetRValue(color)/255.0f
6651                                               green:GetGValue(color)/255.0f 
6652                                                blue:GetBValue(color)/255.0f alpha:1.0f]]; 
6653   }
6655   if ([(id)hwnd isKindOfClass:[SWELL_ListView class]]) 
6656   {
6657     SWELL_ListView *lv = (SWELL_ListView*)hwnd;
6658     [lv->m_selColors release];
6659     lv->m_selColors=ar;
6660   }
6661   else if ([(id)hwnd isKindOfClass:[SWELL_TreeView class]]) 
6662   {
6663     SWELL_TreeView *lv = (SWELL_TreeView*)hwnd;
6664     [lv->m_selColors release];
6665     lv->m_selColors=ar;
6666   }
6667   else 
6668   {
6669     WDL_ASSERT(false);
6670     [ar release];
6671   }
6673 void ListView_SetGridColor(HWND hwnd, int color)
6675   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_ListView class]])) return;
6676   [(NSTableView*)hwnd setGridColor:[NSColor colorWithCalibratedRed:GetRValue(color)/255.0f 
6677               green:GetGValue(color)/255.0f 
6678               blue:GetBValue(color)/255.0f alpha:1.0f]];
6680 void ListView_SetTextBkColor(HWND hwnd, int color)
6682   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_ListView class]])) return;
6683   // not implemented atm
6685 void ListView_SetTextColor(HWND hwnd, int color)
6687   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[SWELL_ListView class]])) return;
6689   SWELL_ListView *f = (SWELL_ListView *)hwnd;
6690   [f->m_fgColor release];
6691   f->m_fgColor = [NSColor colorWithCalibratedRed:GetRValue(color)/255.0f 
6692               green:GetGValue(color)/255.0f 
6693               blue:GetBValue(color)/255.0f alpha:1.0f];
6694   [f->m_fgColor retain];
6698 BOOL ShellExecute(HWND hwndDlg, const char *action,  const char *content1, const char *content2, const char *content3, int blah)
6700   if (content1 && (!strnicmp(content1,"http://",7) || !strnicmp(content1,"https://",8)))
6701   {
6702      NSWorkspace *wk = [NSWorkspace sharedWorkspace];
6703      if (!wk) return FALSE;
6704      NSString *fnstr=(NSString *)SWELL_CStringToCFString(content1);
6705      NSURL *url = [NSURL URLWithString:fnstr];
6706      BOOL ret=url && [wk openURL:url];
6707      [fnstr release];
6708      return ret;
6709   }
6710   
6711   if (content1 && !stricmp(content1,"explorer.exe")) content1="";
6712   else if (content1 && (!stricmp(content1,"notepad.exe")||!stricmp(content1,"notepad"))) content1="TextEdit.app";
6713   
6714   if (content2 && !stricmp(content2,"explorer.exe")) content2="";
6716   if (content1 && content2 && *content1 && *content2)
6717   {
6718       NSWorkspace *wk = [NSWorkspace sharedWorkspace];
6719       if (!wk) return FALSE;
6720       NSString *appstr=(NSString *)SWELL_CStringToCFString(content1);
6721       NSString *fnstr=(NSString *)SWELL_CStringToCFString(content2);
6722       BOOL ret=[wk openFile:fnstr withApplication:appstr andDeactivate:YES];
6723       [fnstr release];
6724       [appstr release];
6725       return ret;
6726   }
6727   else if ((content1&&*content1) || (content2&&*content2))
6728   {
6729       const char *fn = (content1 && *content1) ? content1 : content2;
6730       NSWorkspace *wk = [NSWorkspace sharedWorkspace];
6731       if (!wk) return FALSE;
6732       NSString *fnstr = nil;
6733       BOOL ret = FALSE;
6734     
6735       if (fn && !strnicmp(fn, "/select,\"", 9))
6736       {
6737         char* tmp = strdup(fn+9);
6738         if (*tmp && tmp[strlen(tmp)-1]=='\"') tmp[strlen(tmp)-1]='\0';
6739         if (*tmp)
6740         {
6741           if ([wk respondsToSelector:@selector(activateFileViewerSelectingURLs:)]) // 10.6+
6742           {
6743             fnstr=(NSString *)SWELL_CStringToCFString(tmp);
6744             NSURL *url = [NSURL fileURLWithPath:fnstr isDirectory:false];
6745             if (url)
6746             {
6747               [wk activateFileViewerSelectingURLs:[NSArray arrayWithObjects:url, nil]]; // NSArray (and NSURL) autoreleased
6748               ret=TRUE;
6749             }
6750           }
6751           else
6752           {
6753             if (WDL_remove_filepart(tmp))
6754             {
6755               fnstr=(NSString *)SWELL_CStringToCFString(tmp);
6756               ret=[wk openFile:fnstr];
6757             }
6758           }
6759         }
6760         free(tmp);
6761       }
6762       else if (strlen(fn)>4 && !stricmp(fn+strlen(fn)-4,".app"))
6763       {
6764         fnstr=(NSString *)SWELL_CStringToCFString(fn);
6765         ret=[wk launchApplication:fnstr];
6766       }
6767       else
6768       {
6769         fnstr=(NSString *)SWELL_CStringToCFString(fn);
6770         ret=[wk openFile:fnstr];
6771       }
6772       [fnstr release];
6773       return ret;
6774   }
6775   return FALSE;
6781 @implementation SWELL_FocusRectWnd
6783 -(BOOL)isOpaque { return YES; }
6784 -(void) drawRect:(NSRect)rect
6786   NSColor *col=[NSColor colorWithCalibratedRed:0.5 green:0.5 blue:0.5 alpha:1.0];
6787   [col set];
6788   
6789   CGRect r = CGRectMake(rect.origin.x,rect.origin.y,rect.size.width,rect.size.height);
6790   
6791   CGContextRef ctx = (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort];
6792   
6793   CGContextFillRect(ctx,r);              
6794   
6796 @end
6798 // r=NULL to "free" handle
6799 // otherwise r is in hwndPar coordinates
6800 void SWELL_DrawFocusRect(HWND hwndPar, RECT *rct, void **handle)
6802   if (!handle) return;
6803   NSWindow *wnd = (NSWindow *)*handle;
6804   
6805   if (!rct)
6806   {
6807     if (wnd)
6808     {
6809       NSWindow *ow=[wnd parentWindow];
6810       if (ow) [ow removeChildWindow:wnd];
6811 //      [wnd setParentWindow:nil];
6812       [wnd close];
6813       *handle=0;
6814     }
6815   }
6816   else 
6817   {
6818     RECT r=*rct;
6819     if (hwndPar)
6820     {
6821       ClientToScreen(hwndPar,((LPPOINT)&r));
6822       ClientToScreen(hwndPar,((LPPOINT)&r)+1);
6823     }
6824     else
6825     {
6826       // todo: flip?
6827     }
6828     if (r.top>r.bottom) { int a=r.top; r.top=r.bottom;r.bottom=a; }
6829     NSRect rr=NSMakeRect(r.left,r.top,r.right-r.left,r.bottom-r.top);
6830     
6831     NSWindow *par=nil;
6832     if (hwndPar)
6833     {
6834       if ([(id)hwndPar isKindOfClass:[NSWindow class]]) par=(NSWindow *)hwndPar;
6835       else if ([(id)hwndPar isKindOfClass:[NSView class]]) par=[(NSView *)hwndPar window];
6836       else return;
6837     }
6838     
6839     if (wnd && ([wnd parentWindow] != par))
6840     {
6841       NSWindow *ow=[wnd parentWindow];
6842       if (ow) [ow removeChildWindow:wnd];
6843       //      [wnd setParentWindow:nil];
6844       [wnd close];
6845       *handle=0;
6846       wnd=0;    
6847     }
6848     
6849     if (!wnd)
6850     {
6851       *handle  = wnd = [[NSWindow alloc] initWithContentRect:rr styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES];
6852       [wnd setOpaque:YES];
6853       [wnd setAlphaValue:0.5];
6854       [wnd setExcludedFromWindowsMenu:YES];
6855       [wnd setIgnoresMouseEvents:YES];
6856       [wnd setContentView:[[SWELL_FocusRectWnd alloc] init]];
6857       
6858       if (par) [par addChildWindow:wnd ordered:NSWindowAbove];
6859       else 
6860       {
6861         [wnd setLevel:NSPopUpMenuWindowLevel];
6862         [wnd orderFront:wnd];
6863       }
6864       //    [wnd setParentWindow:par];
6865 //      [wnd orderWindow:NSWindowAbove relativeTo:[par windowNumber]];
6866     }
6867     
6868     [wnd setFrame:rr display:YES];    
6869   }
6873 @implementation SWELL_PopUpButton
6874 STANDARD_CONTROL_NEEDSDISPLAY_IMPL("combobox")
6877 -(id) init {
6878   self = [super init];
6879   if (self != nil) {
6880     m_userdata=0;
6881     m_style=0;
6882   }
6883   return self;
6886 -(void)setSwellStyle:(LONG)style { m_style=style; }
6887 -(LONG)getSwellStyle { return m_style; }
6888 -(LONG_PTR)getSwellUserData { return m_userdata; }
6889 -(void)setSwellUserData:(LONG_PTR)val {   m_userdata=val; }
6890 @end
6892 @implementation SWELL_ComboBox
6893 STANDARD_CONTROL_NEEDSDISPLAY_IMPL("combobox")
6895 -(void)setSwellStyle:(LONG)style { m_style=style; }
6896 -(LONG)getSwellStyle { return m_style; }
6897 -(id)init {
6898   self = [super init];
6899   if (self)
6900   {
6901     m_ids=new WDL_PtrList<char>;
6902     m_ignore_selchg = -1;
6903     m_disable_menu = false;
6904     m_userdata=0;
6905     m_style=0;
6906   }
6907   return self;
6909 -(void)dealloc { delete m_ids; [super dealloc];  }
6910 - (BOOL)becomeFirstResponder;
6912   BOOL didBecomeFirstResponder = [super becomeFirstResponder];
6913   if (didBecomeFirstResponder) SendMessage(GetParent((HWND)self),WM_COMMAND,MAKELONG([self tag],EN_SETFOCUS),(LPARAM)self);
6914   return didBecomeFirstResponder;
6917 - (NSMenu *)textView:(NSTextView *)view
6918                 menu:(NSMenu *)menu
6919             forEvent:(NSEvent *)event
6920              atIndex:(NSUInteger)charIndex
6922   return m_disable_menu ? nil : menu;
6925 - (void)swellDisableContextMenu:(bool)dis
6927   m_disable_menu=dis;
6929 -(LONG_PTR)getSwellUserData { return m_userdata; }
6930 -(void)setSwellUserData:(LONG_PTR)val {   m_userdata=val; }
6932 @end
6937 bool SWELL_HandleMouseEvent(NSEvent *evt)
6939   NSEventType etype = [evt type];
6940   if (GetCapture()) return false;
6941   if (etype >= NSLeftMouseDown && etype <= NSRightMouseDragged)
6942   {
6943   }
6944   else return false;
6945   
6946   NSWindow *w = [evt window];
6947   if (w)
6948   {
6949     NSView *cview = [w contentView];
6950     NSView *besthit=NULL;
6951     if (cview)
6952     {
6953       NSPoint lpt = [evt locationInWindow];    
6954       NSView *hitv=[cview hitTest:lpt];
6955       lpt = [w convertBaseToScreen:lpt];
6956       
6957       int xpos=(int)floor(lpt.x + 0.5);
6958       int ypos=(int)floor(lpt.y + 0.5);
6959       
6960       while (hitv)
6961       {
6962         int ht=(int)sendSwellMessage(hitv,WM_NCHITTEST,0,MAKELPARAM(xpos,ypos));
6963         if (ht && ht != HTCLIENT) besthit=hitv;
6965         if (hitv==cview) break;
6966         hitv = [hitv superview];
6967       }
6968     }
6969     if (besthit)
6970     {
6971       if (etype == NSLeftMouseDown) [besthit mouseDown:evt];
6972       else if (etype == NSLeftMouseUp) [besthit mouseUp:evt];
6973       else if (etype == NSLeftMouseDragged) [besthit mouseDragged:evt];
6974       else if (etype == NSRightMouseDown) [besthit rightMouseDown:evt];
6975       else if (etype == NSRightMouseUp) [besthit rightMouseUp:evt];
6976       else if (etype == NSRightMouseDragged) [besthit rightMouseDragged:evt];
6977       else if (etype == NSMouseMoved) [besthit mouseMoved:evt];
6978       else return false;
6979       
6980       return true;
6981     }
6982   }
6983   return false;
6986 int SWELL_GetWindowWantRaiseAmt(HWND h)
6988   SWELL_ModelessWindow* mw=0;
6989   if ([(id)h isKindOfClass:[SWELL_ModelessWindow class]])
6990   {
6991     mw=(SWELL_ModelessWindow*)h;
6992   }
6993   else if ([(id)h isKindOfClass:[NSView class]])
6994   {
6995     NSWindow* wnd=[(NSView*)h window];
6996     if (wnd && [wnd isKindOfClass:[SWELL_ModelessWindow class]])
6997     {
6998       mw=(SWELL_ModelessWindow*)wnd;
6999     }
7000   }
7001   else
7002   {
7003     WDL_ASSERT(false);
7004   }
7005   if (mw) return mw->m_wantraiseamt;  
7006   return 0; 
7009 void SWELL_SetWindowWantRaiseAmt(HWND h, int  amt)
7011   SWELL_ModelessWindow *mw=NULL;
7012   if ([(id)h isKindOfClass:[SWELL_ModelessWindow class]]) mw=(SWELL_ModelessWindow *)h;
7013   else if ([(id)h isKindOfClass:[NSView class]])
7014   {
7015     NSWindow *w = [(NSView *)h window];
7016     if (w && [w isKindOfClass:[SWELL_ModelessWindow class]]) mw = (SWELL_ModelessWindow*)w;
7017   }
7018   if (mw) 
7019   {
7020     int diff = amt - mw->m_wantraiseamt;
7021     mw->m_wantraiseamt = amt;
7022     if (diff && [NSApp isActive]) [mw setLevel:[mw level]+diff];
7023   }
7024   else
7025   {
7026     WDL_ASSERT(false);
7027   }
7031 int SWELL_SetWindowLevel(HWND hwnd, int newlevel)
7033   NSWindow *w = (NSWindow *)hwnd;
7034   if (w && [w isKindOfClass:[NSView class]]) w= [(NSView *)w window];
7035   
7036   if (WDL_NORMALLY(w && [w isKindOfClass:[NSWindow class]]))
7037   {
7038     int ol = (int)[w level];
7039     [w setLevel:newlevel];
7040     return ol;
7041   }
7042   return 0;
7045 void SetAllowNoMiddleManRendering(HWND h, bool allow)
7047   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_hwndChild class]])) return;
7048   SWELL_hwndChild* v = (SWELL_hwndChild*)h;
7049   v->m_allow_nomiddleman = allow;
7052 void SetOpaque(HWND h, bool opaque)
7054   if (WDL_NOT_NORMALLY(!h || ![(id)h isKindOfClass:[SWELL_hwndChild class]])) return;
7055   SWELL_hwndChild* v = (SWELL_hwndChild*)h;
7056   [v setOpaque:opaque];
7059 void SetTransparent(HWND h)
7061   if (WDL_NOT_NORMALLY(!h)) return;
7062   NSWindow* wnd=0;
7063   if ([(id)h isKindOfClass:[NSWindow class]]) wnd=(NSWindow*)h;
7064   else if ([(id)h isKindOfClass:[NSView class]]) wnd=[(NSView*)h window];
7065   if (WDL_NORMALLY(wnd)) 
7066   {
7067     [wnd setBackgroundColor:[NSColor clearColor]];
7068     [wnd setOpaque:NO];
7069   }  
7072 int SWELL_GetDefaultButtonID(HWND hwndDlg, bool onlyIfEnabled)
7074   if (WDL_NOT_NORMALLY(![(id)hwndDlg isKindOfClass:[NSView class]])) return 0;
7075   NSWindow *wnd = [(NSView *)hwndDlg window];
7076   NSButtonCell * cell = wnd ? [wnd defaultButtonCell] : nil;
7077   NSView *view;
7078   if (!cell || !(view=[cell controlView])) return 0;
7079   int cmdid = (int)[view tag];
7080   if (cmdid && onlyIfEnabled)
7081   {
7082     if (![cell isEnabled]) return 0;
7083   }
7084   return cmdid;
7088 void SWELL_SetWindowRepre(HWND hwnd, const char *fn, bool isDirty)
7090   if (WDL_NOT_NORMALLY(!hwnd)) return;
7091   NSWindow *w = NULL;
7092   if ([(id)hwnd isKindOfClass:[NSWindow class]]) w=(NSWindow *)hwnd;
7093   if ([(id)hwnd isKindOfClass:[NSView class]]) w=[(NSView *)hwnd window];
7094   
7095   if (WDL_NORMALLY(w))
7096   {
7097     if (GetProp((HWND)[w contentView],"SWELL_DisableWindowRepre")) return;
7098     
7099     [w setDocumentEdited:isDirty];
7100     
7101     if (!fn || !*fn) [w setRepresentedFilename:@""];
7102     else
7103     {
7104       NSString *str = (NSString *)SWELL_CStringToCFString(fn);
7105       [w setRepresentedFilename:str];
7106       [str release];
7107     }
7108   }
7111 void SWELL_SetWindowShadow(HWND hwnd, bool shadow)
7113   if (WDL_NOT_NORMALLY(!hwnd)) return;
7114   NSWindow *w = (NSWindow *)hwnd;
7115   if ([w isKindOfClass:[NSView class]]) w = [(NSView *)w window];
7116   if (WDL_NORMALLY(w && [w isKindOfClass:[NSWindow class]])) [w setHasShadow:shadow];
7119 #if 0 // not sure if this will interfere with coolSB
7120 BOOL ShowScrollBar(HWND hwnd, int nBar, BOOL vis)
7122   int v=0;
7123   if (nBar == SB_HORZ || nBar == SB_BOTH) v |= WS_HSCROLL;
7124   if (nBar == SB_VERT || nBar == SB_BOTH) v |= WS_VSCROLL;
7125   if (v)
7126   {
7127     int s=GetWindowLong(hwnd, GWL_STYLE);
7128     if (vis) s |= v;
7129     else s &= ~v;
7130     SetWindowLong(hwnd, GWL_STYLE, s);
7131     SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED|SWP_NOZORDER|SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
7132     return TRUE;
7133   }
7134   return FALSE;
7136 #endif
7139 void SWELL_GenerateDialogFromList(const void *_list, int listsz)
7141 #define SIXFROMLIST list->p1,list->p2,list->p3, list->p4, list->p5, list->p6
7142   SWELL_DlgResourceEntry *list = (SWELL_DlgResourceEntry*)_list;
7143   while (listsz>0)
7144   {
7145     if (!strcmp(list->str1,"__SWELL_BUTTON"))
7146     {
7147       SWELL_MakeButton(list->flag1,list->str2, SIXFROMLIST);
7148     } 
7149     else if (!strcmp(list->str1,"__SWELL_EDIT"))
7150     {
7151       SWELL_MakeEditField(SIXFROMLIST);
7152     }
7153     else if (!strcmp(list->str1,"__SWELL_COMBO"))
7154     {
7155       SWELL_MakeCombo(SIXFROMLIST);
7156     }
7157     else if (!strcmp(list->str1,"__SWELL_LISTBOX"))
7158     {
7159       SWELL_MakeListBox(SIXFROMLIST);
7160     }
7161     else if (!strcmp(list->str1,"__SWELL_GROUP"))
7162     {
7163       SWELL_MakeGroupBox(list->str2,SIXFROMLIST);
7164     }
7165     else if (!strcmp(list->str1,"__SWELL_CHECKBOX"))
7166     {
7167       SWELL_MakeCheckBox(list->str2,SIXFROMLIST);
7168     }
7169     else if (!strcmp(list->str1,"__SWELL_LABEL"))
7170     {
7171       SWELL_MakeLabel(list->flag1, list->str2, SIXFROMLIST);
7172     }
7173     else if (!strcmp(list->str1,"__SWELL_ICON"))
7174     {
7175       // todo (str2 is likely a (const char *)(INT_PTR)resid
7176     }
7177     else if (*list->str2)
7178     {
7179       SWELL_MakeControl(list->str1, list->flag1, list->str2, SIXFROMLIST);
7180     }
7181     listsz--;
7182     list++;
7183   }
7186 BOOL EnumChildWindows(HWND hwnd, BOOL (*cwEnumFunc)(HWND,LPARAM),LPARAM lParam)
7188   if (WDL_NOT_NORMALLY(!hwnd || ![(id)hwnd isKindOfClass:[NSView class]])) return TRUE;
7189   NSArray *ar = [(NSView *)hwnd subviews];
7190   if (ar)
7191   {
7192     [ar retain];
7193     NSInteger x,n=[ar count];
7194     for (x=0;x<n;x++)
7195     {
7196       NSView *v = [ar objectAtIndex:x];
7197       if (v)
7198       {
7199         if ([v isKindOfClass:[NSScrollView class]])
7200         {
7201           NSView *sv=[(NSScrollView *)v documentView];
7202           if (sv) v=sv;
7203         }
7204         if ([v isKindOfClass:[NSClipView class]]) 
7205         {
7206           NSView *sv = [(NSClipView *)v documentView];
7207           if (sv) v=sv;
7208         }
7210         if (!cwEnumFunc((HWND)v,lParam) || !EnumChildWindows((HWND)v,cwEnumFunc,lParam)) 
7211         {
7212           [ar release];
7213           return FALSE;
7214         }
7215       }
7216     }
7217     [ar release];
7218   }
7219   return TRUE;
7222 void SWELL_GetDesiredControlSize(HWND hwnd, RECT *r)
7224   if (WDL_NORMALLY(hwnd && r && [(id)hwnd isKindOfClass:[NSControl class]]))
7225   {
7226     NSControl *c = (NSControl *)hwnd;
7227     NSRect fr = [c frame];
7228     [c sizeToFit];
7229     NSRect frnew=[c frame];
7230     [c setFrame:fr];
7231     r->left=r->top=0;
7232     r->right = (int)(frnew.size.width+0.5);
7233     r->bottom = (int)(frnew.size.height+0.5);
7234   }
7237 BOOL SWELL_IsGroupBox(HWND hwnd)
7239   if (WDL_NORMALLY(hwnd) && [(id)hwnd isKindOfClass:[SWELL_BoxView class]])
7240   {
7241     if (![(id)hwnd respondsToSelector:@selector(swellIsEtchBox)] || ![(SWELL_BoxView *)hwnd swellIsEtchBox])
7242       return TRUE;
7243   }
7244   return FALSE;
7246 BOOL SWELL_IsButton(HWND hwnd)
7248   if (WDL_NORMALLY(hwnd) && [(id)hwnd isKindOfClass:[SWELL_Button class]]) return TRUE;
7249   return FALSE;
7251 BOOL SWELL_IsStaticText(HWND hwnd)
7253   if (WDL_NORMALLY(hwnd) && [(id)hwnd isKindOfClass:[NSTextField class]])
7254   {
7255     NSTextField *obj = (NSTextField *)hwnd;
7256     if (![obj isEditable] && ![obj isSelectable])
7257       return TRUE;
7258   }
7259   return FALSE;
7262 void SWELL_SetClassName(HWND hwnd, const char *p)
7264   if (WDL_NORMALLY(hwnd && [(id)hwnd isKindOfClass:[SWELL_hwndChild class]]))
7265     ((SWELL_hwndChild *)hwnd)->m_classname=p;
7268 int GetClassName(HWND hwnd, char *buf, int bufsz)
7270   if (WDL_NOT_NORMALLY(!hwnd || !buf || bufsz<1)) return 0;
7271   buf[0]=0;
7272   if ([(id)hwnd respondsToSelector:@selector(getSwellClass)])
7273   {
7274     const char *cn = [(SWELL_hwndChild*)hwnd getSwellClass];
7275     if (cn) lstrcpyn_safe(buf,cn,bufsz);
7276   }
7277   else if ([(id)hwnd isKindOfClass:[NSButton class]])
7278   {
7279     lstrcpyn_safe(buf,"Button",bufsz);
7280   }
7281   else if ([(id)hwnd isKindOfClass:[NSTextField class]])
7282   {
7283     NSTextField *obj = (NSTextField *)hwnd;
7284     if (![obj isEditable] && ![obj isSelectable])
7285       lstrcpyn_safe(buf,"Static",bufsz);
7286     else
7287       lstrcpyn_safe(buf,"Edit",bufsz);
7288   }
7289   else
7290   {
7291     // default handling of other controls?
7292   }
7294   return (int)strlen(buf);
7299 bool SWELL_SetAppAutoHideMenuAndDock(int ah) 
7301   static char _init;
7302   static NSUInteger _defpres;
7303   if (!_init)
7304   {
7305     if (SWELL_GetOSXVersion()>=0x1060)
7306     {
7307       _init=1;
7308       _defpres = [(SWELL_AppExtensions*)[NSApplication sharedApplication] presentationOptions];
7309     }
7310     else
7311     {
7312       _init=-1;
7313     }
7314   }
7315   if (_init > 0)
7316   {
7317     const int NSApplicationPresentationAutoHideDock               = (1 <<  0),
7318               NSApplicationPresentationHideDock = (1<<1),
7319               NSApplicationPresentationAutoHideMenuBar            = (1 <<  2);
7321     if (ah>0) [(SWELL_AppExtensions*)[NSApplication sharedApplication] setPresentationOptions:((ah>=2?NSApplicationPresentationHideDock:NSApplicationPresentationAutoHideDock)|NSApplicationPresentationAutoHideMenuBar)];
7322     else [(SWELL_AppExtensions*)[NSApplication sharedApplication] setPresentationOptions:_defpres];
7323     return true;
7324   }
7325   return false;
7329 void SWELL_DisableContextMenu(HWND hwnd, bool dis)
7331   if (WDL_NORMALLY(hwnd && [(id)hwnd respondsToSelector:@selector(swellDisableContextMenu:)]))
7332     [(SWELL_TextField*)hwnd swellDisableContextMenu:dis];
7335 extern char g_swell_disable_retina;
7336 int SWELL_IsRetinaHWND(HWND hwnd)
7338   if (!hwnd || SWELL_GetOSXVersion() < 0x1070) return 0;
7340   int retina_disabled = g_swell_disable_retina;
7341   NSWindow *w=NULL;
7342   if ([(id)hwnd isKindOfClass:[NSView class]])
7343   {
7344     if (retina_disabled &&
7345         [(id)hwnd isKindOfClass:[SWELL_hwndChild class]] &&
7346         ((SWELL_hwndChild*)hwnd)->m_glctx != NULL)
7347       retina_disabled = 0;
7349     w = [(NSView *)hwnd window];
7350   }
7351   else if ([(id)hwnd isKindOfClass:[NSWindow class]]) w = (NSWindow *)hwnd;
7353   if (retina_disabled) return 0;
7355   if (w)
7356   {
7357     NSRect r=NSMakeRect(0,0,1,1);
7358 #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_6
7359     NSRect str = [w convertRectToBacking:r];
7360 #else
7361     NSRect (*tmp)(id receiver, SEL operation, NSRect) = (NSRect (*)(id, SEL, NSRect))objc_msgSend_stret;
7362     NSRect str = tmp(w,sel_getUid("convertRectToBacking:"),r);
7363 #endif
7365     if (str.size.width > 1.9) return 1;
7366   }
7367   return 0;
7370 const char *SWELL_GetRecentPrefixRemoval(const char *p)
7372   for (int x = 0; x < s_prefix_removals.GetSize(); x ++)
7373   {
7374     const char *s = s_prefix_removals.Get(x);
7375     if (!strcmp(s,p)) return s+strlen(s)+1;
7376   }
7377   return NULL;
7380 #endif