hit_matrix_graph.cpp
上传用户:yhdzpy8989
上传日期:2007-06-13
资源大小:13604k
文件大小:20k
- /*
- * ===========================================================================
- * PRODUCTION $Log: hit_matrix_graph.cpp,v $
- * PRODUCTION Revision 1000.3 2004/06/01 21:10:56 gouriano
- * PRODUCTION PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.11
- * PRODUCTION
- * ===========================================================================
- */
- /* $Id: hit_matrix_graph.cpp,v 1000.3 2004/06/01 21:10:56 gouriano Exp $
- * ===========================================================================
- *
- * PUBLIC DOMAIN NOTICE
- * National Center for Biotechnology Information
- *
- * This software/database is a "United States Government Work" under the
- * terms of the United States Copyright Act. It was written as part of
- * the author's official duties as a United States Government employee and
- * thus cannot be copyrighted. This software/database is freely available
- * to the public for use. The National Library of Medicine and the U.S.
- * Government have not placed any restriction on its use or reproduction.
- *
- * Although all reasonable efforts have been taken to ensure the accuracy
- * and reliability of the software and data, the NLM and the U.S.
- * Government do not and cannot warrant the performance or results that
- * may be obtained by using this software or data. The NLM and the U.S.
- * Government disclaim all warranties, express or implied, including
- * warranties of performance, merchantability or fitness for any particular
- * purpose.
- *
- * Please cite the author in any work or product based on this material.
- *
- * ===========================================================================
- *
- * Authors: Andrey Yazhuk
- *
- * File Description:
- *
- */
- #include <ncbi_pch.hpp>
- #include <corelib/ncbistl.hpp>
- #include <gui/opengl/glhelpers.hpp>
- #include <gui/graph/igraph_utils.hpp>
- #include <gui/widgets/hit_matrix/hit_matrix_graph.hpp>
- #include <FL/Fl.H>
- #include <math.h>
- BEGIN_NCBI_SCOPE
- CHitElemGlyph::CHitElemGlyph()
- : m_ColorIndex(-1),
- m_bSelected(false)
- {
- }
- CHitElemGlyph::CHitElemGlyph(const CHitElemDataAdapter& hit_elem)
- : m_HitElem(hit_elem),
- m_ColorIndex(-1),
- m_bSelected(false)
- {
- }
- void CHitElemGlyph::GetModelRect(TModelRect& rc) const
- {
- rc.Init(m_HitElem.GetSubjectStart(), m_HitElem.GetQueryStart());
- TSeqPos size = 1 + m_HitElem.GetLength();
- rc.SetSize(size, size);
- }
- void CHitElemGlyph::Render(CGlPane& pane, ERenderingPass what)
- {
- TModelUnit x1 = 0.5 + m_HitElem.GetSubjectStart();
- TModelUnit y1 = 0.5 + m_HitElem.GetQueryStart();
- TModelUnit l = m_HitElem.GetLength();
- TModelUnit x2 = x1 + l;
- TModelUnit y2 = y1 + l;
- switch(what) {
- case eEndPoints:
- case eHitElemLines: {
- glVertex2d(x1, y1);
- glVertex2d(x2, y2);
- }; break;
- case eProjLines: {
- TModelUnit left = min(x1, pane.GetVisibleRect().Left());
- TModelUnit bottom = min(y1, pane.GetVisibleRect().Bottom());
- glVertex2d(x1, y1);
- glVertex2d(left, y1);
-
- glVertex2d(left, y2);
- glVertex2d(x2, y2);
- glVertex2d(x1, y1);
- glVertex2d(x1, bottom);
-
- glVertex2d(x2, bottom);
- glVertex2d(x2, y2);
- }; break;
- default: break;
- }
- }
- void CHitElemGlyph::StartVertex()
- {
- TModelUnit x1 = 0.5 + m_HitElem.GetSubjectStart();
- TModelUnit y1 = 0.5 + m_HitElem.GetQueryStart();
- glVertex2d(x1, y1);
- }
- void CHitElemGlyph::EndVertex()
- {
- TModelUnit l = m_HitElem.GetLength();
- TModelUnit x1 = 0.5 + m_HitElem.GetSubjectStart() + l;
- TModelUnit y1 = 0.5 + m_HitElem.GetQueryStart() + l;
- glVertex2d(x1, y1);
- }
- inline double square(double x) { return pow(x, 2); }
- double CHitElemGlyph::GetDistPixels(CGlPane& pane, const TVPPoint& pt) const
- {
- TModelUnit x1 = 0.5 + m_HitElem.GetSubjectStart();
- TModelUnit y1 = 0.5 + m_HitElem.GetQueryStart();
- TVPPoint p1 = pane.Project(x1, y1);
-
- TModelUnit l = m_HitElem.GetLength();
- TModelUnit x2 = x1 + l;
- TModelUnit y2 = y1 + l;
- TVPPoint p2 = pane.Project(x2, y2);
- double alpha = atan2((double)(p2.Y() - p1.Y()), (double)(p2.X() - p1.X()));
- double max_proj = sqrt( square(p2.Y() - p1.Y()) + square(p2.X() - p1.X()));
- double beta = atan2((double)(pt.Y() - p1.Y()), (double)(pt.X() - p1.X()));
- double hyp = sqrt( square(pt.Y() - p1.Y()) + square(pt.X() - p1.X()));
- double angle = beta - alpha;
-
- double proj = hyp * cos(angle);
- if(proj >= 0 && proj <= max_proj) { // projection point is on hit
- double ortho_d = fabs( hyp * sin(angle));
- return ortho_d;
- } else { // calculate min of distances to hit's ends
- double d2 = sqrt( square(pt.Y() - p2.Y()) + square(pt.X() - p2.X()));
- return min(hyp, d2);
- }
- }
- bool CHitElemGlyph::InRect(CGlPane& pane, const TVPRect& rc) const
- {
- TModelUnit x1 = 0.5 + m_HitElem.GetSubjectStart();
- TModelUnit y1 = 0.5 + m_HitElem.GetQueryStart();
- TVPPoint p1 = pane.Project(x1, y1);
-
- if(rc.PtInRect(p1)) {
- TModelUnit l = m_HitElem.GetLength();
- TModelUnit x2 = x1 + l;
- TModelUnit y2 = y1 + l;
- TVPPoint p2 = pane.Project(x2, y2);
- return rc.PtInRect(p2);
- }
- return false;
- }
- ///////////////////////////////////////////////////////////////////////////////
- /// CHitGlyph
- CHitGlyph::CHitGlyph(const CHitDataAdapter* p_hit)
- : m_pHit(p_hit)
- {
- _ASSERT(m_pHit);
- // create Elem glyphs
- size_t size = p_hit->GetElemsCount();
- m_Elems.reserve(size);
-
- for( size_t i = 0; i < size; i++ ) {
- CHitElemDataAdapter elem = p_hit->GetElem(i);
- if(elem.GetQueryStart() >=0 && elem.GetSubjectStart() >= 0 ) {
- // not gap - create glyph
- m_Elems.push_back(CHitElemGlyph(elem));
- }
- }
- }
- CHitGlyph::TElemGlyphCont& CHitGlyph::GetElems()
- {
- return m_Elems;
- }
- void CHitGlyph::SetColorIndex(int index)
- {
- NON_CONST_ITERATE(TElemGlyphCont, it, m_Elems) {
- it->SetColorIndex(index);
- }
- }
- void CHitGlyph::Render(CGlPane& pane, CHitElemGlyph::ERenderingPass what)
- {
- int size = m_Elems.size();
- switch(what) {
- case CHitElemGlyph::eConnectionLines: {
- for( int i = 0; i < size - 1; ) {
- m_Elems[i].EndVertex();
- m_Elems[++i].StartVertex();
- }
- }; break;
- default: {
- for( int i = 0; i < size; i++ ) {
- m_Elems[i].Render(pane, what);
- }
- }; //default
- }// switch
- }
- ///////////////////////////////////////////////////////////////////////////////
- /// CHitMatrixGraph
- static float kHitAlpha = 1.0;
- CHitMatrixGraph::CHitMatrixGraph()
- : m_DefaultColor(0.25f, 0.25f, 0.25f, kHitAlpha),
- m_SelColor(0.5f, 0.5f, 1.0f, 1.0f),
- m_HighLightColor(0.8f, 0.8f, 1.0f, 0.5),
- m_PathColor(0.2f, 0.2f, 0.2f, 1.0f),
- m_ProjLinesColor(0.8f, 0.8f, 1.0f, 0.25f),
- m_ProjBackColor(0.8f, 0.8f, 1.0f, 0.25f),
- m_pHost(NULL),
- m_pPane(NULL),
- m_State(eIdle)
- {
- CreateColorTable(32);
- }
- CHitMatrixGraph::~CHitMatrixGraph()
- {
- DeleteGlyphs();
- }
- void CHitMatrixGraph::DeleteGlyphs()
- {
- destroy_and_erase_elems(m_vGlyphs);
- }
- void CHitMatrixGraph::CreateGlyph(const CHitDataAdapter* p_hit_elem)
- {
- m_vGlyphs.push_back(new CHitGlyph(p_hit_elem));
- }
- void CHitMatrixGraph::Render(CGlPane& pane)
- {
- pane.OpenOrtho();
- x_RenderPath(pane);
- x_RenderSelection(pane);
- x_RenderHits(pane);
-
- pane.Close();
- x_RenderEventHandler(pane);
- }
- void CHitMatrixGraph::x_RenderHits(CGlPane& pane)
- {
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- glEnable(GL_LINE_SMOOTH);
- glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
-
- // draw Hit Element Lines
- glColorC(x_GetColorByIndex(m_CurrColorIndex));
-
- glLineWidth(1.5f);
- glBegin(GL_LINES);
- NON_CONST_ITERATE(THitGlyphVector, it, m_vGlyphs) { // for each Hit
- CHitGlyph::TElemGlyphCont& elems = (*it)->GetElems();
- // for each Hit Element
- NON_CONST_ITERATE(CHitGlyph::TElemGlyphCont, itE, elems) {
- if(itE->IsSelected()) {
- glColorC(m_SelColor);
- m_CurrColorIndex = -2;
- } else {
- int ind = itE->GetColorIndex();
- if(ind != m_CurrColorIndex) {
- m_CurrColorIndex = ind;
- glColorC(x_GetColorByIndex(m_CurrColorIndex));
- }
- }
- itE->Render(pane, CHitElemGlyph::eHitElemLines);
- }
- }
- glEnd();
- glDisable(GL_LINE_SMOOTH);
-
- // draw connecting lines between Hit Elements
- glColorC(m_DefaultColor);
- glLineWidth(1.0f);
- glEnable(GL_LINE_STIPPLE);
- glLineStipple(1, 0x5555);
-
- glBegin(GL_LINES);
- NON_CONST_ITERATE(THitGlyphVector, it, m_vGlyphs) {
- (*it)->Render(pane, CHitElemGlyph::eConnectionLines);
- }
- glEnd();
- glDisable(GL_LINE_STIPPLE);
- }
- void CHitMatrixGraph::x_RenderPath(CGlPane& pane)
- {
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- glEnable(GL_LINE_SMOOTH);
- glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
- glLineWidth(6.0f);
- glColor4d(0.8, 0.8, 0.8, 0.5); //###
-
- glBegin(GL_LINES);
- NON_CONST_ITERATE(TPathCont, it, m_Paths) { // for each Hit
- TPath& path = *it;
- // for each Hit Elem in the path
- NON_CONST_ITERATE(TPath, itE, path) {
- if(! (*itE)->IsSelected()) {
- int ind = (*itE)->GetColorIndex();
- const CGlColor* pC = & x_GetColorByIndex(ind);
- glColor4d(pC->GetRed(), pC->GetGreen(), pC->GetBlue(), 0.25);
- (*itE)->Render(pane, CHitElemGlyph::eHitElemLines);
- }
- }
- }
-
- // draw connecting lines
- NON_CONST_ITERATE(TPathCont, it, m_Paths) { // for each Hit
- TPath& path = *it;
- // for each Hit Elem in the path
- TPath::const_iterator it1 = path.begin();
- TPath::const_iterator it2 = ++it1;
- while( it2 != path.end() ) {
- (*it1)->EndVertex();
- (*it2)->StartVertex();
- it1 = it2;
- it2++;
- }
- }
- glEnd();
- }
- void CHitMatrixGraph::x_RenderSelection(CGlPane& pane)
- {
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-
- // draw projection quads
- glColorC(m_ProjBackColor);
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- glBegin(GL_QUADS);
- NON_CONST_ITERATE(TElemGlyphSet, it, m_SelGlyphs) {
- (*it)->Render(pane, CHitElemGlyph::eProjLines);
- }
- glEnd();
-
- // draw projection lines
- glLineWidth(0.5f);
- glColorC(m_ProjLinesColor);
-
- glBegin(GL_LINES);
- NON_CONST_ITERATE(TElemGlyphSet, it, m_SelGlyphs) {
- (*it)->Render(pane, CHitElemGlyph::eProjLines);
- }
- glEnd();
- // draw highlight
- glEnable(GL_LINE_SMOOTH);
- glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
-
- glLineWidth(8.0f);
- glColorC(m_HighLightColor);
- glBegin(GL_LINES);
- NON_CONST_ITERATE(TElemGlyphSet, it, m_SelGlyphs) {
- (*it)->Render(pane, CHitElemGlyph::eHitElemLines);
- }
- glEnd();
- }
- void CHitMatrixGraph::x_RenderEventHandler(CGlPane& pane)
- {
- if(m_State == eSelRect) {
- _ASSERT(m_pHost);
- CGlAttrGuard AttrGuard(GL_POLYGON_BIT);
- pane.OpenPixels();
- glLineWidth(1.0f);
- glColor3f(0.0f, 0.0f, 0.0f);
- glLineStipple(1, 0x0F0F);
- glEnable(GL_LINE_STIPPLE);
- glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
- int x1 = m_StartPoint.X();
- int y1 = m_pHost->HMGH_GetVPPosByY(m_StartPoint.Y());
- int x2 = m_DragPoint.X();
- int y2 = m_pHost->HMGH_GetVPPosByY(m_DragPoint.Y());
- if(x2 < x1)
- swap(x1, x2);
- if(y2 < y1)
- swap(y1, y2);
- glBegin(GL_LINES);
- glVertex2d(x1, y2);
- glVertex2d(x2, y2);
- glVertex2d(x2, y2);
- glVertex2d(x2, y1);
- glVertex2d(x1, y2);
- glVertex2d(x1, y1);
-
- glVertex2d(x1, y1);
- glVertex2d(x2, y1);
- glEnd();
- glDisable(GL_LINE_STIPPLE);
- pane.Close();
- }
- }
- void CHitMatrixGraph::CreateColorTable(int size)
- {
- m_vColors.resize(size);
- static const float base = 0.5f;
- static const float delta = 0.5f;
- float K = 1.0f / (size - 1);
- for( int i = 0; i < size; i++ ) {
- float score = i * K;
- float v = (2 * score - 1) * delta;
- float bad = base - v;
- float good = base + v;
- m_vColors[i] = CGlColor(bad, good, 0, kHitAlpha);
- }
- }
- void CHitMatrixGraph::AssignColorsByScore(CConstRef<CObject_id> score_id)
- {
- double min_v = 0, max_v = 0;
- bool b_init = false;
- size_t size = m_vGlyphs.size();
- vector<double> values(size);
- for( size_t i = 0; i < size; i++ ) {
- double v = m_vGlyphs[i]->GetHit().GetScoreValue(score_id.GetObject());
- values[i] = v;
- if(b_init) {
- min_v = min(min_v, v);
- max_v = max(max_v, v);
- } else {
- min_v = max_v = v;
- b_init = true;
- }
- }
- size_t color_n = m_vColors.size();
- double range = max_v - min_v;
- for( size_t i = 0; i < size; i++ ) {
- double norm = (values[i] - min_v) / range;
- size_t ind = (size_t) floor(norm * color_n);
- ind = (ind == color_n) ? --ind : ind;
- m_vGlyphs[i]->SetColorIndex(ind);
- }
- }
- void CHitMatrixGraph::SetHost(IHitMatrixGraphHost* pHost)
- {
- m_pHost = pHost;
- }
- int CHitMatrixGraph::handle(CGUIEvent& event, CGlPane& pane)
- {
- m_pPane = &pane;
- int res = 0;
- switch(event.GetFLTKEvent()) {
- case FL_PUSH: res = x_OnMousePush(event); break;
- case FL_DRAG: res = x_OnMouseDrag(event); break;
- case FL_RELEASE: res = x_OnMouseRelease(event); break;
- case FL_MOVE: res = x_OnMouseMove(); break;
- case FL_KEYDOWN: res = x_OnKeyDown(); break;
- case FL_KEYUP: res = x_OnKeyUp(); break;
- }
- m_pPane = NULL;
- return res;
- }
- ////////////////////////////////////////////////////////////////////////////////
- /// event handlers
- int CHitMatrixGraph::x_OnMousePush(CGUIEvent& event)
- {
- CGUIEvent::EGUIState state = event.GetGUIState();
- if((state == CGUIEvent::eSelectState || state == CGUIEvent::eSelectIncState)
- && event.GetGUISignal() == CGUIEvent::eSelectSignal) {
- if(Fl::event_clicks() == 0) {
- m_State = eSelPoint;
- m_StartPoint.m_X = Fl::event_x();
- m_StartPoint.m_Y = Fl::event_y();
- m_DragPoint = m_StartPoint;
- bool b_inc = event.GetGUIState() == CGUIEvent::eSelectIncState;
- bool b_selected = x_SelectByPoint(b_inc);
- m_State = b_selected ? eSelPoint : eSelRect;
-
- m_pHost->HMGH_Redraw();
- x_OnSelectCursor();
- }
- return 1;
- }
- return 0;
- }
- int CHitMatrixGraph::x_OnMouseDrag(CGUIEvent& event)
- {
- CGUIEvent::EGUIState state = event.GetGUIState();
- if(m_State == eSelRect && (state == CGUIEvent::eSelectState
- || state == CGUIEvent::eSelectIncState) ) {
-
- if(Fl::event_x() != m_DragPoint.X() || Fl::event_y() != m_DragPoint.Y()) {
- m_State = eSelRect;
- m_DragPoint.m_X = Fl::event_x();
- m_DragPoint.m_Y = Fl::event_y();
- m_pHost->HMGH_Redraw();
- x_OnSelectCursor();
- }
- }
- return 1; // always handle drags
- }
- int CHitMatrixGraph::x_OnMouseRelease(CGUIEvent& event)
- {
- CGUIEvent::EGUIState state = event.GetGUIState();
- if(m_State == eSelPoint) {
- return 1;
- }
- if(m_State == eSelRect && (state == CGUIEvent::eSelectState
- || state == CGUIEvent::eSelectIncState)
- && event.GetGUISignal() == CGUIEvent::eRelease) {
-
- bool b_inc = event.GetGUIState() == CGUIEvent::eSelectIncState;
- x_SelectByRect(b_inc);
-
- m_State = eIdle;
- m_pHost->HMGH_Redraw();
- x_OnSelectCursor();
- return 1;
- }
- return 0;
- }
- int CHitMatrixGraph::x_OnMouseMove(void)
- {
- return (m_State == eIdle) ? 0 : 1;
- }
- int CHitMatrixGraph::x_OnKeyDown(void)
- {
- return (m_State == eIdle) ? 0 : 1;
- }
- int CHitMatrixGraph::x_OnKeyUp(void)
- {
- return (m_State == eIdle) ? 0 : 1;
- }
- void CHitMatrixGraph::x_OnSelectCursor(void)
- {
- switch(m_State) {
- case eIdle: m_Cursor = FL_CURSOR_DEFAULT; break;
- case eSelPoint: m_Cursor = FL_CURSOR_DEFAULT; break;
- case eSelRect: m_Cursor = FL_CURSOR_CROSS; break;
- default: break;
- }
- fl_cursor(m_Cursor, FL_BLACK, FL_WHITE);
- }
- static int kHitTestPrecision = 5;
- bool CHitMatrixGraph::x_SelectByPoint(bool b_inc)
- {
- double min_d = -1;
- CHitElemGlyph* p_hit_elem = NULL;
- TVPPoint pt(m_StartPoint.X(), m_pHost->HMGH_GetVPPosByY(m_StartPoint.Y()));
- NON_CONST_ITERATE(THitGlyphVector, it, m_vGlyphs) { // for each Hit
- CHitGlyph::TElemGlyphCont& elems = (*it)->GetElems();
- // for each Hit Element
- NON_CONST_ITERATE(CHitGlyph::TElemGlyphCont, itE, elems) {
- double d = itE->GetDistPixels(*m_pPane, pt);
- if(! p_hit_elem || d < min_d ) {
- min_d = d;
- p_hit_elem = &(*itE);
- }
- }
- }
- if(! b_inc) { // operation not incremental - clear selection
- NON_CONST_ITERATE(TElemGlyphSet, it, m_SelGlyphs) {
- (*it)->SetSelected(false);
- }
- m_SelGlyphs.clear();
- }
- if(p_hit_elem && min_d <= kHitTestPrecision) {
- if(b_inc && p_hit_elem->IsSelected()) {
- p_hit_elem->SetSelected(false);
- m_SelGlyphs.erase(p_hit_elem);
- } else {
- p_hit_elem->SetSelected(true);
- m_SelGlyphs.insert(p_hit_elem);
- }
- return true;
- }
- return false;
- }
- void CHitMatrixGraph::x_SelectByRect(bool b_inc)
- {
- if(!b_inc) { // operation not incremental - reset selection
- NON_CONST_ITERATE(TElemGlyphSet, it, m_SelGlyphs) {
- (*it)->SetSelected(false);
- }
- m_SelGlyphs.clear();
- }
- int x1 = m_StartPoint.X();
- int y1 = m_pHost->HMGH_GetVPPosByY(m_StartPoint.Y());
- int x2 = m_DragPoint.X();
- int y2 = m_pHost->HMGH_GetVPPosByY(m_DragPoint.Y());
- TVPRect rc( min(x1, x2), min(y1, y2), max(x1, x2), max(y1, y2));
- NON_CONST_ITERATE(THitGlyphVector, it, m_vGlyphs) { // for each Hit
- CHitGlyph::TElemGlyphCont& elems = (*it)->GetElems();
- // for each Hit Element
- NON_CONST_ITERATE(CHitGlyph::TElemGlyphCont, itE, elems) {
- bool b_inside = itE->InRect(*m_pPane, rc);
- if(b_inside) {
- itE->SetSelected(true);
- m_SelGlyphs.insert(&(*itE));
- }
- }
- }
- }
- END_NCBI_SCOPE
- /*
- * ===========================================================================
- * $Log: hit_matrix_graph.cpp,v $
- * Revision 1000.3 2004/06/01 21:10:56 gouriano
- * PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.11
- *
- * Revision 1.11 2004/05/21 22:27:54 gorelenk
- * Added PCH ncbi_pch.hpp
- *
- * Revision 1.10 2004/03/08 15:54:20 yazhuk
- * Fixed CGlAttrGuard usage
- *
- * Revision 1.9 2004/02/17 15:21:52 yazhuk
- * Refactoring - removed CGlEnabler CGlParam
- *
- * Revision 1.8 2004/01/08 19:48:05 yazhuk
- * Minor clean-up
- *
- * Revision 1.7 2003/12/05 17:45:13 yazhuk
- * Added function for retrieving selection, modified AssignColorByScore(); cleaned-up
- *
- * Revision 1.6 2003/12/01 22:36:56 yazhuk
- * Fixed GCC compilation problems
- *
- * Revision 1.5 2003/12/01 17:08:28 yazhuk
- * Redesigned classes in accordance with the new data model.
- *
- * Revision 1.4 2003/11/18 18:06:14 ucko
- * Fixed gcc ERRORS - should #include <math.h> and call fabs() rather than
- * abs() for doubles.
- *
- * Revision 1.3 2003/11/18 17:57:23 yazhuk
- * Fixed GCC warnings
- *
- * Revision 1.2 2003/11/18 00:05:59 yazhuk
- * GCC compilation fixes
- *
- * Revision 1.1 2003/11/17 20:41:19 yazhuk
- * Initial revision
- *
- * ===========================================================================
- */