Exemplo n.º 1
0
listview *listview_create ( const char *name, listview_update_callback cb, void *udata, unsigned int eh, gboolean reverse )
{
    listview *lv  = g_malloc0 ( sizeof ( listview ) );
    gchar    *box = g_strjoin ( ".", name, "box", NULL );
    widget_init ( WIDGET ( lv ), box );
    g_free ( box );
    lv->listview_name             = g_strdup ( name );
    lv->widget.free               = listview_free;
    lv->widget.resize             = listview_resize;
    lv->widget.draw               = listview_draw;
    lv->widget.clicked            = listview_clicked;
    lv->widget.motion_notify      = listview_motion_notify;
    lv->widget.get_desired_height = listview_get_desired_height;
    lv->widget.enabled            = TRUE;
    lv->eh                        = eh;

    char *n = g_strjoin ( ".", lv->listview_name, "scrollbar", NULL );
    lv->scrollbar = scrollbar_create ( n );
    // Default position on right.
    lv->scrollbar->widget.index = rofi_theme_get_integer_exact ( WIDGET ( lv->scrollbar ), "index", 1 );
    g_free ( n );
    widget_set_clicked_handler ( WIDGET ( lv->scrollbar ), listview_scrollbar_clicked, lv );
    lv->scrollbar->widget.parent = WIDGET ( lv );
    // Calculate height of an element.
    //
    char    *tb_name = g_strjoin ( ".", lv->listview_name, "element", NULL );
    textbox *tb      = textbox_create ( tb_name, 0, NORMAL, "" );
    lv->element_height = textbox_get_estimated_height ( tb, lv->eh );
    g_free ( tb_name );
    widget_free ( WIDGET ( tb ) );

    lv->callback = cb;
    lv->udata    = udata;

    // Some settings.
    lv->spacing         = rofi_theme_get_distance ( WIDGET ( lv ), "spacing", DEFAULT_SPACING );
    lv->menu_columns    = rofi_theme_get_integer  ( WIDGET ( lv ), "columns", config.menu_columns );
    lv->fixed_num_lines = rofi_theme_get_boolean  ( WIDGET ( lv ), "fixed-height", config.fixed_num_lines );
    lv->dynamic         = rofi_theme_get_boolean  ( WIDGET ( lv ), "dynamic", TRUE );
    lv->reverse         = rofi_theme_get_boolean  ( WIDGET ( lv ), "reverse", reverse );
    listview_set_show_scrollbar ( lv, rofi_theme_get_boolean ( WIDGET ( lv ), "scrollbar", !config.hide_scrollbar ) );
    lv->cycle = rofi_theme_get_boolean ( WIDGET ( lv ), "cycle", config.cycle );

    return lv;
}
Exemplo n.º 2
0
int main ( G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv )
{
    scrollbar * sb = scrollbar_create ( NULL, "scrollbar" );
    widget_resize ( WIDGET (sb), 10, 100);

    scrollbar_set_handle ( NULL, 10213);
    scrollbar_set_max_value ( NULL, 10 );
    scrollbar_set_handle_length ( NULL , 1000);

    scrollbar_set_max_value ( sb, 10000);
    TASSERTE ( sb->length, 10000 );
    scrollbar_set_handle_length ( sb, 10);
    TASSERTE ( sb->pos_length, 10 );
    scrollbar_set_handle ( sb , 5000 );
    TASSERTE ( sb->pos, 5000 );
    scrollbar_set_handle ( sb , 15000 );
    TASSERTE ( sb->pos, 10000 );
    scrollbar_set_handle ( sb , UINT32_MAX );
    TASSERTE ( sb->pos, 10000 );
    scrollbar_set_handle_length ( sb, 15000);
    TASSERTE ( sb->pos_length, 10000 );
    scrollbar_set_handle_length ( sb, 0);
    TASSERTE ( sb->pos_length, 1 );


    guint cl = scrollbar_scroll_get_line ( sb, 10 );
    TASSERTE ( cl, 1010);
    cl = scrollbar_scroll_get_line ( sb, 20 );
    TASSERTE ( cl, 2020);
    cl = scrollbar_scroll_get_line ( sb, 0 );
    TASSERTE ( cl, 0);
    cl = scrollbar_scroll_get_line ( sb, 99 );
    TASSERTE ( cl, 9999);
    scrollbar_set_handle_length ( sb, 1000);
    cl = scrollbar_scroll_get_line ( sb, 10 );
    TASSERTE ( cl, 555);
    cl = scrollbar_scroll_get_line ( sb, 20 );
    TASSERTE ( cl, 1666);
    cl = scrollbar_scroll_get_line ( sb, 0 );
    TASSERTE ( cl, 0);
    cl = scrollbar_scroll_get_line ( sb, 99 );
    TASSERTE ( cl, 9999);

    widget_free( WIDGET (sb ) );
}
Exemplo n.º 3
0
void list_create(list_t *m,int x,int y,int w,int h,char *(*refresh)(list_t*,int))
{
	memset(m,0,sizeof(list_t));

	m->info.type = G_LIST;
	m->info.x = x;
	m->info.y = y;
	m->info.w = w;
	m->info.h = h;
	m->info.draw = (drawfunc_t)list_draw;
	m->info.event = (eventfunc_t)list_event;

	m->refresh = refresh;

	m->selected = -1;

	strcpy(m->path,config.path_rom);

	scrollbar_create(&m->scrollbar,x + w - 8,y,h,0,click_scrollbar);
	m->scrollbar.user = m;
}
Exemplo n.º 4
0
/**
 * Callback for (i)frame scrollbars.
 */
