/* This file is part of the KDE project Copyright (C) 1999 David Faure Copyright (C) 2005 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /* GNUmeric import filter by Phillip Ezolt 6-2-2001 */ /* phillipezolt@hotmail.com */ /* additions: Norbert Andres nandres@web.de */ #include #include #include #include #include #include #include #include #include #include // hehe >:-> #include #include #include #include #include #include #include #include #define SECS_PER_DAY 86400 #define HALF_SEC (0.5 / SECS_PER_DAY) using namespace KSpread; // copied from gnumeric: src/format.c: static const int g_dateSerial_19000228 = 59; /* One less that the Julian day number of 19000101. */ static int g_dateOrigin = 0; // copied from gnumeric: src/formats.c: static char const * cell_format_date [] = { "m/d/yy", /* 0 Cell::date_format5*/ "m/d/yyyy", /* 1 Cell::date_format6*/ "d-mmm-yy", /* 2 Cell::date_format1 18-Feb-99 */ "d-mmm-yyyy", /* 3 Cell::date_format2 18-Feb-1999 */ "d-mmm", /* 4 Cell::date_format3 18-Feb */ "d-mm", /* 5 Cell::date_format4 18-05 */ "mmm/d", /* 6 Cell::date_format11*/ "mm/d", /* 7 Cell::date_format12*/ "mm/dd/yy", /* 8 Cell::date_format19*/ "mm/dd/yyyy", /* 9 Cell::date_format18*/ "mmm/dd/yy", /* 10 Cell::date_format20*/ "mmm/dd/yyyy", /* 11 Cell::date_format21*/ "mmm/ddd/yy", /* 12 */ "mmm/ddd/yyyy", /* 13 */ "mm/ddd/yy", /* 14 */ "mm/ddd/yyyy", /* 15 */ "mmm-yy", /* 16 Cell::date_format7*/ "mmm-yyyy", /* 17 Cell::date_format22*/ "mmmm-yy", /* 18 Cell::date_format8*/ "mmmm-yyyy", /* 19 Cell::date_format9*/ "m/d/yy h:mm", /* 20 */ "m/d/yyyy h:mm", /* 21 */ "yyyy/mm/d", /* 22 Cell::date_format25*/ "yyyy/mmm/d", /* 23 Cell::date_format14*/ "yyyy/mm/dd", /* 24 Cell::date_format25*/ "yyyy/mmm/dd", /* 25 Cell::date_format26*/ "yyyy-mm-d", /* 26 Cell::date_format16*/ "yyyy-mmm-d", /* 27 Cell::date_format15*/ "yyyy-mm-dd", /* 28 Cell::date_format16*/ "yyyy-mmm-dd", /* 29 Cell::date_format15*/ "yy", /* 30 Cell::date_format24*/ "yyyy", /* 31 Cell::date_format23*/ NULL }; // copied from gnumeric: src/formats.c: static char const * cell_format_time [] = { "h:mm AM/PM", // Cell::Time_format1 9 : 01 AM "h:mm:ss AM/PM", // Cell::Time_format2 9:01:05 AM "h:mm", // Cell::Time_format4 9:01 "h:mm:ss", // Cell::Time_format5 9:01:12 "m/d/yy h:mm", "mm:ss", // Cell::Time_format6 01:12 "mm:ss.0", // Cell::Time_format6 01:12 "[h]:mm:ss", "[h]:mm", "[mm]:ss", "[ss]", NULL }; namespace gnumeric_import_LNS { TQStringList list1; TQStringList list2; } using namespace gnumeric_import_LNS; void GNUMERICFilter::dateInit() { // idea copied form gnumeric src/format.c: /* Day 1 means 1st of January of 1900 */ g_dateOrigin = GnumericDate::greg2jul( 1900, 1, 1 ) - 1; } uint GNUMERICFilter::GnumericDate::greg2jul( int y, int m, int d ) { return TQDate::gregorianToJulian( y, m, d ); } void GNUMERICFilter::GnumericDate::jul2greg( double num, int & y, int & m, int & d ) { int i = (int) floor( num + HALF_SEC ); if (i > g_dateSerial_19000228) --i; else if (i == g_dateSerial_19000228 + 1) kdWarning(30521) << "Request for date 02/29/1900." << endl; kdDebug(30521) << "***** Num: " << num << ", i: " << i << endl; TQDate::julianToGregorian( i + g_dateOrigin, y, m, d ); kdDebug(30521) << "y: " << y << ", m: " << m << ", d: " << d << endl; } TQTime GNUMERICFilter::GnumericDate::getTime( double num ) { // idea copied from gnumeric: src/datetime.c num += HALF_SEC; int secs = tqRound( (num - floor(num)) * SECS_PER_DAY ); kdDebug(30521) << "***** Num: " << num << ", secs " << secs << endl; const int h = secs / 3600; secs -= h * 3600; const int m = secs / 60; secs -= h * 60; kdDebug(30521) << "****** h: " << h << ", m: " << m << ", secs: " << secs << endl; const TQTime time( h, m, ( secs < 0 || secs > 59 ? 0 : secs ) ); return time; } typedef KGenericFactory GNUMERICFilterFactory; K_EXPORT_COMPONENT_FACTORY( libgnumericimport, GNUMERICFilterFactory( "kofficefilters" ) ) GNUMERICFilter::GNUMERICFilter( KoFilter *, const char *, const TQStringList & ) : KoFilter() { } /* This converts GNUmeric's color string "0:0:0" to a TQColor. */ void convert_string_to_qcolor(TQString color_string, TQColor * color) { int red, green, blue, first_col_pos, second_col_pos; bool number_ok; first_col_pos = color_string.find(":", 0); second_col_pos = color_string.find(":", first_col_pos + 1); /* Fore="0:0:FF00" */ /* If GNUmeric kicks out some invalid colors, we could crash. */ /* GNUmeric gives us two bytes of color data per element. */ /* We only care about the top byte. */ red = color_string.mid( 0, first_col_pos).toInt( &number_ok, 16 ) >> 8; green = color_string.mid(first_col_pos + 1, (second_col_pos-first_col_pos) - 1).toInt( &number_ok, 16) >> 8; blue = color_string.mid(second_col_pos + 1, (color_string.length() - first_col_pos) - 1).toInt( &number_ok, 16) >> 8; color->setRgb(red, green, blue); } void areaNames( Doc * ksdoc, const TQString &_name, TQString _zone ) { //Sheet2!$A$2:$D$8 TQString tableName; int pos = _zone.find( '!' ); if ( pos != -1 ) { tableName = _zone.left( pos ); _zone = _zone.right( _zone.length()-pos-1 ); pos = _zone.find( ':' ); TQRect rect; if ( pos != -1 ) { TQString left = _zone.mid( 1, pos-1 ); TQString right = _zone.mid( pos+2, _zone.length()-pos-2 ); int pos = left.find( '$' ); rect.setLeft( util_decodeColumnLabelText(left.left(pos ) ) ); rect.setTop( left.right( left.length()-pos-1 ).toInt() ); pos = right.find( '$' ); rect.setRight( util_decodeColumnLabelText(right.left(pos ) ) ); rect.setBottom( right.right( right.length()-pos-1 ).toInt() ); } else { TQString left = _zone; int pos = left.find( '$' ); int leftPos = util_decodeColumnLabelText(left.left(pos ) ); rect.setLeft( leftPos ); rect.setRight( leftPos ); int top = left.right( left.length()-pos-1 ).toInt(); rect.setTop( top ); rect.setBottom( top ); } ksdoc->addAreaName( rect, _name ,tableName); } } void set_document_area_names( Doc * ksdoc, TQDomElement * docElem ) { TQDomNode areaNamesElement = docElem->namedItem( "gmr:Names" ); if ( areaNamesElement.isNull() ) return; TQDomNode areaNameItem = areaNamesElement.namedItem( "gmr:Name" ); while ( !areaNameItem.isNull() ) { TQDomNode gmr_name = areaNameItem.namedItem("gmr:name"); TQDomNode gmr_value = areaNameItem.namedItem("gmr:value"); TQString name = gmr_name.toElement().text(); areaNames( ksdoc, name, gmr_value.toElement().text() ); areaNameItem = areaNameItem.nextSibling(); } } void set_document_attributes( Doc * ksdoc, TQDomElement * docElem) { ksdoc->loadConfigFromFile(); TQDomNode attributes = docElem->namedItem("gmr:Attributes"); if ( attributes.isNull() ) return; TQDomNode attributeItem = attributes.namedItem("gmr:Attribute"); while( !attributeItem.isNull() ) { TQDomNode gmr_name = attributeItem.namedItem("gmr:name"); TQDomNode gmr_value = attributeItem.namedItem("gmr:value"); if (gmr_name.toElement().text() == "WorkbookView::show_horizontal_scrollbar") { ksdoc->setShowHorizontalScrollBar( gmr_value.toElement().text().lower()=="true"? true : false ); } else if ( gmr_name.toElement().text() == "WorkbookView::show_vertical_scrollbar") { ksdoc->setShowVerticalScrollBar( gmr_value.toElement().text().lower()=="true"? true : false ); } else if ( gmr_name.toElement().text() == "WorkbookView::show_notebook_tabs") { ksdoc->setShowTabBar(gmr_value.toElement().text().lower()=="true"? true : false ); } else if ( gmr_name.toElement().text() == "WorkbookView::do_auto_completion") { ksdoc->setCompletionMode( TDEGlobalSettings::CompletionAuto); } else if ( gmr_name.toElement().text() == "WorkbookView::is_protected") { //TODO protect document ??? //ksdoc->map()->isProtected() } attributeItem = attributeItem.nextSibling(); } } /* This sets the documentation information from the information stored in the GNUmeric file. Particularly in the "gmr:Summary" subcategory. */ void set_document_info(KoDocument * document, TQDomElement * docElem) { /* Summary Handling START */ TQDomNode summary = docElem->namedItem("gmr:Summary"); TQDomNode gmr_item = summary.namedItem("gmr:Item"); while( !gmr_item.isNull() ) { TQDomNode gmr_name = gmr_item.namedItem("gmr:name"); TQDomNode gmr_value = gmr_item.namedItem("gmr:val-string"); KoDocumentInfo * DocumentInfo = document->documentInfo(); KoDocumentInfoAbout * aboutPage = static_cast(DocumentInfo->page( "about" )); KoDocumentInfoAuthor * authorPage = static_cast(DocumentInfo->page( "author" )); if (gmr_name.toElement().text() == "title") { aboutPage->setTitle(gmr_value.toElement().text()); } else if (gmr_name.toElement().text() == "keywords") { aboutPage->setKeywords( gmr_value.toElement().text()); } else if (gmr_name.toElement().text() == "comments") { aboutPage->setAbstract(gmr_value.toElement().text()); } else if (gmr_name.toElement().text() == "category") { /* Not supported by KSpread */ } else if (gmr_name.toElement().text() == "manager") { /* Not supported by KSpread */ } else if (gmr_name.toElement().text() == "application") { /* Not supported by KSpread */ } else if (gmr_name.toElement().text() == "author") { authorPage->setFullName(gmr_value.toElement().text()); } else if (gmr_name.toElement().text() == "company") { authorPage->setCompany(gmr_value.toElement().text()); } gmr_item = gmr_item.nextSibling(); } /* Summany Handling STOP */ } void setColInfo(TQDomNode * sheet, Sheet * table) { TQDomNode columns = sheet->namedItem("gmr:Cols"); TQDomNode columninfo = columns.namedItem("gmr:ColInfo"); TQDomElement def = columns.toElement(); if ( def.hasAttribute( "DefaultSizePts" ) ) { bool ok = false; double d = def.attribute( "DefaultSizePts" ).toDouble( &ok ); if ( ok ) { Format::setGlobalColWidth( d ); table->setDefaultWidth( d ); } } while( !columninfo.isNull() ) { TQDomElement e = columninfo.toElement(); // try to convert the node to an element. int column_number; column_number = e.attribute("No").toInt()+1; ColumnFormat *cl = new ColumnFormat(table, column_number); if (e.hasAttribute("Hidden")) { if (e.attribute("Hidden")=="1") { cl->setHide(true); } } if (e.hasAttribute("Unit")) { // xmm = (x_points) * (1 inch / 72 points) * (25.4 mm/ 1 inch) cl->setDblWidth(e.attribute("Unit").toDouble()); //cl->setWidth(e.attribute("Unit").toInt()); } table->insertColumnFormat(cl); columninfo = columninfo.nextSibling(); } } void setRowInfo(TQDomNode *sheet, Sheet *table) { TQDomNode rows = sheet->namedItem("gmr:Rows"); TQDomNode rowinfo = rows.namedItem("gmr:RowInfo"); double d; bool ok = false; TQDomElement def = rows.toElement(); if ( def.hasAttribute( "DefaultSizePts" ) ) { d = def.attribute( "DefaultSizePts" ).toDouble( &ok ); if ( ok ) { Format::setGlobalRowHeight( d ); table->setDefaultHeight( d ); } } while( !rowinfo.isNull() ) { TQDomElement e = rowinfo.toElement(); // try to convert the node to an element. int row_number; row_number = e.attribute("No").toInt() + 1; RowFormat *rl = new RowFormat(table, row_number); if (e.hasAttribute("Hidden")) { if (e.attribute("Hidden") == "1") { rl->setHide(true); } } if (e.hasAttribute("Unit")) { double dbl = e.attribute( "Unit" ).toDouble( &ok ); if ( ok ) rl->setDblHeight( dbl ); } table->insertRowFormat(rl); rowinfo = rowinfo.nextSibling(); } } void setSelectionInfo( TQDomNode * sheet, Sheet * /* table */ ) { TQDomNode selections = sheet->namedItem("gmr:Selections"); TQDomNode selection = selections.namedItem("gmr:Selection"); /* Kspread does not support mutiple selections.. */ /* This code will set the selection to the last one GNUmeric's multiple selections. */ while( !selection.isNull() ) { TQDomElement e = selection.toElement(); // try to convert the node to an element. TQRect kspread_selection; kspread_selection.setLeft((e.attribute("startCol").toInt() + 1)); kspread_selection.setTop((e.attribute("startRow").toInt() + 1)); kspread_selection.setRight((e.attribute("endCol").toInt() + 1)); kspread_selection.setBottom((e.attribute("endRow").toInt() + 1)); /* can't set it in the table -- must set it to a view */ // table->setSelection(kspread_selection); selection = selection.nextSibling(); } } void setObjectInfo(TQDomNode * sheet, Sheet * table) { TQDomNode gmr_objects = sheet->namedItem("gmr:Objects"); TQDomNode gmr_cellcomment = gmr_objects.namedItem("gmr:CellComment"); while( !gmr_cellcomment.isNull() ) { TQDomElement e = gmr_cellcomment.toElement(); // try to convert the node to an element. if (e.hasAttribute("Text")) { if (e.hasAttribute("ObjectBound")) { Point point(e.attribute("ObjectBound")); Cell * cell = table->nonDefaultCell( point.pos().x(), point.pos().y() ); cell->format()->setComment(e.attribute("Text")); } } gmr_cellcomment = gmr_cellcomment.nextSibling(); } } void convertToPen( TQPen & pen, int style ) { switch( style ) { case 0: break; case 1: pen.setStyle( Qt::SolidLine ); pen.setWidth( 1 ); break; case 2: pen.setStyle( Qt::SolidLine ); pen.setWidth( 2 ); break; case 3: pen.setStyle( Qt::DashLine ); pen.setWidth( 1 ); break; case 4: // width should be 1 but otherwise it would be the same as 7 pen.setStyle( Qt::DotLine ); pen.setWidth( 2 ); break; case 5: pen.setStyle( Qt::SolidLine ); pen.setWidth( 3 ); break; case 6: // TODO should be double pen.setStyle( Qt::SolidLine ); pen.setWidth( 1 ); break; case 7: // very thin dots => no match in KSpread pen.setStyle( Qt::DotLine ); pen.setWidth( 1 ); break; case 8: pen.setStyle( Qt::DashLine ); pen.setWidth( 2 ); break; case 9: pen.setStyle( Qt::DashDotLine ); pen.setWidth( 1 ); break; case 10: pen.setStyle( Qt::DashDotLine ); pen.setWidth( 2 ); break; case 11: pen.setStyle( Qt::DashDotDotLine ); pen.setWidth( 1 ); break; case 12: pen.setStyle( Qt::DashDotDotLine ); pen.setWidth( 2 ); break; case 13: // TODO: long dash, short dash, long dash,... pen.setStyle( Qt::DashDotLine ); pen.setWidth( 3 ); break; default: pen.setStyle( Qt::SolidLine ); pen.setWidth( 1 ); } } void GNUMERICFilter::ParseBorder( TQDomElement & gmr_styleborder, Cell * kspread_cell ) { TQDomNode gmr_diagonal = gmr_styleborder.namedItem("gmr:Diagonal"); TQDomNode gmr_rev_diagonal = gmr_styleborder.namedItem("gmr:Rev-Diagonal"); TQDomNode gmr_top = gmr_styleborder.namedItem("gmr:Top"); TQDomNode gmr_bottom = gmr_styleborder.namedItem("gmr:Bottom"); TQDomNode gmr_left = gmr_styleborder.namedItem("gmr:Left"); TQDomNode gmr_right = gmr_styleborder.namedItem("gmr:Right"); // NoPen - no line at all. For example, // TQPainter::drawRect() fills but does not // draw any explicit boundary // line. SolidLine - a simple line. DashLine // - dashes, separated by a few // pixels. DotLine - dots, separated by a // few pixels. DashDotLine - alternately // dots and dashes. DashDotDotLine - one dash, two dots, one dash, two dots... if ( !gmr_left.isNull() ) { TQDomElement e = gmr_left.toElement(); // try to convert the node to an element. importBorder( e, Left, kspread_cell); } if ( !gmr_right.isNull() ) { TQDomElement e = gmr_right.toElement(); // try to convert the node to an element. importBorder( e, Right, kspread_cell); } if ( !gmr_top.isNull() ) { TQDomElement e = gmr_top.toElement(); // try to convert the node to an element. importBorder( e, Top, kspread_cell); } if ( !gmr_bottom.isNull() ) { TQDomElement e = gmr_bottom.toElement(); // try to convert the node to an element. importBorder( e, Bottom, kspread_cell); } if ( !gmr_diagonal.isNull() ) { TQDomElement e = gmr_diagonal.toElement(); // try to convert the node to an element. importBorder( e, Diagonal, kspread_cell); } if ( !gmr_rev_diagonal.isNull() ) { TQDomElement e = gmr_rev_diagonal.toElement(); // try to convert the node to an element. importBorder( e, Revdiagonal, kspread_cell); } // TQDomElement gmr_styleborder_element = gmr_styleborder.toElement(); } void GNUMERICFilter::importBorder( TQDomElement border, borderStyle _style, Cell *cell) { if ( !border.isNull() ) { TQDomElement e = border.toElement(); // try to convert the node to an element. if ( e.hasAttribute( "Style" ) ) { int style = e.attribute( "Style" ).toInt(); TQPen pen; convertToPen( pen, style ); if ( style > 0 ) { switch( _style ) { case Left: cell->setLeftBorderPen( pen ); break; case Right: cell->setRightBorderPen( pen ); break; case Top: cell->setTopBorderPen( pen ); break; case Bottom: cell->setBottomBorderPen( pen ); break; case Diagonal: cell->format()->setFallDiagonalPen( pen ); // check if this is really Fall border break; case Revdiagonal: cell->format()->setGoUpDiagonalPen( pen ); // check if this is really GoUp break; } } if ( e.hasAttribute( "Color" ) ) { TQColor color; TQString colorString = e.attribute( "Color" ); convert_string_to_qcolor( colorString, &color ); { switch( _style ) { case Left: cell->format()->setLeftBorderColor( color ); break; case Right: cell->format()->setRightBorderColor( color ); break; case Top: cell->format()->setTopBorderColor( color ); break; case Bottom: cell->format()->setBottomBorderColor( color ); break; case Diagonal: cell->format()->setFallDiagonalColor( color ); break; case Revdiagonal: cell->format()->setGoUpDiagonalPen( color ); break; } } } } } } bool GNUMERICFilter::setType( Cell * kspread_cell, TQString const & formatString, TQString & cell_content ) { int i = 0; for ( i = 0; cell_format_date[i] ; ++i ) { kdDebug(30521) << "Cell_format: " << cell_format_date[i] << ", FormatString: " << formatString << endl; if ( ( formatString == "d/m/yy" ) || ( formatString == cell_format_date[i] ) ) { kdDebug(30521) << " FormatString: Date: " << formatString << ", CellContent: " << cell_content << endl; TQDate date; if ( !kspread_cell->isDate() ) { // convert cell_content to date int y, m, d; bool ok = true; int val = cell_content.toInt( &ok ); kdDebug(30521) << "!!! FormatString: Date: " << formatString << ", CellContent: " << cell_content << ", Double: " << val << endl; if ( !ok ) return false; GnumericDate::jul2greg( val, y, m, d ); kdDebug(30521) << " num: " << val << ", y: " << y << ", m: " << m << ", d: " << d << endl; date.setYMD( y, m, d ); } else date = kspread_cell->value().asDate(); FormatType type; switch( i ) { case 0: type = date_format5; break; case 1: type = date_format6; break; case 2: type = date_format1; break; case 3: type = date_format2; break; case 4: type = date_format3; break; case 5: type = date_format4; break; case 6: type = date_format11; break; case 7: type = date_format12; break; case 8: type = date_format19; break; case 9: type = date_format18; break; case 10: type = date_format20; break; case 11: type = date_format21; break; case 16: type = date_format7; break; case 17: type = date_format22; break; case 18: type = date_format8; break; case 19: type = date_format9; break; case 22: type = date_format25; break; case 23: type = date_format14; break; case 24: type = date_format25; break; case 25: type = date_format26; break; case 26: type = date_format16; break; case 27: type = date_format15; break; case 28: type = date_format16; break; case 29: type = date_format15; break; case 30: type = date_format24; break; case 31: type = date_format23; break; default: type = ShortDate_format; break; /* 12, 13, 14, 15, 20, 21 */ } kdDebug(30521) << "i: " << i << ", Type: " << type << ", Date: " << TQString(date.toString()) << endl; kspread_cell->setValue( date ); kspread_cell->format()->setFormatType( type ); return true; } } for ( i = 0; cell_format_time[i] ; ++i ) { if (formatString == cell_format_time[i]) { TQTime time; if ( !kspread_cell->isTime() ) { bool ok = true; double content = cell_content.toDouble( &ok ); kdDebug(30521) << " FormatString: Time: " << formatString << ", CellContent: " << cell_content << ", Double: " << content << endl; if ( !ok ) return false; time = GnumericDate::getTime( content ); } else time = kspread_cell->value().asTime(); FormatType type; switch( i ) { case 0: type = Time_format1; break; case 1: type = Time_format2; break; case 2: type = Time_format4; break; case 3: type = Time_format5; break; case 5: type = Time_format6; break; case 6: type = Time_format6; break; default: type = Time_format1; break; } kdDebug(30521) << "i: " << i << ", Type: " << type << endl; kspread_cell->setValue( time ); kspread_cell->format()->setFormatType( type ); return true; } } return false; // no date or time } TQString GNUMERICFilter::convertVars( TQString const & str, Sheet * table ) const { TQString result( str ); uint count = list1.count(); if ( count == 0 ) { list1 << "&[TAB]" << "&[DATE]" << "&[PAGE]" << "&[PAGES]"<<"&[TIME]" << "&[FILE]"; list2 << "" << "" << "" << "" << "