Example #1
0
static gint nitrox_sort_func(GtkTreeModel *model,
	GtkTreeIter *iter_a,
	GtkTreeIter *iter_b,
	gpointer user_data)
{
	int index_a, index_b;
	struct dive *a, *b;
	int a_o2, b_o2;
	int a_he, b_he;
	int a_o2low, b_o2low;

	gtk_tree_model_get(model, iter_a, DIVE_INDEX, &index_a, -1);
	gtk_tree_model_get(model, iter_b, DIVE_INDEX, &index_b, -1);
	a = get_dive(index_a);
	b = get_dive(index_b);
	get_dive_gas(a, &a_o2, &a_he, &a_o2low);
	get_dive_gas(b, &b_o2, &b_he, &b_o2low);

	/* Sort by Helium first, O2 second */
	if (a_he == b_he) {
		if (a_o2 == b_o2)
			return a_o2low - b_o2low;
		return a_o2 - b_o2;
	}
	return a_he - b_he;
}
Example #2
0
/*
 * When adding dives to the dive table, we try to renumber
 * the new dives based on any old dives in the dive table.
 *
 * But we only do it if:
 *
 *  - the last dive in the old dive table was numbered
 *
 *  - all the new dives are strictly at the end (so the
 *    "last dive" is at the same location in the dive table
 *    after re-sorting the dives.
 *
 *  - none of the new dives have any numbers
 *
 * This catches the common case of importing new dives from
 * a dive computer, and gives them proper numbers based on
 * your old dive list. But it tries to be very conservative
 * and not give numbers if there is *any* question about
 * what the numbers should be - in which case you need to do
 * a manual re-numbering.
 */