void browser_window_scroll_callback(void *client_data,
		struct scrollbar_msg_data *scrollbar_data)
{
	struct browser_window *bw = client_data;

	switch(scrollbar_data->msg) {
	case SCROLLBAR_MSG_MOVED:
		if (bw->browser_window_type == BROWSER_WINDOW_IFRAME) {
			html_redraw_a_box(bw->parent->current_content, bw->box);
		} else {
			struct rect rect;

			rect.x0 = scrollbar_get_offset(bw->scroll_x);
			rect.y0 = scrollbar_get_offset(bw->scroll_y);
			rect.x1 = rect.x0 + bw->width;
			rect.y1 = rect.y0 + bw->height;

			browser_window_update_box(bw, &rect);
		}
		break;
	case SCROLLBAR_MSG_SCROLL_START:
	{
		struct rect rect = {
			.x0 = scrollbar_data->x0,
			.y0 = scrollbar_data->y0,
			.x1 = scrollbar_data->x1,
			.y1 = scrollbar_data->y1
		};

		if (scrollbar_is_horizontal(scrollbar_data->scrollbar))
			browser_window_set_drag_type(bw, DRAGGING_SCR_X, &rect);
		else
			browser_window_set_drag_type(bw, DRAGGING_SCR_Y, &rect);
	}
		break;
	case SCROLLBAR_MSG_SCROLL_FINISHED:
		browser_window_set_drag_type(bw, DRAGGING_NONE, NULL);

		browser_window_set_pointer(bw, BROWSER_POINTER_DEFAULT);
		break;
	}
}

