From d67a57c1e94f8b36b2c8f6785b6b7b404f85de21 Mon Sep 17 00:00:00 2001 From: Adrian Lees Date: Sat, 10 Jan 2009 00:18:34 +0000 Subject: Assorted saving-related changes (WIP) svn path=/trunk/netsurf/; revision=6010 --- riscos/configure.c | 4 +- riscos/dialog.c | 64 +++++++------- riscos/dialog.h | 1 + riscos/download.c | 75 ++++++++++++++-- riscos/gui.c | 13 ++- riscos/query.c | 49 +++++++++-- riscos/query.h | 3 + riscos/save.c | 244 ++++++++++++++++++++++++++++++++++++++++++++--------- 8 files changed, 368 insertions(+), 85 deletions(-) (limited to 'riscos') diff --git a/riscos/configure.c b/riscos/configure.c index a90a6fc62..c56811cfd 100644 --- a/riscos/configure.c +++ b/riscos/configure.c @@ -70,8 +70,8 @@ static bool ro_gui_configure_click(wimp_pointer *pointer); static void ro_gui_configure_open_window(wimp_open *open); static void ro_gui_configure_close(wimp_w w); static bool ro_gui_configure_translate(void); -static void ro_gui_configure_register(const char *window, - bool (*initialise)(wimp_w w), void (*finalise)(wimp_w w)); +static void ro_gui_configure_register(const char *window, + bool (*initialise)(wimp_w w), void (*finalise)(wimp_w w)); void ro_gui_configure_initialise(void) { diff --git a/riscos/dialog.c b/riscos/dialog.c index 4c870a0f2..d9854f09e 100644 --- a/riscos/dialog.c +++ b/riscos/dialog.c @@ -311,13 +311,13 @@ wimp_window * ro_gui_dialog_load_template(const char *template_name) /** - * Open a dialog box, centered on the screen. + * Open a dialog box, centred on the screen. */ void ro_gui_dialog_open(wimp_w w) { int screen_x, screen_y, dx, dy; - wimp_window_state open; + wimp_window_state state; os_error *error; /* find screen centre in os units */ @@ -326,31 +326,24 @@ void ro_gui_dialog_open(wimp_w w) screen_y /= 2; /* centre and open */ - open.w = w; - error = xwimp_get_window_state(&open); + state.w = w; + error = xwimp_get_window_state(&state); if (error) { LOG(("xwimp_get_window_state: 0x%x: %s", error->errnum, error->errmess)); warn_user("WimpError", error->errmess); return; } - dx = (open.visible.x1 - open.visible.x0) / 2; - dy = (open.visible.y1 - open.visible.y0) / 2; - open.visible.x0 = screen_x - dx; - open.visible.x1 = screen_x + dx; - open.visible.y0 = screen_y - dy; - open.visible.y1 = screen_y + dy; - open.next = wimp_TOP; - error = xwimp_open_window((wimp_open *) &open); - if (error) { - LOG(("xwimp_open_window: 0x%x: %s", - error->errnum, error->errmess)); - warn_user("WimpError", error->errmess); - return; - } + dx = (state.visible.x1 - state.visible.x0) / 2; + dy = (state.visible.y1 - state.visible.y0) / 2; + state.visible.x0 = screen_x - dx; + state.visible.x1 = screen_x + dx; + state.visible.y0 = screen_y - dy; + state.visible.y1 = screen_y + dy; + state.next = wimp_TOP; + ro_gui_open_window_request((wimp_open*)&state); - /* Set the caret position - */ + /* Set the caret position */ ro_gui_set_caret_first(w); } @@ -486,8 +479,6 @@ bool ro_gui_dialog_open_top(wimp_w w, struct toolbar *toolbar, void ro_gui_dialog_open_at_pointer(wimp_w w) { - int dx, dy; - wimp_window_state state; wimp_pointer ptr; os_error *error; @@ -500,6 +491,20 @@ void ro_gui_dialog_open_at_pointer(wimp_w w) return; } + ro_gui_dialog_open_xy(w, ptr.pos.x - 64, ptr.pos.y); +} + + +/** + * Open window at a specified location. + */ + +void ro_gui_dialog_open_xy(wimp_w w, int x, int y) +{ + wimp_window_state state; + os_error *error; + int dx, dy; + /* move the window */ state.w = w; error = xwimp_get_window_state(&state); @@ -511,10 +516,10 @@ void ro_gui_dialog_open_at_pointer(wimp_w w) } dx = (state.visible.x1 - state.visible.x0); dy = (state.visible.y1 - state.visible.y0); - state.visible.x0 = ptr.pos.x - 64; - state.visible.x1 = ptr.pos.x - 64 + dx; - state.visible.y0 = ptr.pos.y - dy; - state.visible.y1 = ptr.pos.y; + state.visible.x0 = x; + state.visible.x1 = x + dx; + state.visible.y0 = y - dy; + state.visible.y1 = y; /* if the window is already open, close it first so that it opens fully * on screen */ @@ -598,7 +603,7 @@ void ro_gui_dialog_open_centre_parent(wimp_w parent, wimp_w child) { void ro_gui_dialog_open_persistent(wimp_w parent, wimp_w w, bool pointer) { if (pointer) - ro_gui_dialog_open_at_pointer(w); + ro_gui_dialog_open_at_pointer(w); else ro_gui_dialog_open_centre_parent(parent, w); @@ -608,7 +613,6 @@ void ro_gui_dialog_open_persistent(wimp_w parent, wimp_w w, bool pointer) { wimp_WINDOW_CLOSE_ICON); ro_gui_dialog_add_persistent(parent, w); ro_gui_set_caret_first(w); - } @@ -650,8 +654,8 @@ void ro_gui_dialog_close_persistent(wimp_w parent) { for (i = 0; i < MAX_PERSISTENT; i++) { if (persistent_dialog[i].parent == parent && persistent_dialog[i].dialog != NULL) { - if (!ro_gui_wimp_event_close_window(persistent_dialog[i].dialog)) - ro_gui_dialog_close(persistent_dialog[i].dialog); + ro_gui_dialog_close(persistent_dialog[i].dialog); + ro_gui_wimp_event_close_window(persistent_dialog[i].dialog); persistent_dialog[i].parent = NULL; persistent_dialog[i].dialog = NULL; } diff --git a/riscos/dialog.h b/riscos/dialog.h index 63a53d68c..29c2e5024 100644 --- a/riscos/dialog.h +++ b/riscos/dialog.h @@ -35,6 +35,7 @@ void ro_gui_dialog_close(wimp_w close); bool ro_gui_dialog_open_top(wimp_w w, struct toolbar *toolbar, int width, int height); void ro_gui_dialog_open_at_pointer(wimp_w w); +void ro_gui_dialog_open_xy(wimp_w, int x, int y); void ro_gui_dialog_open_centre_parent(wimp_w parent, wimp_w w); void ro_gui_dialog_open_persistent(wimp_w parent, wimp_w w, bool pointer); diff --git a/riscos/download.c b/riscos/download.c index c18c487a7..55496451f 100644 --- a/riscos/download.c +++ b/riscos/download.c @@ -125,6 +125,9 @@ static int download_progress_x0; static int download_progress_y0; static int download_progress_y1; +/** Current download directory. */ +static char *download_dir = NULL; +static size_t download_dir_len; static const char *ro_gui_download_temp_name(struct gui_download_window *dw); @@ -136,6 +139,7 @@ static bool ro_gui_download_check_space(struct gui_download_window *dw, const char *dest_file, const char *orig_file); static os_error *ro_gui_download_move(struct gui_download_window *dw, const char *dest_file, const char *src_file); +static void ro_gui_download_remember_dir(const char *path); static bool ro_gui_download_save(struct gui_download_window *dw, const char *file_name, bool force_overwrite); static void ro_gui_download_send_dataload(struct gui_download_window *dw); @@ -221,6 +225,7 @@ struct gui_download_window *gui_download_window_create(const char *url, url_func_result res; char *local_path; utf8_convert_ret err; + size_t leaf_ofst; size_t i; dw = malloc(sizeof *dw); @@ -329,20 +334,36 @@ struct gui_download_window *gui_download_window_create(const char *url, download_template->icons[ICON_DOWNLOAD_ICON].data.indirected_sprite.id = (osspriteop_id) dw->sprite_name; + if (download_dir) { + memcpy(dw->path, download_dir, download_dir_len); + dw->path[download_dir_len] = '.'; + leaf_ofst = download_dir_len + 1; + } + else + leaf_ofst = 0; + if ((res = url_nice(url, &nice, option_strip_extensions)) == URL_FUNC_OK) { - for (i = 0; nice[i]; i++) { + int imax = sizeof dw->path - (leaf_ofst + 1); + for (i = 0; i < imax && nice[i]; i++) { if (nice[i] == '.') nice[i] = '/'; else if (nice[i] <= ' ' || strchr(":*#$&@^%\\", nice[i])) nice[i] = '_'; } - strncpy(dw->path, nice, sizeof dw->path); + memcpy(dw->path + leaf_ofst, nice, i); + dw->path[leaf_ofst + i] = '\0'; free(nice); } - else - strcpy(dw->path, messages_get("SaveObject")); + else { + const char *leaf = messages_get("SaveObject"); + size_t len = strlen(leaf); + if (len >= sizeof dw->path - leaf_ofst) + len = sizeof dw->path - leaf_ofst - 1; + memcpy(dw->path + leaf_ofst, leaf, len); + dw->path[leaf_ofst + len] = '\0'; + } err = utf8_to_local_encoding(dw->path, 0, &local_path); if (err != UTF8_CONVERT_OK) { @@ -772,9 +793,21 @@ bool ro_gui_download_click(wimp_pointer *pointer) if (pointer->i == ICON_DOWNLOAD_ICON && !dw->error && !dw->saved) { const char *sprite = ro_gui_get_icon_string(pointer->w, pointer->i); + int x = pointer->pos.x, y = pointer->pos.y; + wimp_window_state wstate; + wimp_icon_state istate; + /* start the drag from the icon's exact location, rather than the pointer */ + istate.w = wstate.w = pointer->w; + istate.i = pointer->i; + if (!xwimp_get_window_state(&wstate) && !xwimp_get_icon_state(&istate)) { + x = (istate.icon.extent.x1 + istate.icon.extent.x0)/2 + + wstate.visible.x0 - wstate.xscroll; + y = (istate.icon.extent.y1 + istate.icon.extent.y0)/2 + + wstate.visible.y1 - wstate.yscroll; + } gui_current_drag_type = GUI_DRAG_DOWNLOAD_SAVE; download_window_current = dw; - ro_gui_drag_icon(pointer->pos.x, pointer->pos.y, sprite); + ro_gui_drag_icon(x, y, sprite); } else if (pointer->i == ICON_DOWNLOAD_DESTINATION) { strncpy(command + 14, dw->path, 242); @@ -1152,6 +1185,34 @@ os_error *ro_gui_download_move(struct gui_download_window *dw, } +/** + * Remember the directory containing the given file, + * for use in further downloads. + * + * \param path pathname of downloaded file + * \return none + */ + +void ro_gui_download_remember_dir(const char *path) +{ + char *lastdot = NULL; + char *p = path; + while (*p >= 0x20) { + if (*p == '.') lastdot = p; + p++; + } + if (lastdot) { + /* remember the directory */ + char *new_dir = realloc(download_dir, (lastdot+1)-path); + if (new_dir) { + download_dir_len = lastdot - path; + memcpy(new_dir, path, download_dir_len); + new_dir[download_dir_len] = '\0'; + download_dir = new_dir; + } + } +} + /** * Start of save operation, user has specified where the file should be saved. * @@ -1174,7 +1235,7 @@ bool ro_gui_download_save(struct gui_download_window *dw, temp_name = ro_gui_download_temp_name(dw); /* does the user want to check for collisions when saving? */ - if (true && !force_overwrite) { + if (!force_overwrite) { /* check whether the destination file/dir already exists */ error = xosfile_read_stamped(file_name, &obj_type, NULL, NULL, NULL, NULL, NULL); @@ -1236,6 +1297,8 @@ bool ro_gui_download_save(struct gui_download_window *dw, dw->saved = true; strncpy(dw->path, file_name, sizeof dw->path); + ro_gui_download_remember_dir(file_name); + /* grey out file icon */ error = xwimp_set_icon_state(dw->window, ICON_DOWNLOAD_ICON, wimp_ICON_SHADED, wimp_ICON_SHADED); diff --git a/riscos/gui.c b/riscos/gui.c index 2f01f7881..348db5d0e 100644 --- a/riscos/gui.c +++ b/riscos/gui.c @@ -1292,10 +1292,17 @@ void ro_gui_drag_end(wimp_dragged *drag) void ro_gui_keypress(wimp_key *key) { - os_error *error; + if (key->c == wimp_KEY_ESCAPE && + (gui_current_drag_type == GUI_DRAG_SAVE || + gui_current_drag_type == GUI_DRAG_DOWNLOAD_SAVE)) { - if (!ro_gui_wimp_event_keypress(key)) { - error = xwimp_process_key(key->c); + /* Allow Escape key to be used for cancelling a drag save + (easier than finding somewhere safe to abort the drag) */ + ro_gui_drag_box_cancel(); + gui_current_drag_type = GUI_DRAG_NONE; + } + else if (!ro_gui_wimp_event_keypress(key)) { + os_error *error = xwimp_process_key(key->c); if (error) { LOG(("xwimp_process_key: 0x%x: %s", error->errnum, error->errmess)); diff --git a/riscos/query.c b/riscos/query.c index afab8b185..3cf18ac63 100644 --- a/riscos/query.c +++ b/riscos/query.c @@ -94,7 +94,9 @@ struct gui_query_window *ro_gui_query_window_lookup_id(query_id id) /** - * Display a query to the user, requesting a response. + * Display a query to the user, requesting a response, near the current + * pointer position to keep the required mouse travel small, but also + * protecting against spurious mouse clicks. * * \param query message token of query * \param detail parameter used in expanding tokenised message @@ -108,6 +110,37 @@ struct gui_query_window *ro_gui_query_window_lookup_id(query_id id) query_id query_user(const char *query, const char *detail, const query_callback *cb, void *pw, const char *yes, const char *no) +{ + wimp_pointer pointer; + if (xwimp_get_pointer_info(&pointer)) + pointer.pos.y = pointer.pos.x = -1; + + return query_user_xy(query, detail, cb, pw, yes, no, + pointer.pos.x, pointer.pos.y); +} + + +/** + * Display a query to the user, requesting a response, at a specified + * screen position (x,y). The window is positioned relative to the given + * location such that the required mouse travel is small, but non-zero + * for protection spurious double-clicks. + * + * \param query message token of query + * \param detail parameter used in expanding tokenised message + * \param cb table of callback functions to be called when user responds + * \param pw handle to be passed to callback functions + * \param yes text to use for 'Yes' button' (or NULL for default) + * \param no text to use for 'No' button (or NULL for default) + * \param x x position in screen coordinates (-1 = centred on screen) + * \param y y position in screen coordinates (-1 = centred on screen) + * \return id number of the query (or QUERY_INVALID if it failed) + */ + +query_id query_user_xy(const char *query, const char *detail, + const query_callback *cb, void *pw, + const char *yes, const char *no, + int x, int y) { struct gui_query_window *qw; char query_buffer[300]; @@ -115,7 +148,7 @@ query_id query_user(const char *query, const char *detail, wimp_icon *icn; int width; int len; - int x; + int tx; char *local_text = NULL; utf8_convert_ret err; @@ -164,7 +197,7 @@ query_id query_user(const char *query, const char *detail, width += 44; if (width < query_yes_width) width = query_yes_width; - icn->extent.x0 = x = icn->extent.x1 - width; + icn->extent.x0 = tx = icn->extent.x1 - width; /* set the text of the 'No' button and size accordingly */ err = utf8_to_local_encoding(no, 0, &local_text); @@ -185,7 +218,7 @@ query_id query_user(const char *query, const char *detail, local_text = NULL; if (!query_no_width) query_no_width = icn->extent.x1 - icn->extent.x0; - icn->extent.x1 = x - 16; + icn->extent.x1 = tx - 16; error = xwimptextop_string_width(icn->data.indirected_text.text, len, &width); if (error) { LOG(("xwimptextop_string_width: 0x%x:%s", @@ -214,7 +247,13 @@ query_id query_user(const char *query, const char *detail, xwimp_set_icon_state(qw->window, ICON_QUERY_HELP, wimp_ICON_DELETED, wimp_ICON_DELETED); - ro_gui_dialog_open(qw->window); + if (x >= 0 && y >= 0) { + x -= tx - 8; + y += (query_template->visible.y1 - query_template->visible.y0) / 2; + ro_gui_dialog_open_xy(qw->window, x, y); + } + else + ro_gui_dialog_open(qw->window); ro_gui_wimp_event_set_user_data(qw->window, qw); ro_gui_wimp_event_register_mouse_click(qw->window, ro_gui_query_click); diff --git a/riscos/query.h b/riscos/query.h index 7105bef8e..92e3e292e 100644 --- a/riscos/query.h +++ b/riscos/query.h @@ -23,6 +23,9 @@ #include "oslib/wimp.h" #include "utils/utils.h" +query_id query_user_xy(const char *query, const char *detail, + const query_callback *cb, void *pw, const char *yes, const char *no, + int x, int y); void ro_gui_query_init(void); void ro_gui_query_window_bring_to_front(query_id id); diff --git a/riscos/save.c b/riscos/save.c index 37818590d..b866097d7 100644 --- a/riscos/save.c +++ b/riscos/save.c @@ -46,6 +46,7 @@ #include "riscos/menus.h" #include "riscos/message.h" #include "riscos/options.h" +#include "riscos/query.h" #include "riscos/save.h" #include "riscos/save_complete.h" #include "riscos/save_draw.h" @@ -61,11 +62,25 @@ #include "utils/utf8.h" #include "utils/utils.h" +//typedef enum +//{ +// QueryRsn_Quit, +// QueryRsn_Abort, +// QueryRsn_Overwrite +//} query_reason; + + +/**todo - much of the state information for a save should probably be moved into a structure + now since we could have multiple saves outstanding */ static gui_save_type gui_save_current_type; static struct content *gui_save_content = NULL; static struct selection *gui_save_selection = NULL; static int gui_save_filetype; +static query_id gui_save_query; +static bool gui_save_send_dataload; +static wimp_message gui_save_message; +static bool gui_save_close_after = true; static bool dragbox_active = false; /** in-progress Wimp_DragBox/DragASprite op */ static bool using_dragasprite = true; @@ -75,16 +90,29 @@ static wimp_w gui_save_sourcew = (wimp_w)-1; #define LEAFNAME_MAX 200 static char save_leafname[LEAFNAME_MAX]; +/** Current save directory (updated by and used for dialog-based saving) */ +static char *save_dir = NULL; +static size_t save_dir_len; + typedef enum { LINK_ACORN, LINK_ANT, LINK_TEXT } link_format; static bool ro_gui_save_complete(struct content *c, char *path); -static bool ro_gui_save_content(struct content *c, char *path); +static bool ro_gui_save_content(struct content *c, char *path, bool force_overwrite); +static void ro_gui_save_done(void); static void ro_gui_save_bounced(wimp_message *message); static void ro_gui_save_object_native(struct content *c, char *path); static bool ro_gui_save_link(struct content *c, link_format format, char *path); static void ro_gui_save_set_state(struct content *c, gui_save_type save_type, char *leaf_buf, char *icon_buf); static bool ro_gui_save_create_thumbnail(struct content *c, const char *name); +static void ro_gui_save_overwrite_confirmed(query_id, enum query_response res, void *p); +static void ro_gui_save_overwrite_cancelled(query_id, enum query_response res, void *p); + +static const query_callback overwrite_funcs = +{ + ro_gui_save_overwrite_confirmed, + ro_gui_save_overwrite_cancelled +}; /** An entry in gui_save_table. */ @@ -180,7 +208,7 @@ wimp_w ro_gui_saveas_create(const char *template_name) /** - * Clean-up function that releases our sprite area. + * Clean-up function that releases our sprite area and memory. */ void ro_gui_saveas_quit(void) @@ -193,6 +221,9 @@ void ro_gui_saveas_quit(void) } saveas_area = NULL; } + + free(save_dir); + save_dir = NULL; } /** @@ -209,7 +240,8 @@ void ro_gui_saveas_quit(void) void ro_gui_save_prepare(gui_save_type save_type, struct content *c) { - char name_buf[LEAFNAME_MAX]; + char name_buf[FILENAME_MAX]; + size_t leaf_offset = 0; char icon_buf[20]; assert((save_type == GUI_SAVE_HOTLIST_EXPORT_HTML) || @@ -218,7 +250,13 @@ void ro_gui_save_prepare(gui_save_type save_type, struct content *c) gui_save_current_type = save_type; gui_save_content = c; - ro_gui_save_set_state(c, save_type, name_buf, icon_buf); + if (save_dir) { + leaf_offset = save_dir_len; + memcpy(name_buf, save_dir, leaf_offset); + name_buf[leaf_offset++] = '.'; + } + + ro_gui_save_set_state(c, save_type, name_buf + leaf_offset, icon_buf); ro_gui_set_icon_sprite(dialog_saveas, ICON_SAVE_ICON, saveas_area, icon_buf); @@ -234,12 +272,25 @@ void ro_gui_save_prepare(gui_save_type save_type, struct content *c) */ void ro_gui_save_start_drag(wimp_pointer *pointer) { - if (pointer->buttons == wimp_DRAG_SELECT) { + if (pointer->buttons & (wimp_DRAG_SELECT | wimp_DRAG_ADJUST)) { const char *sprite = ro_gui_get_icon_string(pointer->w, pointer->i); + int x = pointer->pos.x, y = pointer->pos.y; + wimp_window_state wstate; + wimp_icon_state istate; + /* start the drag from the icon's exact location, rather than the pointer */ + istate.w = wstate.w = pointer->w; + istate.i = pointer->i; + if (!xwimp_get_window_state(&wstate) && !xwimp_get_icon_state(&istate)) { + x = (istate.icon.extent.x1 + istate.icon.extent.x0)/2 + + wstate.visible.x0 - wstate.xscroll; + y = (istate.icon.extent.y1 + istate.icon.extent.y0)/2 + + wstate.visible.y1 - wstate.yscroll; + } gui_current_drag_type = GUI_DRAG_SAVE; gui_save_sourcew = pointer->w; saving_from_dialog = true; - ro_gui_drag_icon(pointer->pos.x, pointer->pos.y, sprite); + gui_save_close_after = !(pointer->buttons & wimp_DRAG_ADJUST); + ro_gui_drag_icon(x, y, sprite); } } @@ -253,6 +304,7 @@ void ro_gui_save_start_drag(wimp_pointer *pointer) bool ro_gui_save_ok(wimp_w w) { const char *name = ro_gui_get_icon_string(w, ICON_SAVE_PATH); + wimp_pointer pointer; char path[256]; if (!strrchr(name, '.')) { @@ -263,7 +315,14 @@ bool ro_gui_save_ok(wimp_w w) ro_gui_convert_save_path(path, sizeof path, name); gui_save_sourcew = w; saving_from_dialog = true; - return ro_gui_save_content(gui_save_content, path); + gui_save_close_after = xwimp_get_pointer_info(&pointer) + || !(pointer.buttons & wimp_CLICK_ADJUST); + if (!ro_gui_save_content(gui_save_content, path, !option_confirm_overwrite)) { + memcpy(&gui_save_message.data.data_xfer.file_name, path, 1 + strlen(path)); + gui_save_send_dataload = false; + return false; + } + return true; } @@ -626,13 +685,15 @@ void ro_gui_save_bounced(wimp_message *message) /** - * Handle Message_DataSaveAck for a drag from the save dialog or browser window. + * Handle Message_DataSaveAck for a drag from the save dialog or browser window, + * or Clipboard protocol. */ void ro_gui_save_datasave_ack(wimp_message *message) { char *path = message->data.data_xfer.file_name; struct content *c = gui_save_content; + bool force_overwrite; switch (gui_save_current_type) { case GUI_SAVE_HOTLIST_EXPORT_HTML: @@ -653,31 +714,18 @@ void ro_gui_save_datasave_ack(wimp_message *message) ro_gui_set_icon_string(gui_save_sourcew, ICON_SAVE_PATH, path, true); - if (ro_gui_save_content(c, path)) { - os_error *error; - - /* Ack successful save with message_DATA_LOAD */ - message->action = message_DATA_LOAD; - message->your_ref = message->my_ref; - error = xwimp_send_message(wimp_USER_MESSAGE, message, - message->sender); - if (error) { - LOG(("xwimp_send_message: 0x%x: %s", - error->errnum, error->errmess)); - warn_user("SaveError", error->errmess); - } + gui_save_send_dataload = true; + memcpy(&gui_save_message, message, sizeof(gui_save_message)); - /* Close the save window */ - ro_gui_dialog_close(dialog_saveas); - error = xwimp_create_menu(wimp_CLOSE_MENU, 0, 0); - if (error) { - LOG(("xwimp_create_menu: 0x%x: %s", - error->errnum, error->errmess)); - warn_user("MenuError", error->errmess); - } + /* if saving/pasting to another application, don't request user + confirmation; a ScrapFile almost certainly exists already */ + if (message->data.data_xfer.est_size == -1) + force_overwrite = true; + else + force_overwrite = !option_confirm_overwrite; - gui_save_content = 0; - } + if (ro_gui_save_content(c, path, force_overwrite)) + ro_gui_save_done(); } @@ -685,15 +733,48 @@ void ro_gui_save_datasave_ack(wimp_message *message) /** * Does the actual saving * - * \param c content to save (or NULL for other) - * \param path path to save as - * \return true on success, false on error and error reported + * \param c content to save (or NULL for other) + * \param path path to save as + * \param force_overwrite true iff required to overwrite without prompting + * \return true on success, + * false on (i) error and error reported + * or (ii) deferred awaiting user confirmation */ -bool ro_gui_save_content(struct content *c, char *path) +bool ro_gui_save_content(struct content *c, char *path, bool force_overwrite) { os_error *error; + /* does the user want to check for collisions when saving? */ + if (!force_overwrite) { + fileswitch_object_type obj_type; + /* check whether the destination file/dir already exists */ + error = xosfile_read_stamped(path, &obj_type, + NULL, NULL, NULL, NULL, NULL); + if (error) { + LOG(("xosfile_read_stamped: 0x%x:%s", error->errnum, error->errmess)); + warn_user("SaveError", error->errmess); + return false; + } + + switch (obj_type) { + case osfile_NOT_FOUND: + break; + + case osfile_IS_FILE: + gui_save_query = query_user("OverwriteFile", NULL, &overwrite_funcs, NULL, + messages_get("Replace"), messages_get("DontReplace")); +// gui_save_query_rsn = QueryRsn_Overwrite; + return false; + + default: + error = xosfile_make_error(path, obj_type); + assert(error); + warn_user("SaveError", error->errmess); + return false; + } + } + switch (gui_save_current_type) { #ifdef WITH_DRAW_EXPORT case GUI_SAVE_DRAW: @@ -783,6 +864,67 @@ bool ro_gui_save_content(struct content *c, char *path) } +/** + * Save completed, inform recipient and close our 'save as' dialog. + */ + +void ro_gui_save_done(void) +{ + os_error *error; + + if (gui_save_send_dataload) { + /* Ack successful save with message_DATA_LOAD */ + wimp_message *message = &gui_save_message; + message->action = message_DATA_LOAD; + message->your_ref = message->my_ref; + error = xwimp_send_message(wimp_USER_MESSAGE, message, + message->sender); + if (error) { + LOG(("xwimp_send_message: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("SaveError", error->errmess); + } + } + + if (saving_from_dialog) { + /* */ + char *sp = gui_save_message.data.data_xfer.file_name; + char *ep = sp + sizeof(gui_save_message.data.data_xfer.file_name); + char *lastdot = NULL; + char *p = sp; + + while (p < ep && *p >= 0x20) { + if (*p == '.') lastdot = p; + p++; + } + if (lastdot) { + /* remember the directory */ + char *new_dir = realloc(save_dir, (lastdot+1)-sp); + if (new_dir) { + save_dir_len = lastdot - sp; + memcpy(new_dir, sp, save_dir_len); + new_dir[save_dir_len] = '\0'; + save_dir = new_dir; + } + } + + if (gui_save_close_after) { + /* Close the save window */ + ro_gui_dialog_close(dialog_saveas); + error = xwimp_create_menu(wimp_CLOSE_MENU, 0, 0); + if (error) { + LOG(("xwimp_create_menu: 0x%x: %s", + error->errnum, error->errmess)); + warn_user("MenuError", error->errmess); + } + } + } + + if (!saving_from_dialog || gui_save_close_after) + gui_save_content = 0; +} + + /** * Prepare an application directory and save_complete() to it. * @@ -808,7 +950,7 @@ bool ro_gui_save_complete(struct content *c, char *path) char *dot; int i; - /* Create dir */ + /* Create dir */ error = xosfile_create_dir(path, 0); if (error) { LOG(("xosfile_create_dir: 0x%x: %s", @@ -817,7 +959,7 @@ bool ro_gui_save_complete(struct content *c, char *path) return false; } - /* Save !Run file */ + /* Save !Run file */ snprintf(buf, sizeof buf, "%s.!Run", path); fp = fopen(buf, "w"); if (!fp) { @@ -850,8 +992,8 @@ bool ro_gui_save_complete(struct content *c, char *path) memcpy(sprite->name, dot, len); memset(sprite->name + len, 0, 12 - len); for (i = 0; i < 12; i++) /* convert to lower case */ - if (sprite->name[i] != '\0') - sprite->name[i] = tolower(sprite->name[i]); + if (sprite->name[i] != '\0') + sprite->name[i] = tolower(sprite->name[i]); /* Create !Sprites */ snprintf(buf, sizeof buf, "%s.!Sprites", path); @@ -1140,3 +1282,27 @@ bool ro_gui_save_create_thumbnail(struct content *c, const char *name) return true; } + + +/** + * User has opted not to overwrite the existing file. + */ + +void ro_gui_save_overwrite_cancelled(query_id id, enum query_response res, void *p) +{ + if (!saving_from_dialog) { +// ro_gui_save_prepare(gui_save_current_type, gui_save_content); +// ro_gui_dialog_open_persistent(g->window, dialog_saveas, true); + } +} + + +/** + * Overwrite of existing file confirmed, proceed with the save. + */ + +void ro_gui_save_overwrite_confirmed(query_id id, enum query_response res, void *p) +{ + if (ro_gui_save_content(gui_save_content, gui_save_message.data.data_xfer.file_name, true)) + ro_gui_save_done(); +} -- cgit v1.2.3