static void try_to_renumber(struct dive *last, int preexisting)
{
	int i, nr;

	/*
	 * If the new dives aren't all strictly at the end,
	 * we're going to expect the user to do a manual
	 * renumbering.
	 */
	if (get_dive(preexisting-1) != last)
		return;

	/*
	 * If any of the new dives already had a number,
	 * we'll have to do a manual renumbering.
	 */
	for (i = preexisting; i < dive_table.nr; i++) {
		struct dive *dive = get_dive(i);
		if (dive->number)
			return;
	}

	/*
	 * Ok, renumber..
	 */
	nr = last->number;
	for (i = preexisting; i < dive_table.nr; i++) {
		struct dive *dive = get_dive(i);
		dive->number = ++nr;
	}
}
void DivePictureModel::updateDivePictures(int divenr)
{
	if (numberOfPictures != 0) {
		beginRemoveRows(QModelIndex(), 0, numberOfPictures - 1);
		numberOfPictures = 0;
		endRemoveRows();
	}

	struct dive *d = get_dive(divenr);
	numberOfPictures = dive_get_picture_count(d);
	if (!d || numberOfPictures == 0) {
		return;
	}

	stringPixmapCache.clear();
	QStringList pictures;
	FOR_EACH_PICTURE (d) {
		stringPixmapCache[QString(picture->filename)].picture = picture;
		pictures.push_back(QString(picture->filename));
	}


	SPixmapList retList = QtConcurrent::blockingMapped<SPixmapList>(pictures, scaleImages);
	Q_FOREACH (const SPixmap &pixmap, retList)
		stringPixmapCache[pixmap.first].image = pixmap.second;

	beginInsertRows(QModelIndex(), 0, numberOfPictures - 1);
	endInsertRows();
}
Example #4
0
static void nitrox_data_func(GtkTreeViewColumn *col,
			     GtkCellRenderer *renderer,
			     GtkTreeModel *model,
			     GtkTreeIter *iter,
			     gpointer data)
{
	int index, o2, he, o2low;
	char buffer[80];
	struct dive *dive;

	gtk_tree_model_get(model, iter, DIVE_INDEX, &index, -1);
	dive = get_dive(index);
	get_dive_gas(dive, &o2, &he, &o2low);
	o2 = (o2 + 5) / 10;
	he = (he + 5) / 10;
	o2low = (o2low + 5) / 10;

	if (he)
		snprintf(buffer, sizeof(buffer), "%d/%d", o2, he);
	else if (o2)
		if (o2 == o2low)
			snprintf(buffer, sizeof(buffer), "%d", o2);
		else
			snprintf(buffer, sizeof(buffer), "%d" UTF8_ELLIPSIS "%d", o2low, o2);
	else
		strcpy(buffer, "air");

	g_object_set(renderer, "text", buffer, NULL);
}
Example #5
0
void save_dives(const char *filename)
{
	int i;
	GList *trip = NULL;

	FILE *f = fopen(filename, "w");

	if (!f)
		return;

	/* Flush any edits of current dives back to the dives! */
	update_dive(current_dive);

	fprintf(f, "<dives>\n<program name='subsurface' version='%d'></program>\n", VERSION);

	/* save the trips */
	while ((trip = NEXT_TRIP(trip)) != NULL)
		save_trip(f, trip->data);

	/* save the dives */
	for (i = 0; i < dive_table.nr; i++)
		save_dive(f, get_dive(i));
	fprintf(f, "</dives>\n");
	fclose(f);
}
Example #6
0
void save_dives(const char *filename)
{
	int i;
	struct dive *dive;
	dive_trip_t *trip = NULL;

	FILE *f = g_fopen(filename, "w");

	if (!f)
		return;

	/* Flush any edits of current dives back to the dives! */
	update_dive(current_dive);

	fprintf(f, "<dives>\n<program name='subsurface' version='%d'></program>\n", VERSION);

	/* save the dives */
	for_each_dive(i, dive) {
		dive_trip_t *thistrip = dive->divetrip;
		if (trip != thistrip) {
			/* Close the old trip? */
			if (trip)
				fprintf(f, "</trip>\n");
			/* Open the new one */
			if (thistrip)
				save_trip(f, thistrip);
			trip = thistrip;
		}
		save_dive(f, get_dive(i));
	}
Example #7
0
void MainWindow::current_dive_changed(int divenr)
{
	if (divenr >= 0) {
		select_dive(divenr);
		ui->globe->centerOn(get_dive(selected_dive));
		redrawProfile();
	}
	ui->InfoWidget->updateDiveInfo(divenr);
}
Example #8
0
void MainWindow::planCanceled()
{
	// while planning we might have modified the displayed_dive
	// let's refresh what's shown on the profile
	showProfile();
	ui.newProfile->replot();
	refreshDisplay(false);
	ui.newProfile->plotDive(get_dive(selected_dive));
	DivePictureModel::instance()->updateDivePictures();
}
Example #9
0
void TestRenumber::testMergeAndAppend()
{
	QCOMPARE(parse_file(SUBSURFACE_TEST_DATA "/dives/test47c.xml"), 0);
	process_dives(true, false);
	QCOMPARE(dive_table.nr, 2);
	QCOMPARE(unsaved_changes(), 1);
	struct dive *d = get_dive(1);
	QVERIFY(d != NULL);
	if (d)
		QCOMPARE(d->number, 2);
}
Example #10
0
void ProfileGraphicsView::showEvent(QShowEvent* event)
{
    // Program just opened,
    // but the dive was not ploted.
    // force a replot by modifying the dive
    // hold by the view, and issuing a plot.
    if (dive && !scene()->items().count()) {
        dive = 0;
        plot(get_dive(selected_dive));
    }
}
Example #11
0
QList<int> getDivesInTrip(dive_trip_t *trip)
{
	QList<int> ret;
	for (int i = 0; i < dive_table.nr; i++) {
		struct dive *d = get_dive(i);
		if (d->divetrip == trip) {
			ret.push_back(get_divenr(d));
		}
	}
	return ret;
}
Example #12
0
void MainWindow::current_dive_changed(int divenr)
{
	if (divenr >= 0) {
		select_dive(divenr);
		ui.globe->centerOn(get_dive(selected_dive));
	}

	/* It looks like it's a bit too cumberstone to send *one* dive using a QList,
	 * but this is just futureproofness, it's the best way in the future to show more than
	 * a single profile plot on the canvas. I know that we are using only one right now,
	 * but let's keep like this so it's easy to change when we need? :)
	 */
	ui.newProfile->plotDives(QList<dive *>() << (current_dive));
	ui.InfoWidget->updateDiveInfo(divenr);
}
Example #13
0
// create a new dive. set the current time and add it to the end of the dive list
QString DiveListModel::startAddDive()
{
	struct dive *d;
	d = alloc_dive();
	d->when = QDateTime::currentMSecsSinceEpoch() / 1000L + gettimezoneoffset();
	struct dive *pd = get_dive(dive_table.nr - 1);
	int nr = 1;
	if (pd && pd->number > 0)
		nr = pd->number + 1;
	d->number = nr;
	d->dc.model = strdup("manually added dive");
	add_single_dive(-1, d);
	addDive(d);
	return QString::number(d->id);
}
Example #14
0
int dive_nr_sort(int idx_a, int idx_b, timestamp_t when_a, timestamp_t when_b)
{
	struct dive *a, *b;
	dive_trip_t *tripa = NULL, *tripb = NULL;

	if (idx_a < 0) {
		a = NULL;
		tripa = find_trip_by_idx(idx_a);
	} else {
		a = get_dive(idx_a);
		if (a)
			tripa = a->divetrip;
	}

	if (idx_b < 0) {
		b = NULL;
		tripb = find_trip_by_idx(idx_b);
	} else {
		b = get_dive(idx_b);
		if (b)
			tripb = b->divetrip;
	}

	/*
	 * Compare dive dates within the same trip (or when there
	 * are no trips involved at all). But if we have two
	 * different trips use the trip dates for comparison
	 */
	if (tripa != tripb) {
		if (tripa)
			when_a = tripa->when;
		if (tripb)
			when_b = tripb->when;
	}
	return when_a - when_b;
}
Example #15
0
void DivePlannerPointsModel::setupStartTime()
{
	// if the latest dive is in the future, then start an hour after it ends
	// otherwise start an hour from now
	startTime = QDateTime::currentDateTimeUtc().addSecs(3600 + gettimezoneoffset());
	if (dive_table.nr) {
		struct dive *d = get_dive(dive_table.nr - 1);
		time_t ends = d->when + d->duration.seconds;
		time_t diff = ends - startTime.toTime_t();
		if (diff > 0) {
			startTime = startTime.addSecs(diff + 3600);
		}
	}
	emit startTimeChanged(startTime);
}
Example #16
0
/*
 * This doesn't really report anything at all. We just sort the
 * dives, the GUI does the reporting
 */
void report_dives(gboolean imported)
{
	int i;
	int preexisting = dive_table.preexisting;
	struct dive *last;

	/* This does the right thing for -1: NULL */
	last = get_dive(preexisting-1);

	qsort(dive_table.dives, dive_table.nr, sizeof(struct dive *), sortfn);

	for (i = 1; i < dive_table.nr; i++) {
		struct dive **pp = &dive_table.dives[i-1];
		struct dive *prev = pp[0];
		struct dive *dive = pp[1];
		struct dive *merged;

		if (prev->when + prev->duration.seconds < dive->when)
			continue;

		merged = try_to_merge(prev, dive);
		if (!merged)
			continue;

		free(prev);
		free(dive);
		*pp = merged;
		dive_table.nr--;
		memmove(pp+1, pp+2, sizeof(*pp)*(dive_table.nr - i));

		/* Redo the new 'i'th dive */
		i--;
	}

	if (imported) {
		/* Was the previous dive table state numbered? */
		if (last && last->number)
			try_to_renumber(last, preexisting);

		/* did we have dives in the table and added more? */
		if (last && preexisting != dive_table.nr)
			mark_divelist_changed(TRUE);
	}
	dive_table.preexisting = dive_table.nr;
	dive_list_update_dives();
}
Example #17
0
void save_dives(const char *filename)
{
	int i;
	FILE *f = fopen(filename, "w");

	if (!f)
		return;

	/* Flush any edits of current dives back to the dives! */
	update_dive(current_dive);

	fprintf(f, "<dives>\n<program name='subsurface' version='%d'></program>\n", VERSION);
	for (i = 0; i < dive_table.nr; i++)
		save_dive(f, get_dive(i));
	fprintf(f, "</dives>\n");
	fclose(f);
}
Example #18
0
void DiveTripModel::setupModelData()
{
	int i = dive_table.nr;

	if (rowCount()){
		beginRemoveRows(QModelIndex(), 0, rowCount()-1);
		endRemoveRows();
	}

	if (autogroup)
		autogroup_dives();
	dive_table.preexisting = dive_table.nr;
	while (--i >= 0) {
		struct dive* dive = get_dive(i);
		update_cylinder_related_info(dive);
		dive_trip_t* trip = dive->divetrip;

		DiveItem* diveItem = new DiveItem();
		diveItem->dive = dive;

		if (!trip || currentLayout == LIST) {
			diveItem->parent = rootItem;
			rootItem->children.push_back(diveItem);
			continue;
		}
		if (currentLayout == LIST)
			continue;

		if (!trips.keys().contains(trip)) {
			TripItem* tripItem  = new TripItem();
			tripItem->trip = trip;
			tripItem->parent = rootItem;
			tripItem->children.push_back(diveItem);
			trips[trip] = tripItem;
			rootItem->children.push_back(tripItem);
			continue;
		}
		TripItem* tripItem  = trips[trip];
		tripItem->children.push_back(diveItem);
	}

	if (rowCount()){
		beginInsertRows(QModelIndex(), 0, rowCount() - 1);
		endInsertRows();
	}
}
Example #19
0
static gboolean set_one_dive(GtkTreeModel *model,
			     GtkTreePath *path,
			     GtkTreeIter *iter,
			     gpointer data)
{
	GValue value = {0, };
	struct dive *dive;

	/* Get the dive number */
	gtk_tree_model_get_value(model, iter, DIVE_INDEX, &value);
	dive = get_dive(g_value_get_int(&value));
	if (!dive)
		return TRUE;
	if (data && dive != data)
		return FALSE;

	fill_one_dive(dive, model, iter);
	return dive == data;
}
Example #20
0
/* this gets called when at least two but not all dives are selected */
static void get_ranges(char *buffer, int size)
{
	int i, len;
	int first, last = -1;

	snprintf(buffer, size, _("for dives #"));
	for (i = 0; i < dive_table.nr; i++) {
		struct dive *dive = get_dive(i);
		if (! dive->selected)
			continue;
		if (dive->number < 1) {
			/* uhh - weird numbers - bail */
			snprintf(buffer, size, _("for selected dives"));
			return;
		}
		len = strlen(buffer);
		if (last == -1) {
			snprintf(buffer + len, size - len, "%d", dive->number);
			first = last = dive->number;
		} else {
			if (dive->number == last + 1) {
				last++;
				continue;
			} else {
				if (first == last)
					snprintf(buffer + len, size - len, ", %d", dive->number);
				else if (first + 1 == last)
					snprintf(buffer + len, size - len, ", %d, %d", last, dive->number);
				else
					snprintf(buffer + len, size - len, "-%d, %d", last, dive->number);
				first = last = dive->number;
			}
		}
	}
	len = strlen(buffer);
	if (first != last) {
		if (first + 1 == last)
			snprintf(buffer + len, size - len, ", %d", last);
		else
			snprintf(buffer + len, size - len, "-%d", last);
	}
}
Example #21
0
static void renumber_dialog(GtkWidget *w, gpointer data)
{
	int result;
	struct dive *dive;
	GtkWidget *dialog, *frame, *button, *vbox;

	dialog = gtk_dialog_new_with_buttons("Renumber",
		GTK_WINDOW(main_window),
		GTK_DIALOG_DESTROY_WITH_PARENT,
		GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
		GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
		NULL);

	vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog));

	frame = gtk_frame_new("New starting number");
	gtk_box_pack_start(GTK_BOX(vbox), frame, FALSE, FALSE, 5);

	button = gtk_spin_button_new_with_range(1, 50000, 1);
	gtk_container_add(GTK_CONTAINER(frame), button);

	/*
	 * Do we have a number for the first dive already? Use that
	 * as the default.
	 */
	dive = get_dive(0);
	if (dive && dive->number)
		gtk_spin_button_set_value(GTK_SPIN_BUTTON(button), dive->number);

	gtk_widget_show_all(dialog);
	result = gtk_dialog_run(GTK_DIALOG(dialog));
	if (result == GTK_RESPONSE_ACCEPT) {
		int nr = gtk_spin_button_get_value(GTK_SPIN_BUTTON(button));
		renumber_dives(nr);
		repaint_dive();
	}
	gtk_widget_destroy(dialog);
}
Example #22
0
void MainWindow::on_actionAddDive_triggered()
{
	// clear the selection
	for (int i = 0; i < dive_table.nr; i++) {
		struct dive *d = get_dive(i);
		if (d && d->selected)
			deselect_dive(i);
	}
	disableDcShortcuts();
	DivePlannerPointsModel::instance()->setPlanMode(false);
	// now cheat - create one dive that we use to store the info tab data in
	struct dive *dive = alloc_dive();
	dive->when = QDateTime::currentMSecsSinceEpoch() / 1000L;
	const char* model = strdup(tr("manually added dive").toLocal8Bit().constData());
	dive->dc.model = model; // do not use tr here since it expects a char*.
	record_dive(dive);
	select_dive(get_divenr(dive));
	ui.InfoWidget->updateDiveInfo(selected_dive);
	ui.stackedWidget->setCurrentIndex(PLANNERPROFILE); // Planner.
	ui.infoPane->setCurrentIndex(MAINTAB);
	DivePlannerPointsModel::instance()->createSimpleDive();
	refreshDisplay();
	ui.InfoWidget->addDiveStarted();
}
Example #23
0
/*
 * This doesn't really report anything at all. We just sort the
 * dives, the GUI does the reporting
 */