/* exported interface, documented in browser.h */
void browser_window_handle_scrollbars(struct browser_window *bw)
{
	hlcache_handle *h = bw->current_content;
	bool scroll_x;
	bool scroll_y;
	int c_width = 0;
	int c_height = 0;

	assert(!bw->window); /* Core-handled windows only */

	if (h != NULL) {
		c_width  = content_get_width(h);
		c_height = content_get_height(h);
	}

	if (bw->scrolling == SCROLLING_YES) {
		scroll_x = true;
		scroll_y = true;
	} else if (bw->scrolling == SCROLLING_AUTO &&
			bw->current_content) {
		int bw_width = bw->width;
		int bw_height = bw->height;

		/* subtract existing scrollbar width */
		bw_width -= bw->scroll_y ? SCROLLBAR_WIDTH : 0;
		bw_height -= bw->scroll_x ? SCROLLBAR_WIDTH : 0;

		scroll_y = (c_height > bw_height) ? true : false;
		scroll_x = (c_width > bw_width) ? true : false;
	} else {
		/* No scrollbars */
		scroll_x = false;
		scroll_y = false;
	}

	if (!scroll_x && bw->scroll_x != NULL) {
		scrollbar_destroy(bw->scroll_x);
		bw->scroll_x = NULL;
	}

	if (!scroll_y && bw->scroll_y != NULL) {
		scrollbar_destroy(bw->scroll_y);
		bw->scroll_y = NULL;
	}

	if (scroll_y) {
		int length = bw->height;
		int visible = bw->height - (scroll_x ? SCROLLBAR_WIDTH : 0);

		if (bw->scroll_y == NULL) {
			/* create vertical scrollbar */
			if (!scrollbar_create(false, length, c_height, visible,
					bw, browser_window_scroll_callback,
					&(bw->scroll_y)))
				return;
		} else {
			/* update vertical scrollbar */
			scrollbar_set_extents(bw->scroll_y, length,
					visible, c_height);
		}
	}

	if (scroll_x) {
		int length = bw->width - (scroll_y ? SCROLLBAR_WIDTH : 0);
		int visible = length;

		if (bw->scroll_x == NULL) {
			/* create horizontal scrollbar */
			if (!scrollbar_create(true, length, c_width, visible,
					bw, browser_window_scroll_callback,
					&(bw->scroll_x)))
				return;
		} else {
			/* update horizontal scrollbar */
			scrollbar_set_extents(bw->scroll_x, length,
					visible, c_width);
		}
	}

	if (scroll_x && scroll_y)
		scrollbar_make_pair(bw->scroll_x, bw->scroll_y);
}
Exemplo n.º 5
0
/**
 * Enable scrollbar.
 * @param list
 * List to enable scrollbar on.
 */
