1/*
2   <Title>GSToolbarView.m</title>
3
4   <abstract>The toolbar view class.</abstract>
5
6   Copyright (C) 2004-2015 Free Software Foundation, Inc.
7
8   Author:  Quentin Mathe <qmathe@club-internet.fr>
9   Date: January 2004
10
11   This file is part of the GNUstep GUI Library.
12
13   This library is free software; you can redistribute it and/or
14   modify it under the terms of the GNU Lesser General Public
15   License as published by the Free Software Foundation; either
16   version 2 of the License, or (at your option) any later version.
17
18   This library is distributed in the hope that it will be useful,
19   but WITHOUT ANY WARRANTY; without even the implied warranty of
20   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
21   Lesser General Public License for more details.
22
23   You should have received a copy of the GNU Lesser General Public
24   License along with this library; see the file COPYING.LIB.
25   If not, see <http://www.gnu.org/licenses/> or write to the
26   Free Software Foundation, 51 Franklin Street, Fifth Floor,
27   Boston, MA 02110-1301, USA.
28*/
29
30#import <Foundation/NSObject.h>
31#import <Foundation/NSArray.h>
32#import <Foundation/NSDictionary.h>
33#import <Foundation/NSEnumerator.h>
34#import <Foundation/NSException.h>
35#import <Foundation/NSNotification.h>
36#import <Foundation/NSUserDefaults.h>
37#import <Foundation/NSString.h>
38#import "AppKit/NSButton.h"
39#import "AppKit/NSClipView.h"
40#import "AppKit/NSDragging.h"
41#import "AppKit/NSEvent.h"
42#import "AppKit/NSImage.h"
43#import "AppKit/NSMenu.h"
44#import "AppKit/NSPasteboard.h"
45// It contains GSMovableToolbarItemPboardType declaration
46#import "AppKit/NSToolbarItem.h"
47#import "AppKit/NSView.h"
48#import "AppKit/NSWindow.h"
49
50#import "GNUstepGUI/GSTheme.h"
51#import "GNUstepGUI/GSToolbarView.h"
52
53#import "NSToolbarFrameworkPrivate.h"
54
55typedef enum {
56  ToolbarViewDefaultHeight = 62,
57  ToolbarViewRegularHeight = 62,
58  ToolbarViewSmallHeight = 52
59} ToolbarViewHeight;
60
61// Borrow this from  NSToolbarItem.m
62static const int InsetItemViewX = 10;
63
64static const int ClippedItemsViewWidth = 28;
65
66static NSUInteger draggedItemIndex = NSNotFound;
67
68/*
69 * Toolbar related code
70 */
71
72@interface GSToolbarButton
73- (NSToolbarItem *) toolbarItem;
74@end
75
76@interface GSToolbarBackView
77- (NSToolbarItem *) toolbarItem;
78@end
79
80@interface GSToolbarClippedItemsButton : NSButton
81{
82  NSToolbar *_toolbar;
83}
84
85- (id) init;
86
87// Accessors
88- (NSMenu *) overflowMenu;
89/* This method cannot be called "menu" otherwise it would override NSResponder
90   method with the same name. */
91
92- (void) layout;
93- (void) setToolbar: (NSToolbar *)toolbar;
94@end
95
96@implementation GSToolbarClippedItemsButton
97- (id) init
98{
99  NSImage *image = [NSImage imageNamed: @"common_ToolbarClippedItemsMark"];
100  NSRect dummyRect = NSMakeRect(0, 0, ClippedItemsViewWidth, 100);
101  // The correct height will be set by the layout method
102
103  if ((self = [super initWithFrame: dummyRect]) != nil)
104    {
105      [self setBordered: NO];
106      [[self cell] setHighlightsBy: NSChangeGrayCellMask
107        | NSChangeBackgroundCellMask];
108      [self setAutoresizingMask: NSViewNotSizable];
109      [self setImagePosition: NSImageOnly];
110      [image setScalesWhenResized: YES];
111      // [image setSize: NSMakeSize(20, 20)];
112      [self setImage: image];
113      return self;
114    }
115  return nil;
116}
117
118/*
119 * Not really used, it is here to be used by the developer who want to adjust
120 * easily a toolbar view attached to a toolbar which is not bind to a window.
121 */
122- (void) layout
123{
124  NSSize layoutSize = NSMakeSize([self frame].size.width,
125    [[_toolbar _toolbarView] _heightFromLayout]);
126
127  [self setFrameSize: layoutSize];
128}
129
130- (void) mouseDown: (NSEvent *)event
131{
132  NSMenu *clippedItemsMenu = [self menuForEvent: event];
133
134  [super highlight: YES];
135
136  if (clippedItemsMenu != nil)
137    {
138      [NSMenu popUpContextMenu: clippedItemsMenu withEvent: event
139              forView: self];
140    }
141
142  [super highlight: NO];
143}
144
145- (NSMenu *) menuForEvent: (NSEvent *)event
146{
147  if ([event type] == NSLeftMouseDown)
148    {
149      return [self overflowMenu];
150    }
151  return nil;
152}
153
154- (NSMenu *) overflowMenu
155{
156  /* This method cannot be called "menu" otherwise it would
157     override NSResponder method with the same name. */
158  NSMenu *menu = [[NSMenu alloc] initWithTitle: @""];
159  NSEnumerator *e;
160  id item;
161  NSArray *visibleItems;
162
163  visibleItems = [_toolbar visibleItems];
164
165  e = [[_toolbar items] objectEnumerator];
166  while ((item = [e nextObject]) != nil)
167    {
168      if (![visibleItems containsObject: item])
169        {
170          id menuItem;
171
172          menuItem = [item menuFormRepresentation];
173          if (menuItem == nil)
174            menuItem = [item _defaultMenuFormRepresentation];
175
176          if (menuItem != nil)
177            {
178              [item validate];
179              [menu addItem: menuItem];
180            }
181        }
182    }
183
184  return AUTORELEASE(menu);
185}
186
187// Accessors
188
189- (void) setToolbar: (NSToolbar *)toolbar
190{
191  // Don't do an ASSIGN here, the toolbar view retains us.
192  _toolbar = toolbar;
193}
194@end
195
196// ---
197
198// Implementation GSToolbarView
199
200@implementation GSToolbarView
201+ (void) initialize
202{
203  if (self == [GSToolbarView class])
204    {
205    }
206}
207
208- (id) initWithFrame: (NSRect)frame
209{
210  if ((self = [super initWithFrame: frame]) == nil)
211    {
212      return nil;
213    }
214
215  _heightFromLayout = ToolbarViewDefaultHeight;
216  [self setFrame: NSMakeRect(frame.origin.x, frame.origin.y,
217                             frame.size.width, _heightFromLayout)];
218
219  _clipView = [[NSClipView alloc] initWithFrame:
220                                      NSMakeRect(0, 0, frame.size.width,
221                                                 _heightFromLayout)];
222  [_clipView setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable)];
223  [_clipView setDrawsBackground: NO];
224  [self addSubview: _clipView];
225  // Adjust the clip view frame
226  [self setBorderMask: GSToolbarViewTopBorder | GSToolbarViewBottomBorder
227        | GSToolbarViewRightBorder | GSToolbarViewLeftBorder];
228
229  _clippedItemsMark = [[GSToolbarClippedItemsButton alloc] init];
230
231  [self registerForDraggedTypes:
232            [NSArray arrayWithObject: GSMovableToolbarItemPboardType]];
233
234  return self;
235}
236
237- (void) dealloc
238{
239  //NSLog(@"Toolbar view dealloc");
240
241  [[NSNotificationCenter defaultCenter] removeObserver: self];
242
243  RELEASE(_clippedItemsMark);
244  RELEASE(_clipView);
245
246  [super dealloc];
247}
248
249// Dragging related methods
250
251+ (NSUInteger) draggedItemIndex
252{
253  return draggedItemIndex;
254}
255
256+ (void) setDraggedItemIndex:(NSUInteger)sourceIndex
257{
258  draggedItemIndex = sourceIndex;
259}
260
261- (int) _insertionIndexAtPoint: (NSPoint)location
262{
263  NSUInteger index;
264  NSArray *visibleBackViews = [self _visibleBackViews];
265
266  location = [_clipView convertPoint:location fromView:nil];
267  if (draggedItemIndex == NSNotFound)
268    {
269      //simply locate the nearest location between existing items
270      for (index = 0; index < [visibleBackViews count]; index++)
271        {
272          NSRect itemRect = [[visibleBackViews objectAtIndex:index] frame];
273          if (location.x < (itemRect.origin.x + (itemRect.size.width/2)))
274            {
275              NSLog(@"At location %lu", (unsigned long)index);
276              return index;
277            }
278        }
279      return [visibleBackViews count];
280    }
281  else
282    {
283      // don't return a different index unless drag has crossed the midpoint of its neighbor
284      NSRect itemRect;
285      BOOL draggingLeft = YES;
286      if (draggedItemIndex < [visibleBackViews count])
287        {
288          itemRect = [[visibleBackViews objectAtIndex:draggedItemIndex] frame];
289          draggingLeft = (location.x < (itemRect.origin.x + (itemRect.size.width/2)));
290        }
291      if (draggingLeft)
292        {
293          // dragging to the left of dragged item's current location
294          for (index=0; index < draggedItemIndex; index++)
295            {
296              itemRect = [[visibleBackViews objectAtIndex:index] frame];
297              if (location.x < (itemRect.origin.x + (itemRect.size.width/2)))
298                {
299                  return index;
300                }
301            }
302        }
303      else
304        {
305          // dragging to the right of current location
306          // Never called for [visibleBackViews count] == 0
307          for (index=[visibleBackViews count]-1; index > draggedItemIndex; index--)
308            {
309              itemRect = [[visibleBackViews objectAtIndex:index] frame];
310              if (location.x > (itemRect.origin.x + (itemRect.size.width/2)))
311                {
312                  return index;
313                }
314            }
315        }
316      return draggedItemIndex;
317    }
318}
319
320#define OUTSIDE_INDEX (NSNotFound - 1)
321
322- (NSDragOperation) updateItemWhileDragging:(id <NSDraggingInfo>)info exited:(BOOL)exited
323{
324  NSToolbarItem *item = [[info draggingSource] toolbarItem];
325  NSString *identifier = [item itemIdentifier];
326  NSToolbar *toolbar = [self toolbar];
327  NSArray *allowedItemIdentifiers = [toolbar _allowedItemIdentifiers];
328  int newIndex;
329
330  // don't accept any dragging if the customization palette isn't running for this toolbar
331  if (![toolbar customizationPaletteIsRunning] || ![allowedItemIdentifiers containsObject: identifier])
332    {
333      return NSDragOperationNone;
334    }
335
336  if (draggedItemIndex == NSNotFound) // initialize the index for this drag session
337    {
338      // if duplicate items aren't allowed, see if we already have such an item
339      if (![item allowsDuplicatesInToolbar])
340        {
341          NSArray *items = [toolbar items];
342          NSUInteger index;
343          for (index=0; index<[items count]; index++)
344            {
345              NSToolbarItem *anItem = [items objectAtIndex:index];
346              if ([[anItem itemIdentifier] isEqual:identifier])
347                {
348                  draggedItemIndex = index; // drag the existing item
349                  break;
350                }
351            }
352        }
353    }
354  else if (draggedItemIndex == OUTSIDE_INDEX)
355    {
356      // re-entering after being dragged off -- treat as unknown location
357      draggedItemIndex = NSNotFound;
358    }
359
360  newIndex = [self _insertionIndexAtPoint: [info draggingLocation]];
361
362  if (draggedItemIndex != NSNotFound)
363    {
364      // existing item being dragged -- either move or remove it
365      if (exited)
366        {
367          [toolbar _removeItemAtIndex:draggedItemIndex broadcast:YES];
368          draggedItemIndex = OUTSIDE_INDEX; // no longer in our items
369        }
370      else
371        {
372          if (newIndex != draggedItemIndex)
373            {
374              [toolbar _moveItemFromIndex: draggedItemIndex toIndex: newIndex broadcast: YES];
375              draggedItemIndex = newIndex;
376            }
377        }
378    }
379  else if (!exited)
380    {
381      // new item being dragged in -- add it
382      [toolbar _insertItemWithItemIdentifier: identifier
383          atIndex: newIndex
384          broadcast: YES];
385      draggedItemIndex = newIndex;
386    }
387  return NSDragOperationGeneric;
388}
389
390- (NSDragOperation) draggingEntered: (id <NSDraggingInfo>)info
391{
392  return [self updateItemWhileDragging: info exited: NO];
393}
394
395- (NSDragOperation) draggingUpdated: (id <NSDraggingInfo>)info
396{
397  return [self updateItemWhileDragging: info exited: NO];
398}
399
400- (void) draggingEnded: (id <NSDraggingInfo>)info
401{
402  draggedItemIndex = NSNotFound;
403}
404
405- (void) draggingExited: (id <NSDraggingInfo>)info
406{
407  [self updateItemWhileDragging: info exited: YES];
408}
409
410- (BOOL) prepareForDragOperation: (id <NSDraggingInfo>)info
411{
412  return YES;
413}
414
415- (BOOL) performDragOperation: (id <NSDraggingInfo>)info
416{
417  NSToolbar *toolbar = [self toolbar];
418
419  [self updateItemWhileDragging: info exited: NO];
420
421  draggedItemIndex = NSNotFound;
422
423  // save the configuration...
424  [toolbar _saveConfig];
425
426  return YES;
427}
428
429- (void) concludeDragOperation: (id <NSDraggingInfo>)info
430{
431  // Nothing to do currently
432}
433
434// More overrided methods
435
436- (void) drawRect: (NSRect)aRect
437{
438  [[GSTheme theme] drawToolbarRect: aRect
439                   frame: [self frame]
440                   borderMask: _borderMask];
441}
442
443- (BOOL) isOpaque
444{
445  if ([[[GSTheme theme] toolbarBackgroundColor] alphaComponent] < 1.0)
446    {
447      return NO;
448    }
449  else
450    {
451      return YES;
452    }
453}
454
455- (void) windowDidResize: (NSNotification *)notification
456{
457  if ([self superview] == nil)
458    return;
459
460  [self _reload];
461}
462
463- (void) viewWillMoveToSuperview: (NSView *)newSuperview
464{
465  [super viewWillMoveToSuperview: newSuperview];
466
467  [_toolbar _toolbarViewWillMoveToSuperview: newSuperview];
468  // Allow to update the validation system which is window specific
469}
470
471- (void) viewDidMoveToWindow
472{
473  NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
474
475  /* NSView method called when a view is moved to a window (NSView has a
476     variable _window). */
477  [super viewDidMoveToWindow];
478
479  [nc removeObserver: self name: NSWindowDidResizeNotification object: nil];
480  [nc addObserver: self selector: @selector(windowDidResize:)
481                            name: NSWindowDidResizeNotification
482                          object: _window];
483}
484
485// Accessors
486
487- (unsigned int) borderMask
488{
489  return _borderMask;
490}
491
492- (NSToolbar *) toolbar
493{
494  return _toolbar;
495}
496
497- (void) setBorderMask: (unsigned int)borderMask
498{
499  NSRect toolbarViewFrame = [self frame];
500  NSRect rect = NSMakeRect(0, 0, toolbarViewFrame.size.width,
501                           toolbarViewFrame.size.height);
502
503  _borderMask = borderMask;
504
505  // Take in account the border
506  if (_borderMask & GSToolbarViewBottomBorder)
507    {
508      rect = NSMakeRect(rect.origin.x, ++rect.origin.y, rect.size.width,
509                        --rect.size.height);
510    }
511
512  if (_borderMask & GSToolbarViewTopBorder)
513    {
514      rect = NSMakeRect(rect.origin.x, rect.origin.y, rect.size.width,
515                        --rect.size.height);
516    }
517
518  if (_borderMask & GSToolbarViewLeftBorder)
519    {
520      rect = NSMakeRect(++rect.origin.x, rect.origin.y, --rect.size.width,
521                        rect.size.height);
522    }
523
524  if (_borderMask & GSToolbarViewRightBorder)
525    {
526      rect = NSMakeRect(rect.origin.x, rect.origin.y, --rect.size.width,
527                        rect.size.height);
528    }
529
530  [_clipView setFrame: rect];
531}
532
533- (void) setToolbar: (NSToolbar *)toolbar
534{
535  if (_toolbar == toolbar)
536    return;
537
538  _toolbar = toolbar;
539
540  [_clippedItemsMark setToolbar: _toolbar];
541  // Load the toolbar in the toolbar view
542  [self _reload];
543}
544
545// Private methods
546
547- (void) _handleBackViewsFrame
548{
549  float x = 0;
550  float newHeight = 0;
551  NSArray *subviews = [_clipView subviews];
552  NSEnumerator *e = [[_toolbar items] objectEnumerator];
553  NSToolbarItem *item;
554
555  while ((item = [e nextObject]) != nil)
556    {
557      NSView *itemBackView;
558      NSRect itemBackViewFrame;
559
560      itemBackView = [item _backView];
561      if ([subviews containsObject: itemBackView] == NO
562        || [item _isModified]
563        || [item _isFlexibleSpace])
564        {
565          // When a label is changed, _isModified returns YES to let us known we
566          // must recalculate the text length and then the size for the edited
567          // item back view
568          [item _layout];
569        }
570
571      itemBackViewFrame = [itemBackView frame];
572      [itemBackView setFrame: NSMakeRect(x, itemBackViewFrame.origin.y,
573        itemBackViewFrame.size.width, itemBackViewFrame.size.height)];
574
575      x += [itemBackView frame].size.width;
576
577      if (itemBackViewFrame.size.height > newHeight)
578        newHeight = itemBackViewFrame.size.height;
579    }
580
581  if (newHeight > 0)
582    _heightFromLayout = newHeight;
583}
584
585- (void) _takeInAccountFlexibleSpaces
586{
587  NSArray *items = [_toolbar items];
588  NSEnumerator *e;
589  NSToolbarItem *item;
590  NSView *backView, *view;
591  CGFloat lengthAvailable;
592  BOOL mustAdjustNext = NO;
593  CGFloat x = 0, visibleItemsMinWidth = 0, backViewsWidth = 0;
594  NSMutableArray *variableWidthItems = [NSMutableArray array];
595  int flexibleItemsCount = 0, maxWidthItemsCount = 0;
596  CGFloat spacePerFlexItem, extraSpace = 0;
597  CGFloat toolbarWidth = [self frame].size.width;
598  int i, n = [items count];
599  NSMutableArray *visibleItems = [NSMutableArray array];
600  static const int FlexItemWeight = 4; // non-space flexible item counts as much as 4 flexible spaces
601
602  if (n == 0)
603    return;
604
605  // First determine which items can fit in toolbar if all are at their minimum width.
606  // We'd like to show as many items as possible. These are our visibleItems.
607  for (i=0; i < n; i++)
608    {
609      item = [items objectAtIndex:i];
610      backView = [item _backView];
611      view = [item view];
612      if (view != nil)
613        backViewsWidth += [item minSize].width + 2*InsetItemViewX;
614      else
615        backViewsWidth += [backView frame].size.width;
616
617      if ((backViewsWidth + ClippedItemsViewWidth <= toolbarWidth)
618        || (i == n - 1 && backViewsWidth <= toolbarWidth))
619        {
620          visibleItemsMinWidth = backViewsWidth;
621          [visibleItems addObject:item];
622        }
623      else
624        {
625          break;
626        }
627    }
628  // next, figure out how much additional space there is for expanding flexible items
629  lengthAvailable = toolbarWidth - visibleItemsMinWidth;
630  if ([visibleItems count] < n)
631    lengthAvailable -= ClippedItemsViewWidth;
632
633  if (lengthAvailable < 1)
634    return;
635
636  // We want to divide available space evenly among all flexible items, but some items may
637  // reach their maximum width, making more space available for the other items.
638  // To do this, first we count the flexible items, gathering a list of those that may
639  // have a maximum width.
640  // To match observed behavior on Cocoa (which is NOT as documented!) we allocate only 1/4
641  // as much space to flexible spaces as we do to other flexible items.
642  e = [visibleItems objectEnumerator];
643  while ((item = [e nextObject]) != nil)
644    {
645      if ([item _isFlexibleSpace])
646        {
647          flexibleItemsCount++;
648        }
649      else
650        {
651          CGFloat minWidth = [item minSize].width;
652          CGFloat maxWidth = [item maxSize].width;
653          if (minWidth < maxWidth)
654            {
655              [variableWidthItems addObject:item];
656              flexibleItemsCount += FlexItemWeight; // gets FlexItemWeight times the weight of a flexible space
657            }
658        }
659    }
660  if (flexibleItemsCount == 0)
661    return;
662
663  // Now go through any variableWidthItems to see if the available space per item would
664  // cause any of them to exceed their maximum width, and calculate the extra space available
665  spacePerFlexItem = MAX(lengthAvailable / flexibleItemsCount, 0);
666  e = [variableWidthItems objectEnumerator];
667  while ((item = [e nextObject]) != nil)
668    {
669      CGFloat minWidth = [item minSize].width;
670      CGFloat maxWidth = [item maxSize].width;
671      if (maxWidth-minWidth < spacePerFlexItem * FlexItemWeight)
672        {
673          extraSpace += spacePerFlexItem * FlexItemWeight - (maxWidth-minWidth); // give back unneeded space
674          maxWidthItemsCount += FlexItemWeight;
675        }
676    }
677  // Recalculate spacePerFlexItem (unless all flexible items are going to their max width)
678  if (flexibleItemsCount > maxWidthItemsCount)
679    spacePerFlexItem += extraSpace / (flexibleItemsCount-maxWidthItemsCount);
680
681  // Finally, go through all items, adjusting their width and positioning them as needed
682  e = [items objectEnumerator];
683  while ((item = [e nextObject]) != nil)
684  {
685    backView = [item _backView];
686    if ([item _isFlexibleSpace])
687      {
688        NSRect backViewFrame = [backView frame];
689
690        [backView setFrame: NSMakeRect(x, backViewFrame.origin.y,
691          spacePerFlexItem,
692          backViewFrame.size.height)];
693        mustAdjustNext = YES;
694      }
695    else if ([variableWidthItems indexOfObjectIdenticalTo:item] != NSNotFound)
696      {
697        NSRect backViewFrame = [backView frame];
698        CGFloat maxFlex = [item maxSize].width - [item minSize].width;
699        CGFloat flexAmount = MIN(maxFlex, spacePerFlexItem * FlexItemWeight);
700        CGFloat newWidth = [item minSize].width + flexAmount + 2 * InsetItemViewX;
701        [backView setFrame: NSMakeRect(x, backViewFrame.origin.y,
702          newWidth,
703          backViewFrame.size.height)];
704        mustAdjustNext = YES;
705      }
706    else if (mustAdjustNext)
707      {
708        NSRect backViewFrame = [backView frame];
709
710        [backView setFrame: NSMakeRect(x, backViewFrame.origin.y,
711          backViewFrame.size.width, backViewFrame.size.height)];
712      }
713    view = [item view];
714    if (view != nil)
715      {
716        NSRect viewFrame = [view frame];
717        // Subtract InsetItemViewX
718        viewFrame.size.width = [backView frame].size.width - 2 * InsetItemViewX;
719        viewFrame.origin.x = InsetItemViewX;
720        [view setFrame: viewFrame];
721      }
722    x += [backView frame].size.width;
723  }
724}
725
726- (void) _handleViewsVisibility
727{
728  NSArray *backViews;
729  NSArray *subviews;
730  NSEnumerator *e;
731  NSView *backView;
732
733  /* The back views which are associated with each toolbar item (the toolbar
734     items doesn't reflect the toolbar view content) */
735  backViews = [[_toolbar items] valueForKey: @"_backView"];
736
737  // We remove each back view associated with a removed toolbar item
738  e = [[_clipView subviews] objectEnumerator];
739  while ((backView = [e nextObject]) != nil)
740    {
741      if ([backViews containsObject: backView] == NO)
742        {
743          if ([backView superview] != nil)
744            [backView removeFromSuperview];
745        }
746    }
747
748  // We add each backView associated with an added toolbar item
749  subviews = [_clipView subviews];
750  e = [backViews objectEnumerator];
751  while ((backView = [e nextObject]) != nil)
752  {
753    if ([subviews containsObject: backView] == NO)
754      {
755        [_clipView addSubview: backView];
756      }
757  }
758}
759
760- (void) _manageClipView
761{
762  NSRect clipViewFrame = [_clipView frame];
763  int count = [[_toolbar items] count];
764  // Retrieve the back views which should be visible now that the resize
765  // process has been taken in account
766  NSArray *visibleBackViews = [self _visibleBackViews];
767
768  if ([visibleBackViews count] < count)
769    {
770      NSView *lastVisibleBackView = [visibleBackViews lastObject];
771      float width = 0;
772
773      // Resize the clip view
774      if (lastVisibleBackView != nil)
775        width = NSMaxX([lastVisibleBackView frame]);
776      [_clipView setFrame: NSMakeRect(clipViewFrame.origin.x,
777                                      clipViewFrame.origin.y,
778                                      width,
779                                      clipViewFrame.size.height)];
780
781      // Adjust the clipped items mark frame handling
782      [_clippedItemsMark layout];
783
784      // We get the new _clipView frame
785      clipViewFrame = [_clipView frame];
786      [_clippedItemsMark setFrameOrigin: NSMakePoint(
787        [self frame].size.width - ClippedItemsViewWidth, clipViewFrame.origin.y)];
788
789      if ([_clippedItemsMark superview] == nil)
790        [self addSubview: _clippedItemsMark];
791
792    }
793  else if (([_clippedItemsMark superview] != nil)
794    && ([visibleBackViews count] == count))
795    {
796      [_clippedItemsMark removeFromSuperview];
797
798      [_clipView setFrame: NSMakeRect(clipViewFrame.origin.x,
799                                      clipViewFrame.origin.y,
800                                      [self frame].size.width,
801                                      clipViewFrame.size.height)];
802    }
803}
804
805- (void) _reload
806{
807  // First, we resize
808  [self _handleBackViewsFrame];
809  [self _takeInAccountFlexibleSpaces];
810
811  [self _handleViewsVisibility];
812  /* We manage the clipped items view in the case it should become visible or
813     invisible */
814  [self _manageClipView];
815
816  [self setNeedsDisplay: YES];
817}
818
819// Accessors private methods
820
821- (float) _heightFromLayout
822{
823  float height = _heightFromLayout;
824
825  if (_borderMask & GSToolbarViewBottomBorder)
826    {
827      height++;
828    }
829
830  if (_borderMask & GSToolbarViewTopBorder)
831    {
832      height++;
833    }
834
835  return height;
836}
837
838/*
839 * Will return the visible (not clipped) back views in the toolbar view even
840 * when the toolbar is not visible.
841 * May be should be renamed _notClippedBackViews method.
842 */
843- (NSArray *) _visibleBackViews
844{
845  NSArray *items = [_toolbar items];
846  NSView *backView, *view;
847  int i, n = [items count];
848  float backViewsWidth = 0, toolbarWidth = [self frame].size.width;
849
850  NSMutableArray *visibleBackViews = [NSMutableArray array];
851
852  for (i = 0; i < n; i++)
853    {
854      NSToolbarItem *item = [items objectAtIndex:i];
855      backView = [item _backView];
856      view = [item view];
857      if (view != nil)
858        backViewsWidth += [item minSize].width + 2*InsetItemViewX;
859      else
860        backViewsWidth += [backView frame].size.width;
861
862      if ((backViewsWidth + ClippedItemsViewWidth <= toolbarWidth)
863        || (i == n - 1 && backViewsWidth <= toolbarWidth))
864        {
865          [visibleBackViews addObject: backView];
866        }
867    }
868
869  return visibleBackViews;
870}
871
872- (NSColor *) standardBackgroundColor
873{
874  NSLog(@"Use of deprecated method %@", NSStringFromSelector(_cmd));
875  return nil;
876}
877
878- (BOOL) _usesStandardBackgroundColor
879{
880  NSLog(@"Use of deprecated method %@", NSStringFromSelector(_cmd));
881  return NO;
882}
883
884- (void) _setUsesStandardBackgroundColor: (BOOL)standard
885{
886  NSLog(@"Use of deprecated method %@", NSStringFromSelector(_cmd));
887}
888
889- (NSMenu *) menuForEvent: (NSEvent *)event
890{
891  NSMenu *menu = [[[NSMenu alloc] initWithTitle: @""] autorelease];
892  id <NSMenuItem> customize = [menu insertItemWithTitle: _(@"Customize Toolbar") action:@selector(runCustomizationPalette:) keyEquivalent:@"" atIndex:0];
893  [customize setTarget: _toolbar];
894  return menu;
895}
896
897
898
899@end
900