void report_dives(gboolean is_imported, gboolean prefer_imported)
{
	int i;
	int preexisting = dive_table.preexisting;
	struct dive *last;

	/* check if we need a nickname for the divecomputer for newly downloaded dives;
	 * since we know they all came from the same divecomputer we just check for the
	 * first one */
	if (preexisting < dive_table.nr && dive_table.dives[preexisting]->downloaded)
		set_dc_nickname(dive_table.dives[preexisting]);
	else
		/* they aren't downloaded, so record / check all new ones */
		for (i = preexisting; i < dive_table.nr; i++)
			set_dc_nickname(dive_table.dives[i]);

	/* This does the right thing for -1: NULL */
	last = get_dive(preexisting-1);

	sort_table(&dive_table);

	for (i = 1; i < dive_table.nr; i++) {
		struct dive **pp = &dive_table.dives[i-1];
		struct dive *prev = pp[0];
		struct dive *dive = pp[1];
		struct dive *merged;

		/* only try to merge overlapping dives - or if one of the dives has
		 * zero duration (that might be a gps marker from the webservice) */
		if (prev->duration.seconds && dive->duration.seconds &&
		    prev->when + prev->duration.seconds < dive->when)
			continue;

		merged = try_to_merge(prev, dive, prefer_imported);
		if (!merged)
			continue;

		/* careful - we might free the dive that last points to. Oops... */
		if (last == prev || last == dive)
			last = merged;

		/* Redo the new 'i'th dive */
		i--;
		add_single_dive(i, merged);
		delete_single_dive(i+1);
		delete_single_dive(i+1);
	}
	/* make sure no dives are still marked as downloaded */
	for (i = 1; i < dive_table.nr; i++)
		dive_table.dives[i]->downloaded = FALSE;

	if (is_imported) {
		/* If there are dives in the table, are they numbered */
		if (!last || last->number)
			try_to_renumber(last, preexisting);

		/* did we add dives to the dive table? */
		if (preexisting != dive_table.nr)
			mark_divelist_changed(TRUE);
	}
	dive_list_update_dives();
}
Example #24
0
void Printer::render()
{
	QPointer<ProfileWidget2> profile = MainWindow::instance()->graphics();

	// keep original preferences
	int profileFrameStyle = profile->frameStyle();
	int animationOriginal = prefs.animation_speed;
	double fontScale = profile->getFontPrintScale();

	// apply printing settings to profile
	profile->setFrameStyle(QFrame::NoFrame);
	profile->setPrintMode(true, !printOptions->color_selected);
	profile->setFontPrintScale(0.6);
	profile->setToolTipVisibile(false);
	prefs.animation_speed = 0;

	// render the Qwebview
	QPainter painter;
	QRect viewPort(0, 0, pageSize.width(), pageSize.height());
	painter.begin(printer);
	painter.setRenderHint(QPainter::Antialiasing);
	painter.setRenderHint(QPainter::SmoothPixmapTransform);

	int divesPerPage;
	switch (printOptions->p_template) {
	case print_options::ONE_DIVE:
		divesPerPage = 1;
		break;
	case print_options::TWO_DIVE:
		divesPerPage = 2;
		break;
	}
	int Pages = ceil(getTotalWork() / (float)divesPerPage);

	// get all refereces to diveprofile class in the Html template
	QWebElementCollection collection = webView->page()->mainFrame()->findAllElements(".diveprofile");

	QSize originalSize = profile->size();
	if (collection.count() > 0) {
		profile->resize(collection.at(0).geometry().size());
	}

	int elemNo = 0;
	for (int i = 0; i < Pages; i++) {
		// render the base Html template
		webView->page()->mainFrame()->render(&painter, QWebFrame::ContentsLayer);

		// render all the dive profiles in the current page
		while (elemNo < collection.count() && collection.at(elemNo).geometry().y() < viewPort.y() + viewPort.height()) {
			// dive id field should be dive_{{dive_no}} se we remove the first 5 characters
			int diveNo = collection.at(elemNo).attribute("id").remove(0, 5).toInt(0, 10);
			putProfileImage(collection.at(elemNo).geometry(), viewPort, &painter, get_dive(diveNo - 1), profile);
			elemNo++;
		}

		// scroll the webview to the next page
		webView->page()->mainFrame()->scroll(0, pageSize.height());
		viewPort.adjust(0, pageSize.height(), 0, pageSize.height());

		// rendering progress is 4/5 of total work
		emit(progessUpdated((i * 80.0 / Pages) + done));
		if (i < Pages - 1)
			printer->newPage();
	}
	painter.end();

	// return profle settings
	profile->setFrameStyle(profileFrameStyle);
	profile->setPrintMode(false);
	profile->setFontPrintScale(fontScale);
	profile->setToolTipVisibile(true);
	profile->resize(originalSize);
	prefs.animation_speed = animationOriginal;

	//replot the dive after returning the settings
	profile->plotDive(0, true);
}
Example #25
0
void MainWindow::redrawProfile()
{
	ui->ProfileWidget->plot(get_dive(selected_dive));
}
bool DivelogsDeWebServices::prepare_dives_for_divelogs(const QString &tempfile, const bool selected)
{
	static const char errPrefix[] = "divelog.de-upload:";
	if (!amount_selected) {
		report_error(tr("no dives were selected").toUtf8());
		return false;
	}

	xsltStylesheetPtr xslt = NULL;
	struct zip *zip;

	xslt = get_stylesheet("divelogs-export.xslt");
	if (!xslt) {
		qDebug() << errPrefix << "missing stylesheet";
		return false;
	}


	int error_code;
	zip = zip_open(QFile::encodeName(tempfile), ZIP_CREATE, &error_code);
	if (!zip) {
		char buffer[1024];
		zip_error_to_str(buffer, sizeof buffer, error_code, errno);
		report_error(tr("failed to create zip file for upload: %s").toUtf8(), buffer);
		return false;
	}

	/* walk the dive list in chronological order */
	for (int i = 0; i < dive_table.nr; i++) {
		FILE *f;
		char filename[PATH_MAX];
		int streamsize;
		char *membuf;
		xmlDoc *transformed;
		struct zip_source *s;

		/*
		 * Get the i'th dive in XML format so we can process it.
		 * We need to save to a file before we can reload it back into memory...
		 */
		struct dive *dive = get_dive(i);
		if (!dive)
			continue;
		if (selected && !dive->selected)
			continue;
		f = tmpfile();
		if (!f) {
			report_error(tr("cannot create temporary file: %s").toUtf8(), qt_error_string().toUtf8().data());
			goto error_close_zip;
		}
		save_dive(f, dive);
		fseek(f, 0, SEEK_END);
		streamsize = ftell(f);
		rewind(f);

		membuf = (char *)malloc(streamsize + 1);
		if (!membuf || (streamsize = fread(membuf, streamsize, 1, f)) == 0) {
			report_error(tr("internal error: %s").toUtf8(), qt_error_string().toUtf8().data());
			fclose(f);
			free((void *)membuf);
			goto error_close_zip;
		}
		membuf[streamsize] = 0;
		fclose(f);

		/*
		 * Parse the memory buffer into XML document and
		 * transform it to divelogs.de format, finally dumping
		 * the XML into a character buffer.
		 */
		xmlDoc *doc = xmlReadMemory(membuf, streamsize, "divelog", NULL, 0);
		if (!doc) {
			qWarning() << errPrefix << "could not parse back into memory the XML file we've just created!";
			report_error(tr("internal error").toUtf8());
			free((void *)membuf);
			goto error_close_zip;
		}
		free((void *)membuf);

		transformed = xsltApplyStylesheet(xslt, doc, NULL);
		xmlDocDumpMemory(transformed, (xmlChar **)&membuf, &streamsize);
		xmlFreeDoc(doc);
		xmlFreeDoc(transformed);

		/*
		 * Save the XML document into a zip file.
		 */
		snprintf(filename, PATH_MAX, "%d.xml", i + 1);
		s = zip_source_buffer(zip, membuf, streamsize, 1);
		if (s) {
			int64_t ret = zip_add(zip, filename, s);
			if (ret == -1)
				qDebug() << errPrefix << "failed to include dive:" << i;
		}
	}
	zip_close(zip);
	xsltFreeStylesheet(xslt);
	return true;

error_close_zip:
	zip_close(zip);
	QFile::remove(tempfile);
	xsltFreeStylesheet(xslt);
	return false;
}