void list_scrollbar_enable(list_struct *list)
{
    list->scrollbar_enabled = 1;
    scrollbar_create(&list->scrollbar, 9, LIST_ROWS_HEIGHT(list) + 1, &list->row_offset, &list->rows, list->max_rows);
}
Exemplo n.º 6
0
/** @copydoc widgetdata::draw_func */
static void widget_draw(widgetdata *widget)
{
    SDL_Rect box;
    char buf[HUGE_BUF];
    size_t i;

    /* The list doesn't exist yet, create it. */
    if (!list_mplayer) {
        char version[MAX_BUF];

        /* Create the list and set up settings. */
        list_mplayer = list_create(12, 1, 8);
        list_mplayer->handle_enter_func = list_handle_enter;
        list_mplayer->text_color_hook = list_text_color_hook;
        list_mplayer->surface = widget->surface;
        list_scrollbar_enable(list_mplayer);
        list_set_column(list_mplayer, 0, 130, 7, NULL, -1);
        list_set_font(list_mplayer, FONT_ARIAL10);

        /* Add default media directory songs. */
        get_data_dir_file(buf, sizeof(buf), DIRECTORY_MEDIA);
        mplayer_list_init(list_mplayer, buf, 0);

        /* Now add custom ones, but ignore duplicates. */
        snprintf(buf, sizeof(buf), "%s/.atrinik/%s/"DIRECTORY_MEDIA, get_config_dir(), package_get_version_partial(version, sizeof(version)));
        mplayer_list_init(list_mplayer, buf, 1);

        /* If we added any, sort the list alphabetically and add an entry
         * to disable background music. */
        if (list_mplayer->rows) {
            FILE *fp;

            /* Allocate the blacklist. + 1 is for the last entry added
             * further down. It is not actually used by the blacklist as
             * it's not possible to toggle it on/off using the button, but
             * it simplifies other logic checks. */
            shuffle_blacklist = ecalloc(1, sizeof(*shuffle_blacklist) * (list_mplayer->rows + 1));

            /* Sort the list. */
            list_sort(list_mplayer, LIST_SORT_ALPHA);

            /* Read the blacklist file contents. */
            fp = path_fopen(FILE_MPLAYER_BLACKLIST, "r");

            if (fp) {
                size_t row;

                while (fgets(buf, sizeof(buf) - 1, fp)) {
                    for (row = 0; row < list_mplayer->rows; row++) {
                        if (!strncmp(buf, list_mplayer->text[row][0], strlen(buf) - 1)) {
                            shuffle_blacklist[row] = 1;
                            break;
                        }
                    }
                }

                fclose(fp);
            }

            list_add(list_mplayer, list_mplayer->rows, 0, "Disable music");
        }

        scrollbar_create(&scrollbar_progress, 130, 11, &scrollbar_progress_info.scroll_offset, &scrollbar_progress_info.num_lines, 1);
        scrollbar_progress.redraw = &scrollbar_progress_info.redraw;
    }

    if (widget->redraw) {
        const char *bg_music;

        box.h = 0;
        box.w = widget->w;
        text_show(widget->surface, FONT_SERIF12, "Music Player", 0, 3, COLOR_HGOLD, TEXT_ALIGN_CENTER, &box);
        list_set_parent(list_mplayer, widget->x, widget->y);
        list_show(list_mplayer, 10, 2);
        box.w /= 2;
        text_show(widget->surface, FONT_SANS10, "Currently playing:", widget->w / 2, 22, COLOR_WHITE, TEXT_ALIGN_CENTER, &box);

        bg_music = sound_get_bg_music_basename();
        box.h = 0;
        box.w = widget->w / 2;

        /* Store the background music file name in temporary buffer and
         * make sure it won't overflow by truncating it if necessary. */
        if (bg_music) {
            strncpy(buf, bg_music, sizeof(buf) - 1);
            buf[sizeof(buf) - 1] = '\0';
            text_truncate_overflow(FONT_SANS11, buf, 150);
        }

        /* Show the music that is being played. */
        text_show(widget->surface, FONT_SANS11, bg_music ? buf : "No music", widget->w / 2 - 5, 34, COLOR_HGOLD, TEXT_ALIGN_CENTER, &box);

        scrollbar_progress.px = widget->x;
        scrollbar_progress.py = widget->y;
        scrollbar_show(&scrollbar_progress, widget->surface, 170, 50);

        box.h = 120;
        box.w -= 6 * 2;
        text_show(widget->surface, FONT_ARIAL10, "You can use the music player to play your favorite tunes from the game, or play them all one-by-one in random order (shuffle).\n\nNote that if you use the music player, in-game areas won't change your music until you click [b]Stop[/b].", widget->w / 2 + 6, 62, COLOR_WHITE, TEXT_WORD_WRAP | TEXT_MARKUP, &box);

        for (i = 0; i < BUTTON_NUM; i++) {
            buttons[i].surface = widget->surface;
            button_set_parent(&buttons[i], widget->x, widget->y);
        }

        buttons[BUTTON_PLAY].x = 10;
        buttons[BUTTON_PLAY].y = widget->h - TEXTURE_CLIENT("button")->h - 4;
        button_show(&buttons[BUTTON_PLAY], sound_map_background(-1) ? "Stop" : "Play");

        buttons[BUTTON_SHUFFLE].x = 10 + TEXTURE_CLIENT("button")->w + 5;
        buttons[BUTTON_SHUFFLE].y = widget->h - TEXTURE_CLIENT("button")->h - 4;
        buttons[BUTTON_SHUFFLE].pressed_forced = shuffle;
        button_show(&buttons[BUTTON_SHUFFLE], "Shuffle");

        buttons[BUTTON_BLACKLIST].x = 10 + TEXTURE_CLIENT("button")->w * 2 + 5 * 2;
        buttons[BUTTON_BLACKLIST].y = widget->h - TEXTURE_CLIENT("button_round")->h - 5;
        buttons[BUTTON_BLACKLIST].disabled = list_mplayer->row_selected == list_mplayer->rows;
        button_show(&buttons[BUTTON_BLACKLIST], mplayer_blacklisted(list_mplayer) ? "+" : "-");

        /* Show close button. */
        buttons[BUTTON_CLOSE].x = widget->w - TEXTURE_CLIENT("button_round")->w - 4;
        buttons[BUTTON_CLOSE].y = 4;
        button_show(&buttons[BUTTON_CLOSE], "X");

        /* Show help button. */
        buttons[BUTTON_HELP].x = widget->w - TEXTURE_CLIENT("button_round")->w * 2 - 4;
        buttons[BUTTON_HELP].y = 4;
        button_show(&buttons[BUTTON_HELP], "?");
    }
}
Exemplo n.º 7
0
/**
 * Load the book interface.
 * @param data
 * Book's content.
 * @param len
 * Length of 'data'.
 */
