Index: src/proto/gui_mac.pro
===================================================================
*** src/proto/gui_mac.pro	(revision 47)
--- src/proto/gui_mac.pro	(working copy)
***************
*** 84,89 ****
--- 84,93 ----
  int gui_mch_dialog __ARGS((int type, char_u *title, char_u *message, char_u *buttons, int dfltbutton, char_u *textfield));
  char_u *gui_mch_browse __ARGS((int saving, char_u *title, char_u *dflt, char_u *ext, char_u *initdir, char_u *filter));
  void gui_mch_set_foreground __ARGS((void));
+ void gui_mch_show_tabline __ARGS((int showit));
+ int gui_mch_showing_tabline __ARGS((void));
+ void gui_mch_update_tabline __ARGS((void));
+ void gui_mch_set_curtab __ARGS((int nr));
  
  char_u *C2Pascal_save __ARGS((char_u *Cstring));
  char_u *C2Pascal_save_and_remove_backslash __ARGS((char_u *Cstring));
Index: src/gui.c
===================================================================
*** src/gui.c	(revision 47)
--- src/gui.c	(working copy)
***************
*** 1158,1164 ****
  #endif
  
  # if defined(FEAT_GUI_TABLINE) && (defined(FEAT_GUI_MSWIN) \
! 	|| defined(FEAT_GUI_MOTIF))
      if (gui_has_tabline())
  	text_area_y += gui.tabline_height;
  #endif
--- 1158,1164 ----
  #endif
  
  # if defined(FEAT_GUI_TABLINE) && (defined(FEAT_GUI_MSWIN) \
!  	|| defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_MAC))
      if (gui_has_tabline())
  	text_area_y += gui.tabline_height;
  #endif
Index: src/gui.h
===================================================================
*** src/gui.h	(revision 47)
--- src/gui.h	(working copy)
***************
*** 421,427 ****
  #endif	/* FEAT_GUI_GTK */
  
  #if defined(FEAT_GUI_TABLINE) \
! 	&& (defined(FEAT_GUI_W32) || defined(FEAT_GUI_MOTIF))
      int		tabline_height;
  #endif
  
--- 421,428 ----
  #endif	/* FEAT_GUI_GTK */
  
  #if defined(FEAT_GUI_TABLINE) \
!  	&& (defined(FEAT_GUI_W32) || defined(FEAT_GUI_MOTIF) \
!                  || defined(FEAT_GUI_MAC))
      int		tabline_height;
  #endif
  
Index: src/gui_mac.c
===================================================================
*** src/gui_mac.c	(revision 47)
--- src/gui_mac.c	(working copy)
***************
*** 260,265 ****
--- 260,269 ----
  OSErr HandleUnusedParms(const AppleEvent *theAEvent);
  #endif
  
+ #ifdef FEAT_GUI_TABLINE //XXXNICO
+ static void initialise_tabline(void);
+ #endif
+ 
  /*
   * ------------------------------------------------------------
   * Conversion Utility
***************
*** 3115,3120 ****
--- 3119,3131 ----
  #endif
  */
  
+ #ifdef FEAT_GUI_TABLINE
+     /*
+      * Create the tabline
+      */
+     initialise_tabline();
+ #endif
+ 
      /* TODO: Load bitmap if using TOOLBAR */
      return OK;
  }
***************
*** 6108,6111 ****
--- 6119,6499 ----
      return (script != smRoman
  	    && script == GetScriptManagerVariable(smSysScript)) ? 1 : 0;
  }
+ 
  #endif /* defined(USE_IM_CONTROL) || defined(PROTO) */
