home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / cmd / xfe / src / AttachPanel.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  31.7 KB  |  1,164 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18. /* 
  19.    AttachPanel.cpp -- panel for displaying icon view of attached files.
  20.    Created: Alastair Gourlay(SGI) c/o Dora Hsu<dora@netscape.com>, 26 Nov 1996
  21.  */
  22.  
  23.  
  24.  
  25. // Classes in this file:
  26. //      XFE_AttachPanel
  27. //      XFE_AttachPanelItem
  28. //
  29.  
  30.  
  31. #include "AttachPanel.h"
  32. #include <stdlib.h>
  33. #include <Xm/XmAll.h>
  34. #include <X11/cursorfont.h>
  35. #include "mozilla.h"
  36. #include "xfe.h"
  37. #include "net.h"
  38. #include "icons.h"
  39. #include "icondata.h"
  40. #include "DragDrop.h"
  41.  
  42. #ifdef DEBUG_sgidev
  43. #define XDEBUG(x) x
  44. #else
  45. #define XDEBUG(x)
  46. #endif
  47.  
  48. //
  49. // XFE_AttachPanel
  50. //
  51.  
  52. // static initialization
  53.  
  54. const int XFE_AttachPanel::_allocIncrement=50; // item list management
  55. const int XFE_AttachPanel::_marginWidth=5; // panel margin
  56. const int XFE_AttachPanel::_marginHeight=5; // panel margin
  57. const int XFE_AttachPanel::_horizSpacing=10; // panel spacing
  58.  
  59. #define ATTACH_ICON_NAME "attachItemImage"
  60. #define ATTACH_LABEL_NAME "attachItemLabel"
  61.  
  62. // callback stubs
  63.  
  64. void XFE_AttachPanel::ExposeCb(Widget,XtPointer cd,XtPointer) {
  65.     XFE_AttachPanel *ad=(XFE_AttachPanel*)cd;
  66.     if (ad && ad->_firstExpose) {
  67.     ad->_firstExpose=FALSE;
  68.         ad->realize();
  69.     }
  70. }
  71.  
  72. void XFE_AttachPanel::InputCb(Widget,XtPointer cd,XtPointer cb) {
  73.     XFE_AttachPanel *ad=(XFE_AttachPanel*)cd;
  74.     
  75.     if (ad && cb) {
  76.         XmDrawingAreaCallbackStruct *dcb=(XmDrawingAreaCallbackStruct*)cb;
  77.         ad->inputCb(dcb->event);
  78.     }
  79. }
  80.  
  81. void XFE_AttachPanel::ScrollTraversalCb(Widget,XtPointer cd,XtPointer cb) {
  82.     XFE_AttachPanel *ad=(XFE_AttachPanel*)cd;
  83.     XmTraverseObscuredCallbackStruct *scb=(XmTraverseObscuredCallbackStruct*)cb;
  84.     if (ad && ad->_traversalProcId==0 &&
  85.         scb && scb->reason==XmCR_OBSCURED_TRAVERSAL) {
  86.         // delay scroll until after completion of armCb(). Of course :)
  87.         // if only Motif passed a valid cb->event, all this would be unecessary
  88.         ad->_traversalWidget=scb->traversal_destination;
  89.         ad->_traversalProcId=XtAppAddTimeOut(XtWidgetToApplicationContext(ad->_top),
  90.                                    0,ScrollTraversalProc,cd);
  91.     }
  92. }
  93.  
  94. void XFE_AttachPanel::ScrollTraversalProc(XtPointer cd, XtIntervalId *id) {
  95.     XFE_AttachPanel *ad=(XFE_AttachPanel*)cd;
  96.     if (ad) {
  97.         if (ad->_traversalProcId==*id)
  98.             ad->_traversalProcId=0;
  99.         if (ad->_traversalWidget) {
  100.             ad->scrollTraversalProc(ad->_traversalWidget);
  101.             ad->_traversalWidget=NULL;
  102.         }
  103.     }
  104. }
  105.  
  106. void XFE_AttachPanel::ScrollResizeCb(Widget,XtPointer cd,XtPointer) {
  107.     XFE_AttachPanel *ad=(XFE_AttachPanel*)cd;
  108.     if (ad && ad->_resizeProcId==0) {
  109.         // delay resize until after completion of resize callback. Of course :)
  110.         ad->_resizeProcId=XtAppAddTimeOut(XtWidgetToApplicationContext(ad->_top),
  111.                                    0,ScrollResizeProc,cd);
  112.     }
  113. }
  114. void XFE_AttachPanel::ScrollResizeProc(XtPointer cd, XtIntervalId *id) {
  115.     XFE_AttachPanel *ad=(XFE_AttachPanel*)cd;
  116.     if (ad) {
  117.         if (ad->_resizeProcId==*id)
  118.             ad->_resizeProcId=0;
  119.         ad->scrollResizeProc();
  120.     }
  121. }
  122.  
  123.  
  124. void XFE_AttachPanel::PopupProc(Widget,XtPointer cd,XEvent *event,Boolean*)
  125. {
  126.     XFE_AttachPanel *ad=(XFE_AttachPanel*)cd;
  127.  
  128.     if (!ad->_popupMenu) return;
  129.     if (event->xany.type!=ButtonPress) return;
  130.     if (((XButtonEvent *)event)->button != Button3) return;
  131.     if (((XButtonEvent *)event)->time <= ad->_popupLastEventTime) return;
  132.     XmMenuPosition(ad->_popupMenu,(XButtonEvent*)event);
  133.     XtManageChild (ad->_popupMenu);
  134. }
  135.  
  136. /* By default, cancelling a popup menu with Button 3 will cause the
  137.  * popup to be reposted at the location of the cancelling click.
  138.  *
  139.  * To switch off this behavior, remember when the menu was popped down.
  140.  * In PopupHandler, don't repost the menu if the posting click just
  141.  * cancelled a popup menu.
  142.  */
  143. void XFE_AttachPanel::PopdownCb(Widget w,XtPointer cd,XtPointer)
  144. {
  145.     XFE_AttachPanel *ad=(XFE_AttachPanel*)cd;
  146.  
  147.     ad->_popupLastEventTime = XtLastTimestampProcessed (XtDisplay(w));
  148. }
  149.  
  150.  
  151.  
  152. // constructor
  153.  
  154. XFE_AttachPanel::XFE_AttachPanel(MWContext* context) : XFE_Component()
  155. {
  156.     _context=context;
  157.     
  158.     _inDestructor=FALSE;
  159.     _parent=NULL;
  160.     _top=NULL;
  161.     _topClip=NULL;
  162.     _topScrollBar=NULL;
  163.     _pane=NULL;
  164.     _resizeProcId=0;
  165.     _traversalProcId=0;
  166.     _firstExpose=TRUE;
  167.     _popupParent=NULL;
  168.     _popupMenu=NULL;
  169.     _popupLastEventTime=0;
  170.     _armedWidget=NULL;
  171.     _traversalWidget=NULL;
  172.     _multiClickEnabled=FALSE;
  173.     _iconTranslations=NULL;
  174.     _prefHeight=0;
  175.     
  176.     _items=NULL;
  177.     _numItems=0;
  178.     _allocItems=0;
  179.     _currentSelection=NULL;    
  180. }
  181.  
  182. XFE_AttachPanel::~XFE_AttachPanel()
  183. {
  184.     _inDestructor=TRUE;
  185.  
  186.     if (_resizeProcId!=0)
  187.         XtRemoveTimeOut(_resizeProcId);
  188.  
  189.     if (_traversalProcId!=0)
  190.         XtRemoveTimeOut(_traversalProcId);
  191.  
  192.     // XFE_Component will destroy the widget tree
  193. }
  194.  
  195. void XFE_AttachPanel::createWidgets(Widget parent)
  196. {
  197.     _parent=parent;
  198.     
  199.     Arg args[10];
  200.     
  201.     // toplevel scrolled window
  202.     
  203.     XtSetArg(args[0],XmNscrollingPolicy, XmAUTOMATIC);
  204.     _top=XmCreateScrolledWindow(_parent,"attach",args,1);
  205.     XtAddCallback(_top,XmNtraverseObscuredCallback,ScrollTraversalCb,(XtPointer)this);
  206.     //XtManageChild(_top);
  207.     Widget vsb;
  208.     XtVaGetValues(_top,XmNverticalScrollBar,&vsb,NULL);
  209.     if (vsb) XtVaSetValues(vsb,XmNtraversalOn,False,NULL);
  210.  
  211.     // attachment pane
  212.     _pane=XmCreateDrawingArea(_top,"pane",NULL,0);
  213.     XtVaSetValues(_pane,
  214.                   XmNmarginWidth,0,
  215.                   XmNmarginHeight,0,
  216.                   NULL);
  217.     // nyi - best way to avoid multiple redraw?
  218.     XtAddCallback(_pane,XmNexposeCallback,ExposeCb,(XtPointer)this);
  219.     XtAddCallback(_pane,XmNinputCallback,InputCb,(XtPointer)this);
  220.     XtManageChild(_pane);
  221.  
  222.     // only want vertical scrolling - make form track width of outer window
  223.     Widget hsb;
  224.     XtVaGetValues(_top,
  225.                   XmNclipWindow,&_topClip,
  226.                   XmNverticalScrollBar,&_topScrollBar,
  227.                   XmNhorizontalScrollBar,&hsb,
  228.                   NULL);
  229.     if (XmIsDrawingArea(_topClip)) { // it better be!
  230.         XtAddCallback(_topClip,XmNresizeCallback,ScrollResizeCb,(XtPointer)this);
  231.         XtAddCallback(_topClip,XmNinputCallback,InputCb,(XtPointer)this);
  232.     }
  233.     
  234.     // hide horizontal scrollbar - prevent it flickering on during resize
  235.     if (hsb) {
  236.         XtVaSetValues(hsb,
  237.                       XmNheight,1,
  238.                       XmNmappedWhenManaged,FALSE,
  239.                       NULL);
  240.     }
  241.     
  242.     // make scroller background match child
  243.     Pixel bg;
  244.     XtVaGetValues(_pane,XmNbackground,&bg,NULL);
  245.     XtVaSetValues(_topClip,XmNbackground,bg,NULL);
  246.     
  247.     _popupParent=_top;
  248.  
  249.     setBaseWidget(_top);
  250. }
  251.  
  252. void XFE_AttachPanel::initializePopupMenu()
  253. {
  254.     if (!_popupMenu || !_popupParent)
  255.         return;
  256.  
  257.     XtAddEventHandler (_popupParent,ButtonPressMask,False,PopupProc,this);
  258.     //XtAddCallback(XtParent(_popupMenu),XmNpopdownCallback,PopdownCb,this);
  259.  
  260.     updateSelectionUI();
  261. }
  262.  
  263. //
  264. // item list management
  265. //
  266.  
  267. void XFE_AttachPanel::addItem(const char *itemData,const char *itemLabel,const char *itemType,int pos)
  268. {
  269.     if (!itemData || strlen(itemData)==0)
  270.         return;
  271.  
  272.     XFE_AttachPanelItem *ai=new XFE_AttachPanelItem(this,itemData,itemLabel,itemType);
  273.     addItem(ai,pos);
  274. }
  275.  
  276.  
  277. void XFE_AttachPanel::addItem(XFE_AttachPanelItem *newItem,int pos) 
  278. {
  279.     int i;
  280.  
  281.     if (pos==-1)
  282.         pos=_numItems;
  283.  
  284.     if (pos<0 || pos>_numItems)
  285.         return;
  286.  
  287.     // save pointer to passed item.
  288.     // adopt it, and delete in removeItem(), destructor
  289.     if (_numItems >= _allocItems) {
  290.         _allocItems+=_allocIncrement;
  291.         XFE_AttachPanelItem **newList=new XFE_AttachPanelItem*[_allocItems];
  292.         for (i=0;i<_numItems;i++) {
  293.             newList[i]=_items[i];
  294.             _items[i]=NULL;
  295.         }
  296.         if (_items)
  297.             delete _items;
  298.         _items=newList;
  299.     }
  300.  
  301.     for (i=_numItems;i>pos;i--) {
  302.         _items[i]=_items[i-1];
  303.     }
  304.     _items[pos]=newItem;
  305.     _numItems++;
  306. }
  307.  
  308. int XFE_AttachPanel::findItem(XFE_AttachPanelItem *ai)
  309. {
  310.     if (ai==NULL)
  311.         return -1;
  312.     
  313.     for (int i=0;i<_numItems;i++) {
  314.         if (_items[i]==ai) {
  315.             return i;
  316.         }
  317.     }
  318.     return -1;
  319. }
  320.  
  321.  
  322. void XFE_AttachPanel::removeItem(XFE_AttachPanelItem *item)
  323. {
  324.     int pos=findItem(item);
  325.  
  326.     if (pos>=0)
  327.         removeItem(pos);
  328. }
  329.  
  330.  
  331. void XFE_AttachPanel::removeItem(int r)
  332. {
  333.     // Warning: never decreases list alloc'd size
  334.     // if XFE_AttachPanel object is long-lived, then it should.
  335.     // use removeAllItems() method to force a cleanup.
  336.  
  337.     if (r<0 || r>=_numItems)
  338.         return;
  339.  
  340.     if (_items[r]) {
  341.         // if focus is on last item, move it to last-but-one item
  342.         // to ensure that attach panel keeps focus after delete.
  343.         if (r==_numItems-1 && r>0) {
  344.             Widget focusWidget=XmGetFocusWidget(getBaseWidget());
  345.             Widget lastButOne=NULL;
  346.             if (_items[r-1])
  347.                 lastButOne=_items[r-1]->image();
  348.             if (focusWidget &&
  349.                 focusWidget==_items[r]->image() &&
  350.                 lastButOne!=NULL) {    
  351.                 XmProcessTraversal(lastButOne,XmTRAVERSE_CURRENT);
  352.             }    
  353.         }
  354.             
  355.         delete _items[r];
  356.     }
  357.     
  358.     for (int i=r; i<_numItems-1;i++)
  359.         _items[i]=_items[i+1];
  360.     _items[_numItems-1]=NULL;
  361.     _numItems--;
  362. }
  363.  
  364.  
  365. void XFE_AttachPanel::removeAllItems()
  366. {
  367.     if (_items) {
  368.         for (int i=0; i<_numItems;i++) {
  369.             if (_items[i]) {
  370.                 if (_inDestructor)
  371.                     _items[i]->inParentDestructor();
  372.                 delete _items[i];
  373.                 _items[i]=NULL;
  374.             }
  375.         }
  376.         delete _items;
  377.         _items=NULL;
  378.         _numItems=0;
  379.         _allocItems=0;
  380.     }
  381. }
  382.  
  383.  
  384. XFE_AttachPanelItem **XFE_AttachPanel::items()
  385. {
  386.     return _items;
  387. }
  388.  
  389.  
  390. int XFE_AttachPanel::numItems()
  391. {
  392.     return _numItems;
  393. }
  394.  
  395. void XFE_AttachPanel::selectItem(XFE_AttachPanelItem* item)
  396. {
  397.     if (!item || item->beingDestroyed())
  398.         return;
  399.  
  400.     if (_currentSelection && item!=_currentSelection)
  401.         _currentSelection->select(FALSE);
  402.     
  403.     item->select(TRUE);
  404.     _currentSelection=item;
  405.  
  406.     updateSelectionUI();
  407. }
  408.  
  409. void XFE_AttachPanel::deselectItem(XFE_AttachPanelItem* item)
  410. {
  411.     if (!item || item!=_currentSelection)
  412.         return;
  413.  
  414.     if (!item->beingDestroyed())
  415.         item->select(FALSE);
  416.     
  417.     _currentSelection=NULL;
  418.  
  419.     updateSelectionUI();
  420. }
  421.  
  422. //
  423. // display management
  424. //
  425.  
  426. void XFE_AttachPanel::updateDisplay()
  427. {
  428.         layout();
  429. }
  430.  
  431. void XFE_AttachPanel::traverseItem(int pos)
  432. {
  433.     if (pos<0 || pos >=_numItems)
  434.         return;
  435.  
  436.     _items[pos]->traverse();
  437. }
  438.  
  439. void XFE_AttachPanel::scrollToItem(int pos)
  440. {
  441.     if (pos<0 || pos >=_numItems)
  442.         return;
  443.  
  444.     _items[pos]->scrollVisible();
  445. }
  446.  
  447. int XFE_AttachPanel::getPreferredHeight()
  448. {
  449.     if (_prefHeight>0)
  450.         return _prefHeight;
  451.     else
  452.         return 20;
  453. }
  454.  
  455. //#define DEBUG_LAYOUT
  456. void XFE_AttachPanel::layout()
  457. {
  458. #if 0
  459.     if (_firstExpose)
  460.         return;
  461. #endif
  462.     
  463.     if (_items==NULL || _numItems==0)
  464.         return;
  465.  
  466.     // examine items
  467.     int i;
  468.     int maxWidth=0;
  469.     int maxHeight=0;
  470.     for (i=0;i<_numItems;i++) {        
  471.         if (_items[i] && !_items[i]->beingDestroyed()) {
  472.             _items[i]->calculatePrefGeometry();
  473.             if (_items[i]->prefWidth()>maxWidth) maxWidth=_items[i]->prefWidth();
  474.             if (_items[i]->prefHeight()>maxHeight) maxHeight=_items[i]->prefHeight();
  475.             
  476.         }
  477.     }
  478.  
  479.     if (maxWidth==0 || maxHeight==0) 
  480.         return;
  481.  
  482.     // calculate basic parameters
  483.     Position wd;
  484.     XtVaGetValues(_topClip,XmNwidth,&wd,NULL);
  485.     unsigned int availWidth=(unsigned int)(wd>2*_marginWidth ?
  486.                                            (wd-2*_marginWidth) : 1);
  487.     unsigned int vSpacing=(_items[0] ? _items[0]->labelHeight()/2 : 10);
  488.     //nyi - need a good way to calc cell width - deal with large, rare cases
  489.     unsigned int cellWidth=maxWidth+_horizSpacing;
  490.     if (cellWidth<1) cellWidth=1;
  491.     unsigned int cellHeight=maxHeight;
  492.     int cellsPerRow=availWidth/cellWidth;
  493.     if (cellsPerRow<1) cellsPerRow=1;
  494.     
  495.     // lay out row by row.
  496.     // give each item enough cells to prevent overlap
  497. #ifdef DEBUG_LAYOUT
  498.     printf("\nlayout()\n\n");
  499.     printf("  availWidth=%d\n",availWidth);
  500.     printf("  cellWidth=%d\n",cellWidth);
  501.     printf("  cellHeight=%d\n",cellHeight);
  502.     printf("  cellsPerRow=%d\n",cellsPerRow);
  503.     printf("\n");
  504. #endif
  505.     
  506.     int itemPos=0;
  507.     int cellNum=0;
  508.     int row=0;
  509.  
  510.     // if an icon has focus, restore it when we remap the pane
  511.     Widget focusWidget=XmGetFocusWidget(_top);
  512.     if (focusWidget && XtParent(focusWidget)!=_pane)
  513.         focusWidget=NULL;
  514.     
  515.     unmapPane();
  516.  
  517.     // calculate ideal height for this layout + border
  518.     Dimension st;
  519.     XtVaGetValues(_top,XmNshadowThickness,&st,NULL);
  520.     _prefHeight=2*st + 2*_marginHeight + cellHeight + vSpacing;
  521.  
  522.     // do layout
  523.     while (itemPos<_numItems) {
  524. #ifdef DEBUG_LAYOUT        
  525.         printf("[%d]\n",itemPos);
  526. #endif        
  527.         if (_items[itemPos] && !_items[itemPos]->beingDestroyed()) {
  528.             int numCells=_items[itemPos]->prefWidth()/cellWidth + 1;
  529.             if (numCells>cellsPerRow) numCells=cellsPerRow;
  530. #ifdef DEBUG_LAYOUT            
  531.             printf("  numCells=%d\n",numCells);
  532. #endif            
  533.             // see if it makes sense to move to a new row
  534.             if (numCells>cellsPerRow-cellNum && cellNum!=0) {
  535.                 row++;
  536.                 _prefHeight+=cellHeight+vSpacing;
  537.                 cellNum=0;
  538.             }
  539. #ifdef DEBUG_LAYOUT
  540.             printf("  row:%d-%d    (%d,%d)  %dx%d\n",
  541.                    row,cellNum,
  542.                    cellNum*cellWidth,row*cellHeight,
  543.                    numCells*cellWidth,cellHeight);
  544. #endif                   
  545.             
  546.             //do layout
  547.             _items[itemPos]->layout(_marginWidth+cellNum*cellWidth,
  548.                                     _marginHeight+row*(cellHeight+vSpacing),
  549.                                     numCells*cellWidth,
  550.                                     cellHeight);
  551.             cellNum+=numCells;
  552.         }
  553.         itemPos++;
  554.     }
  555.  
  556.     mapPane();
  557.     
  558.     // restore focus if we had it before the unmapPane()
  559.     if (focusWidget)
  560.         XmProcessTraversal(focusWidget,XmTRAVERSE_CURRENT);
  561. }
  562.  
  563. void XFE_AttachPanel::mapPane() 
  564. {
  565.     if (_pane)
  566.         XtSetMappedWhenManaged(_pane,TRUE);
  567.     if (_topScrollBar)
  568.         XtSetMappedWhenManaged(_topScrollBar,TRUE);
  569. }
  570.  
  571. void XFE_AttachPanel::unmapPane() 
  572. {
  573.     if (_pane)
  574.         XtSetMappedWhenManaged(_pane,FALSE);
  575.     if (_topScrollBar)
  576.         XtSetMappedWhenManaged(_topScrollBar,FALSE);
  577. }
  578.  
  579. int XFE_AttachPanel::isAttachPanelWidget(Widget w)
  580. {
  581.     if (w==NULL)
  582.         return FALSE;
  583.     
  584.     if (w==_top ||
  585.         w==_topClip ||
  586.         w==_topScrollBar ||
  587.         w==_pane ||
  588.         XtParent(w)==_pane)
  589.         return TRUE;
  590.     
  591.     return FALSE;
  592. }
  593.  
  594.  
  595. //
  596. // callback methods
  597. //
  598.  
  599. void XFE_AttachPanel::realize()
  600. {
  601.     // realize processing for existing items
  602.     if (_items) {
  603.         for (int i=0;i<_numItems;i++)
  604.             _items[i]->realize();
  605.         layout();
  606.     }
  607. }
  608.  
  609. void XFE_AttachPanel::doubleClickCb(int)
  610. {
  611.     // no default double-click action - it's for the derived classes.
  612. }
  613.  
  614. void XFE_AttachPanel::inputCb(XEvent *event)
  615. {
  616.     // button up on panel background cancels selection
  617.     if (_currentSelection!=NULL &&
  618.         event &&
  619.         event->xany.type==ButtonRelease &&
  620.         event->xbutton.button==1) {
  621.         deselectItem(_currentSelection);
  622.     }
  623. }
  624.  
  625.  
  626. void XFE_AttachPanel::scrollTraversalProc(Widget dest)
  627. {
  628.     // let disarmCb handle scrolling for button click
  629.     if (!dest || dest==_armedWidget)
  630.         return;
  631.  
  632.     char *destName=XtName(dest);    
  633.     if (!destName || strcmp(destName,ATTACH_ICON_NAME)!=0)
  634.         return;
  635.  
  636.     // if this is the icon of XFE_AttachItem, scroll it into view.
  637.     XtPointer userData;
  638.     XtVaGetValues(dest,XmNuserData,&userData,NULL);
  639.     if (!userData)
  640.         return;
  641.  
  642.     XFE_AttachPanelItem *ai=(XFE_AttachPanelItem*)userData;
  643.  
  644.     ai->scrollVisible();
  645. }
  646.  
  647. void XFE_AttachPanelItem::scrollVisible() 
  648. {
  649.     // scroll to icon, with enough margin to show label
  650.  
  651.     XmScrollVisible(_attachPanel->getBaseWidget(),_image,0,labelHeight()+2);
  652.     XmScrollVisible(_attachPanel->getBaseWidget(),_label,0,0);
  653. }
  654.  
  655. void XFE_AttachPanel::scrollResizeProc()
  656. {
  657.     const int rspace=10; // must be >= 2, or window will oscillate
  658.     Dimension width;
  659.  
  660.     if (!_topClip || !_pane)
  661.     return;
  662.  
  663.     XtVaGetValues(_topClip,XmNwidth,&width,NULL);
  664.     if (width<rspace+1)
  665.         width=rspace+1;
  666.     XtVaSetValues(_pane,XmNwidth,width-rspace,NULL);
  667.  
  668.     layout();
  669. }
  670.  
  671. //
  672. // XFE_AttachPanelItem
  673. //
  674.  
  675. // static initialization
  676.  
  677. Pixmap XFE_AttachPanelItem::_audioPixmap=XmUNSPECIFIED_PIXMAP;
  678. Pixmap XFE_AttachPanelItem::_binaryPixmap=XmUNSPECIFIED_PIXMAP;
  679. Pixmap XFE_AttachPanelItem::_imagePixmap=XmUNSPECIFIED_PIXMAP;
  680. Pixmap XFE_AttachPanelItem::_messagePixmap=XmUNSPECIFIED_PIXMAP;
  681. Pixmap XFE_AttachPanelItem::_moviePixmap=XmUNSPECIFIED_PIXMAP;
  682. Pixmap XFE_AttachPanelItem::_textPixmap=XmUNSPECIFIED_PIXMAP;
  683. Pixmap XFE_AttachPanelItem::_unknownPixmap=XmUNSPECIFIED_PIXMAP;
  684. Pixmap XFE_AttachPanelItem::_vcardPixmap=XmUNSPECIFIED_PIXMAP;
  685. Pixmap XFE_AttachPanelItem::_urlPixmap=XmUNSPECIFIED_PIXMAP;
  686. Cursor XFE_AttachPanelItem::_cursor=None;
  687.  
  688.  
  689. // callback stubs
  690.  
  691. void XFE_AttachPanelItem::ActivateCb(Widget,XtPointer cd,XtPointer cb) {
  692.     XFE_AttachPanelItem *ad=(XFE_AttachPanelItem*)cd;
  693.     XmPushButtonCallbackStruct *pcb=(XmPushButtonCallbackStruct*)cb;
  694.     
  695.     if (ad && pcb) {
  696.         if (ad->_attachPanel->multiClickEnabled()) {
  697.             switch(pcb->click_count) {
  698.             case 1: ad->activateCb(); break;
  699.             case 2: ad->doubleClickCb(); break;
  700.             default: break;
  701.             }
  702.         }
  703.         else {
  704.             ad->activateCb();
  705.         }
  706.     }
  707. }
  708.  
  709. void XFE_AttachPanelItem::ArmCb(Widget w,XtPointer cd,XtPointer) {
  710.     XFE_AttachPanelItem *ad=(XFE_AttachPanelItem*)cd;
  711.     if (ad)
  712.         ad->armCb(w);
  713. }
  714.  
  715. void XFE_AttachPanelItem::DisarmCb(Widget,XtPointer cd,XtPointer) {
  716.     XFE_AttachPanelItem *ad=(XFE_AttachPanelItem*)cd;
  717.     if (ad)
  718.         ad->disarmCb();
  719. }
  720.  
  721.  
  722. // constructor
  723.  
  724. XFE_AttachPanelItem::XFE_AttachPanelItem(XFE_AttachPanel *attach,
  725.                                          const char *data,
  726.                                          const char *dataLabel,
  727.                                          const char *dataType)
  728. {
  729.     _attachPanel=attach;
  730.     
  731.     _inParentDestructor=FALSE;
  732.     _beingDestroyed=FALSE;
  733.     
  734.     if (data)
  735.         _data=strdup(data);
  736.     else
  737.         _data=NULL;
  738.  
  739.     if (dataLabel)
  740.         _dataLabel=strdup(dataLabel);
  741.     else
  742.         _dataLabel=(_data ? strdup(_data) : (char*)NULL);
  743.  
  744.     if (dataType) {
  745.         // app specified a mime type for this data
  746.         _dataType=strdup(dataType);
  747.     }
  748.     else {
  749.         // try to determine type of attachment
  750.         _dataType=strdup(XFE_DragBase::guessUrlMimeType(data));
  751.     }
  752.  
  753.     _parent=_attachPanel->pane();
  754.  
  755.     _image=NULL;
  756.     _label=NULL;
  757.     _prefWidth=1;
  758.     _prefHeight=1;
  759.  
  760.     // use link cursor over attach icons to indicate that they are active
  761.     if (_cursor==None)
  762.         _cursor=CONTEXT_DATA(_attachPanel->context())->link_cursor;
  763.     
  764.     // create widgets
  765.     
  766.     Arg args[10];
  767.  
  768.     Pixel bg;
  769.     XtVaGetValues(_parent,XmNbackground,&bg,NULL);
  770.  
  771.     // image
  772.     
  773.     XtSetArg(args[0],XmNbackground,bg);
  774.     _image=XmCreatePushButton(_parent,ATTACH_ICON_NAME,args,1);
  775.     XtVaSetValues(_image,
  776.                   XmNalignment,XmALIGNMENT_CENTER,
  777.                   XmNshadowThickness,0,
  778.                   XmNmarginWidth,0,
  779.                   XmNmarginHeight,0,
  780.                   XmNlabelType,XmPIXMAP,
  781.                   XmNlabelPixmap,iconPixmap(),
  782.                   XmNarmColor,bg,
  783.                   XmNmultiClick,XmMULTICLICK_KEEP,
  784.                   XmNuserData,(XtPointer)this, // scrollTraversalProc needs to get to item class
  785.                   NULL);
  786.     XtAddCallback(_image,XmNactivateCallback,ActivateCb,(XtPointer)this);
  787.     XtAddCallback(_image,XmNarmCallback,ArmCb,(XtPointer)this);
  788.     XtAddCallback(_image,XmNdisarmCallback,DisarmCb,(XtPointer)this);
  789.     if (_attachPanel->iconTranslations())
  790.         XtOverrideTranslations(_image,_attachPanel->iconTranslations());
  791.  
  792.     // label
  793.     
  794.     XtSetArg(args[0],XmNbackground,bg);
  795.     _label=XmCreateLabel(_parent,ATTACH_LABEL_NAME,args,1);
  796.     XmString xms=XmStringCreateLocalized(_dataLabel ? _dataLabel : "");
  797.     XtVaSetValues(_label,
  798.                   XmNalignment,XmALIGNMENT_CENTER,
  799.                   XmNshadowThickness,0,
  800.                   //XmNmarginWidth,0,
  801.                   //XmNmarginHeight,0,
  802.                   XmNlabelString,xms,
  803.                   XmNuserData,FALSE, // selection marker for AttachPanelItem::select()
  804.                   NULL);
  805.     XmStringFree(xms);
  806.  
  807.     show();
  808.     calculatePrefGeometry();
  809.     if (XtWindow(_image)) {
  810.         XDefineCursor(XtDisplay(_image),XtWindow(_image),_cursor);
  811.     }
  812. }
  813.  
  814. // destructor
  815.  
  816. XFE_AttachPanelItem::~XFE_AttachPanelItem()
  817. {
  818.     _beingDestroyed=TRUE;
  819.  
  820.     if (this==_attachPanel->currentSelection())
  821.         _attachPanel->deselectItem(this);
  822.     
  823.     if (_data)
  824.         free((void*)_data);
  825.  
  826.     if (_dataLabel)
  827.         free((void*)_dataLabel);
  828.  
  829.     if (_dataType)
  830.         free((void*)_dataType);
  831.  
  832.     if (_image) {
  833.         XtUnmanageChild(_image);
  834.         if (!_inParentDestructor)
  835.             XtDestroyWidget(_image);
  836.     }
  837.     
  838.     if (_label) {
  839.         XtUnmanageChild(_label);
  840.         if (!_inParentDestructor)
  841.             XtDestroyWidget(_label);
  842.     }
  843. }
  844.  
  845. void XFE_AttachPanelItem::inParentDestructor()
  846. {
  847.     _inParentDestructor=TRUE;
  848. }
  849.  
  850. void XFE_AttachPanelItem::realize()
  851. {
  852.     if (_image) {
  853.         XtVaSetValues(_image,XmNlabelPixmap,iconPixmap(),NULL);
  854.         calculatePrefGeometry();
  855.         XDefineCursor(XtDisplay(_image),XtWindow(_image),_cursor);
  856.     }
  857.     
  858.     if (this==_attachPanel->currentSelection())
  859.         select(TRUE);
  860.     else
  861.         select(FALSE);
  862. }
  863.  
  864.  
  865. // this method should only be called when creating item.
  866.  
  867. Pixmap XFE_AttachPanelItem::iconPixmap()
  868. {
  869.     if (!XtIsRealized(_parent))
  870.         return XmUNSPECIFIED_PIXMAP;
  871.  
  872.     XDEBUG(printf("iconPixmap(%s)\n",_dataType?_dataType:"NULL"));
  873.     
  874.     // return pixmap matching item's mime type
  875.     // create Pixmaps on first request
  876.  
  877.     MWContext *context=_attachPanel->context();
  878.     
  879.     Pixel bg;
  880.     XtVaGetValues(_parent,XmNbackground,&bg,NULL);
  881.     
  882.     if (!_dataType ||
  883.         strcmp(_dataType,UNKNOWN_CONTENT_TYPE)==0) {
  884.         if (_unknownPixmap==XmUNSPECIFIED_PIXMAP) {
  885.             fe_icon icon={ 0 };
  886.             fe_MakeIcon(context,bg, &icon, NULL,
  887.                         GUnknown.width, GUnknown.height,
  888.                         GUnknown.mono_bits, GUnknown.color_bits, GUnknown.mask_bits,
  889.                         FALSE);
  890.             _unknownPixmap=icon.pixmap;
  891.         }
  892.         return _unknownPixmap;
  893.     }
  894.  
  895.     // internal type name - used for attached document URL's
  896.     if (strncmp(_dataType,"_url",5)==0){
  897.         if (_urlPixmap==XmUNSPECIFIED_PIXMAP) {
  898.             fe_icon icon={ 0 };
  899.             fe_MakeIcon(context,bg, &icon, NULL,
  900.                         LocationProxy.width, LocationProxy.height,
  901.                         LocationProxy.mono_bits, LocationProxy.color_bits, LocationProxy.mask_bits,
  902.                         FALSE);
  903.             _urlPixmap=icon.pixmap;
  904.         }
  905.         return _urlPixmap;
  906.     }
  907.     
  908.     if (strncmp(_dataType,"audio",5)==0){
  909.         if (_audioPixmap==XmUNSPECIFIED_PIXMAP) {
  910.             fe_icon icon={ 0 };
  911.             fe_MakeIcon(context,bg, &icon, NULL,
  912.                         GAudio.width, GAudio.height,
  913.                         GAudio.mono_bits, GAudio.color_bits, GAudio.mask_bits,
  914.                         FALSE);
  915.             _audioPixmap=icon.pixmap;
  916.         }
  917.         return _audioPixmap;
  918.     }
  919.     
  920.     if (strncmp(_dataType,"application",11)==0) {
  921.         if (_binaryPixmap==XmUNSPECIFIED_PIXMAP) {
  922.             fe_icon icon={ 0 };
  923.             fe_MakeIcon(context,bg, &icon, NULL,
  924.                         GBinary.width, GBinary.height,
  925.                         GBinary.mono_bits, GBinary.color_bits, GBinary.mask_bits,
  926.                         FALSE);
  927.             _binaryPixmap=icon.pixmap;
  928.         }
  929.         return _binaryPixmap;
  930.     }
  931.     
  932.     if (strncmp(_dataType,"image",5)==0) {
  933.         if (_imagePixmap==XmUNSPECIFIED_PIXMAP) {
  934.             fe_icon icon={ 0 };
  935.             fe_MakeIcon(context,bg, &icon, NULL,
  936.                         GImage.width, GImage.height,
  937.                         GImage.mono_bits, GImage.color_bits, GImage.mask_bits,
  938.                         FALSE);
  939.             _imagePixmap=icon.pixmap;
  940.         }
  941.         return _imagePixmap;
  942.     }
  943.     
  944.     if (strncmp(_dataType,"message",7)==0) {
  945.         if (_messagePixmap==XmUNSPECIFIED_PIXMAP) {
  946.             fe_icon icon={ 0 };
  947.             fe_MakeIcon(context,bg, &icon, NULL,
  948.                         MNTB_Forward.width, MNTB_Forward.height,
  949.                         MNTB_Forward.mono_bits, MNTB_Forward.color_bits, MNTB_Forward.mask_bits,
  950.                         FALSE);
  951.             _messagePixmap=icon.pixmap;
  952.         }        
  953.         return _messagePixmap;
  954.     }
  955.     
  956.     if (strncmp(_dataType,"video",5)==0) {
  957.         if (_moviePixmap==XmUNSPECIFIED_PIXMAP) {
  958.             fe_icon icon={ 0 };
  959.             fe_MakeIcon(context,bg, &icon, NULL,
  960.                         GMovie.width, GMovie.height,
  961.                         GMovie.mono_bits, GMovie.color_bits, GMovie.mask_bits,
  962.                         FALSE);
  963.             _moviePixmap=icon.pixmap;
  964.         }        
  965.         return _moviePixmap;
  966.     }
  967.     
  968.     if (strcmp(_dataType,"text/x-vcard")==0) {
  969.         if (_vcardPixmap==XmUNSPECIFIED_PIXMAP) {
  970.             fe_icon icon={ 0 };
  971.             fe_MakeIcon(context,bg, &icon, NULL,
  972.                         MNAB_NewPerson.width, MNAB_NewPerson.height,
  973.                         MNAB_NewPerson.mono_bits, MNAB_NewPerson.color_bits, MNAB_NewPerson.mask_bits,
  974.                         FALSE);
  975.             _vcardPixmap=icon.pixmap;
  976.         }
  977.         return _vcardPixmap;
  978.     }
  979.  
  980.     if (strncmp(_dataType,"text",4)==0) {
  981.         if (_textPixmap==XmUNSPECIFIED_PIXMAP) {
  982.             fe_icon icon={ 0 };
  983.             fe_MakeIcon(context,bg, &icon, NULL,
  984.                         GText.width, GText.height,
  985.                         GText.mono_bits, GText.color_bits, GText.mask_bits,
  986.                         FALSE);
  987.             _textPixmap=icon.pixmap;
  988.         }
  989.         return _textPixmap;
  990.     }
  991.  
  992.     // fallback to generic document pixmap
  993.     
  994.     if (_unknownPixmap==XmUNSPECIFIED_PIXMAP) {
  995.         fe_icon icon={ 0 };
  996.         fe_MakeIcon(context,bg, &icon, NULL,
  997.                     GUnknown.width, GUnknown.height,
  998.                     GUnknown.mono_bits, GUnknown.color_bits, GUnknown.mask_bits,
  999.                     FALSE);
  1000.         _unknownPixmap=icon.pixmap;
  1001.     }
  1002.     return _unknownPixmap;
  1003. }
  1004.  
  1005.  
  1006. const char *XFE_AttachPanelItem::data() 
  1007. {
  1008.     return _data;
  1009. }
  1010.  
  1011. const char *XFE_AttachPanelItem::dataLabel() 
  1012. {
  1013.     return _dataLabel;
  1014. }
  1015.  
  1016. const char *XFE_AttachPanelItem::dataType() 
  1017. {
  1018.     return _dataType;
  1019. }
  1020.  
  1021. void XFE_AttachPanelItem::select(int selected)
  1022. {
  1023.     // Hack to do selection highlight.
  1024.     // May need to do real X drawing in AttachPanel[Item]::exposeCb()
  1025.  
  1026.     if (_label) {
  1027.         Pixel fg;
  1028.         Pixel bg;
  1029.         XtPointer userData;
  1030.         
  1031.         XtVaGetValues(_label,
  1032.                       XmNbackground,&bg,
  1033.                       XmNforeground,&fg,
  1034.                       XmNuserData,&userData,
  1035.                       NULL);
  1036.  
  1037.         int labelHighlighted=(int)userData;
  1038.         
  1039.         if (selected && !labelHighlighted) {
  1040.             XtVaSetValues(_label,
  1041.                           XmNbackground,fg,
  1042.                           XmNforeground,bg,
  1043.                           XmNuserData,1,
  1044.                           NULL);
  1045.         }
  1046.  
  1047.         if (!selected && labelHighlighted) {
  1048.             XtVaSetValues(_label,
  1049.                           XmNbackground,fg,
  1050.                           XmNforeground,bg,
  1051.                           XmNuserData,0,
  1052.                           NULL);
  1053.         }
  1054.     }
  1055. }
  1056.  
  1057. void XFE_AttachPanelItem::calculatePrefGeometry()
  1058. {
  1059.     Dimension iw,ih,lw,lh;
  1060.     
  1061.     XtVaGetValues(_image,
  1062.                   XmNwidth,&iw,
  1063.                   XmNheight,&ih,
  1064.                   NULL);
  1065.     XtVaGetValues(_label,
  1066.                   XmNwidth,&lw,
  1067.                   XmNheight,&lh,
  1068.                   NULL);
  1069.  
  1070.     _imageWidth=iw;
  1071.     _imageHeight=ih;
  1072.     _labelWidth=lw;
  1073.     _labelHeight=lh;
  1074.  
  1075.     // ? save image plus label geometry    
  1076.     _prefWidth=(unsigned int)(iw>lw ? iw : lw);
  1077.     _prefHeight=(unsigned int)(ih+lh);
  1078. }
  1079.  
  1080. void XFE_AttachPanelItem::layout(int x, int y, unsigned int width, unsigned int height) 
  1081. {
  1082.     if (_label==NULL || _image==NULL ||
  1083.         _prefWidth==0 || _prefHeight==0 ||
  1084.         width==0 || height==0 )
  1085.         return;
  1086.     
  1087.     int imageX;
  1088.     int imageY;
  1089.     int labelX;
  1090.     int labelY;    
  1091.  
  1092.     imageX=x+(width-_imageWidth)/2;
  1093.     imageY=y+height-_labelHeight-_imageHeight;
  1094.     labelX=x+(width-_labelWidth)/2;
  1095.     labelY=y+height-_labelHeight;
  1096.  
  1097.     XtVaSetValues(_image,
  1098.                   XmNx,imageX,
  1099.                   XmNy,imageY,
  1100.                   NULL);
  1101.     XtVaSetValues(_label,
  1102.                   XmNx,labelX,
  1103.                   XmNy,labelY,
  1104.                   NULL);
  1105. }
  1106.  
  1107.  
  1108. void XFE_AttachPanelItem::show() 
  1109. {
  1110.     if (_image)
  1111.         XtManageChild(_image);
  1112.     if (_label)
  1113.         XtManageChild(_label);
  1114. }
  1115.  
  1116. void XFE_AttachPanelItem::hide() 
  1117. {
  1118.     if (_image)
  1119.         XtUnmanageChild(_image);
  1120.     if (_label)
  1121.         XtUnmanageChild(_label);
  1122. }
  1123.  
  1124. void XFE_AttachPanelItem::traverse() 
  1125. {
  1126.     if (_image)
  1127.         XmProcessTraversal(_image,XmTRAVERSE_CURRENT);
  1128. }
  1129.  
  1130. //
  1131. // callback methods
  1132. //
  1133.  
  1134. void XFE_AttachPanelItem::activateCb()
  1135. {
  1136.     if (this!=_attachPanel->currentSelection())
  1137.         _attachPanel->selectItem(this);
  1138.     else
  1139.         _attachPanel->deselectItem(this);
  1140. }
  1141.  
  1142. void XFE_AttachPanelItem::doubleClickCb()
  1143. {
  1144.     _attachPanel->doubleClickCb(_attachPanel->findItem(this));
  1145. }
  1146.  
  1147. void XFE_AttachPanelItem::armCb(Widget w)
  1148. {
  1149.     // flag button as down, use to discard ScrollTraversalCb calls for button press
  1150.     // (since the event field in the traverseObscuredCallbackStruct is useless.)
  1151.  
  1152.     _attachPanel->armedWidget(w);
  1153. }
  1154.  
  1155. void XFE_AttachPanelItem::disarmCb()
  1156. {
  1157.     _attachPanel->armedWidget(NULL);
  1158.     
  1159.     // make sure label is visible after BtnUp on icon
  1160.     scrollVisible();
  1161. }
  1162.  
  1163.  
  1164.