void book_load(const char *data, int len)
{
    SDL_Rect box;
    int pos;

    /* Nothing to do. */
    if (!data || !len) {
        return;
    }

    /* Free old book data and reset the values. */
    if (book_content != NULL) {
        efree(book_content);
    }

    book_lines = 0;
    book_scroll_lines = 0;
    book_scroll = 0;

    /* Store the data. */
    book_content = estrdup(data);
    book_name_change("Book", 4);

    /* Strip trailing newlines. */
    for (pos = len - 1; pos >= 0; pos--) {
        if (book_content[pos] != '\n') {
            break;
        }

        book_content[pos] = '\0';
    }

    /* No data... */
    if (book_content[0] == '\0') {
        return;
    }

    /* Calculate the line numbers. */
    box.w = BOOK_TEXT_WIDTH;
    box.h = BOOK_TEXT_HEIGHT;
    text_show(NULL, FONT_ARIAL11, book_content, BOOK_TEXT_STARTX, BOOK_TEXT_STARTY, COLOR_WHITE, TEXT_WORD_WRAP | TEXT_MARKUP | TEXT_LINES_CALC, &box);
    book_lines = box.h;
    book_scroll_lines = box.y;

    /* Create the book popup if it doesn't exist yet. */
    if (!popup_get_head() || popup_get_head()->texture != texture_get(TEXTURE_TYPE_CLIENT, "book")) {
        popup_struct *popup;

        popup = popup_create(texture_get(TEXTURE_TYPE_CLIENT, "book"));
        popup->draw_func = popup_draw_func;
        popup->draw_post_func = popup_draw_post_func;
        popup->event_func = popup_event_func;
        popup->destroy_callback_func = popup_destroy_callback;
        popup->clipboard_copy_func = popup_clipboard_copy_func;
        popup->disable_texture_drawing = 1;

        popup->button_left.x = 25;
        popup->button_left.y = 25;

        if (book_help_history_enabled) {
            popup->button_left.event_func = popup_button_event_func;
            popup_button_set_text(&popup->button_left, "<");
        }

        popup->button_right.x = 649;
        popup->button_right.y = 25;
    }

    scrollbar_create(&scrollbar, BOOK_SCROLLBAR_WIDTH, BOOK_SCROLLBAR_HEIGHT, &book_scroll, &book_lines, book_scroll_lines);
    scrollbar.redraw = &popup_get_head()->redraw;

    popup_get_head()->redraw = 1;
}
Exemplo n.º 8
0
/**
 * Callback for (i)frame scrollbars.
 */
void browser_window_scroll_callback(void *client_data,
		struct scrollbar_msg_data *scrollbar_data)
{
	struct browser_window *bw = client_data;

	switch(scrollbar_data->msg) {
	case SCROLLBAR_MSG_MOVED:
		if (bw->browser_window_type == BROWSER_WINDOW_IFRAME) {
			html_redraw_a_box(bw->parent->current_content, bw->box);
		} else {
			struct rect rect;

			rect.x0 = scrollbar_get_offset(bw->scroll_x);
			rect.y0 = scrollbar_get_offset(bw->scroll_y);
			rect.x1 = rect.x0 + bw->width;
			rect.y1 = rect.y0 + bw->height;

			browser_window_update_box(bw, &rect);
		}
		break;
	case SCROLLBAR_MSG_SCROLL_START:
	{
		struct rect rect = {
			.x0 = scrollbar_data->x0,
			.y0 = scrollbar_data->y0,
			.x1 = scrollbar_data->x1,
			.y1 = scrollbar_data->y1
		};

		if (scrollbar_is_horizontal(scrollbar_data->scrollbar))
			browser_window_set_drag_type(bw, DRAGGING_SCR_X, &rect);
		else
			browser_window_set_drag_type(bw, DRAGGING_SCR_Y, &rect);
	}
		break;
	case SCROLLBAR_MSG_SCROLL_FINISHED:
		browser_window_set_drag_type(bw, DRAGGING_NONE, NULL);

		browser_window_set_pointer(bw, BROWSER_POINTER_DEFAULT);
		break;
	}
}