+ 
+ 
+ 
+ //XXXNICO
+ #if defined(FEAT_GUI_TABLINE) || defined(PROTO)
+ 
+ MenuRef contextMenu = NULL;
+ enum
+ { 
+     kTabContextMenuId = 42,
+ };
+ 
+ //the caller has to CFRelease() the returned string
+ CFStringRef getTabLabel(tabpage_T* page)
+ {
+     get_tabline_label(page, FALSE);
+ #ifdef MACOS_CONVERT
+     return mac_enc_to_cfstring(NameBuff, STRLEN(NameBuff));
+ #else
+     //TODO: check internal encoding?
+     return CFStringCreateWithCString(kCFAllocatorDefault, (char*)NameBuff,
+             kCFStringEncodingMacRoman);
+ #endif
+ }
+ 
+ 
+ #define DRAWER_SIZE 150
+ 
+ //drawer implementation
+ WindowRef drawer = NULL; //TODO: put into gui.h
+ ControlRef dataBrowser = NULL;
+ 
+ //when the tabline is hidden, vim doesn't call update_tabline(). When
+ //the tabline is shown again, show_tabline() is called before upate_tabline(),
+ //and because of this, the tab labels and vims internal tabs are out of sync
+ //for a very short time. to prevent inconsistent state, we store the labels
+ //of the tabs, not pointers to the tabs (which are invalid for a short time).
+ CFStringRef* tabLabels = NULL;
+ int tabLabelsSize = 0;
+ 
+ enum
+ {
+     kTabsColumn = 'Tabs'
+ };
+ 
+ int getTabCount()
+ {
+     tabpage_T* tp;
+     int numTabs = 0;
+     for (tp = first_tabpage; tp != NULL; tp = tp->tp_next)
+         ++numTabs;
+     return numTabs;
+ }
+ 
+ 
+ //resizes the drawer so that the data browser fits into it (if possible)
+ void adjustDrawerSize()
+ {
+     //this creates problems... :-|
+     return;
+ 
+     //calculate desired drawer height
+     Rect outerRect, innerRect;
+     GetWindowBounds(drawer, kWindowStructureRgn, &outerRect);
+     GetWindowBounds(drawer, kWindowContentRgn, &innerRect);
+     int drawerFrameHeight = (innerRect.top - outerRect.top) +
+         (outerRect.bottom - innerRect.bottom);
+ 
+     //get number of items in the data browser and the height of the
+     //first row (all rows are currently of the same size, so this is
+     //the general row size)
+     UInt32 numItems;
+     UInt16 rowHeight;
+     GetDataBrowserItemCount(dataBrowser, kDataBrowserNoItem, false,
+             0, &numItems);
+     GetDataBrowserTableViewItemRowHeight(dataBrowser, 1, &rowHeight);
+     numItems = getTabCount(); //TODO: GetItemCount() doesn't work...
+ 
+     //this assumes that the data browser has no header. we leave room
+     //for one empty row.
+     int desiredHeight = drawerFrameHeight + (numItems + 1)*rowHeight;
+ 
+     //set drawer offset
+     GetWindowBounds(gui.VimWindow, kWindowContentRgn, &innerRect);
+     int contentHeight = innerRect.bottom - innerRect.top;
+     float lowerInset = contentHeight - desiredHeight;
+     if(lowerInset < 0.f)
+         lowerInset = 0.f;
+ 
+     SetDrawerOffsets(drawer, kWindowOffsetUnchanged, lowerInset);
+ }
+ 
+ //data browser item display callback
+ OSStatus dbItemDataCallback(ControlRef browser, DataBrowserItemID itemID, 
+         DataBrowserPropertyID property /* column id */, 
+         DataBrowserItemDataRef itemData, Boolean changeValue)
+ {
+   OSStatus status = noErr;
+ 
+   //assert(property == kTabsColumn); //why is this violated??
+ 
+   //changeValue is true if we have a modifieable list and data was changed.
+   //in our case, it's always false.
+   //(that is: if(changeValue) updateInternalData(); else return internalData();
+   if (!changeValue)
+   {
+       CFStringRef str;
+       assert(itemID - 1 >= 0 && itemID - 1 < tabLabelsSize);
+       str = tabLabels[itemID - 1];
+       status = SetDataBrowserItemDataText(itemData, str);
+   }
+   else status = errDataBrowserPropertyNotSupported; 
+ 
+   return status;
+ }
+ 
+ //data browser action callback
+ void dbItemNotificationCallback(ControlRef browser, DataBrowserItemID item,
+         DataBrowserItemNotification message)
+ {
+   switch(message)
+   {
+     case kDataBrowserItemSelected:
+       send_tabline_event(item);
+       break;
+   }
+ }
+ 
+ //callbacks needed for contextual menu:
+ void dbGetContextualMenuCallback(ControlRef browser, MenuRef *menu,
+         UInt32 *helpType, CFStringRef *helpItemString,
+         AEDesc *selection)
+ {
+   //on mac os 9: kCMHelpItemNoHelp, but it's not the same
+   *helpType = kCMHelpItemRemoveHelp; //OS X only ;-)
+   *helpItemString = NULL;
+   
+   *menu = contextMenu;
+ }
+ 
+ void dbSelectContextualMenuCallback(ControlRef browser, MenuRef menu,
+                                     UInt32 selectionType, SInt16 menuID,
+                                     MenuItemIndex menuItem)
+ { 
+   if(selectionType == kCMMenuItemSelected )
+   {
+     MenuCommand command;
+     GetMenuItemCommandID(menu, menuItem, &command);
+ 
+     //get tab that was selected when the context menu appeared
+     //(there is always one tab selected). TODO: check if the context menu
+     //isn't opened on an item but on empty space (has to be possible some
+     //way, the finder does it too ;-) )
+     Handle items = NewHandle(0);
+     if(items != NULL)
+     {
+         GetDataBrowserItems(browser, kDataBrowserNoItem, false,
+                 kDataBrowserItemIsSelected, items);
+ 
+         int numItems = GetHandleSize(items)/sizeof(DataBrowserItemID);
+         if(numItems > 0)
+         {
+             HLock(items);
+             DataBrowserItemID* itemsPtr = (DataBrowserItemID*)*items;
+             int idx = itemsPtr[0];
+             HUnlock(items);
+             send_tabline_menu_event(idx, command);
+         }
+         DisposeHandle(items);
+     }
+   }
+ }
+ 
+ //focus callback of the data browser to always leave focus in vim
+ static OSStatus dbFocusCallback(EventHandlerCallRef handler, 
+         EventRef event, void *data)
+ {
+   assert(GetEventClass(event) == kEventClassControl &&
+           GetEventKind(event) == kEventControlSetFocusPart);
+ 
+   return paramErr;
+ }
+ 
+ 
+ //drawer callback to resize data browser to drawer size
+ static OSStatus drawerCallback(EventHandlerCallRef handler,
+         EventRef event, void *data)
+ {
+   switch(GetEventKind(event))
+   {
+     case kEventWindowBoundsChanged: //move or resize
+     {
+         UInt32 attribs;
+         GetEventParameter(event, kEventParamAttributes, typeUInt32, NULL,
+                 sizeof(attribs), NULL, &attribs);
+         if(attribs & kWindowBoundsChangeSizeChanged) //resize
+         {
+             adjustDrawerSize();
+ 
+             Rect r;
+             GetWindowBounds(drawer, kWindowContentRgn, &r);
+             SetRect(&r, 0, 0, r.right - r.left, r.bottom - r.top);
+             SetControlBounds(dataBrowser, &r);
+             SetDataBrowserTableViewNamedColumnWidth(dataBrowser,
+                     kTabsColumn, r.right);
+         }
+     }break;
+   }
+   
+   return eventNotHandledErr;
+ }
+ 
+     static void
+ initialise_tabline(void)
+ {
+     Rect drawerRect = { 0, 0, 0, DRAWER_SIZE };
+     //drawers have to have compositing enabled
+     CreateNewWindow(kDrawerWindowClass, kWindowStandardHandlerAttribute |
+             kWindowCompositingAttribute, &drawerRect, &drawer);
+  
+     SetThemeWindowBackground (drawer, kThemeBrushDrawerBackground, true);
+     SetDrawerParent(drawer, gui.VimWindow);
+ 
+     //create list view embedded in drawer
+     CreateDataBrowserControl(drawer, &drawerRect, kDataBrowserListView,
+             &dataBrowser);
+ 
+     DataBrowserCallbacks dbCallbacks;
+     dbCallbacks.version = kDataBrowserLatestCallbacks;
+     InitDataBrowserCallbacks(&dbCallbacks);
+     dbCallbacks.u.v1.itemDataCallback =
+         NewDataBrowserItemDataUPP(dbItemDataCallback);
+     dbCallbacks.u.v1.itemNotificationCallback =
+         NewDataBrowserItemNotificationUPP(dbItemNotificationCallback);
+     dbCallbacks.u.v1.getContextualMenuCallback =
+         NewDataBrowserGetContextualMenuUPP(dbGetContextualMenuCallback);
+     dbCallbacks.u.v1.selectContextualMenuCallback =
+         NewDataBrowserSelectContextualMenuUPP(dbSelectContextualMenuCallback);
+ 
+     SetDataBrowserCallbacks(dataBrowser, &dbCallbacks);
+ 
+     SetDataBrowserListViewHeaderBtnHeight(dataBrowser, 0); //no header
+     SetDataBrowserHasScrollBars(dataBrowser, false, true); //only vertical
+     SetDataBrowserSelectionFlags(dataBrowser, 
+             kDataBrowserSelectOnlyOne | kDataBrowserNeverEmptySelectionSet);
+     SetDataBrowserTableViewHiliteStyle(dataBrowser, 
+             kDataBrowserTableViewFillHilite);
+     Boolean b = false;
+     SetControlData(dataBrowser, kControlEntireControl,
+             kControlDataBrowserIncludesFrameAndFocusTag, sizeof(b), &b);
+ 
+     //this is only in 10.4:
+     DataBrowserChangeAttributes(dataBrowser,
+             kDataBrowserAttributeListViewAlternatingRowColors, 0);
+ 
+     //install callback that keeps focus in vim and away from the data browser
+     EventTypeSpec focusEvent = {kEventClassControl, kEventControlSetFocusPart};
+     InstallControlEventHandler(dataBrowser, dbFocusCallback, 1, &focusEvent,
+             NULL, NULL);
+  
+     //install callback that keeps data browser at the size of the drawer
+     EventTypeSpec resizeEvent = {kEventClassWindow, kEventWindowBoundsChanged};
+     InstallWindowEventHandler(drawer, drawerCallback, 1, &resizeEvent,
+             NULL, NULL);
+ 
+     //add "tabs" column to data browser
+     DataBrowserListViewColumnDesc colDesc;
+     colDesc.propertyDesc.propertyID = kTabsColumn;
+     colDesc.propertyDesc.propertyType = kDataBrowserTextType;
+ 
+     //add if items can be selected (?): kDataBrowserListViewSelectionColumn
+     colDesc.propertyDesc.propertyFlags = kDataBrowserDefaultPropertyFlags;
+ 
+     colDesc.headerBtnDesc.version = kDataBrowserListViewLatestHeaderDesc;
+     colDesc.headerBtnDesc.minimumWidth = 100;
+     colDesc.headerBtnDesc.maximumWidth = 150;
+     colDesc.headerBtnDesc.titleOffset = 0;
+     colDesc.headerBtnDesc.titleString = CFSTR("Tabs");
+     colDesc.headerBtnDesc.initialOrder = kDataBrowserOrderIncreasing;
+     colDesc.headerBtnDesc.btnFontStyle.flags = 0; //use default font
+     colDesc.headerBtnDesc.btnContentInfo.contentType = kControlContentTextOnly;
+ 
+     AddDataBrowserListViewColumn(dataBrowser, &colDesc, 0);
+ 
+ 
+ 
+     //create tabline popup menu required by vim docs (see :he tabline-menu)
+     CreateNewMenu(kTabContextMenuId, 0, &contextMenu);
+     AppendMenuItemTextWithCFString(contextMenu, CFSTR("Close"), 0, 
+             TABLINE_MENU_CLOSE, NULL);
+     AppendMenuItemTextWithCFString(contextMenu, CFSTR("New Tab"), 0,
+             TABLINE_MENU_NEW, NULL);
+     AppendMenuItemTextWithCFString(contextMenu, CFSTR("Open Tab..."), 0,
+             TABLINE_MENU_OPEN, NULL);
+ }
+ 
+ 
+ /*
+  * Show or hide the tabline.
+  */
+     void
+ gui_mch_show_tabline(int showit)
+ {
+     if(showit == 0)
+         CloseDrawer(drawer, true);
+     else
+         OpenDrawer(drawer, kWindowEdgeRight, true);
+ }
+ 
+ /*
+  * Return TRUE when tabline is displayed.
+  */
+     int
+ gui_mch_showing_tabline(void)
+ {
+     WindowDrawerState state = GetDrawerState(drawer);
+     return state == kWindowDrawerOpen || state == kWindowDrawerOpening;
+ }
+ 
+ /*
+  * Update the labels of the tabline.
+  */
+     void
+ gui_mch_update_tabline(void)
+ {
+     tabpage_T *tp;
+     int numTabs = getTabCount(), nr = 1, curtabidx = 1;
+ 
+     //adjust data browser
+     if(tabLabels != NULL) {
+         int i;
+         for(i = 0; i < tabLabelsSize; ++i)
+             CFRelease(tabLabels[i]);
+         free(tabLabels);
+     }
+     tabLabels = (CFStringRef*)malloc(numTabs*sizeof(CFStringRef));
+     tabLabelsSize = numTabs;
+ 
+     for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr)
+     {
+ 	if (tp == curtab)
+ 	    curtabidx = nr;
+         tabLabels[nr-1] = getTabLabel(tp);
+     }
+ 
+     RemoveDataBrowserItems(dataBrowser, kDataBrowserNoItem, 0, NULL,
+             kDataBrowserItemNoProperty);
+     //data browser uses ids 1, 2, 3, ... numTabs per default, so we
+     //can pass NULL for the id array
+     AddDataBrowserItems(dataBrowser, kDataBrowserNoItem, numTabs, NULL,
+             kDataBrowserItemNoProperty);
+ 
+     DataBrowserItemID item = curtabidx;
+     SetDataBrowserSelectedItems(dataBrowser, 1, &item, kDataBrowserItemsAssign);
+ 
+     adjustDrawerSize();
+ }
+ 
+ /*
+  * Set the current tab to "nr".  First tab is 1.
+  */
+     void
+ gui_mch_set_curtab(nr)
+     int		nr;
+ {
+     DataBrowserItemID item = nr;
+     SetDataBrowserSelectedItems(dataBrowser, 1, &item, kDataBrowserItemsAssign);
+  
+     //TODO: call something like this?: (or restore scroll position, or...)
+     RevealDataBrowserItem(dataBrowser, item, kTabsColumn,
+             kDataBrowserRevealOnly);
+ 
+ }
+ 
+ #endif //FEAT_GUI_TABLINE
+ 
Index: src/feature.h
===================================================================
*** src/feature.h	(revision 47)
--- src/feature.h	(working copy)
***************
*** 756,761 ****
--- 756,762 ----
  #if defined(FEAT_WINDOWS) && defined(FEAT_NORMAL) \
      && (defined(FEAT_GUI_GTK) \
  	|| (defined(FEAT_GUI_MOTIF) && defined(HAVE_XM_NOTEBOOK_H)) \
+ 	|| defined(FEAT_GUI_MAC) \
  	|| (defined(FEAT_GUI_MSWIN) && (!defined(_MSC_VER) || _MSC_VER > 1020)))
  # define FEAT_GUI_TABLINE
  #endif
