3576 lines
83 KiB
C++
3576 lines
83 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
/*
|
|
Outliner.cpp -- Outliner class hack.
|
|
Created: Chris Toshok <toshok@netscape.com>, 7-Aug-96.
|
|
*/
|
|
|
|
|
|
|
|
#include "xpassert.h"
|
|
#include "xp_mem.h"
|
|
|
|
#include "Outliner.h"
|
|
#include "Outlinable.h"
|
|
|
|
#if defined(USE_MOTIF_DND)
|
|
#include "OutlinerDrop.h"
|
|
#endif /* USE_MOTIF_DND */
|
|
|
|
#include "xfe.h"
|
|
#include "icondata.h"
|
|
#include "prefapi.h"
|
|
#include "xp_qsort.h"
|
|
|
|
#include <Xfe/XfeP.h>
|
|
|
|
#if !defined(USE_MOTIF_DND)
|
|
#include <Xm/Label.h>
|
|
#endif /* USE_MOTIF_DND */
|
|
|
|
#ifdef DEBUG_toshok
|
|
#define D(x) x
|
|
#else
|
|
#define D(x)
|
|
#endif
|
|
|
|
extern "C" {
|
|
|
|
#include "XmL/GridP.h"
|
|
|
|
};
|
|
|
|
// please change this if you change any of the *parent.gif images.
|
|
#define PIPEIMAGESIZE 16
|
|
|
|
#if defined(USE_MOTIF_DND)
|
|
#undef NONMOTIF_DND
|
|
#else
|
|
#define NONMOTIF_DND
|
|
#endif /* USE_MOTIF_DND */
|
|
|
|
#ifdef FREEIF
|
|
#undef FREEIF
|
|
#endif
|
|
#define FREEIF(obj) do { if (obj) { XP_FREE (obj); obj = 0; }} while (0)
|
|
|
|
#define MIN_COLUMN_WIDTH 6 /* So that the user can manipulate it -- make it the same as the grid's minimum */
|
|
#define MAX_COLUMN_WIDTH 10000 /* So that we don't overflow any 16-bit
|
|
numbers. */
|
|
|
|
#ifdef DEBUG_toshok
|
|
#define D(x) x
|
|
#else
|
|
#define D(x)
|
|
#endif
|
|
|
|
#include "xpgetstr.h"
|
|
extern int XFE_SHOW_COLUMN;
|
|
extern int XFE_HIDE_COLUMN;
|
|
|
|
fe_icon XFE_Outliner::closedParentIcon = { 0 };
|
|
fe_icon XFE_Outliner::openParentIcon = { 0 };
|
|
fe_icon XFE_Outliner::showColumnIcon = { 0 };
|
|
fe_icon XFE_Outliner::showColumnInsensIcon = { 0 };
|
|
fe_icon XFE_Outliner::hideColumnIcon = { 0 };
|
|
fe_icon XFE_Outliner::hideColumnInsensIcon = { 0 };
|
|
|
|
#if !defined(USE_MOTIF_DND)
|
|
fe_dnd_Source _xfe_dragsource = {0};
|
|
#endif /* USE_MOTIF_DND */
|
|
|
|
//
|
|
#if defined(DEBUG_tao_)
|
|
#define XDBG(x) x
|
|
#else
|
|
#define XDBG(x)
|
|
#endif
|
|
|
|
#if defined(DEBUG_tao)
|
|
#define TRACE(str) printf("\n At %s: %d, %s", __FILE__, __LINE__, str)
|
|
#else
|
|
#define TRACE(str)
|
|
#endif
|
|
|
|
void
|
|
XFE_Outliner::tip_cb(Widget w, XtPointer clientData, XtPointer cb_data)
|
|
{
|
|
XFE_Outliner *obj = (XFE_Outliner *) clientData;
|
|
|
|
obj->tipCB(w, cb_data);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::tipCB(Widget /* w */, XtPointer cb_data)
|
|
{
|
|
XFE_TipStringCallbackStruct* cbs = (XFE_TipStringCallbackStruct*)cb_data;
|
|
char *strFromView = 0;
|
|
XP_FREEIF(m_doc_msg_buf);
|
|
XP_FREEIF(m_tip_msg_buf);
|
|
|
|
//
|
|
// cbs->x <= -10 is not really possible to happen!
|
|
//
|
|
if (cbs->x <= -10 ||
|
|
cbs->x >= m_totalLines ||
|
|
cbs->y < 0 ||
|
|
cbs->y >= m_numcolumns)
|
|
return;
|
|
|
|
int sourcecol = m_columnIndex[cbs->y];
|
|
if (cbs->reason == XFE_DOCSTRING) {
|
|
strFromView = m_outlinable->getCellDocString(cbs->x, sourcecol);
|
|
|
|
m_doc_msg_buf = XP_STRDUP(strFromView?strFromView:"");
|
|
*cbs->string = m_doc_msg_buf;
|
|
cbs->x = -10;
|
|
cbs->y = -10;
|
|
} else if (cbs->reason == XFE_TIPSTRING) {
|
|
if (cbs->y >= m_numvisible && m_hideColumnsAllowed) {
|
|
if (cbs->x != -1) {
|
|
m_tip_msg_buf = XP_STRDUP("");
|
|
*cbs->string = m_tip_msg_buf;
|
|
return;
|
|
}/* if */
|
|
|
|
if (cbs->y == m_numvisible) {
|
|
/* show header */
|
|
strFromView = XP_GetString(XFE_SHOW_COLUMN);
|
|
}/* if */
|
|
else {
|
|
/* hide header */
|
|
strFromView = XP_GetString(XFE_HIDE_COLUMN);
|
|
}/* else */
|
|
}/* if */
|
|
else
|
|
strFromView = m_outlinable->getCellTipString(cbs->x, sourcecol);
|
|
|
|
m_tip_msg_buf = XP_STRDUP(strFromView?strFromView:"");
|
|
*cbs->string = m_tip_msg_buf;
|
|
|
|
XRectangle rect;
|
|
if (XmLGridRowColumnToXY(m_widget,
|
|
(cbs->x < 0)?XmHEADING:XmCONTENT,
|
|
(cbs->x < 0)?0:cbs->x,
|
|
XmCONTENT,
|
|
cbs->y,
|
|
False,
|
|
&rect) < 0) {
|
|
cbs->x = -10;
|
|
cbs->y = -10;
|
|
}/* if */
|
|
else {
|
|
XDBG(printf("\n tipCB: (total,r,c,w,h)=(%d,%d,%d,%d,%d)\n",
|
|
m_totalLines, cbs->x, cbs->y, rect.width, rect.height));
|
|
cbs->x = rect.x + textStart(cbs->y, rect.width);
|
|
cbs->y = rect.y + rect.height;
|
|
}/* else */
|
|
}
|
|
}
|
|
|
|
int XFE_Outliner::textStart(int col, int maxX)
|
|
{
|
|
fe_icon *icon;
|
|
int sourcecol = m_columnIndex[col];
|
|
int startX = 0;
|
|
icon = m_outlinable->getColumnIcon(sourcecol);
|
|
|
|
if (sourcecol == m_pipeColumn) {
|
|
OutlinerAncestorInfo * ancestorInfo;
|
|
int depth;
|
|
|
|
m_outlinable->getTreeInfo(NULL, NULL, &depth, &ancestorInfo);
|
|
|
|
for (int i = 0; i < depth; i ++) {
|
|
if ( ( i + 1 ) * PIPEIMAGESIZE <= maxX )
|
|
if (ancestorInfo && ancestorInfo[ i ].has_next) {
|
|
// the the vertical pipe for this line's ancestors.
|
|
startX += PIPEIMAGESIZE;
|
|
}
|
|
else {
|
|
// don't know about this block.. must ask will.
|
|
startX += PIPEIMAGESIZE;
|
|
}
|
|
}/* for i */
|
|
|
|
if (ancestorInfo)
|
|
startX += PIPEIMAGESIZE;
|
|
}/* if pipeCol */
|
|
|
|
/* if there's an icon, we need to push the text over. */
|
|
if (icon) {
|
|
startX = startX + icon->width + 4;
|
|
}/* if */
|
|
return startX;
|
|
}
|
|
|
|
XP_Bool XFE_Outliner::isColTextFit(char *str, int row, int col)
|
|
{
|
|
XDBG(printf("\n XFE_Outliner::isColTextFit, row=%d, col=%d, str=%s\n",
|
|
row, col, str));
|
|
if (str && XP_STRLEN(str)) {
|
|
|
|
XRectangle rect;
|
|
if (XmLGridRowColumnToXY(m_widget,
|
|
(row < 0)?XmHEADING:XmCONTENT,
|
|
(row < 0)?0:row,
|
|
XmCONTENT,
|
|
col,
|
|
False,
|
|
&rect) < 0) {
|
|
return False;
|
|
}/* if */
|
|
else {
|
|
char *style =
|
|
(row < 0)?
|
|
(char*)styleToTag(m_outlinable->getColumnHeaderStyle(col)):
|
|
(char*)styleToTag(m_outlinable->getColumnStyle(col));
|
|
|
|
XmString xmstr = XmStringCreate(str, style);
|
|
XmFontList fontList;
|
|
XtVaGetValues(m_widget,
|
|
XmNfontList, &fontList,
|
|
NULL);
|
|
Dimension strWidth = XmStringWidth(fontList, xmstr);
|
|
XmStringFree(xmstr);
|
|
strWidth += textStart(col, rect.width);
|
|
XDBG(printf("\n (str, strWidth, rect.width, row, col)=(%s, %d, %d, %d, %d)\n",
|
|
str, strWidth, rect.width, row, col));
|
|
return (strWidth < (rect.width-1));
|
|
}/* else */
|
|
}/* if */
|
|
else
|
|
return True;
|
|
}/* XFE_Outliner::getStringWidth */
|
|
|
|
//
|
|
|
|
XFE_Outliner::XFE_Outliner(const char *name,
|
|
XFE_Outlinable *o,
|
|
XFE_Component *toplevel_component,
|
|
Widget widget_parent,
|
|
Boolean constantSize,
|
|
XP_Bool hasHeadings,
|
|
int num_columns,
|
|
int num_visible,
|
|
int *column_widths,
|
|
char *geom_prefname)
|
|
: XFE_Component(toplevel_component)
|
|
{
|
|
m_lastRow = -2;
|
|
m_lastCol = -2;
|
|
m_inGrid = False;
|
|
|
|
m_tip_msg_buf = 0;
|
|
m_doc_msg_buf = 0;
|
|
|
|
Widget outline;
|
|
Arg av[10];
|
|
int ac = 0;
|
|
short avgwidth, avgheight;
|
|
XmFontList fontList;
|
|
int i;
|
|
XP_Bool defaultGeometryOK = FALSE;
|
|
|
|
m_geominfo = NULL;
|
|
m_prefname = NULL;
|
|
|
|
if (geom_prefname)
|
|
{
|
|
m_prefname = XP_STRDUP(geom_prefname);
|
|
PREF_CopyCharPref(geom_prefname, &m_geominfo);
|
|
}
|
|
|
|
m_visibleLine = -1;
|
|
m_DataRow = -1;
|
|
m_draggc = NULL;
|
|
m_dragrow = -1;
|
|
|
|
#if !defined(USE_MOTIF_DND)
|
|
m_dragtype = FE_DND_NONE;
|
|
#else
|
|
m_dragTargetFunc = NULL;
|
|
m_dropTargetFunc = NULL;
|
|
m_dragIconDataFunc = NULL;
|
|
m_outlinerdrag = NULL;
|
|
#endif /* USE_MOTIF_DND */
|
|
|
|
m_activity = 0;
|
|
m_lastx = -999;
|
|
m_lasty = -999;
|
|
#if !defined(USE_MOTIF_DND)
|
|
m_lastmotionx = 0;
|
|
m_lastmotiony = 0;
|
|
#endif /* USE_MOTIF_DND */
|
|
m_totalLines = 0;
|
|
m_numcolumns = num_columns;
|
|
m_numvisible = num_visible;
|
|
m_pipeColumn = -1;
|
|
m_sortColumn = -1;
|
|
m_resizeColumn = -1;
|
|
m_visibleTimer = 0;
|
|
|
|
m_hideColumnsAllowed = False;
|
|
m_descendantSelectAllowed = False;
|
|
m_isFocus = False;
|
|
|
|
if (constantSize)
|
|
{
|
|
XtSetArg(av[ac], XmNhorizontalSizePolicy, XmCONSTANT); ac++;
|
|
}
|
|
else
|
|
{
|
|
XtSetArg(av[ac], XmNhorizontalSizePolicy, XmVARIABLE); ac++;
|
|
}
|
|
|
|
XtSetArg(av[ac], XmNautoSelect, False); ac++;
|
|
XtSetArg(av[ac], XmNtraversalOn, False); ac++;
|
|
|
|
outline = XmLCreateGrid(widget_parent, (char*)name, av, ac);
|
|
|
|
Pixel bgColor;
|
|
XtVaGetValues(outline, XmNbackground, &bgColor, 0);
|
|
XtVaSetValues(outline,
|
|
XmNborderWidth, 2,
|
|
XmNborderColor, bgColor,
|
|
XmNcellDefaults, True,
|
|
XmNcellLeftBorderType, XmBORDER_NONE,
|
|
XmNcellRightBorderType, XmBORDER_NONE,
|
|
XmNcellTopBorderType, XmBORDER_NONE,
|
|
XmNcellBottomBorderType, XmBORDER_NONE,
|
|
XmNcellAlignment, XmALIGNMENT_LEFT,
|
|
0);
|
|
|
|
m_columnwidths = (int*)XP_CALLOC(m_numcolumns, sizeof(int));
|
|
|
|
// whether or not columns are resizable
|
|
m_columnResizable = (XP_Bool*)XP_CALLOC(m_numcolumns, sizeof(XP_Bool));
|
|
memset(m_columnResizable, True, sizeof(XP_Bool) * m_numcolumns);
|
|
|
|
m_columnIndex = (int*) XP_ALLOC(sizeof(int) * (m_numcolumns));
|
|
|
|
#if defined(USE_MOTIF_DND)
|
|
XtAddEventHandler(outline,
|
|
ButtonPressMask | ButtonReleaseMask,
|
|
False,
|
|
buttonEventHandler, this);
|
|
#else
|
|
XtInsertEventHandler(outline, ButtonPressMask | ButtonReleaseMask
|
|
| PointerMotionMask, False,
|
|
buttonEventHandler, this, XtListHead);
|
|
#endif
|
|
XtAddCallback(outline, XmNcellDrawCallback, celldrawCallback, this);
|
|
|
|
m_buttonfunc_data = XP_NEW_ZAP(OutlineButtonFuncData);
|
|
m_flippyfunc_data = XP_NEW_ZAP(OutlineFlippyFuncData);
|
|
|
|
#if !defined(USE_MOTIF_DND)
|
|
// initialize the drag/drop stuff
|
|
m_dragtype = FE_DND_NONE;
|
|
m_dragicon = 0;
|
|
m_sourcedropfunc = 0;
|
|
#endif
|
|
|
|
// initialize the selection stuff
|
|
m_selBlocked = False;
|
|
m_selectedIndices = new int[5];
|
|
m_selectedItems = NULL;
|
|
m_selectedItemCount = 0;
|
|
m_selectedCount = 0;
|
|
m_selectedSize = 5;
|
|
m_firstSelectedIndex = (unsigned int)(1 << 31) - 1; // INT_MAX
|
|
m_lastSelectedIndex = -1;
|
|
m_selectionDirection = 1; // safe default.
|
|
|
|
// initialize the magic list change (finished, starting) depth.
|
|
m_listChangeDepth = 0;
|
|
|
|
// keep track of our peer
|
|
m_outlinable = o;
|
|
|
|
// and set our base widget
|
|
setBaseWidget(outline);
|
|
|
|
#if defined(USE_MOTIF_DND)
|
|
m_outlinerdrop = new XFE_OutlinerDrop(m_widget, this);
|
|
m_outlinerdrop->enable();
|
|
#endif /* USE_MOTIF_DND */
|
|
|
|
defaultGeometryOK = setDefaultGeometry();
|
|
|
|
if (hasHeadings)
|
|
XmLGridAddRows(m_widget, XmHEADING, 0, 1);
|
|
|
|
if (!defaultGeometryOK)
|
|
{
|
|
int i;
|
|
|
|
D(printf ("default geometry is NOT OK!\n");)
|
|
|
|
m_numvisible = num_visible;
|
|
for (i = 0; i < m_numcolumns; i ++)
|
|
m_columnIndex[i] = i;
|
|
|
|
XtVaGetValues(outline, XmNfontList, &fontList, NULL);
|
|
XmLFontListGetDimensions(fontList, &avgwidth, &avgheight, TRUE);
|
|
}
|
|
|
|
XmLGridAddColumns(outline, XmCONTENT, -1, m_numvisible);
|
|
|
|
for (i = 0; i < m_numcolumns; i ++)
|
|
{
|
|
if (!defaultGeometryOK)
|
|
m_columnwidths[i] = column_widths[i] * avgwidth;
|
|
|
|
if (i < m_numvisible)
|
|
XtVaSetValues(m_widget,
|
|
XmNcolumn, i,
|
|
XmNcolumnSizePolicy, XmCONSTANT,
|
|
XmNcolumnWidth, m_columnwidths[i],
|
|
0);
|
|
}
|
|
|
|
if (!defaultGeometryOK)
|
|
{
|
|
// since it wasn't ok before, we can be assured that it is now.
|
|
// Save it so we won't do the above stuff again.
|
|
rememberGeometry();
|
|
}
|
|
|
|
XtVaSetValues(m_widget, XmNallowColumnResize, True, 0);
|
|
|
|
XtAddCallback(m_widget, XmNresizeCallback, resizeCallback, this);
|
|
|
|
// Tooltip
|
|
fe_AddTipStringCallback(outline, XFE_Outliner::tip_cb, this);
|
|
|
|
// create our icons, if we need to.
|
|
if (!closedParentIcon.pixmap)
|
|
{
|
|
Pixel bg_pixel;
|
|
|
|
XtVaGetValues(m_widget, XmNbackground, &bg_pixel, 0);
|
|
|
|
fe_NewMakeIcon(m_toplevel->getBaseWidget(),
|
|
toplevel_component->getFGPixel(),
|
|
bg_pixel,
|
|
&closedParentIcon,
|
|
NULL,
|
|
cparent.width, cparent.height,
|
|
cparent.mono_bits, cparent.color_bits,
|
|
cparent.mask_bits, FALSE);
|
|
|
|
fe_NewMakeIcon(m_toplevel->getBaseWidget(),
|
|
toplevel_component->getFGPixel(),
|
|
bg_pixel,
|
|
&openParentIcon,
|
|
NULL,
|
|
oparent.width, oparent.height,
|
|
oparent.mono_bits, oparent.color_bits,
|
|
oparent.mask_bits, FALSE);
|
|
|
|
fe_NewMakeIcon(m_toplevel->getBaseWidget(),
|
|
toplevel_component->getFGPixel(),
|
|
bg_pixel,
|
|
&showColumnIcon,
|
|
NULL,
|
|
showcolumn.width, showcolumn.height,
|
|
showcolumn.mono_bits, showcolumn.color_bits,
|
|
showcolumn.mask_bits, FALSE);
|
|
|
|
fe_NewMakeIcon(m_toplevel->getBaseWidget(),
|
|
toplevel_component->getFGPixel(),
|
|
bg_pixel,
|
|
&showColumnInsensIcon,
|
|
NULL,
|
|
showcolumn_i.width, showcolumn_i.height,
|
|
showcolumn_i.mono_bits, showcolumn_i.color_bits,
|
|
showcolumn_i.mask_bits, FALSE);
|
|
|
|
fe_NewMakeIcon(m_toplevel->getBaseWidget(),
|
|
toplevel_component->getFGPixel(),
|
|
bg_pixel,
|
|
&hideColumnIcon,
|
|
NULL,
|
|
hidecolumn.width, hidecolumn.height,
|
|
hidecolumn.mono_bits, hidecolumn.color_bits,
|
|
hidecolumn.mask_bits, FALSE);
|
|
|
|
fe_NewMakeIcon(m_toplevel->getBaseWidget(),
|
|
toplevel_component->getFGPixel(),
|
|
bg_pixel,
|
|
&hideColumnInsensIcon,
|
|
NULL,
|
|
hidecolumn_i.width, hidecolumn_i.height,
|
|
hidecolumn_i.mono_bits, hidecolumn_i.color_bits,
|
|
hidecolumn_i.mask_bits, FALSE);
|
|
}
|
|
}
|
|
|
|
XFE_Outliner::XFE_Outliner()
|
|
{
|
|
m_selBlocked = False;
|
|
m_outlinable = NULL;
|
|
}
|
|
|
|
XFE_Outliner::~XFE_Outliner()
|
|
{
|
|
D(printf ("In XFE_Outliner::~XFE_Outliner()\n");)
|
|
FREEIF(m_columnwidths);
|
|
FREEIF(m_columnIndex);
|
|
FREEIF(m_columnResizable);
|
|
FREEIF(m_prefname);
|
|
FREEIF(m_selectedItems);
|
|
FREEIF(m_geominfo);
|
|
//
|
|
XP_FREEIF(m_doc_msg_buf);
|
|
XP_FREEIF(m_tip_msg_buf);
|
|
|
|
// if our outlinable still has line data allocated,
|
|
// tell it to release it.
|
|
if (m_DataRow != -1)
|
|
m_outlinable->releaseLineData();
|
|
|
|
#if defined(USE_MOTIF_DND)
|
|
if (m_outlinerdrop)
|
|
delete m_outlinerdrop;
|
|
#endif /* USE_MOTIF_DND */
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::setPipeColumn(int column)
|
|
{
|
|
XP_ASSERT(column >= 0 && column < m_numcolumns);
|
|
|
|
m_pipeColumn = column;
|
|
}
|
|
|
|
int
|
|
XFE_Outliner::getPipeColumn()
|
|
{
|
|
return m_pipeColumn;
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::setSortColumn(int column, EOutlinerSortDirection direction)
|
|
{
|
|
XP_ASSERT(column >= -1 && column < m_numcolumns);
|
|
|
|
m_sortColumn = column;
|
|
m_sortDirection = direction;
|
|
}
|
|
|
|
int
|
|
XFE_Outliner::getSortColumn()
|
|
{
|
|
return m_sortColumn;
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::toggleSortDirection()
|
|
{
|
|
if (m_sortDirection == OUTLINER_SortAscending)
|
|
m_sortDirection = OUTLINER_SortDescending;
|
|
else
|
|
m_sortDirection = OUTLINER_SortAscending;
|
|
}
|
|
|
|
EOutlinerSortDirection
|
|
XFE_Outliner::getSortDirection()
|
|
{
|
|
return m_sortDirection;
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::setColumnResizable(int column,
|
|
XP_Bool resizable)
|
|
{
|
|
XP_ASSERT(column >= 0 && column < m_numcolumns);
|
|
int column_index;
|
|
|
|
for (column_index = 0; column_index < m_numcolumns; column_index ++)
|
|
if (m_columnIndex[ column_index ] == column)
|
|
break;
|
|
|
|
setColumnResizableByIndex(column_index, resizable);
|
|
}
|
|
|
|
XP_Bool
|
|
XFE_Outliner::getColumnResizable(int column)
|
|
{
|
|
XP_ASSERT(column >= 0 && column < m_numcolumns);
|
|
int column_index;
|
|
|
|
for (column_index = 0; column_index < m_numcolumns; column_index ++)
|
|
if (m_columnIndex[ column_index ] == column)
|
|
break;
|
|
|
|
return getColumnResizableByIndex(column_index);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::setColumnWidth(int column,
|
|
int width)
|
|
{
|
|
XP_ASSERT(column >= 0 && column < m_numcolumns);
|
|
int column_index;
|
|
|
|
for (column_index = 0; column_index < m_numcolumns; column_index ++)
|
|
if (m_columnIndex[ column_index ] == column)
|
|
break;
|
|
|
|
setColumnWidthByIndex(column_index, width);
|
|
}
|
|
|
|
int
|
|
XFE_Outliner::getColumnWidth(int column)
|
|
{
|
|
XP_ASSERT(column >= 0 && column < m_numcolumns);
|
|
int column_index;
|
|
|
|
for (column_index = 0; column_index < m_numcolumns; column_index ++)
|
|
if (m_columnIndex[ column_index ] == column)
|
|
break;
|
|
|
|
return getColumnWidthByIndex(column_index);
|
|
}
|
|
|
|
|
|
// Return the number of visible rows in the grid, including header rows.
|
|
int XFE_Outliner::getNumContextAndHeaderRows()
|
|
{
|
|
XP_ASSERT(m_widget != 0);
|
|
|
|
int numContentRows = 0;
|
|
int numHeadingRows = 0;
|
|
XtVaGetValues(m_widget,
|
|
XmNrows, &numContentRows,
|
|
XmNheadingRows, &numHeadingRows,
|
|
0);
|
|
|
|
return numContentRows + numHeadingRows;
|
|
}
|
|
|
|
|
|
/*
|
|
** Resize the grid to show all rows, but enforce a
|
|
** minimum and maximum number of rows.
|
|
*/
|
|
void XFE_Outliner::showAllRowsWithRange(int minRows, int maxRows)
|
|
{
|
|
XP_ASSERT(m_widget != 0);
|
|
|
|
int numRows = this->getNumContextAndHeaderRows();
|
|
if((numRows > minRows)&&(numRows < maxRows)) {
|
|
// We are within the range, show them all.
|
|
XtVaSetValues(m_widget,
|
|
XmNvisibleRows, numRows,
|
|
0);
|
|
} else if(numRows >= maxRows) {
|
|
// Maximum number of rows.
|
|
XtVaSetValues(m_widget,
|
|
XmNvisibleRows, maxRows,
|
|
0);
|
|
} else if(numRows <= minRows) {
|
|
// Minimum number of rows.
|
|
XtVaSetValues(m_widget,
|
|
XmNvisibleRows, minRows,
|
|
0);
|
|
} else {
|
|
XP_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::setBlockSel(XP_Bool b)
|
|
{
|
|
m_selBlocked = b;
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::scroll2Item(int index)
|
|
{
|
|
if (index >=0 && index < m_totalLines) {
|
|
selectItemExclusive(index);
|
|
makeVisible(index);
|
|
}/* if */
|
|
else
|
|
deselectAllItems();
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::setMultiSelectAllowed(Boolean flag)
|
|
{
|
|
XP_ASSERT(m_widget != 0);
|
|
|
|
if (flag)
|
|
{
|
|
XtVaSetValues(m_widget,
|
|
XmNselectionPolicy, XmSELECT_MULTIPLE_ROW,
|
|
0);
|
|
}
|
|
else
|
|
{
|
|
XtVaSetValues(m_widget,
|
|
XmNselectionPolicy, XmSELECT_BROWSE_ROW,
|
|
0);
|
|
}
|
|
}
|
|
|
|
Boolean
|
|
XFE_Outliner::getMultiSelectAllowed()
|
|
{
|
|
unsigned char selectionPolicy;
|
|
|
|
XP_ASSERT(m_widget != 0);
|
|
|
|
XtVaGetValues(m_widget,
|
|
XmNselectionPolicy, &selectionPolicy,
|
|
NULL);
|
|
|
|
return (selectionPolicy == XmSELECT_MULTIPLE_ROW);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::setDescendantSelectAllowed(XP_Bool flag)
|
|
{
|
|
m_descendantSelectAllowed = flag;
|
|
}
|
|
|
|
XP_Bool
|
|
XFE_Outliner::getDescendantSelectAllowed()
|
|
{
|
|
return m_descendantSelectAllowed;
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::setHideColumnsAllowed(XP_Bool flag)
|
|
{
|
|
if (m_hideColumnsAllowed != flag)
|
|
{
|
|
if (flag)
|
|
{
|
|
// show the hide/show buttons.
|
|
XtVaSetValues(m_widget,
|
|
XmNlayoutFrozen, True,
|
|
NULL);
|
|
|
|
applyDelta(-1 * (12 + 12));
|
|
|
|
XmLGridAddColumns(m_widget,XmCONTENT, m_numvisible, 2);
|
|
|
|
XtVaSetValues(m_widget,
|
|
XmNcolumn, m_numvisible,
|
|
XmNcolumnWidth, 12, //XX magic number alert !!!
|
|
XmNcolumnSizePolicy, XmCONSTANT,
|
|
XmNcolumnResizable, False, // can't resize the show/hide columns.
|
|
NULL);
|
|
|
|
XtVaSetValues(m_widget,
|
|
XmNcolumn, m_numvisible + 1,
|
|
XmNcolumnWidth, 12, //XX magic number alert !!!
|
|
XmNcolumnSizePolicy, XmCONSTANT,
|
|
XmNcolumnResizable, False, // can't resize the show/hide columns.
|
|
NULL);
|
|
|
|
XtVaSetValues(m_widget,
|
|
XmNlayoutFrozen, False,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
// remove the hide/show buttons.
|
|
XmLGridDeleteColumns(m_widget, XmCONTENT, m_numvisible, 2);
|
|
|
|
applyDelta(12 + 12);
|
|
|
|
}
|
|
|
|
m_hideColumnsAllowed = flag;
|
|
}
|
|
}
|
|
|
|
XP_Bool
|
|
XFE_Outliner::getHideColumnsAllowed()
|
|
{
|
|
return m_hideColumnsAllowed;
|
|
}
|
|
|
|
#if !defined(USE_MOTIF_DND)
|
|
void
|
|
XFE_Outliner::setDragType(fe_dnd_Type dragtype, fe_icon *dragicon,
|
|
XFE_Component *drag_source, fe_dnd_SourceDropFunc sourcedropfunc)
|
|
{
|
|
m_dragtype = dragtype;
|
|
m_dragicon = dragicon;
|
|
m_source = drag_source;
|
|
m_sourcedropfunc = sourcedropfunc;
|
|
}
|
|
|
|
fe_dnd_Type
|
|
XFE_Outliner::getDragType()
|
|
{
|
|
return m_dragtype;
|
|
}
|
|
|
|
fe_icon *
|
|
XFE_Outliner::getDragIcon()
|
|
{
|
|
return m_dragicon;
|
|
}
|
|
|
|
fe_dnd_SourceDropFunc
|
|
XFE_Outliner::getSourceDropFunc()
|
|
{
|
|
return m_sourcedropfunc;
|
|
}
|
|
|
|
#else
|
|
void
|
|
XFE_Outliner::enableDragDrop(void *this_ptr,
|
|
FEGetDropTargetFunc drop_target_func,
|
|
FEGetDragTargetFunc drag_target_func,
|
|
FEGetDragIconDataFunc drag_icon_func,
|
|
FEConvertDragDataFunc drag_conv_func,
|
|
FEProcessTargetsFunc process_targets_func)
|
|
{
|
|
m_dragsource = this_ptr;
|
|
m_dropTargetFunc = drop_target_func;
|
|
m_dragTargetFunc = drag_target_func;
|
|
m_dragIconDataFunc = drag_icon_func;
|
|
m_dragConvFunc = drag_conv_func;
|
|
m_processTargetsFunc = process_targets_func;
|
|
|
|
if (!m_outlinerdrag)
|
|
m_outlinerdrag = new XFE_OutlinerDrag(m_widget, this);
|
|
|
|
/* update the dropsite's idea of its targets. */
|
|
m_outlinerdrop->update();
|
|
}
|
|
|
|
|
|
void
|
|
XFE_Outliner::getDropTargets(Atom **targets,
|
|
int *numtargets)
|
|
{
|
|
if (m_dropTargetFunc == NULL)
|
|
{
|
|
*targets = NULL;
|
|
*numtargets = 0;
|
|
}
|
|
else
|
|
{
|
|
(*m_dropTargetFunc)(m_outlinable, targets, numtargets);
|
|
}
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::getDragTargets(int row, int column,
|
|
Atom **targets,
|
|
int *numtargets)
|
|
{
|
|
if (m_dragTargetFunc == NULL)
|
|
{
|
|
*targets = NULL;
|
|
*numtargets = 0;
|
|
}
|
|
else
|
|
{
|
|
(*m_dragTargetFunc)(m_dragsource, row, column, targets, numtargets);
|
|
}
|
|
}
|
|
|
|
fe_icon_data *
|
|
XFE_Outliner::getDragIconData(int row, int column)
|
|
{
|
|
if (m_dragIconDataFunc == NULL)
|
|
return NULL;
|
|
else
|
|
return (*m_dragIconDataFunc)(m_dragsource, row, column);
|
|
}
|
|
|
|
char *
|
|
XFE_Outliner::dragConvert(Atom atom)
|
|
{
|
|
if (m_dragConvFunc == NULL)
|
|
return NULL;
|
|
else
|
|
return (*m_dragConvFunc)(m_dragsource, atom);
|
|
}
|
|
|
|
|
|
int
|
|
XFE_Outliner::processTargets(int row, int col,
|
|
Atom *targets,
|
|
const char **data,
|
|
int numItems)
|
|
{
|
|
if (m_processTargetsFunc == NULL)
|
|
return FALSE;
|
|
else
|
|
return (*m_processTargetsFunc)(m_dragsource, row, col,
|
|
targets, data, numItems);
|
|
}
|
|
#endif /* USE_MOTIF_DND */
|
|
|
|
XP_Bool
|
|
XFE_Outliner::setDefaultGeometry()
|
|
{
|
|
int i;
|
|
char* ptr;
|
|
char* ptr2;
|
|
char* ptr3;
|
|
int value;
|
|
int width;
|
|
int height = 0;
|
|
|
|
for (i = 0; i < m_numcolumns; i ++)
|
|
m_columnIndex[i] = i;
|
|
|
|
ptr = m_geominfo;
|
|
|
|
// first parse the height of the outliner in pixel
|
|
if (ptr != NULL)
|
|
{
|
|
ptr2 = XP_STRCHR(ptr, 'x');
|
|
if (ptr2) *ptr2 = '\0';
|
|
height = atoi(ptr);
|
|
|
|
if (height > 0 && height < MAX_COLUMN_WIDTH)
|
|
{
|
|
*ptr2 = 'x';
|
|
|
|
ptr = ptr2 + 1;
|
|
}
|
|
else
|
|
{
|
|
FREEIF(m_geominfo);
|
|
ptr = m_geominfo;
|
|
}
|
|
|
|
}
|
|
|
|
// then parse the number of visible columns
|
|
if (ptr != NULL)
|
|
{
|
|
ptr2 = XP_STRCHR(ptr, ')');
|
|
if (ptr2) *ptr2 = '\0';
|
|
ptr3 = XP_STRCHR(ptr, '(');
|
|
if (ptr3)
|
|
{
|
|
m_numvisible = atoi(ptr3 + 1);
|
|
|
|
if (m_numvisible <= 0 || m_numvisible > m_numcolumns)
|
|
m_numvisible = m_numcolumns;
|
|
|
|
*ptr3 = '(';
|
|
*ptr2 = ')';
|
|
ptr = ptr2 + 1;
|
|
}
|
|
else
|
|
{
|
|
FREEIF(m_geominfo);
|
|
ptr = m_geominfo;
|
|
}
|
|
}
|
|
|
|
for (i=0 ; i<m_numcolumns ; i++)
|
|
{
|
|
if (ptr == NULL)
|
|
{
|
|
FREEIF(m_geominfo);
|
|
break;
|
|
}
|
|
|
|
ptr2 = XP_STRCHR(ptr, ';');
|
|
if (ptr2) *ptr2 = '\0';
|
|
ptr3 = XP_STRCHR(ptr, ':');
|
|
if (!ptr3)
|
|
{
|
|
FREEIF(m_geominfo);
|
|
break;
|
|
}
|
|
*ptr3 = '\0';
|
|
|
|
for (value = 0 ; value < m_numcolumns ; value++)
|
|
{
|
|
if (m_outlinable->getColumnName(value) == NULL
|
|
|| strcmp(m_outlinable->getColumnName(value), ptr) == 0)
|
|
break;
|
|
}
|
|
|
|
if (value >= m_numcolumns)
|
|
{
|
|
FREEIF(m_geominfo);
|
|
break;
|
|
}
|
|
|
|
m_columnIndex[i] = value;
|
|
width = atoi(ptr3 + 1);
|
|
*ptr3 = ':';
|
|
|
|
if (width < MIN_COLUMN_WIDTH) width = MIN_COLUMN_WIDTH;
|
|
if (width > MAX_COLUMN_WIDTH) width = MAX_COLUMN_WIDTH;
|
|
|
|
m_columnwidths[ i ] = width;
|
|
|
|
if (ptr2) *ptr2++ = ';';
|
|
ptr = ptr2;
|
|
}
|
|
|
|
if (m_geominfo)
|
|
{
|
|
/* Check that every column was mentioned, and no duplicates
|
|
occurred. */
|
|
int* check = (int*)XP_CALLOC(m_numcolumns, sizeof(int));
|
|
for (i=0 ; i<m_numcolumns ; i++) check[i] = 0;
|
|
for (i=0 ; i<m_numcolumns ; i++)
|
|
{
|
|
int w = m_columnIndex[i];
|
|
if (w < 0 || w > m_numcolumns || check[w])
|
|
{
|
|
FREEIF(m_geominfo);
|
|
break;
|
|
}
|
|
check[w] = 1;
|
|
}
|
|
XP_FREE(check);
|
|
}
|
|
|
|
if (m_geominfo != NULL)
|
|
XtVaSetValues(m_widget,
|
|
XmNheight, height,
|
|
NULL);
|
|
|
|
return (m_geominfo != NULL);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::setInFocus(XP_Bool infocus)
|
|
{
|
|
Pixel bgColor, fgColor;
|
|
|
|
XtVaGetValues(m_widget,
|
|
XmNbackground, &bgColor,
|
|
XmNforeground, &fgColor, 0);
|
|
if ( infocus )
|
|
XtVaSetValues(m_widget, XmNborderColor,
|
|
fgColor, 0);
|
|
else
|
|
XtVaSetValues(m_widget, XmNborderColor,
|
|
bgColor, 0);
|
|
|
|
m_isFocus = infocus;
|
|
|
|
}
|
|
|
|
XP_Bool
|
|
XFE_Outliner::isFocus()
|
|
{
|
|
return m_isFocus;
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::selectItem(int index)
|
|
{
|
|
XP_ASSERT(m_widget != 0);
|
|
|
|
/*
|
|
** invalidate the line without calling invalidateLine, since
|
|
** that function calls XmLGridRedrawRow, and we're already
|
|
** going to redraw it with the selectrow call below.
|
|
*/
|
|
if (m_DataRow != -1)
|
|
m_outlinable->releaseLineData();
|
|
|
|
m_DataRow = -1;
|
|
|
|
XmLGridSelectRow(m_widget, index, False);
|
|
|
|
addSelection(index);
|
|
}
|
|
|
|
|
|
void
|
|
XFE_Outliner::selectItemExclusive(int index)
|
|
{
|
|
XP_ASSERT(m_widget != 0);
|
|
|
|
deselectAllItems();
|
|
|
|
selectItem(index);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::deselectItem(int index)
|
|
{
|
|
XP_ASSERT(m_widget != 0);
|
|
|
|
/*
|
|
** invalidate the line without calling invalidateLine, since
|
|
** that function calls XmLGridRedrawRow, and we're already
|
|
** going to redraw it with the selectrow call below.
|
|
*/
|
|
if (m_DataRow != -1)
|
|
m_outlinable->releaseLineData();
|
|
|
|
m_DataRow = -1;
|
|
|
|
XmLGridDeselectRow(m_widget, index, False);
|
|
|
|
if (m_selectedCount)
|
|
removeSelection(index);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::toggleSelected(int index)
|
|
{
|
|
XP_ASSERT(m_widget != 0);
|
|
|
|
if (isSelected(index))
|
|
deselectItem(index);
|
|
else
|
|
selectItem(index);
|
|
}
|
|
|
|
/*
|
|
** Here, we either expand or contract the selected area by
|
|
** following rules.
|
|
**
|
|
** 1) if new_index < first_index, select from new_index to last_index.
|
|
** 2) if new_index > last_index, select from first_index to new_index.
|
|
** 3) if new_index > first_index && new_index < last_index
|
|
**
|
|
** 3a) if new_index > (last_index + first_index) / 2, select
|
|
** from new_index to last_index.
|
|
** 3b) if new_index < (last_index + first_index) / 2, select
|
|
** from first_index to new_index.
|
|
**
|
|
** funkygroovycool
|
|
*/
|
|
void
|
|
XFE_Outliner::trimOrExpandSelection(int new_index)
|
|
{
|
|
// always do something reasonable...
|
|
if (m_selectedCount == 0)
|
|
selectItem(new_index);
|
|
else
|
|
{
|
|
if (m_selectionDirection == -1)
|
|
{
|
|
if (new_index < m_lastSelectedIndex)
|
|
{
|
|
if (new_index < m_firstSelectedIndex)
|
|
selectRangeByIndices(m_firstSelectedIndex, new_index);
|
|
else if (new_index > m_firstSelectedIndex)
|
|
deselectRangeByIndices(new_index - 1, m_firstSelectedIndex);
|
|
|
|
/* don't need to do anything if
|
|
new_index == m_firstSelectedIndex */
|
|
}
|
|
else
|
|
{
|
|
deselectRangeByIndices(m_lastSelectedIndex - 1, m_firstSelectedIndex);
|
|
selectRangeByIndices(m_lastSelectedIndex, new_index);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (new_index > m_firstSelectedIndex)
|
|
{
|
|
if (new_index > m_lastSelectedIndex)
|
|
selectRangeByIndices(m_lastSelectedIndex, new_index);
|
|
else if (new_index < m_lastSelectedIndex)
|
|
deselectRangeByIndices(new_index + 1, m_lastSelectedIndex);
|
|
|
|
/* don't need to do anything if
|
|
new_index == m_lastSelectedIndex */
|
|
}
|
|
else
|
|
{
|
|
deselectRangeByIndices(m_firstSelectedIndex + 1, m_lastSelectedIndex);
|
|
selectRangeByIndices(m_firstSelectedIndex, new_index);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::selectAllItems()
|
|
{
|
|
XP_ASSERT(m_widget != 0);
|
|
|
|
XmLGridSelectAllRows(m_widget, False);
|
|
|
|
for (int i = 0; i < m_totalLines; i ++)
|
|
{
|
|
selectItem(i);
|
|
}
|
|
|
|
m_selectionDirection = 1;
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::deselectAllItems()
|
|
{
|
|
XP_ASSERT(m_widget != 0);
|
|
|
|
XmLGridDeselectAllRows(m_widget, False);
|
|
|
|
deselectAll();
|
|
}
|
|
|
|
int
|
|
XFE_Outliner::visibleRows(int& first, int& last)
|
|
{
|
|
int firstrow, lastrow;
|
|
Dimension height, shadowthickness;
|
|
|
|
XtVaGetValues(m_widget,
|
|
XmNscrollRow, &firstrow,
|
|
XmNheight, &height,
|
|
XmNshadowThickness, &shadowthickness,
|
|
NULL);
|
|
first = firstrow;
|
|
|
|
height -= shadowthickness;
|
|
|
|
for (lastrow = firstrow + 1 ; ; lastrow++) {
|
|
XRectangle rect;
|
|
|
|
if (XmLGridRowColumnToXY(m_widget,
|
|
XmCONTENT, lastrow,
|
|
XmCONTENT, 0,
|
|
False, &rect) < 0)
|
|
break;
|
|
|
|
if (rect.y + rect.height >= height)
|
|
break;
|
|
}/* for lastrow */
|
|
last = lastrow;
|
|
return (lastrow-firstrow+1);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::makeVisible(int index)
|
|
{
|
|
int firstrow, lastrow;
|
|
Dimension height, shadowthickness;
|
|
|
|
if (index < 0)
|
|
return;
|
|
|
|
if (m_visibleTimer)
|
|
{
|
|
m_visibleLine = index;
|
|
return;
|
|
}
|
|
|
|
XtVaGetValues(m_widget,
|
|
XmNscrollRow, &firstrow,
|
|
XmNheight, &height,
|
|
XmNshadowThickness, &shadowthickness,
|
|
NULL);
|
|
|
|
height -= shadowthickness;
|
|
|
|
for (lastrow = firstrow + 1 ; ; lastrow++)
|
|
{
|
|
XRectangle rect;
|
|
|
|
if (XmLGridRowColumnToXY(m_widget,
|
|
XmCONTENT, lastrow,
|
|
XmCONTENT, 0,
|
|
False, &rect) < 0)
|
|
{
|
|
break;
|
|
}
|
|
if (rect.y + rect.height >= height) break;
|
|
}
|
|
|
|
if (index >= firstrow && index < lastrow)
|
|
return;
|
|
|
|
firstrow = index - ((lastrow - firstrow) / 2);
|
|
|
|
if (firstrow < 0)
|
|
firstrow = 0;
|
|
|
|
XtVaSetValues(m_widget,
|
|
XmNscrollRow, firstrow,
|
|
NULL);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::invalidate()
|
|
{
|
|
if (m_DataRow != -1)
|
|
m_outlinable->releaseLineData();
|
|
|
|
m_DataRow = -1;
|
|
|
|
XmLGridRedrawAll(m_widget);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::invalidateLine(int line)
|
|
{
|
|
if (m_DataRow != -1)
|
|
m_outlinable->releaseLineData();
|
|
|
|
m_DataRow = -1;
|
|
|
|
XmLGridRedrawRow(m_widget, XmCONTENT, line);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::invalidateLines(int start, int count)
|
|
{
|
|
int i;
|
|
|
|
for (i = start; i < start + count; i ++)
|
|
invalidateLine(i);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::invalidateHeaders()
|
|
{
|
|
if (m_DataRow != -1)
|
|
m_outlinable->releaseLineData();
|
|
|
|
m_DataRow = -1;
|
|
|
|
XmLGridRedrawRow(m_widget, XmHEADING, 0);
|
|
}
|
|
|
|
int
|
|
XFE_Outliner::numResizableVisibleColumns(int starting_at)
|
|
{
|
|
int i;
|
|
int count = 0;
|
|
|
|
for (i = starting_at; i < m_numvisible; i ++)
|
|
{
|
|
if (getColumnResizableByIndex(i))
|
|
count ++;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
/*
|
|
** applyDelta is one of the grossest functions ever, in my opinion.
|
|
**
|
|
** The idea is that we give it a delta and a column to start applying it at,
|
|
** and it will start there and divide up the delta evenly (well, using
|
|
** percentages) across all the resizable columns from the given one to the
|
|
** right-most one. It will try to resize them all enough to get rid of the
|
|
** delta, but in the event that it can't distribute the delta entirely, it
|
|
** returns the amount left over. This is used by resize() method below to fix
|
|
** up the resized column.
|
|
**
|
|
** That being said, the way things are done here is icky. We build up an
|
|
** array of delta_struct's, one for each resizable column we're going to be
|
|
** dealing with. Then we sort them so that lower indices have smaller widths.
|
|
** This is so that if we can't apply enough of the delta to a smaller width
|
|
** column, it'll get pushed off to the larger ones (well, the largest one --
|
|
** the last one in the array.) We make up for all the multiplicative errors
|
|
** -- and instances of not being able to fully apply a column delta with the
|
|
** last column in the array. Since it's the largest, we have the best chance
|
|
** of doing it there.
|
|
*/
|
|
typedef struct delta_struct
|
|
{
|
|
int column_index;
|
|
int column_width;
|
|
} delta_struct;
|
|
|
|
static int compare_deltas(const void *d1, const void *d2)
|
|
{
|
|
delta_struct *delta1 = (delta_struct *)d1;
|
|
delta_struct *delta2 = (delta_struct *)d2;
|
|
|
|
return delta1->column_width - delta2->column_width;
|
|
}
|
|
|
|
int
|
|
XFE_Outliner::applyDelta(int delta, int starting_at)
|
|
{
|
|
int current_column;
|
|
int num_resizable = numResizableVisibleColumns(starting_at);
|
|
int total_resizable_column_width = 0;
|
|
int delta_distributed = 0;
|
|
delta_struct *deltas;
|
|
int i;
|
|
|
|
D(printf ("Applying a delta of %d from column %d to column %d\n",
|
|
delta,
|
|
starting_at,
|
|
m_numvisible - 1);)
|
|
|
|
if (num_resizable == 0)
|
|
return delta;
|
|
|
|
deltas = (delta_struct*)XP_CALLOC(num_resizable, sizeof(delta_struct));
|
|
i = 0;
|
|
for (current_column = starting_at; current_column < m_numvisible; current_column ++)
|
|
if (getColumnResizableByIndex(current_column))
|
|
{
|
|
deltas[i].column_index = current_column;
|
|
deltas[i].column_width = getColumnWidthByIndex(current_column);
|
|
total_resizable_column_width += deltas[i].column_width;
|
|
i++;
|
|
}
|
|
|
|
XP_ASSERT(total_resizable_column_width > 0);
|
|
if (total_resizable_column_width <= 0)
|
|
{
|
|
XP_FREE(deltas);
|
|
return delta;
|
|
}
|
|
|
|
XP_QSORT(deltas, num_resizable, sizeof(delta_struct), compare_deltas);
|
|
|
|
current_column = starting_at;
|
|
|
|
for (i = 0; i < num_resizable; i ++)
|
|
{
|
|
int orig_column_width = deltas[i].column_width;
|
|
int column_width;
|
|
int column_delta = delta * ((float)orig_column_width / total_resizable_column_width);
|
|
|
|
D(printf ("Column %d (Before delta = %d). Percentage of delta applied = %f (%d pixels)\n",
|
|
deltas[i].column_index,
|
|
orig_column_width,
|
|
(float)orig_column_width / total_resizable_column_width,
|
|
column_delta);)
|
|
|
|
if (i == num_resizable - 1)
|
|
{
|
|
/* make up for our cumulative errors with the last column */
|
|
D(printf ("making up for the differences in column %d by adding %d to its width.\n",
|
|
deltas[i].column_index, delta - delta_distributed);)
|
|
|
|
column_delta += delta - delta_distributed - column_delta;
|
|
}
|
|
|
|
column_width = orig_column_width + column_delta;
|
|
|
|
if (column_width < MIN_COLUMN_WIDTH)
|
|
{
|
|
column_width = MIN_COLUMN_WIDTH;
|
|
|
|
column_delta = column_width - orig_column_width;
|
|
}
|
|
else if (column_width > MAX_COLUMN_WIDTH)
|
|
{
|
|
column_width = MAX_COLUMN_WIDTH;
|
|
|
|
column_delta = column_width - orig_column_width;
|
|
}
|
|
|
|
delta_distributed += column_delta;
|
|
|
|
D(printf ("After delta = %d\n", column_width);)
|
|
setColumnWidthByIndex(deltas[i].column_index, column_width);
|
|
#if defined(DEBUG_tao_)
|
|
printf("\n>>XFE_Outliner::applyDelta:setColumnWidthByIndex(i=%d, ind=%d, wid=%d)",
|
|
i, deltas[i].column_index, column_width);
|
|
#endif
|
|
}
|
|
|
|
XP_FREE(deltas);
|
|
return delta - delta_distributed;
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::hideColumn()
|
|
{
|
|
Dimension width;
|
|
|
|
m_numvisible --;
|
|
|
|
XP_ASSERT(m_numvisible > 0);
|
|
width = getColumnWidthByIndex(m_numvisible);
|
|
|
|
D(printf ("I'm hiding a column of width %d\n", width);)
|
|
|
|
XtVaSetValues(m_widget,
|
|
XmNlayoutFrozen, True,
|
|
NULL);
|
|
|
|
XmLGridDeleteColumns(m_widget, XmCONTENT, m_numvisible, 1);
|
|
|
|
applyDelta(width);
|
|
|
|
XtVaSetValues(m_widget,
|
|
XmNlayoutFrozen, False,
|
|
NULL);
|
|
|
|
rememberGeometry();
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::showColumn()
|
|
{
|
|
int width;
|
|
|
|
XP_ASSERT(m_numvisible < m_numcolumns);
|
|
|
|
width = getColumnWidthByIndex( m_numvisible );
|
|
|
|
D(printf ("I'm showing a column of width %d\n", width);)
|
|
|
|
XtVaSetValues(m_widget,
|
|
XmNlayoutFrozen, True,
|
|
NULL);
|
|
|
|
if (m_resizeColumn != -1 && m_columnIndex [ m_resizeColumn ] < m_numvisible)
|
|
{
|
|
Dimension column_width;
|
|
|
|
column_width = getColumnWidthByIndex(m_columnIndex[ m_resizeColumn] );
|
|
|
|
setColumnWidthByIndex(m_columnIndex[ m_resizeColumn ],
|
|
column_width - width);
|
|
}
|
|
else
|
|
{
|
|
applyDelta(-1 * width);
|
|
}
|
|
|
|
XmLGridRow rowp;
|
|
XmLGridColumn colp;
|
|
|
|
rowp = XmLGridGetRow(m_widget, XmHEADING, 0);
|
|
colp = XmLGridGetColumn(m_widget, XmCONTENT, m_numvisible);
|
|
|
|
Pixel row_cell_bg;
|
|
|
|
XtVaGetValues(m_widget,
|
|
XmNrowPtr, rowp,
|
|
XmNcolumnPtr, colp,
|
|
XmNcellBackground, &row_cell_bg,
|
|
NULL);
|
|
|
|
XmLGridAddColumns(m_widget, XmCONTENT, m_numvisible, 1);
|
|
|
|
XtVaSetValues(m_widget,
|
|
XmNcolumn, m_numvisible,
|
|
XmNcolumnSizePolicy, XmCONSTANT,
|
|
XmNcolumnWidth, width,
|
|
XmNcolumnResizable, m_columnResizable[ m_numvisible ],
|
|
NULL);
|
|
|
|
XtVaSetValues(m_widget,
|
|
XmNlayoutFrozen, False,
|
|
NULL);
|
|
|
|
XtVaSetValues(m_widget,
|
|
XmNrowType, XmHEADING,
|
|
XmNcellBackground, row_cell_bg,
|
|
NULL);
|
|
|
|
m_numvisible ++;
|
|
|
|
rememberGeometry();
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::moveColumn(int column_to_move,
|
|
int destination)
|
|
{
|
|
int delta;
|
|
int tmp;
|
|
int tmp_width;
|
|
XP_Bool tmp_resizable;
|
|
int i;
|
|
|
|
if (column_to_move < destination)
|
|
delta = 1;
|
|
else
|
|
delta = -1;
|
|
|
|
/* save off all information about the column number we're moving */
|
|
tmp = m_columnIndex[ column_to_move ];
|
|
tmp_width = m_columnwidths[ column_to_move ];
|
|
tmp_resizable = m_columnResizable[ column_to_move ];
|
|
|
|
/* move all the other columns out of the way. */
|
|
for (i = column_to_move; i != destination; i += delta)
|
|
{
|
|
m_columnIndex[ i ] = m_columnIndex[ i + delta ];
|
|
m_columnwidths[ i ] = m_columnwidths[ i + delta ];
|
|
m_columnResizable[ i ] = m_columnResizable[ i + delta ];
|
|
}
|
|
|
|
/* replace the column we moved in the place we moved it to */
|
|
m_columnIndex[ destination ] = tmp;
|
|
m_columnwidths[ destination ] = tmp_width;
|
|
m_columnResizable[ destination ] = tmp_resizable;
|
|
|
|
XmLGridMoveColumns(m_widget, destination, column_to_move, 1);
|
|
|
|
rememberGeometry();
|
|
}
|
|
|
|
int
|
|
XFE_Outliner::getSelection(const int **indices, int *count)
|
|
{
|
|
if (indices)
|
|
*indices = m_selectedIndices;
|
|
|
|
if (count)
|
|
*count = m_selectedCount;
|
|
|
|
return m_selectedCount;
|
|
}
|
|
|
|
int
|
|
XFE_Outliner::getTotalLines()
|
|
{
|
|
return m_totalLines;
|
|
}
|
|
|
|
int
|
|
XFE_Outliner::XYToRow(int x, int y, XP_Bool *nearbottom)
|
|
{
|
|
int row;
|
|
int column;
|
|
unsigned char rowtype;
|
|
unsigned char coltype;
|
|
|
|
if (x < 0 || y < 0 ||
|
|
x >= _XfeWidth(m_widget) || y >= _XfeHeight(m_widget))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (XmLGridXYToRowColumn(m_widget, x, y, &rowtype, &row,
|
|
&coltype, &column) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (rowtype != XmCONTENT || coltype != XmCONTENT)
|
|
return -1;
|
|
|
|
if (nearbottom) {
|
|
int row2;
|
|
*nearbottom = (XmLGridXYToRowColumn(m_widget, x, y + 5,
|
|
&rowtype, &row2,
|
|
&coltype, &column) >= 0 &&
|
|
row2 > row);
|
|
}
|
|
return row;
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::addSelection(int selected)
|
|
{
|
|
int i;
|
|
|
|
// if it's already selected, just return.
|
|
for (i = 0; i < m_selectedCount; i ++)
|
|
{
|
|
if ( m_selectedIndices [ i ] == selected )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (i >= m_selectedSize)
|
|
{
|
|
// Resize storage array (exponential growth)
|
|
int *tmp = new int [ m_selectedSize * 2 ];
|
|
memcpy (tmp, m_selectedIndices, m_selectedSize * sizeof(int));
|
|
delete [] m_selectedIndices;
|
|
m_selectedIndices = tmp;
|
|
m_selectedSize *= 2;
|
|
}
|
|
|
|
m_selectedIndices [ i ] = selected;
|
|
m_selectedCount ++;
|
|
|
|
if (selected > m_lastSelectedIndex)
|
|
m_lastSelectedIndex = selected;
|
|
if (selected < m_firstSelectedIndex)
|
|
m_firstSelectedIndex = selected;
|
|
|
|
D(printf ("m_firstSelectedIndex = %d\n", m_firstSelectedIndex);)
|
|
D(printf ("m_lastSelectedIndex = %d\n", m_lastSelectedIndex);)
|
|
|
|
invalidateLine( selected );
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::removeSelection(int selected)
|
|
{
|
|
int i, j;
|
|
|
|
for (i = 0, j = 0; i < m_selectedCount; i ++)
|
|
{
|
|
if (m_selectedIndices [ i ] == selected)
|
|
{
|
|
invalidateLine( selected );
|
|
}
|
|
else
|
|
{
|
|
m_selectedIndices [ j ] = m_selectedIndices [ i ];
|
|
j ++;
|
|
}
|
|
}
|
|
|
|
m_selectedCount = j;
|
|
|
|
m_firstSelectedIndex = (unsigned int)(1 << 31) - 1;
|
|
m_lastSelectedIndex = -1;
|
|
|
|
for (j = 0; j < m_selectedCount; j ++)
|
|
{
|
|
if (m_selectedIndices [ j ] > m_lastSelectedIndex)
|
|
m_lastSelectedIndex = m_selectedIndices [ j ];
|
|
if (m_selectedIndices [ j ] < m_firstSelectedIndex)
|
|
m_firstSelectedIndex = m_selectedIndices [ j ];
|
|
}
|
|
|
|
D(printf ("m_firstSelectedIndex = %d\n", m_firstSelectedIndex);)
|
|
D(printf ("m_lastSelectedIndex = %d\n", m_lastSelectedIndex);)
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::deselectAll()
|
|
{
|
|
m_selectedCount = 0;
|
|
m_firstSelectedIndex = (unsigned int)(1 << 31) - 1;
|
|
m_lastSelectedIndex = -1;
|
|
m_selectionDirection = 1;
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::deselectRangeByIndices(int selection_begin,
|
|
int selection_end)
|
|
{
|
|
int i;
|
|
|
|
if (selection_begin > selection_end)
|
|
{
|
|
int tmp;
|
|
tmp = selection_end;
|
|
selection_end = selection_begin;
|
|
selection_begin = tmp;
|
|
}
|
|
|
|
for (i = selection_begin; i <= selection_end; i ++)
|
|
{
|
|
deselectItem(i);
|
|
}
|
|
}
|
|
|
|
Boolean
|
|
XFE_Outliner::isSelected (int line)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < m_selectedCount; i ++)
|
|
{
|
|
if ( m_selectedIndices[ i ] == line)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::selectRangeByIndices(int selection_begin, int selection_end)
|
|
{
|
|
int i;
|
|
|
|
/*
|
|
** we need to keep track of the direction the user last selected in
|
|
** whether the first selected item was below or above the newest
|
|
** selected item.) This comes in handy in the trimOrExpandSelection
|
|
** routine where we need to know which range to deselect.
|
|
*/
|
|
m_selectionDirection = XfeSgn(selection_end - selection_begin);
|
|
|
|
if (selection_begin > selection_end)
|
|
{
|
|
int tmp;
|
|
tmp = selection_end;
|
|
selection_end = selection_begin;
|
|
selection_begin = tmp;
|
|
}
|
|
|
|
for (i = selection_begin; i <= selection_end; i ++)
|
|
{
|
|
selectItem(i);
|
|
}
|
|
}
|
|
|
|
Boolean
|
|
XFE_Outliner::insertLines(int start, int count)
|
|
{
|
|
int i;
|
|
XP_Bool firstSelectedDone = False;
|
|
XP_Bool lastSelectedDone = False;
|
|
|
|
// shift down the selected items (if they're after the start)
|
|
for (i = 0; i < m_selectedCount; i ++)
|
|
{
|
|
if (m_selectedIndices[ i ] >= start)
|
|
{
|
|
if (!firstSelectedDone &&
|
|
m_selectedIndices[ i ] == m_firstSelectedIndex)
|
|
{
|
|
m_firstSelectedIndex += count;
|
|
firstSelectedDone = True;
|
|
}
|
|
|
|
if (!lastSelectedDone &&
|
|
m_selectedIndices[ i ] == m_lastSelectedIndex)
|
|
{
|
|
m_lastSelectedIndex += count;
|
|
lastSelectedDone = True;
|
|
}
|
|
|
|
m_selectedIndices[ i ] += count;
|
|
}
|
|
}
|
|
|
|
// now invalidate the new lines so they get drawn
|
|
invalidateLines(start, m_totalLines - start + count);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
Boolean
|
|
XFE_Outliner::deleteLines(int start, int count)
|
|
{
|
|
Boolean res = FALSE;
|
|
int i, j;
|
|
XP_Bool firstSelectedDone = False;
|
|
XP_Bool lastSelectedDone = False;
|
|
|
|
|
|
for (i = 0, j = 0; i < m_selectedCount; i++)
|
|
{
|
|
if ( m_selectedIndices[i] >= start )
|
|
{
|
|
if (m_selectedIndices[i] < (start + count))
|
|
{
|
|
res = TRUE;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (!firstSelectedDone &&
|
|
m_selectedIndices[ i ] == m_firstSelectedIndex)
|
|
{
|
|
m_firstSelectedIndex -= count;
|
|
firstSelectedDone = True;
|
|
}
|
|
|
|
if (!lastSelectedDone &&
|
|
m_selectedIndices[ i ] == m_lastSelectedIndex)
|
|
{
|
|
m_lastSelectedIndex -= count;
|
|
lastSelectedDone = True;
|
|
}
|
|
|
|
m_selectedIndices[i] -= count; // Shift into gap
|
|
}
|
|
}
|
|
m_selectedIndices[j] = m_selectedIndices[i];
|
|
j++;
|
|
}
|
|
|
|
invalidateLines(start, m_totalLines - start + count);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::setColumnResizableByIndex(int column_index,
|
|
XP_Bool resizable)
|
|
{
|
|
if (column_index < m_numvisible)
|
|
{
|
|
XtVaSetValues(m_widget,
|
|
XmNcolumn, column_index,
|
|
XmNcolumnResizable, resizable,
|
|
NULL);
|
|
}
|
|
|
|
m_columnResizable[ column_index ] = resizable;
|
|
}
|
|
|
|
XP_Bool
|
|
XFE_Outliner::getColumnResizableByIndex(int column_index)
|
|
{
|
|
return m_columnResizable[ column_index ];
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::setColumnWidthByIndex(int column_index, int width)
|
|
{
|
|
// clamp the width to within acceptable limits.
|
|
if (width < MIN_COLUMN_WIDTH)
|
|
width = MIN_COLUMN_WIDTH;
|
|
else if (width > MAX_COLUMN_WIDTH)
|
|
width = MAX_COLUMN_WIDTH;
|
|
|
|
if (column_index < m_numvisible)
|
|
{
|
|
XtVaSetValues(m_widget,
|
|
XmNcolumn, column_index,
|
|
XmNcolumnSizePolicy, XmCONSTANT,
|
|
XmNcolumnWidth, width,
|
|
NULL);
|
|
}
|
|
|
|
m_columnwidths[ column_index ] = width;
|
|
}
|
|
|
|
int
|
|
XFE_Outliner::getColumnWidthByIndex(int column_index)
|
|
{
|
|
return m_columnwidths[ column_index ];
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::rememberGeometry()
|
|
{
|
|
int i;
|
|
char *ptr;
|
|
int length;
|
|
Dimension height;
|
|
|
|
FREEIF(m_geominfo);
|
|
|
|
length = m_numcolumns * 25 + 40; /* ### got a better size in mind? */
|
|
|
|
XtVaGetValues(m_widget,
|
|
XmNheight, &height,
|
|
NULL);
|
|
|
|
m_geominfo = (char*)XP_ALLOC(length);
|
|
ptr = m_geominfo;
|
|
|
|
PR_snprintf(ptr, m_geominfo + length - ptr,
|
|
"%dx(%d)", height, m_numvisible);
|
|
ptr += strlen(ptr);
|
|
|
|
for (i = 0; i < m_numcolumns; i ++)
|
|
{
|
|
PR_snprintf(ptr, m_geominfo + length - ptr,
|
|
"%s:%d;",
|
|
m_outlinable->getColumnName(m_columnIndex[i]),
|
|
getColumnWidthByIndex(i));
|
|
|
|
ptr += strlen(ptr);
|
|
}
|
|
|
|
if (ptr > m_geominfo) *ptr = '\0';
|
|
|
|
if (m_prefname)
|
|
{
|
|
D(printf ("PREF[%s] = %s\n", m_prefname, m_geominfo);)
|
|
PREF_SetCharPref(m_prefname, m_geominfo);
|
|
}
|
|
|
|
FREEIF(m_geominfo);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::PixmapDraw(Pixmap pixmap, Pixmap mask,
|
|
int pixmapWidth, int pixmapHeight, unsigned char alignment, GC gc,
|
|
XRectangle *rect, XRectangle *clipRect, Pixel bg_pixel)
|
|
{
|
|
Display *dpy;
|
|
Window win;
|
|
int needsClip;
|
|
int x, y, width, height;
|
|
|
|
XP_ASSERT(pixmap != (Pixmap)0);
|
|
|
|
if (pixmap == XmUNSPECIFIED_PIXMAP) return;
|
|
if (rect->width <= 4 || rect->height <= 4) return;
|
|
if (clipRect->width < 3 || clipRect->height < 3) return;
|
|
if (!XtIsRealized(m_widget)) return;
|
|
dpy = XtDisplay(m_widget);
|
|
win = XtWindow(m_widget);
|
|
|
|
width = pixmapWidth;
|
|
height = pixmapHeight;
|
|
if (!width || !height) {
|
|
alignment = XmALIGNMENT_TOP_LEFT;
|
|
width = clipRect->width - 4;
|
|
height = clipRect->height - 4;
|
|
}
|
|
|
|
if (alignment == XmNONE)
|
|
{
|
|
x = rect->x;
|
|
}
|
|
else if (alignment == XmALIGNMENT_TOP_LEFT ||
|
|
alignment == XmALIGNMENT_LEFT ||
|
|
alignment == XmALIGNMENT_BOTTOM_LEFT)
|
|
{
|
|
x = rect->x + 2;
|
|
}
|
|
else if (alignment == XmALIGNMENT_TOP ||
|
|
alignment == XmALIGNMENT_CENTER ||
|
|
alignment == XmALIGNMENT_BOTTOM)
|
|
{
|
|
x = rect->x + ((int)rect->width - width) / 2;
|
|
}
|
|
else
|
|
{
|
|
x = rect->x + rect->width - width - 2;
|
|
}
|
|
|
|
if (alignment == XmNONE)
|
|
{
|
|
y = rect->y;
|
|
}
|
|
if (alignment == XmALIGNMENT_TOP ||
|
|
alignment == XmALIGNMENT_TOP_LEFT ||
|
|
alignment == XmALIGNMENT_TOP_RIGHT)
|
|
{
|
|
y = rect->y + 2;
|
|
}
|
|
else if (alignment == XmALIGNMENT_LEFT ||
|
|
alignment == XmALIGNMENT_CENTER ||
|
|
alignment == XmALIGNMENT_RIGHT)
|
|
{
|
|
y = rect->y + ((int)rect->height - height) / 2;
|
|
}
|
|
else
|
|
{
|
|
y = rect->y + rect->height - height - 2;
|
|
}
|
|
|
|
needsClip = 1;
|
|
if (clipRect->x <= x &&
|
|
clipRect->y <= y &&
|
|
clipRect->x + clipRect->width >= x + width &&
|
|
clipRect->y + clipRect->height >= y + height) {
|
|
needsClip = 0;
|
|
}
|
|
|
|
if (needsClip)
|
|
{
|
|
Pixmap tmp_pixmap;
|
|
GC tmp_gc;
|
|
XWindowAttributes attr;
|
|
|
|
XGetWindowAttributes(dpy, win, &attr);
|
|
tmp_pixmap = XCreatePixmap(dpy, win, clipRect->width, clipRect->height, attr.depth);
|
|
tmp_gc = XCreateGC(dpy, tmp_pixmap, 0, NULL);
|
|
|
|
XSetForeground(dpy, tmp_gc, bg_pixel);
|
|
|
|
XFillRectangle(dpy, tmp_pixmap, tmp_gc, 0, 0, clipRect->width + 1, clipRect->height + 1);
|
|
|
|
if (mask)
|
|
{
|
|
XSetClipMask(dpy, tmp_gc, mask);
|
|
XSetClipOrigin(dpy, tmp_gc, 0, 0);
|
|
}
|
|
|
|
XCopyArea(dpy, pixmap, tmp_pixmap, tmp_gc, 0, 0, width, height, 0, 0);
|
|
|
|
XFreeGC(dpy, tmp_gc);
|
|
|
|
XSetClipRectangles(dpy, gc, 0, 0, clipRect, 1, Unsorted);
|
|
|
|
XSetGraphicsExposures(dpy, gc, False);
|
|
XCopyArea(dpy, tmp_pixmap, win, gc, 0, 0, width, height, x, y);
|
|
XSetGraphicsExposures(dpy, gc, True);
|
|
|
|
XSetClipMask(dpy, gc, None);
|
|
|
|
XFreePixmap(dpy, tmp_pixmap);
|
|
}
|
|
else
|
|
{
|
|
if (mask)
|
|
{
|
|
XSetClipMask(dpy, gc, mask);
|
|
XSetClipOrigin(dpy, gc, x, y);
|
|
}
|
|
XSetGraphicsExposures(dpy, gc, False);
|
|
XCopyArea(dpy, pixmap, win, gc, 0, 0, width, height, x, y);
|
|
XSetGraphicsExposures(dpy, gc, True);
|
|
|
|
if (mask)
|
|
{
|
|
XSetClipMask(dpy, gc, None);
|
|
}
|
|
}
|
|
}
|
|
|
|
EOutlinerPipeType
|
|
XFE_Outliner::getPipeType(XP_Bool expandable,
|
|
XP_Bool expanded,
|
|
int depth,
|
|
OutlinerAncestorInfo *ancestorInfo)
|
|
{
|
|
if (expandable)
|
|
{
|
|
if (ancestorInfo->has_prev && ancestorInfo->has_next)
|
|
return (expanded ? PIPE_OPENMIDDLEPARENT : PIPE_CLOSEDMIDDLEPARENT);
|
|
else if (ancestorInfo->has_prev)
|
|
return (expanded ? PIPE_OPENBOTTOMPARENT : PIPE_CLOSEDBOTTOMPARENT);
|
|
else if (ancestorInfo->has_next && depth)
|
|
return (expanded ? PIPE_OPENMIDDLEPARENT : PIPE_CLOSEDMIDDLEPARENT);
|
|
else if (ancestorInfo->has_next)
|
|
return (expanded ? PIPE_OPENTOPPARENT : PIPE_CLOSEDTOPPARENT);
|
|
else if (depth)
|
|
return (expanded ? PIPE_OPENBOTTOMPARENT : PIPE_CLOSEDBOTTOMPARENT);
|
|
else
|
|
return (expanded ? PIPE_OPENSINGLEPARENT : PIPE_CLOSEDSINGLEPARENT);
|
|
}
|
|
else /* !expandable */
|
|
{
|
|
if (ancestorInfo->has_prev && ancestorInfo->has_next)
|
|
return PIPE_MIDDLEITEM;
|
|
else if (ancestorInfo->has_prev)
|
|
return PIPE_BOTTOMITEM;
|
|
else if (ancestorInfo->has_next && depth)
|
|
return PIPE_MIDDLEITEM;
|
|
else if (ancestorInfo->has_next)
|
|
return PIPE_TOPITEM;
|
|
else if (depth)
|
|
return PIPE_BOTTOMITEM;
|
|
}
|
|
|
|
return PIPE_EMPTYITEM;
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::drawDottedLine(GC gc,
|
|
XRectangle *clipRect,
|
|
int x1,
|
|
int y1,
|
|
int x2,
|
|
int y2)
|
|
{
|
|
int i, x, y;
|
|
XPoint points[100];
|
|
|
|
i = 0;
|
|
for (x = x1; x <= x2; x++)
|
|
for (y = y1; y <= y2; y++)
|
|
{
|
|
if (((x & 1) == (y & 1)) ||
|
|
x < clipRect->x ||
|
|
x >= (clipRect->x + (int)clipRect->width) ||
|
|
y < clipRect->y ||
|
|
y >= (clipRect->y + (int)clipRect->height))
|
|
continue;
|
|
points[i].x = x;
|
|
points[i].y = y;
|
|
if (++i < 100)
|
|
continue;
|
|
XDrawPoints(XtDisplay(m_widget), XtWindow(m_widget), gc, points, i, CoordModeOrigin);
|
|
i = 0;
|
|
}
|
|
if (i)
|
|
XDrawPoints(XtDisplay(m_widget), XtWindow(m_widget), gc, points, i, CoordModeOrigin);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::pipeDraw(XmLGridCallbackStruct *call)
|
|
{
|
|
/*
|
|
** we draw the pipes necessary for this line, and adjust the
|
|
** rectangles within the callback struct to reflect the new,
|
|
** probably smaller, area.
|
|
*/
|
|
XmLGridDrawStruct *draw = call->drawInfo;
|
|
Boolean drawSelected = draw->drawSelected;
|
|
OutlinerAncestorInfo * ancestorInfo;
|
|
int depth;
|
|
int i;
|
|
int maxX = draw->cellRect->width;
|
|
XP_Bool expanded, expandable;
|
|
|
|
m_outlinable->getTreeInfo(&expandable, &expanded, &depth, &ancestorInfo);
|
|
|
|
XSetForeground(XtDisplay(m_widget), draw->gc, draw->foreground);
|
|
|
|
/* save space to draw the descendantselect stuff. */
|
|
if (m_descendantSelectAllowed)
|
|
{
|
|
draw->cellRect->x += PIPEIMAGESIZE; // XXX
|
|
draw->cellRect->width -= PIPEIMAGESIZE;
|
|
}
|
|
|
|
for (i = 0; i < depth; i ++)
|
|
if ( ( i + 1 ) * PIPEIMAGESIZE <= maxX )
|
|
if (ancestorInfo && ancestorInfo[ i ].has_next)
|
|
{
|
|
// the the vertical pipe for this line's ancestors.
|
|
drawDottedLine(draw->gc,
|
|
call->clipRect,
|
|
draw->cellRect->x + PIPEIMAGESIZE/2, draw->cellRect->y,
|
|
draw->cellRect->x + PIPEIMAGESIZE/2, draw->cellRect->y + draw->cellRect->height);
|
|
|
|
draw->cellRect->x += PIPEIMAGESIZE;
|
|
draw->cellRect->width -= PIPEIMAGESIZE;
|
|
}
|
|
else
|
|
{
|
|
// don't know about this block.. must ask will.
|
|
draw->cellRect->x += PIPEIMAGESIZE;
|
|
draw->cellRect->width -= PIPEIMAGESIZE;
|
|
}
|
|
|
|
if (ancestorInfo)
|
|
{
|
|
fe_icon *parent_icon;
|
|
fe_icon *spool_icon = NULL;
|
|
EOutlinerPipeType pipeType = getPipeType(expandable, expanded, depth, &ancestorInfo[depth]);
|
|
|
|
if (m_descendantSelectAllowed && depth == 0 /*&& expandable*/)
|
|
spool_icon = m_outlinable->getColumnIcon(-1 /* special super secret column */);
|
|
|
|
// draw the pipe specific to this line
|
|
|
|
switch (pipeType)
|
|
{
|
|
case PIPE_OPENTOPPARENT:
|
|
case PIPE_OPENMIDDLEPARENT:
|
|
case PIPE_OPENSINGLEPARENT:
|
|
case PIPE_OPENBOTTOMPARENT:
|
|
parent_icon = &openParentIcon;
|
|
break;
|
|
case PIPE_CLOSEDTOPPARENT:
|
|
case PIPE_CLOSEDMIDDLEPARENT:
|
|
case PIPE_CLOSEDSINGLEPARENT:
|
|
case PIPE_CLOSEDBOTTOMPARENT:
|
|
parent_icon = &closedParentIcon;
|
|
break;
|
|
default:
|
|
parent_icon = 0;
|
|
}
|
|
|
|
if (pipeType == PIPE_CLOSEDSINGLEPARENT
|
|
|| pipeType == PIPE_OPENSINGLEPARENT)
|
|
{
|
|
drawDottedLine(draw->gc,
|
|
call->clipRect,
|
|
draw->cellRect->x + PIPEIMAGESIZE/2,
|
|
draw->cellRect->y + draw->cellRect->height/2,
|
|
draw->cellRect->x + PIPEIMAGESIZE - 2,
|
|
draw->cellRect->y + draw->cellRect->height/2);
|
|
}
|
|
|
|
if (pipeType == PIPE_TOPITEM
|
|
|| pipeType == PIPE_OPENTOPPARENT
|
|
|| pipeType == PIPE_CLOSEDTOPPARENT)
|
|
{
|
|
drawDottedLine(draw->gc,
|
|
call->clipRect,
|
|
draw->cellRect->x + PIPEIMAGESIZE/2, draw->cellRect->y + draw->cellRect->height/2,
|
|
draw->cellRect->x + PIPEIMAGESIZE/2, draw->cellRect->y + draw->cellRect->height);
|
|
|
|
drawDottedLine(draw->gc,
|
|
call->clipRect,
|
|
draw->cellRect->x + PIPEIMAGESIZE/2,
|
|
draw->cellRect->y + draw->cellRect->height/2,
|
|
draw->cellRect->x + PIPEIMAGESIZE - 2,
|
|
draw->cellRect->y + draw->cellRect->height/2);
|
|
|
|
}
|
|
else if (pipeType == PIPE_BOTTOMITEM
|
|
|| pipeType == PIPE_OPENBOTTOMPARENT
|
|
|| pipeType == PIPE_CLOSEDBOTTOMPARENT)
|
|
{
|
|
drawDottedLine(draw->gc,
|
|
call->clipRect,
|
|
draw->cellRect->x + PIPEIMAGESIZE/2, draw->cellRect->y,
|
|
draw->cellRect->x + PIPEIMAGESIZE/2, draw->cellRect->y + draw->cellRect->height/2);
|
|
|
|
drawDottedLine(draw->gc,
|
|
call->clipRect,
|
|
draw->cellRect->x + PIPEIMAGESIZE/2,
|
|
draw->cellRect->y + draw->cellRect->height/2,
|
|
draw->cellRect->x + PIPEIMAGESIZE - 2,
|
|
draw->cellRect->y + draw->cellRect->height/2);
|
|
}
|
|
else if (pipeType == PIPE_MIDDLEITEM
|
|
|| pipeType == PIPE_OPENMIDDLEPARENT
|
|
|| pipeType == PIPE_CLOSEDMIDDLEPARENT)
|
|
{
|
|
drawDottedLine(draw->gc,
|
|
call->clipRect,
|
|
draw->cellRect->x + PIPEIMAGESIZE/2, draw->cellRect->y,
|
|
draw->cellRect->x + PIPEIMAGESIZE/2, draw->cellRect->y + draw->cellRect->height);
|
|
|
|
drawDottedLine(draw->gc,
|
|
call->clipRect,
|
|
draw->cellRect->x + PIPEIMAGESIZE/2,
|
|
draw->cellRect->y + draw->cellRect->height/2,
|
|
draw->cellRect->x + PIPEIMAGESIZE - 2,
|
|
draw->cellRect->y + draw->cellRect->height/2);
|
|
}
|
|
|
|
if (m_descendantSelectAllowed && spool_icon)
|
|
{
|
|
// take back our saved space.
|
|
draw->cellRect->x -= PIPEIMAGESIZE;
|
|
draw->cellRect->width += PIPEIMAGESIZE;
|
|
|
|
PixmapDraw(spool_icon->pixmap, spool_icon->mask,
|
|
spool_icon->width, spool_icon->height,
|
|
XmNONE, draw->gc, draw->cellRect,
|
|
call->clipRect,
|
|
drawSelected ? draw->selectBackground : draw->background);
|
|
|
|
draw->cellRect->x += PIPEIMAGESIZE;
|
|
draw->cellRect->width -= PIPEIMAGESIZE;
|
|
}
|
|
|
|
if (parent_icon)
|
|
{
|
|
PixmapDraw(parent_icon->pixmap, parent_icon->mask,
|
|
parent_icon->width, parent_icon->height,
|
|
XmNONE, draw->gc, draw->cellRect,
|
|
call->clipRect,
|
|
drawSelected ? draw->selectBackground : draw->background);
|
|
}
|
|
|
|
#if 0
|
|
if (pipeType != PIPE_EMPTYITEM)
|
|
{
|
|
#endif
|
|
draw->cellRect->x += PIPEIMAGESIZE;
|
|
draw->cellRect->width -= PIPEIMAGESIZE;
|
|
#if 0
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::showHeaderClick()
|
|
{
|
|
D(printf ("trying to show column\n");)
|
|
if (m_numvisible < m_numcolumns)
|
|
{
|
|
showColumn();
|
|
D(printf ("succeeded.\n");)
|
|
}
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::hideHeaderClick()
|
|
{
|
|
D(printf ("trying to hide column\n");)
|
|
if (m_numvisible > 1)
|
|
{
|
|
hideColumn();
|
|
D(printf ("succeeded.\n");)
|
|
}
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::showHeaderDraw(XmLGridCallbackStruct *call)
|
|
{
|
|
XmLGridDrawStruct *draw = call->drawInfo;
|
|
Boolean drawSelected = draw->drawSelected;
|
|
fe_icon *icon;
|
|
|
|
_XmDrawShadows(XtDisplay(m_widget),
|
|
XtWindow(m_widget),
|
|
((XmLGridWidget)m_widget)->manager.top_shadow_GC,
|
|
((XmLGridWidget)m_widget)->manager.bottom_shadow_GC,
|
|
draw->cellRect->x, draw->cellRect->y,
|
|
draw->cellRect->width, draw->cellRect->height,
|
|
1, XmSHADOW_OUT);
|
|
|
|
draw->cellRect->x += 1;
|
|
draw->cellRect->width -= 2;
|
|
draw->cellRect->y += 1;
|
|
draw->cellRect->height -= 2;
|
|
|
|
if (m_numvisible < m_numcolumns)
|
|
icon = &showColumnIcon;
|
|
else
|
|
icon = &showColumnInsensIcon;
|
|
PixmapDraw(icon->pixmap, icon->mask,
|
|
icon->width, icon->height,
|
|
XmALIGNMENT_CENTER, draw->gc, draw->cellRect,
|
|
call->clipRect,
|
|
drawSelected ? draw->selectBackground : draw->background);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::hideHeaderDraw(XmLGridCallbackStruct *call)
|
|
{
|
|
XmLGridDrawStruct *draw = call->drawInfo;
|
|
Boolean drawSelected = draw->drawSelected;
|
|
fe_icon *icon;
|
|
|
|
_XmDrawShadows(XtDisplay(m_widget),
|
|
XtWindow(m_widget),
|
|
((XmLGridWidget)m_widget)->manager.top_shadow_GC,
|
|
((XmLGridWidget)m_widget)->manager.bottom_shadow_GC,
|
|
draw->cellRect->x, draw->cellRect->y,
|
|
draw->cellRect->width, draw->cellRect->height,
|
|
1, XmSHADOW_OUT);
|
|
|
|
draw->cellRect->x += 1;
|
|
draw->cellRect->width -= 2;
|
|
draw->cellRect->y += 1;
|
|
draw->cellRect->height -= 2;
|
|
|
|
if (m_numvisible > 1)
|
|
icon = &hideColumnIcon;
|
|
else
|
|
icon = &hideColumnInsensIcon;
|
|
|
|
PixmapDraw(icon->pixmap, icon->mask,
|
|
icon->width, icon->height,
|
|
XmALIGNMENT_CENTER, draw->gc, draw->cellRect,
|
|
call->clipRect,
|
|
drawSelected ? draw->selectBackground : draw->background);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::headerCellDraw(XmLGridCallbackStruct *call)
|
|
{
|
|
XmLGridDrawStruct *draw = call->drawInfo;
|
|
Boolean drawSelected = draw->drawSelected;
|
|
char *cstr;
|
|
char *style;
|
|
fe_icon *icon;
|
|
int sourcecol;
|
|
XRectangle /*r,*/ textRect;
|
|
XmString xmstr = NULL;
|
|
|
|
sourcecol = m_columnIndex[call->column];
|
|
|
|
style = (char*)styleToTag(m_outlinable->getColumnHeaderStyle(sourcecol));
|
|
cstr = m_outlinable->getColumnHeaderText(sourcecol);
|
|
icon = m_outlinable->getColumnHeaderIcon(sourcecol);
|
|
|
|
/*
|
|
** This is pretty much a hack for the mail/news stuff.
|
|
** we need to keep two columns together, one that has an
|
|
** icon and one that has text, so we just make them one
|
|
** column, and handle the button clicks specially, and also
|
|
** draw funky shadows in the case that a header has both
|
|
** text and an icon.
|
|
*/
|
|
if (cstr && icon)
|
|
{
|
|
_XmDrawShadows(XtDisplay(m_widget),
|
|
XtWindow(m_widget),
|
|
((XmLGridWidget)m_widget)->manager.top_shadow_GC,
|
|
((XmLGridWidget)m_widget)->manager.bottom_shadow_GC,
|
|
draw->cellRect->x, draw->cellRect->y,
|
|
icon->width + 2, draw->cellRect->height,
|
|
1, XmSHADOW_OUT);
|
|
_XmDrawShadows(XtDisplay(m_widget),
|
|
XtWindow(m_widget),
|
|
((XmLGridWidget)m_widget)->manager.top_shadow_GC,
|
|
((XmLGridWidget)m_widget)->manager.bottom_shadow_GC,
|
|
draw->cellRect->x + icon->width + 2,
|
|
draw->cellRect->y,
|
|
draw->cellRect->width - (icon->width + 2),
|
|
draw->cellRect->height,
|
|
1, XmSHADOW_OUT);
|
|
}
|
|
else
|
|
{
|
|
_XmDrawShadows(XtDisplay(m_widget),
|
|
XtWindow(m_widget),
|
|
((XmLGridWidget)m_widget)->manager.top_shadow_GC,
|
|
((XmLGridWidget)m_widget)->manager.bottom_shadow_GC,
|
|
draw->cellRect->x, draw->cellRect->y,
|
|
draw->cellRect->width, draw->cellRect->height,
|
|
1, XmSHADOW_OUT);
|
|
}
|
|
|
|
draw->cellRect->x += 1;
|
|
draw->cellRect->width -= 2;
|
|
draw->cellRect->y += 1;
|
|
draw->cellRect->height -= 2;
|
|
|
|
if (icon)
|
|
{
|
|
PixmapDraw(icon->pixmap, icon->mask,
|
|
icon->width, icon->height,
|
|
XmALIGNMENT_LEFT, draw->gc, draw->cellRect,
|
|
call->clipRect,
|
|
drawSelected ? draw->selectBackground : draw->background);
|
|
|
|
textRect.x = draw->cellRect->x + icon->width + 4;
|
|
textRect.y = draw->cellRect->y;
|
|
textRect.width = draw->cellRect->width - icon->width - 4;
|
|
textRect.height = draw->cellRect->height;
|
|
}
|
|
else
|
|
{
|
|
textRect = *draw->cellRect;
|
|
}
|
|
|
|
if (cstr)
|
|
{
|
|
xmstr = XmStringCreate(cstr, style);
|
|
|
|
XSetForeground(XtDisplay(m_widget), draw->gc,
|
|
draw->foreground);
|
|
|
|
XmLStringDraw(m_widget, xmstr, draw->stringDirection, draw->fontList, draw->alignment,
|
|
draw->gc, &textRect, call->clipRect);
|
|
}
|
|
|
|
if (xmstr) XmStringFree(xmstr);
|
|
|
|
if (sourcecol == m_sortColumn
|
|
&& textRect.width > textRect.height - 4)
|
|
{
|
|
int arrow_direction = (m_sortDirection == OUTLINER_SortAscending
|
|
? XmARROW_DOWN : XmARROW_UP);
|
|
int arrow_size = textRect.height - 4;
|
|
|
|
if (arrow_size > 0)
|
|
{
|
|
XSetForeground(XtDisplay(m_widget), draw->gc,
|
|
draw->background);
|
|
|
|
_XmDrawArrow(XtDisplay(m_widget),
|
|
XtWindow(m_widget),
|
|
((XmManagerWidget)m_widget)->manager.bottom_shadow_GC,
|
|
((XmManagerWidget)m_widget)->manager.top_shadow_GC,
|
|
draw->gc,
|
|
textRect.x + textRect.width - arrow_size - 4,
|
|
textRect.y + (textRect.height / 2 - arrow_size / 2),
|
|
arrow_size,
|
|
arrow_size,
|
|
2,
|
|
arrow_direction);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::contentCellDraw(XmLGridCallbackStruct *call)
|
|
{
|
|
XmLGridDrawStruct *draw = call->drawInfo;
|
|
Boolean drawSelected = draw->drawSelected;
|
|
char *cstr;
|
|
XmString xmstr = NULL;
|
|
fe_icon *icon;
|
|
XRectangle textRect;
|
|
int sourcecol;
|
|
|
|
/*
|
|
** This function will get called (potentially) many times for
|
|
** one line -- once per cell. We only do the aquireLineData
|
|
** once, for the first cell.
|
|
*/
|
|
if (call->row != m_DataRow)
|
|
{
|
|
// if the last line was valid, get rid of it.
|
|
if (m_DataRow != -1)
|
|
m_outlinable->releaseLineData();
|
|
|
|
m_DataRow = call->row;
|
|
|
|
// get the new line's info.
|
|
if (! m_outlinable->acquireLineData(call->row) )
|
|
{
|
|
m_DataRow = -1;
|
|
return;
|
|
}
|
|
}
|
|
|
|
sourcecol = m_columnIndex[call->column];
|
|
|
|
cstr = m_outlinable->getColumnText(sourcecol);
|
|
icon = m_outlinable->getColumnIcon(sourcecol);
|
|
|
|
if (sourcecol == m_pipeColumn)
|
|
pipeDraw(call);
|
|
|
|
/* if there's an icon, we need to push the text over. */
|
|
if (icon)
|
|
{
|
|
PixmapDraw(icon->pixmap, icon->mask,
|
|
icon->width, icon->height,
|
|
XmALIGNMENT_LEFT, draw->gc, draw->cellRect,
|
|
call->clipRect,
|
|
drawSelected ? draw->selectBackground : draw->background);
|
|
|
|
textRect.x = draw->cellRect->x + icon->width + 4;
|
|
textRect.y = draw->cellRect->y;
|
|
textRect.width = draw->cellRect->width - icon->width - 4;
|
|
textRect.height = draw->cellRect->height;
|
|
}
|
|
else
|
|
{
|
|
textRect = *draw->cellRect;
|
|
}
|
|
|
|
if (cstr)
|
|
if (call->clipRect->width > 4)
|
|
{
|
|
/* Impose some spacing between columns. What a hack. ### */
|
|
call->clipRect->width -= 4;
|
|
|
|
xmstr = XmStringCreate(cstr, (char*)styleToTag(m_outlinable->getColumnStyle(sourcecol)));
|
|
|
|
XSetForeground(XtDisplay(m_widget), draw->gc,
|
|
drawSelected ? draw->selectForeground : draw->foreground);
|
|
|
|
XmLStringDraw(m_widget, xmstr, draw->stringDirection, draw->fontList, draw->alignment,
|
|
draw->gc, &textRect, call->clipRect);
|
|
|
|
call->clipRect->width += 4;
|
|
}
|
|
|
|
if (call->row == m_dragrow && sourcecol >= 0)
|
|
{
|
|
int y;
|
|
int x2 = draw->cellRect->x + draw->cellRect->width - 1;
|
|
XP_Bool rightedge = FALSE;
|
|
if (call->column == m_numcolumns - 1)
|
|
{
|
|
rightedge = TRUE;
|
|
#ifdef TERRYS_RIGHT_COLUMN_WIDTH_HACK
|
|
if (xmstr)
|
|
{
|
|
int xx = draw->cellRect->x + XmStringWidth(draw->fontList, xmstr) + 4;
|
|
if (x2 > xx) x2 = xx;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (m_draggc == NULL)
|
|
{
|
|
XGCValues gcv;
|
|
#if 0
|
|
Pixel foreground;
|
|
#endif
|
|
gcv.foreground = drawSelected ? draw->selectForeground : draw->foreground;
|
|
|
|
m_draggc = XCreateGC(XtDisplay(m_widget), XtWindow(m_widget),
|
|
GCForeground, &gcv);
|
|
}
|
|
y = draw->cellRect->y + draw->cellRect->height - 1;
|
|
XDrawLine(XtDisplay(m_widget), XtWindow(m_widget), m_draggc,
|
|
draw->cellRect->x, y, x2, y);
|
|
if (m_dragrowbox)
|
|
{
|
|
int y0 = draw->cellRect->y;
|
|
if (call->column == 0)
|
|
{
|
|
XDrawLine(XtDisplay(m_widget), XtWindow(m_widget), m_draggc,
|
|
draw->cellRect->x, y0, draw->cellRect->x, y);
|
|
}
|
|
if (rightedge)
|
|
{
|
|
XDrawLine(XtDisplay(m_widget), XtWindow(m_widget), m_draggc,
|
|
x2, y0, x2, y);
|
|
}
|
|
XDrawLine(XtDisplay(m_widget), XtWindow(m_widget), m_draggc,
|
|
draw->cellRect->x, y0, x2, y0);
|
|
}
|
|
}
|
|
|
|
if (xmstr) XmStringFree(xmstr);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::celldraw(XtPointer callData)
|
|
{
|
|
XmLGridCallbackStruct* call = (XmLGridCallbackStruct*) callData;
|
|
|
|
if (call->rowType == XmHEADING)
|
|
if (m_hideColumnsAllowed
|
|
&& call->column >= m_numvisible)
|
|
{
|
|
if (call->column == m_numvisible)
|
|
showHeaderDraw(call);
|
|
else
|
|
hideHeaderDraw(call);
|
|
}
|
|
else
|
|
{
|
|
headerCellDraw(call);
|
|
}
|
|
else /* XmCONTENT */
|
|
if (! m_hideColumnsAllowed
|
|
|| call->column < m_numvisible)
|
|
contentCellDraw(call);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::resize(XtPointer callData)
|
|
{
|
|
XmLGridCallbackStruct *cbs = (XmLGridCallbackStruct*)callData;
|
|
int i;
|
|
int width_of_visible_columns = 0;
|
|
int width_of_grid = XtWidth(m_widget) - 2 * MGR_ShadowThickness(m_widget);
|
|
int delta;
|
|
|
|
/*
|
|
** in the event of a widget resize, we do the following:
|
|
** o All resizable, visible columns are resized equally.
|
|
**
|
|
** in the event of a column resize, we do the following:
|
|
** o resize the leftmost resizable column to the right of the column
|
|
** that was resized larger or smaller.
|
|
**
|
|
** we don't allow the right hand side of the right most visible column to
|
|
** be dragged.
|
|
*/
|
|
|
|
XP_ASSERT(m_widget);
|
|
if(!m_widget)
|
|
return;
|
|
|
|
for (i = 0; i < m_numvisible + (m_hideColumnsAllowed ? 2 : 0); i ++)
|
|
{
|
|
/*
|
|
** don't change this to calls to getColumnWidthByIndex. It doesn't
|
|
** work
|
|
** correctly in the face of the show/hide buttons.
|
|
*/
|
|
XmLGridColumn col;
|
|
Dimension column_width = 0;
|
|
|
|
col = XmLGridGetColumn(m_widget, XmCONTENT, i);
|
|
XtVaGetValues(m_widget,
|
|
XmNcolumnPtr, col,
|
|
XmNcolumnWidth, &column_width,
|
|
NULL);
|
|
|
|
width_of_visible_columns += column_width;
|
|
}
|
|
|
|
delta = width_of_grid - width_of_visible_columns;
|
|
|
|
if (delta == 0)
|
|
{
|
|
/*
|
|
** even if the outliner doesn't change in width, we still need to
|
|
** remember the geometry so that the height will come back the same
|
|
*/
|
|
rememberGeometry();
|
|
return;
|
|
}
|
|
|
|
/*
|
|
** The grid resize handling is simple. We just figure out the delta and
|
|
** apply it to all columns of the grid. We also make sure to resize the
|
|
** columns that are not visible, so that when they are next shown they
|
|
** don't overwhelm the outliner.
|
|
*/
|
|
if (cbs->reason == XmCR_RESIZE_GRID)
|
|
{
|
|
D( printf ("Inside XFE_Outliner::resize(GRID)\n");)
|
|
int num_resizable;
|
|
|
|
D( printf ("Width of visible columns is %d. Width of outliner is %d\n",
|
|
width_of_visible_columns, width_of_grid); )
|
|
|
|
num_resizable = numResizableVisibleColumns();
|
|
|
|
XtVaSetValues(m_widget,
|
|
XmNlayoutFrozen, True,
|
|
NULL);
|
|
|
|
// apply the delta to the visible columns.
|
|
applyDelta(delta);
|
|
|
|
/*
|
|
** now we resize the hidden columns so when they are unhidden
|
|
** their size is in line with what we've been doing to the
|
|
** outliner -- shrinking or expanding. You dig?
|
|
*/
|
|
for (i = m_numvisible; i < m_numcolumns; i ++)
|
|
{
|
|
if (getColumnResizableByIndex(i))
|
|
{
|
|
m_columnwidths[ i ] += delta / m_numcolumns;
|
|
|
|
if (m_columnwidths[ i ] < MIN_COLUMN_WIDTH)
|
|
m_columnwidths[ i ] = MIN_COLUMN_WIDTH;
|
|
if (m_columnwidths[ i ] > MAX_COLUMN_WIDTH)
|
|
m_columnwidths[ i ] = MAX_COLUMN_WIDTH;
|
|
}
|
|
}
|
|
|
|
XtVaSetValues(m_widget,
|
|
XmNlayoutFrozen, False,
|
|
NULL);
|
|
}
|
|
/*
|
|
** The column resize handling is a bit more involved than the grid resize
|
|
** handling. The grid actually resizes the column before invoking this
|
|
** callback, so we can figure out the delta the same way we do above. So,
|
|
** the delta will be negative if the user dragged the column to the right,
|
|
** and positive if they dragged to the left.
|
|
**
|
|
** We apply the delta to all the columns to the right of the resized one,
|
|
** and if their is any residual, unapplied delta we add it to the width of
|
|
** the resized column.
|
|
*/
|
|
else if (cbs->reason == XmCR_RESIZE_COLUMN)
|
|
{
|
|
D( printf ("Inside XFE_Outliner::resize(COLUMN, %d)\n", cbs->column);)
|
|
Dimension resize_column_width;
|
|
int delta_left;
|
|
|
|
XtVaSetValues(m_widget,
|
|
XmNlayoutFrozen, True,
|
|
NULL);
|
|
|
|
delta_left = applyDelta(delta, cbs->column + 1);
|
|
|
|
D(printf ("delta_left is %d, delta was %d\n", delta_left, delta);)
|
|
|
|
/*
|
|
** our m_columnwidths array isn't updated by the grid for the
|
|
** resized column, so we have to use GetValues to get it. It's
|
|
** set below (taking into account any unapplied delta.
|
|
*/
|
|
{
|
|
XmLGridColumn col;
|
|
Dimension column_width = 0;
|
|
|
|
col = XmLGridGetColumn(m_widget, XmCONTENT, cbs->column);
|
|
XtVaGetValues(m_widget,
|
|
XmNcolumnPtr, col,
|
|
XmNcolumnWidth, &column_width,
|
|
NULL);
|
|
|
|
resize_column_width = column_width;
|
|
}
|
|
|
|
if (delta_left != 0)
|
|
resize_column_width += delta_left;
|
|
|
|
setColumnWidthByIndex(cbs->column, resize_column_width);
|
|
|
|
XtVaSetValues(m_widget,
|
|
XmNlayoutFrozen, False,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
XP_ASSERT(0);
|
|
}
|
|
|
|
rememberGeometry();
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::outlineLine(int line)
|
|
{
|
|
int old = m_dragrow;
|
|
if (old == line && m_dragrowbox) return;
|
|
m_dragrowbox = TRUE;
|
|
m_dragrow = line;
|
|
if (old >= 0) invalidateLine(old);
|
|
if (line >= 0 && line != old) invalidateLine(line);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::underlineLine(int line)
|
|
{
|
|
int old = m_dragrow;
|
|
if (old == line && !m_dragrowbox) return;
|
|
m_dragrowbox = FALSE;
|
|
m_dragrow = line;
|
|
if (old >= 0) invalidateLine(old);
|
|
if (line >= 0 && line != old) invalidateLine(line);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::sendClick(XEvent *event,
|
|
Boolean only_if_not_selected)
|
|
{
|
|
int x = event->xbutton.x;
|
|
int y = event->xbutton.y;
|
|
int button = ((XButtonEvent *)event)->button;
|
|
int numclicks = 1;
|
|
unsigned char rowtype;
|
|
unsigned char coltype;
|
|
int row;
|
|
int column;
|
|
unsigned int state = event->xbutton.state;
|
|
int sourcecol;
|
|
XRectangle rect;
|
|
|
|
if (!only_if_not_selected &&
|
|
abs(x - m_lastx) < 5 && abs(y - m_lasty) < 5 &&
|
|
(m_lastdowntime - m_lastuptime <=
|
|
XtGetMultiClickTime(XtDisplay(m_widget)))) {
|
|
numclicks = 2; /* ### Allow triple clicks? */
|
|
}
|
|
m_lastx = x;
|
|
m_lasty = y;
|
|
|
|
if (XmLGridXYToRowColumn(m_widget, x, y,
|
|
&rowtype, &row, &coltype, &column) >= 0)
|
|
{
|
|
rect.x = 0;
|
|
rect.y = 0;
|
|
XmLGridRowColumnToXY(m_widget, rowtype, row, coltype, column,
|
|
False, &rect);
|
|
|
|
if (rowtype == XmHEADING) row = -1;
|
|
|
|
m_activity = ButtonPressMask;
|
|
m_ignoreevents = True;
|
|
|
|
if (column >= m_numvisible && m_hideColumnsAllowed)
|
|
{
|
|
if (row != -1)
|
|
return;
|
|
|
|
if (column == m_numvisible)
|
|
{
|
|
showHeaderClick();
|
|
}
|
|
else
|
|
{
|
|
hideHeaderClick();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sourcecol = m_columnIndex[column];
|
|
|
|
/* make the x and y coordinates local to the cell. */
|
|
x -= rect.x;
|
|
y -= rect.y;
|
|
|
|
if (sourcecol == m_pipeColumn && !only_if_not_selected && row >= 0)
|
|
{
|
|
int depth;
|
|
XP_Bool expandable;
|
|
int flippy_offset = 0;
|
|
|
|
m_flippyfunc_data->row = row;
|
|
m_flippyfunc_data->do_selection = FALSE;
|
|
|
|
m_DataRow = row;
|
|
m_outlinable->acquireLineData(m_DataRow);
|
|
m_outlinable->getTreeInfo(&expandable, NULL, &depth, NULL);
|
|
m_outlinable->releaseLineData();
|
|
|
|
if (m_descendantSelectAllowed)
|
|
flippy_offset += PIPEIMAGESIZE; // XXX
|
|
|
|
flippy_offset += depth * PIPEIMAGESIZE;
|
|
|
|
if (expandable)
|
|
if (x >= flippy_offset && x <= flippy_offset + PIPEIMAGESIZE)
|
|
{
|
|
if (numclicks == 1)
|
|
{
|
|
m_outlinable->Flippyfunc(m_flippyfunc_data);
|
|
}
|
|
|
|
return;
|
|
}
|
|
else if (m_descendantSelectAllowed && x > 0 && x < flippy_offset)
|
|
{
|
|
if (numclicks == 1)
|
|
{
|
|
m_flippyfunc_data->do_selection = TRUE;
|
|
m_outlinable->Flippyfunc(m_flippyfunc_data);
|
|
}
|
|
|
|
return;
|
|
}
|
|
// else we drop out and hit the buttonfunc stuff.
|
|
}
|
|
|
|
m_buttonfunc_data->x = x;
|
|
m_buttonfunc_data->y = y;
|
|
m_buttonfunc_data->row = row;
|
|
m_buttonfunc_data->column = sourcecol;
|
|
m_buttonfunc_data->clicks = numclicks;
|
|
m_buttonfunc_data->shift = ((state & ShiftMask) != 0);
|
|
m_buttonfunc_data->ctrl = ((state & ControlMask) != 0);
|
|
m_buttonfunc_data->button = button;
|
|
// alt? how are we supposed to do that?
|
|
|
|
if (m_selBlocked)
|
|
XBell(XtDisplay(m_widget), 100);
|
|
else
|
|
m_outlinable->Buttonfunc(m_buttonfunc_data);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::buttonEvent(XEvent *event, Boolean *c)
|
|
{
|
|
int x = event->xbutton.x;
|
|
int y = event->xbutton.y;
|
|
unsigned char rowtype;
|
|
unsigned char coltype;
|
|
|
|
/* Fix UMR && 83752
|
|
*/
|
|
int row = 0;
|
|
int column = 0;
|
|
|
|
m_ignoreevents = False;
|
|
|
|
switch (event->type)
|
|
{
|
|
case ButtonPress:
|
|
/* Always ignore btn3. Btn3 is for popups. - dp */
|
|
if (event->xbutton.button == 3) break;
|
|
|
|
if (XmLGridXYToRowColumn(m_widget, x, y,
|
|
&rowtype, &row, &coltype, &column) >= 0)
|
|
{
|
|
if (rowtype == XmHEADING)
|
|
{
|
|
|
|
if (XmLGridPosIsResize(m_widget, x, y))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
m_activity |= ButtonPressMask;
|
|
m_ignoreevents = True;
|
|
#if !defined(USE_MOTIF_DND)
|
|
m_dragcolumn = column;
|
|
#endif /* USE_MOTIF_DND */
|
|
}
|
|
|
|
#if !defined(USE_MOTIF_DND)
|
|
m_lastmotionx = x;
|
|
m_lastmotiony = y;
|
|
|
|
// Save this position off so we can create the drag widget
|
|
// in the right place if we're going to drag. Subtract the
|
|
// (row, col) offset first.
|
|
{
|
|
XRectangle hotRect;
|
|
int sourceDepth = 0; // Source icon indented this many times.
|
|
XmLGridRowColumnToXY(m_widget,
|
|
rowtype, row,
|
|
coltype, column,
|
|
False, &hotRect);
|
|
|
|
// Calculate the outline-indent offset.
|
|
m_DataRow = row;
|
|
m_outlinable->acquireLineData(row);
|
|
m_outlinable->getTreeInfo(NULL, NULL, &sourceDepth, NULL);
|
|
m_outlinable->releaseLineData();
|
|
|
|
D(printf("sourceDepth = %d\n", sourceDepth);)
|
|
|
|
// Remember to add one for the flippy.
|
|
m_hotSpot_x = x - hotRect.x - ((sourceDepth+1)*PIPEIMAGESIZE);
|
|
m_hotSpot_y = y - hotRect.y;
|
|
}
|
|
|
|
#endif /* USE_MOTIF_DND */
|
|
m_lastdowntime = event->xbutton.time;
|
|
break;
|
|
|
|
case ButtonRelease:
|
|
if (m_activity & ButtonPressMask)
|
|
#if defined(USE_MOTIF_DND)
|
|
sendClick(event, FALSE);
|
|
#else
|
|
{
|
|
if (m_activity & PointerMotionMask)
|
|
{
|
|
/* handle the drop */
|
|
fe_dnd_DoDrag(&_xfe_dragsource, event, FE_DND_DROP);
|
|
fe_dnd_DoDrag(&_xfe_dragsource, event, FE_DND_END);
|
|
|
|
destroyDragWidget();
|
|
}
|
|
else
|
|
{
|
|
sendClick(event, FALSE);
|
|
}
|
|
}
|
|
|
|
#endif /* USE_MOTIF_DND */
|
|
|
|
m_lastuptime = event->xbutton.time;
|
|
m_activity = 0;
|
|
|
|
break;
|
|
|
|
#if !defined(USE_MOTIF_DND)
|
|
#ifdef NONMOTIF_DND
|
|
case MotionNotify:
|
|
XmLGridXYToRowColumn(m_widget, x, y,
|
|
&rowtype, &row, &coltype, &column);
|
|
|
|
if (!(m_activity & PointerMotionMask) &&
|
|
(abs(x - m_lastmotionx) < 5 && abs(y - m_lastmotiony) < 5))
|
|
{
|
|
/* We aren't yet dragging, and the mouse hasn't moved enough for
|
|
this to be considered a drag. */
|
|
|
|
break;
|
|
}
|
|
|
|
if (m_activity & ButtonPressMask)
|
|
{
|
|
/* The type of stuff we're going to drag. */
|
|
fe_dnd_Type drag_type;
|
|
|
|
/* ok, the pointer moved while a button was held.
|
|
* we're gonna drag some stuff.
|
|
*/
|
|
m_ignoreevents = True;
|
|
|
|
if (!(m_activity & PointerMotionMask))
|
|
{
|
|
if (m_dragtype == FE_DND_NONE)
|
|
{
|
|
/* We don't do drag'n'drop in this context. Do any any visibility
|
|
scrolling that we may have been putting off til the end of user
|
|
activity. */
|
|
m_activity = 0;
|
|
|
|
#ifdef notyet
|
|
if (info->visibleLine >= 0 && info->visibleTimer == NULL) {
|
|
fe_OutlineMakeVisible(info->widget, info->visibleLine);
|
|
info->visibleLine = -1;
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
/* First time. If the item we're pointing at isn't
|
|
selected, deliver a click message for it (which ought to
|
|
select it.) */
|
|
|
|
if (rowtype == XmCONTENT)
|
|
{
|
|
/* Hack things so we click where the mouse down was, not where
|
|
the first notify event was. Bleah. But, only if the
|
|
item was previously unselected. */
|
|
int actual_row = XYToRow(m_lastmotionx, m_lastmotiony);
|
|
|
|
if (!isSelected(actual_row))
|
|
{
|
|
event->xbutton.x = m_lastmotionx;
|
|
event->xbutton.y = m_lastmotiony;
|
|
sendClick(event, TRUE);
|
|
}
|
|
drag_type = m_dragtype;
|
|
}
|
|
else // it happened in a column header.
|
|
{
|
|
// don't allow dragging of the show/hide buttons.
|
|
if (m_dragcolumn >= m_numvisible)
|
|
break;
|
|
|
|
drag_type = FE_DND_COLUMN;
|
|
}
|
|
|
|
|
|
// Create a drag source.
|
|
// We want the mouse-down location, not the last one.
|
|
event->xbutton.x = x;
|
|
event->xbutton.y = y;
|
|
makeDragWidget(m_hotSpot_x, m_hotSpot_y, drag_type);
|
|
|
|
fe_dnd_DoDrag(&_xfe_dragsource, event, FE_DND_START);
|
|
|
|
m_activity |= PointerMotionMask;
|
|
}
|
|
|
|
fe_dnd_DoDrag(&_xfe_dragsource, event, FE_DND_DRAG);
|
|
|
|
/* Now, force all the additional mouse motion events that are
|
|
lingering around on the server to come to us, so that Xt can
|
|
compress them away. Yes, XSync really does improve performance
|
|
in this case, not hurt it. */
|
|
XSync(XtDisplay(m_widget), False);
|
|
}
|
|
|
|
m_lastmotionx = x;
|
|
m_lastmotiony = y;
|
|
break;
|
|
#endif /* NONMOTIF_DND */
|
|
#endif /* !USE_MOTIF_DND */
|
|
}
|
|
if (m_ignoreevents)
|
|
*c = False;
|
|
}
|
|
|
|
#if !defined(USE_MOTIF_DND)
|
|
void
|
|
XFE_Outliner::makeDragWidget(int x, int y, fe_dnd_Type dnd_type)
|
|
{
|
|
Visual *v = 0;
|
|
Colormap cmap = 0;
|
|
Cardinal depth = 0;
|
|
Widget label;
|
|
// fe_icon *icon;
|
|
Widget shell;
|
|
Pixmap dragPixmap;
|
|
|
|
D(printf("hot (x,y) = (%d,%d)\n", x, y);)
|
|
|
|
if (_xfe_dragsource.widget) return;
|
|
|
|
shell = m_toplevel->getBaseWidget();
|
|
|
|
XtVaGetValues (shell, XtNvisual, &v, XtNcolormap, &cmap,
|
|
XtNdepth, &depth, 0);
|
|
|
|
_xfe_dragsource.type = dnd_type;
|
|
|
|
#if 0
|
|
// This only works if we're dragging from the icon.
|
|
// Dragging from some random part of the line gives some
|
|
// really weird offset for the drag. -mcafee
|
|
_xfe_dragsource.hotx = x;
|
|
_xfe_dragsource.hoty = y;
|
|
#else
|
|
// Trying some fixed offset; offset of zero sucks.
|
|
_xfe_dragsource.hotx = 8;
|
|
_xfe_dragsource.hoty = 8;
|
|
#endif
|
|
|
|
_xfe_dragsource.closure = dnd_type == FE_DND_COLUMN ? (void*)this : (void*)m_source;
|
|
_xfe_dragsource.func = m_sourcedropfunc;
|
|
|
|
XP_ASSERT(m_dragicon);
|
|
if (m_dragicon == NULL) return;
|
|
|
|
// XXX this needs to be something special when type == FE_DND_COLUMN
|
|
dragPixmap = m_dragicon->pixmap;
|
|
|
|
_xfe_dragsource.widget = XtVaCreateWidget("drag_win",
|
|
overrideShellWidgetClass,
|
|
m_widget,
|
|
XmNwidth, m_dragicon->width,
|
|
XmNheight, m_dragicon->height,
|
|
XmNvisual, v,
|
|
XmNcolormap, cmap,
|
|
XmNdepth, depth,
|
|
XmNborderWidth, 0,
|
|
NULL);
|
|
|
|
label = XtVaCreateManagedWidget ("label",
|
|
xmLabelWidgetClass,
|
|
_xfe_dragsource.widget,
|
|
XmNlabelType, XmPIXMAP,
|
|
XmNlabelPixmap, dragPixmap,
|
|
NULL);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::destroyDragWidget()
|
|
{
|
|
if (!_xfe_dragsource.widget) return;
|
|
XtDestroyWidget (_xfe_dragsource.widget);
|
|
_xfe_dragsource.widget = NULL;
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::handleDragEvent(XEvent *event,
|
|
fe_dnd_Event type,
|
|
fe_dnd_Source *source)
|
|
{
|
|
// only handle column drags that originated in this outliner.
|
|
if (source->type != FE_DND_COLUMN || source->closure != this) return;
|
|
|
|
switch (type)
|
|
{
|
|
case FE_DND_DROP:
|
|
{
|
|
unsigned char rowtype;
|
|
unsigned char coltype;
|
|
int row;
|
|
int column;
|
|
int x, y;
|
|
|
|
D( printf ("Dropping column %d into an outliner\n", m_dragcolumn);)
|
|
|
|
/* first we make sure that the drop happens within a valid row/column
|
|
pair. */
|
|
|
|
translateFromRootCoords(event->xbutton.x_root, event->xbutton.y_root, &x, &y);
|
|
|
|
D( printf (" Drop position is (%d,%d)\n", x, y); )
|
|
|
|
if (XmLGridXYToRowColumn(m_widget, x, y,
|
|
&rowtype, &row, &coltype, &column) < 0)
|
|
{
|
|
D( printf ("Not dropping in a valid position\n");)
|
|
return;
|
|
}
|
|
|
|
// don't allow dropping onto the show/hide columns */
|
|
if (column >= m_numvisible)
|
|
return;
|
|
|
|
/* we only need to actually do anything if they drop on a different
|
|
column than they started with. */
|
|
if (column != m_dragcolumn)
|
|
moveColumn(m_dragcolumn, column);
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#endif /* USE_MOTIF_DND */
|
|
|
|
void
|
|
XFE_Outliner::celldrawCallback(Widget,
|
|
XtPointer clientData,
|
|
XtPointer callData)
|
|
{
|
|
XFE_Outliner *obj = (XFE_Outliner*)clientData;
|
|
|
|
obj->celldraw(callData);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::resizeCallback(Widget,
|
|
XtPointer clientData,
|
|
XtPointer callData)
|
|
{
|
|
XFE_Outliner *obj = (XFE_Outliner*)clientData;
|
|
|
|
obj->resize(callData);
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::buttonEventHandler(Widget,
|
|
XtPointer clientData,
|
|
XEvent *event,
|
|
Boolean *cont)
|
|
{
|
|
XFE_Outliner *obj = (XFE_Outliner*)clientData;
|
|
|
|
obj->buttonEvent(event, cont);
|
|
}
|
|
|
|
const char *
|
|
XFE_Outliner::styleToTag(EOutlinerTextStyle style)
|
|
{
|
|
switch (style)
|
|
{
|
|
case OUTLINER_Italic:
|
|
return "ITALIC";
|
|
case OUTLINER_Bold:
|
|
return "BOLD";
|
|
case OUTLINER_Default:
|
|
return XmFONTLIST_DEFAULT_TAG;
|
|
default:
|
|
XP_ASSERT(0);
|
|
return XmFONTLIST_DEFAULT_TAG;
|
|
}
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::change(int first, int length, int newnumrows)
|
|
{
|
|
if (newnumrows != m_totalLines)
|
|
{
|
|
int old_totalLines = m_totalLines;
|
|
|
|
// update totalLines here, since adding the rows and
|
|
// redisplaying them might cause the outlinable to ask
|
|
// us how many lines we have, and we should return the
|
|
// new number.
|
|
m_totalLines = newnumrows;
|
|
|
|
if (newnumrows > old_totalLines)
|
|
{
|
|
XmLGridAddRows(m_widget, XmCONTENT, -1,
|
|
m_totalLines - old_totalLines);
|
|
}
|
|
else
|
|
{
|
|
XmLGridDeleteRows(m_widget, XmCONTENT, m_totalLines,
|
|
old_totalLines - m_totalLines);
|
|
}
|
|
|
|
length = m_totalLines - first;
|
|
}
|
|
|
|
if (first == 0 && length == m_totalLines)
|
|
{
|
|
invalidate();
|
|
}
|
|
else
|
|
{
|
|
invalidateLines(first, length);
|
|
}
|
|
|
|
XFlush(XtDisplay(m_widget));
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::listChangeStarting(XP_Bool /*asynchronous*/,
|
|
MSG_NOTIFY_CODE notify,
|
|
MSG_ViewIndex /*where*/,
|
|
int32 /*num*/,
|
|
int32 /*totallines*/)
|
|
{
|
|
/* we need to save off the selected items so they can be restored in
|
|
listChangeFinished. we only do this once for a given series of nested
|
|
changes */
|
|
if (m_listChangeDepth == 0
|
|
/* don't need to save selection when items change their contents*/
|
|
&& notify != MSG_NotifyChanged)
|
|
{
|
|
saveSelection();
|
|
|
|
deselectAllItems();
|
|
}
|
|
|
|
m_listChangeDepth ++;
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::listChangeFinished(XP_Bool /*asynchronous*/,
|
|
MSG_NOTIFY_CODE notify,
|
|
MSG_ViewIndex where,
|
|
int32 num, int32 totallines)
|
|
{
|
|
m_listChangeDepth--;
|
|
|
|
XP_ASSERT(m_listChangeDepth >= 0); // should _never_ be negative.
|
|
|
|
switch (notify) {
|
|
case MSG_NotifyChanged:
|
|
invalidateLines(where, num);
|
|
return;
|
|
case MSG_NotifyNone:
|
|
#if 0
|
|
if (MSG_GetPaneType(pane) == MSG_SEARCHPANE) /* For search dialog */
|
|
if ( CONTEXT_DATA(context)->searchFinishedFunc)
|
|
(*(CONTEXT_DATA(context)->searchFinishedFunc))(context);
|
|
return;
|
|
#endif
|
|
break;
|
|
case MSG_NotifyInsertOrDelete: /* Needed for search */
|
|
break;
|
|
case MSG_NotifyScramble:
|
|
case MSG_NotifyAll:
|
|
where = 0;
|
|
num = totallines;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
change(where, num, totallines);
|
|
|
|
if (m_listChangeDepth == 0)
|
|
restoreSelection();
|
|
}
|
|
|
|
int XFE_Outliner::getListChangeDepth() {
|
|
return m_listChangeDepth;
|
|
}
|
|
|
|
Widget
|
|
XFE_Outliner::getScroller()
|
|
{
|
|
Widget scroller;
|
|
|
|
XtVaGetValues (m_widget, XmNverticalScrollBar, &scroller, 0);
|
|
|
|
return scroller;
|
|
}
|
|
|
|
|
|
void
|
|
XFE_Outliner::saveSelection()
|
|
{
|
|
if (!m_selectedItems)
|
|
{
|
|
int i;
|
|
|
|
if (m_selectedCount)
|
|
{
|
|
m_selectedItems = (void**)XP_CALLOC(m_selectedCount,
|
|
sizeof(void*));
|
|
|
|
for (i = 0; i < m_selectedCount; i ++)
|
|
{
|
|
D( printf ("Saving selecting at index: %d\n",
|
|
m_selectedIndices[ i ]); )
|
|
m_selectedItems[ i ] =
|
|
m_outlinable->ConvFromIndex(m_selectedIndices[i]);
|
|
}
|
|
|
|
m_selectedItemCount = m_selectedCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
XFE_Outliner::restoreSelection()
|
|
{
|
|
if (m_selectedItems)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < m_selectedItemCount; i ++)
|
|
{
|
|
int index = m_outlinable->ConvToIndex(m_selectedItems[i]);
|
|
|
|
if (index > -1 && index < m_totalLines)
|
|
{
|
|
D( printf ("Restoring selecting at index: %d\n",
|
|
index); )
|
|
|
|
selectItem(index);
|
|
}
|
|
}
|
|
|
|
XP_FREE(m_selectedItems);
|
|
m_selectedItemCount = 0;
|
|
m_selectedItems = NULL;
|
|
}
|
|
}
|
|
|
|
XFE_Outlinable *
|
|
XFE_Outliner::getOutlinable()
|
|
{
|
|
return m_outlinable;
|
|
}
|