/* exported interface, documented in browser.h */
void browser_window_handle_scrollbars(struct browser_window *bw)
{
	struct hlcache_handle *h = bw->current_content;
	bool scroll_x;
	bool scroll_y;
	int c_width = 0;
	int c_height = 0;

	assert(!bw->window); /* Core-handled windows only */

	if (h != NULL) {
		c_width  = content_get_width(h);
		c_height = content_get_height(h);
	}

	if (bw->scrolling == BW_SCROLLING_YES) {
		scroll_x = true;
		scroll_y = true;
	} else if (bw->scrolling == BW_SCROLLING_AUTO &&
			bw->current_content) {
		int bw_width = bw->width;
		int bw_height = bw->height;

		/* subtract existing scrollbar width */
		bw_width -= bw->scroll_y ? SCROLLBAR_WIDTH : 0;
		bw_height -= bw->scroll_x ? SCROLLBAR_WIDTH : 0;

		scroll_y = (c_height > bw_height) ? true : false;
		scroll_x = (c_width > bw_width) ? true : false;
	} else {
		/* No scrollbars */
		scroll_x = false;
		scroll_y = false;
	}

	if (!scroll_x && bw->scroll_x != NULL) {
		scrollbar_destroy(bw->scroll_x);
		bw->scroll_x = NULL;
	}

	if (!scroll_y && bw->scroll_y != NULL) {
		scrollbar_destroy(bw->scroll_y);
		bw->scroll_y = NULL;
	}

	if (scroll_y) {
		int length = bw->height;
		int visible = bw->height - (scroll_x ? SCROLLBAR_WIDTH : 0);

		if (bw->scroll_y == NULL) {
			/* create vertical scrollbar */
			if (scrollbar_create(false, length, c_height, visible,
					     bw, browser_window_scroll_callback,
					     &(bw->scroll_y)) != NSERROR_OK) {
				return;
			}
		} else {
			/* update vertical scrollbar */
			scrollbar_set_extents(bw->scroll_y, length,
					visible, c_height);
		}
	}

	if (scroll_x) {
		int length = bw->width - (scroll_y ? SCROLLBAR_WIDTH : 0);
		int visible = length;

		if (bw->scroll_x == NULL) {
			/* create horizontal scrollbar */
			if (scrollbar_create(true, length, c_width, visible,
					     bw, browser_window_scroll_callback,
					     &(bw->scroll_x)) != NSERROR_OK) {
				return;
			}
		} else {
			/* update horizontal scrollbar */
			scrollbar_set_extents(bw->scroll_x, length,
					visible, c_width);
		}
	}

	if (scroll_x && scroll_y)
		scrollbar_make_pair(bw->scroll_x, bw->scroll_y);
}


/* exported function documented in desktop/frames.h */
nserror browser_window_create_iframes(struct browser_window *bw,
		struct content_html_iframe *iframe)
{
	struct browser_window *window;
	struct content_html_iframe *cur;
	struct rect rect;
	int iframes = 0;
	int index;
	nserror ret = NSERROR_OK;

	if (iframe == NULL) {
		return NSERROR_BAD_PARAMETER;
	}

	/* Count iframe list and allocate enough space within the
	 * browser window.
	 */
	for (cur = iframe; cur; cur = cur->next) {
		iframes++;
	}
	bw->iframes = calloc(iframes, sizeof(*bw));
	if (!bw->iframes) {
		return NSERROR_NOMEM;
	}
	bw->iframe_count = iframes;

	index = 0;
	for (cur = iframe; cur; cur = cur->next) {
		window = &(bw->iframes[index++]);

		/* Initialise common parts */
		browser_window_initialise_common(BW_CREATE_NONE,
				window, NULL);

		/* window characteristics */
		window->browser_window_type = BROWSER_WINDOW_IFRAME;
		window->scrolling = cur->scrolling;
		window->border = cur->border;
		window->border_colour = cur->border_colour;
		window->no_resize = true;
		window->margin_width = cur->margin_width;
		window->margin_height = cur->margin_height;
		window->scale = bw->scale;
		if (cur->name != NULL) {
			window->name = strdup(cur->name);
			if (window->name == NULL) {
				free(bw->iframes) ;
				bw->iframes = 0;
				bw->iframe_count = 0;
				return NSERROR_NOMEM;
			}
		}

		/* linking */
		window->box = cur->box;
		window->parent = bw;
		window->box->iframe = window;

		/* iframe dimensions */
		box_bounds(window->box, &rect);

		browser_window_set_position(window, rect.x0, rect.y0);
		browser_window_set_dimensions(window, rect.x1 - rect.x0,
				rect.y1 - rect.y0);
	}

	/* calculate dimensions */
	browser_window_update_extent(bw);
	browser_window_recalculate_iframes(bw);

	index = 0;
	for (cur = iframe; cur; cur = cur->next) {
		window = &(bw->iframes[index++]);
		if (cur->url) {
			/* fetch iframe's content */
			ret = browser_window_navigate(window,
				cur->url,
				hlcache_handle_get_url(bw->current_content),
				BW_NAVIGATE_UNVERIFIABLE,
				NULL,
				NULL,
				bw->current_content);
		}
	}

	return ret;
}


/**
 * Recalculate iframe positions following a resize.
 *
 * \param  bw	    The browser window to reposition iframes for
 */

void browser_window_recalculate_iframes(struct browser_window *bw)
{
	struct browser_window *window;
	int index;

	for (index = 0; index < bw->iframe_count; index++) {
		window = &(bw->iframes[index]);

		if (window != NULL) {
			browser_window_handle_scrollbars(window);
		}
	}
}


/* exported interface documented in desktop/frames.h */
nserror browser_window_create_frameset(struct browser_window *bw,
		struct content_html_frames *frameset)
{
	int row, col, index;
	struct content_html_frames *frame;
	struct browser_window *window;
	hlcache_handle *parent;

	assert(bw && frameset);

	/* 1. Create children */
	assert(bw->children == NULL);
	assert(frameset->cols + frameset->rows != 0);

	bw->children = calloc((frameset->cols * frameset->rows), sizeof(*bw));
	if (!bw->children) {
		return NSERROR_NOMEM;
	}

	bw->cols = frameset->cols;
	bw->rows = frameset->rows;
	for (row = 0; row < bw->rows; row++) {
		for (col = 0; col < bw->cols; col++) {
			index = (row * bw->cols) + col;
			frame = &frameset->children[index];
			window = &bw->children[index];

			/* Initialise common parts */
			browser_window_initialise_common(BW_CREATE_NONE,
					window, NULL);

			/* window characteristics */
			if (frame->children)
				window->browser_window_type =
						BROWSER_WINDOW_FRAMESET;
			else
				window->browser_window_type =
						BROWSER_WINDOW_FRAME;
			window->scrolling = frame->scrolling;
			window->border = frame->border;
			window->border_colour = frame->border_colour;
			window->no_resize = frame->no_resize;
			window->frame_width = frame->width;
			window->frame_height = frame->height;
			window->margin_width = frame->margin_width;
			window->margin_height = frame->margin_height;
			if (frame->name) {
				window->name = strdup(frame->name);
				if (!window->name) {
					free(bw->children);
					bw->children = NULL;
					return NSERROR_NOMEM;
				}
			}

			window->scale = bw->scale;

			/* linking */
			window->parent = bw;

			if (window->name)
				LOG("Created frame '%s'", window->name);
			else
				LOG("Created frame (unnamed)");
		}
	}

	/* 2. Calculate dimensions */
	browser_window_update_extent(bw);
	browser_window_recalculate_frameset(bw);

	/* 3. Recurse for grandchildren */
	for (row = 0; row < bw->rows; row++) {
		for (col = 0; col < bw->cols; col++) {
			index = (row * bw->cols) + col;
			frame = &frameset->children[index];
			window = &bw->children[index];

			if (frame->children)
				browser_window_create_frameset(window, frame);
		}
	}

	/* Use the URL of the first ancestor window containing html content
	 * as the referer */
	for (window = bw; window->parent; window = window->parent) {
		if (window->current_content &&
				content_get_type(window->current_content) ==
				CONTENT_HTML)
			break;
	}

	parent = window->current_content;

	/* 4. Launch content */
	for (row = 0; row < bw->rows; row++) {
		for (col = 0; col < bw->cols; col++) {
			index = (row * bw->cols) + col;
			frame = &frameset->children[index];
			window = &bw->children[index];

			if (frame->url) {
				browser_window_navigate(window,
					frame->url,
					hlcache_handle_get_url(parent),
					BW_NAVIGATE_HISTORY |
					BW_NAVIGATE_UNVERIFIABLE,
					NULL,
					NULL,
					parent);
			}
		}
	}

	return NSERROR_OK;
}