PageRenderTime 121ms CodeModel.GetById 22ms RepoModel.GetById 7ms app.codeStats 0ms

/peek-build/src/netsurf/riscos/save.c

https://bitbucket.org/C0deMaver1ck/peeklinux
C | 1503 lines | 1096 code | 192 blank | 215 comment | 150 complexity | 2e5923d670d41795e5d124c410d4fe41 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, LGPL-2.0
  1. /*
  2. * Copyright 2004-2007 James Bursa <bursa@users.sourceforge.net>
  3. * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
  4. *
  5. * This file is part of NetSurf, http://www.netsurf-browser.org/
  6. *
  7. * NetSurf is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; version 2 of the License.
  10. *
  11. * NetSurf is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. /** \file
  20. * Save dialog and drag and drop saving (implementation).
  21. */
  22. #include <assert.h>
  23. #include <ctype.h>
  24. #include <errno.h>
  25. #include <stdbool.h>
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29. #include "oslib/dragasprite.h"
  30. #include "oslib/osbyte.h"
  31. #include "oslib/osfile.h"
  32. #include "oslib/osmodule.h"
  33. #include "oslib/osspriteop.h"
  34. #include "oslib/wimp.h"
  35. #include "oslib/wimpspriteop.h"
  36. #include "content/content.h"
  37. #include "content/hlcache.h"
  38. #include "desktop/netsurf.h"
  39. #include "desktop/save_complete.h"
  40. #include "desktop/save_text.h"
  41. #include "desktop/selection.h"
  42. #include "image/bitmap.h"
  43. #include "render/box.h"
  44. #include "render/form.h"
  45. #include "riscos/dialog.h"
  46. #include "riscos/gui.h"
  47. #include "riscos/menus.h"
  48. #include "riscos/message.h"
  49. #include "riscos/options.h"
  50. #include "riscos/query.h"
  51. #include "riscos/save.h"
  52. #include "riscos/save_draw.h"
  53. #include "riscos/save_pdf.h"
  54. #include "riscos/textselection.h"
  55. #include "riscos/thumbnail.h"
  56. #include "riscos/wimp.h"
  57. #include "riscos/wimp_event.h"
  58. #include "utils/config.h"
  59. #include "utils/log.h"
  60. #include "utils/messages.h"
  61. #include "utils/url.h"
  62. #include "utils/utf8.h"
  63. #include "utils/utils.h"
  64. //typedef enum
  65. //{
  66. // QueryRsn_Quit,
  67. // QueryRsn_Abort,
  68. // QueryRsn_Overwrite
  69. //} query_reason;
  70. /**todo - much of the state information for a save should probably be moved into a structure
  71. now since we could have multiple saves outstanding */
  72. static gui_save_type gui_save_current_type;
  73. static hlcache_handle *gui_save_content = NULL;
  74. static struct selection *gui_save_selection = NULL;
  75. static const char *gui_save_url = NULL;
  76. static const char *gui_save_title = NULL;
  77. static int gui_save_filetype;
  78. static query_id gui_save_query;
  79. static bool gui_save_send_dataload;
  80. static wimp_message gui_save_message;
  81. static bool gui_save_close_after = true;
  82. static bool dragbox_active = false; /** in-progress Wimp_DragBox/DragASprite op */
  83. static bool using_dragasprite = true;
  84. static bool saving_from_dialog = true;
  85. static osspriteop_area *saveas_area = NULL;
  86. static wimp_w gui_save_sourcew = (wimp_w)-1;
  87. #define LEAFNAME_MAX 200
  88. static char save_leafname[LEAFNAME_MAX];
  89. /** Current save directory (updated by and used for dialog-based saving) */
  90. static char *save_dir = NULL;
  91. static size_t save_dir_len;
  92. typedef enum { LINK_ACORN, LINK_ANT, LINK_TEXT } link_format;
  93. static bool ro_gui_save_complete(hlcache_handle *h, char *path);
  94. static bool ro_gui_save_content(hlcache_handle *h, char *path, bool force_overwrite);
  95. static void ro_gui_save_done(void);
  96. static void ro_gui_save_bounced(wimp_message *message);
  97. static bool ro_gui_save_object_native(hlcache_handle *h, char *path);
  98. static bool ro_gui_save_link(const char *url, const char *title, link_format format, char *path);
  99. static void ro_gui_save_set_state(hlcache_handle *h, gui_save_type save_type,
  100. const char *url, char *leaf_buf, char *icon_buf);
  101. static bool ro_gui_save_create_thumbnail(hlcache_handle *h, const char *name);
  102. static void ro_gui_save_overwrite_confirmed(query_id, enum query_response res, void *p);
  103. static void ro_gui_save_overwrite_cancelled(query_id, enum query_response res, void *p);
  104. static const query_callback overwrite_funcs =
  105. {
  106. ro_gui_save_overwrite_confirmed,
  107. ro_gui_save_overwrite_cancelled
  108. };
  109. /** An entry in gui_save_table. */
  110. struct gui_save_table_entry {
  111. int filetype;
  112. const char *name;
  113. };
  114. /** Table of filetypes and default filenames. Must be in sync with
  115. * gui_save_type (riscos/gui.h). A filetype of 0 indicates the content should
  116. * be used.
  117. */
  118. static const struct gui_save_table_entry gui_save_table[] = {
  119. /* GUI_SAVE_SOURCE, */ { 0, "SaveSource" },
  120. /* GUI_SAVE_DRAW, */ { 0xaff, "SaveDraw" },
  121. /* GUI_SAVE_PDF, */ { 0xadf, "SavePDF" },
  122. /* GUI_SAVE_TEXT, */ { 0xfff, "SaveText" },
  123. /* GUI_SAVE_COMPLETE, */ { 0xfaf, "SaveComplete" },
  124. /* GUI_SAVE_OBJECT_ORIG, */ { 0, "SaveObject" },
  125. /* GUI_SAVE_OBJECT_NATIVE, */ { 0, "SaveObject" },
  126. /* GUI_SAVE_LINK_URI, */ { 0xf91, "SaveLink" },
  127. /* GUI_SAVE_LINK_URL, */ { 0xb28, "SaveLink" },
  128. /* GUI_SAVE_LINK_TEXT, */ { 0xfff, "SaveLink" },
  129. /* GUI_SAVE_HOTLIST_EXPORT_HTML, */ { 0xfaf, "Hotlist" },
  130. /* GUI_SAVE_HISTORY_EXPORT_HTML, */ { 0xfaf, "History" },
  131. /* GUI_SAVE_TEXT_SELECTION, */ { 0xfff, "SaveSelection" },
  132. };
  133. /**
  134. * Create the saveas dialogue from the given template, and the sprite area
  135. * necessary for our thumbnail (full page save)
  136. *
  137. * \param template_name name of template to be used
  138. * \return window handle of created dialogue
  139. */
  140. wimp_w ro_gui_saveas_create(const char *template_name)
  141. {
  142. const int sprite_size = (68 * 68 * 4) + ((68 * 68) / 8); /* 32bpp with mask */
  143. int area_size = sizeof(osspriteop_area) + sizeof(osspriteop_header) +
  144. 256 * 8 + sprite_size;
  145. void *area = NULL;
  146. wimp_window *window;
  147. os_error *error;
  148. wimp_icon *icons;
  149. wimp_w w;
  150. window = ro_gui_dialog_load_template(template_name);
  151. assert(window);
  152. icons = window->icons;
  153. error = xosmodule_alloc(area_size, (void **) &area);
  154. if (error) {
  155. LOG(("xosmodule_alloc: 0x%x: %s", error->errnum, error->errmess));
  156. xwimp_close_template();
  157. die(error->errmess);
  158. } else {
  159. saveas_area = area;
  160. saveas_area->size = area_size;
  161. saveas_area->first = 16;
  162. error = xosspriteop_clear_sprites(osspriteop_USER_AREA, saveas_area);
  163. if (error) {
  164. LOG(("xosspriteop_clear_sprites: 0x%x: %s",
  165. error->errnum, error->errmess));
  166. warn_user("MiscError", error->errmess);
  167. xosmodule_free(saveas_area);
  168. saveas_area = NULL;
  169. }
  170. }
  171. assert((icons[ICON_SAVE_ICON].flags &
  172. (wimp_ICON_TEXT | wimp_ICON_SPRITE | wimp_ICON_INDIRECTED)) ==
  173. (wimp_ICON_SPRITE | wimp_ICON_INDIRECTED));
  174. icons[ICON_SAVE_ICON].data.indirected_sprite.area = saveas_area;
  175. /* create window */
  176. error = xwimp_create_window(window, &w);
  177. if (error) {
  178. LOG(("xwimp_create_window: 0x%x: %s",
  179. error->errnum, error->errmess));
  180. xwimp_close_template();
  181. die(error->errmess);
  182. }
  183. /* the window definition is copied by the wimp and may be freed */
  184. free(window);
  185. return w;
  186. }
  187. /**
  188. * Clean-up function that releases our sprite area and memory.
  189. */
  190. void ro_gui_saveas_quit(void)
  191. {
  192. if (saveas_area) {
  193. os_error *error = xosmodule_free(saveas_area);
  194. if (error) {
  195. LOG(("xosmodule_free: 0x%x: %s", error->errnum, error->errmess));
  196. warn_user("MiscError", error->errmess);
  197. }
  198. saveas_area = NULL;
  199. }
  200. free(save_dir);
  201. save_dir = NULL;
  202. }
  203. /**
  204. * Prepares the save box to reflect gui_save_type and a content, and
  205. * opens it.
  206. *
  207. * \param save_type type of save
  208. * \param h content to save
  209. * \param s selection to save
  210. * \param url url to be saved (link types)
  211. * \param title title (if any), when saving links
  212. */
  213. void ro_gui_save_prepare(gui_save_type save_type, hlcache_handle *h,
  214. struct selection *s, const char *url, const char *title)
  215. {
  216. char name_buf[FILENAME_MAX];
  217. size_t leaf_offset = 0;
  218. char icon_buf[20];
  219. assert( (save_type == GUI_SAVE_LINK_URI) ||
  220. (save_type == GUI_SAVE_LINK_URL) ||
  221. (save_type == GUI_SAVE_LINK_TEXT) ||
  222. (save_type == GUI_SAVE_HOTLIST_EXPORT_HTML) ||
  223. (save_type == GUI_SAVE_HISTORY_EXPORT_HTML) ||
  224. (save_type == GUI_SAVE_TEXT_SELECTION) || h);
  225. gui_save_selection = s;
  226. gui_save_url = url;
  227. gui_save_title = title;
  228. if (save_dir) {
  229. leaf_offset = save_dir_len;
  230. memcpy(name_buf, save_dir, leaf_offset);
  231. name_buf[leaf_offset++] = '.';
  232. }
  233. ro_gui_save_set_state(h, save_type, h ? content_get_url(h) : url,
  234. name_buf + leaf_offset, icon_buf);
  235. ro_gui_set_icon_sprite(dialog_saveas, ICON_SAVE_ICON, saveas_area,
  236. icon_buf);
  237. ro_gui_set_icon_string(dialog_saveas, ICON_SAVE_PATH, name_buf, true);
  238. ro_gui_wimp_event_memorise(dialog_saveas);
  239. }
  240. /**
  241. * Starts a drag for the save dialog
  242. *
  243. * \param pointer mouse position info from Wimp
  244. */
  245. void ro_gui_save_start_drag(wimp_pointer *pointer)
  246. {
  247. if (pointer->buttons & (wimp_DRAG_SELECT | wimp_DRAG_ADJUST)) {
  248. const char *sprite = ro_gui_get_icon_string(pointer->w, pointer->i);
  249. int x = pointer->pos.x, y = pointer->pos.y;
  250. wimp_window_state wstate;
  251. wimp_icon_state istate;
  252. /* start the drag from the icon's exact location, rather than the pointer */
  253. istate.w = wstate.w = pointer->w;
  254. istate.i = pointer->i;
  255. if (!xwimp_get_window_state(&wstate) && !xwimp_get_icon_state(&istate)) {
  256. x = (istate.icon.extent.x1 + istate.icon.extent.x0)/2 +
  257. wstate.visible.x0 - wstate.xscroll;
  258. y = (istate.icon.extent.y1 + istate.icon.extent.y0)/2 +
  259. wstate.visible.y1 - wstate.yscroll;
  260. }
  261. gui_current_drag_type = GUI_DRAG_SAVE;
  262. gui_save_sourcew = pointer->w;
  263. saving_from_dialog = true;
  264. gui_save_close_after = !(pointer->buttons & wimp_DRAG_ADJUST);
  265. ro_gui_drag_icon(x, y, sprite);
  266. }
  267. }
  268. /**
  269. * Handle OK click/keypress in the save dialog.
  270. *
  271. * \param w window handle of save dialog
  272. * \return true on success, false on failure
  273. */
  274. bool ro_gui_save_ok(wimp_w w)
  275. {
  276. const char *name = ro_gui_get_icon_string(w, ICON_SAVE_PATH);
  277. wimp_pointer pointer;
  278. char path[256];
  279. if (!strrchr(name, '.')) {
  280. warn_user("NoPathError", NULL);
  281. return false;
  282. }
  283. ro_gui_convert_save_path(path, sizeof path, name);
  284. gui_save_sourcew = w;
  285. saving_from_dialog = true;
  286. gui_save_send_dataload = false;
  287. gui_save_close_after = xwimp_get_pointer_info(&pointer)
  288. || !(pointer.buttons & wimp_CLICK_ADJUST);
  289. memcpy(&gui_save_message.data.data_xfer.file_name, path, 1 + strlen(path));
  290. if (ro_gui_save_content(gui_save_content, path, !option_confirm_overwrite)) {
  291. ro_gui_save_done();
  292. return true;
  293. }
  294. return false;
  295. }
  296. /**
  297. * Initiates drag saving of an object directly from a browser window
  298. *
  299. * \param save_type type of save
  300. * \param c content to save
  301. * \param g gui window
  302. */
  303. void gui_drag_save_object(gui_save_type save_type, hlcache_handle *c,
  304. struct gui_window *g)
  305. {
  306. wimp_pointer pointer;
  307. char icon_buf[20];
  308. os_error *error;
  309. /* Close the save window because otherwise we need two contexts
  310. */
  311. xwimp_create_menu(wimp_CLOSE_MENU, 0, 0);
  312. ro_gui_dialog_close(dialog_saveas);
  313. gui_save_sourcew = g->window;
  314. saving_from_dialog = false;
  315. error = xwimp_get_pointer_info(&pointer);
  316. if (error) {
  317. LOG(("xwimp_get_pointer_info: 0x%x: %s",
  318. error->errnum, error->errmess));
  319. warn_user("WimpError", error->errmess);
  320. return;
  321. }
  322. ro_gui_save_set_state(c, save_type, content_get_url(c), save_leafname,
  323. icon_buf);
  324. gui_current_drag_type = GUI_DRAG_SAVE;
  325. ro_gui_drag_icon(pointer.pos.x, pointer.pos.y, icon_buf);
  326. }
  327. /**
  328. * Initiates drag saving of a selection from a browser window
  329. *
  330. * \param s selection object
  331. * \param g gui window
  332. */
  333. void gui_drag_save_selection(struct selection *s, struct gui_window *g)
  334. {
  335. wimp_pointer pointer;
  336. char icon_buf[20];
  337. os_error *error;
  338. /* Close the save window because otherwise we need two contexts
  339. */
  340. xwimp_create_menu(wimp_CLOSE_MENU, 0, 0);
  341. ro_gui_dialog_close(dialog_saveas);
  342. gui_save_sourcew = g->window;
  343. saving_from_dialog = false;
  344. error = xwimp_get_pointer_info(&pointer);
  345. if (error) {
  346. LOG(("xwimp_get_pointer_info: 0x%x: %s",
  347. error->errnum, error->errmess));
  348. warn_user("WimpError", error->errmess);
  349. return;
  350. }
  351. gui_save_selection = s;
  352. ro_gui_save_set_state(NULL, GUI_SAVE_TEXT_SELECTION, NULL, save_leafname,
  353. icon_buf);
  354. gui_current_drag_type = GUI_DRAG_SAVE;
  355. ro_gui_drag_icon(pointer.pos.x, pointer.pos.y, icon_buf);
  356. }
  357. /**
  358. * Initiates drag saving of a link/URL file
  359. *
  360. * \param save_type format in which URL should be saved
  361. * \param url url to be saved
  362. * \param title title to be included in URI format, if any
  363. * \param g gui window to save from
  364. * \
  365. */
  366. void ro_gui_drag_save_link(gui_save_type save_type, const char *url,
  367. const char *title, struct gui_window *g)
  368. {
  369. wimp_pointer pointer;
  370. char icon_buf[20];
  371. os_error *error;
  372. /* Close the save window because otherwise we need two contexts
  373. */
  374. xwimp_create_menu(wimp_CLOSE_MENU, 0, 0);
  375. ro_gui_dialog_close(dialog_saveas);
  376. gui_save_url = url;
  377. gui_save_title = title;
  378. gui_save_sourcew = g->window;
  379. saving_from_dialog = false;
  380. error = xwimp_get_pointer_info(&pointer);
  381. if (error) {
  382. LOG(("xwimp_get_pointer_info: 0x%x: %s",
  383. error->errnum, error->errmess));
  384. warn_user("WimpError", error->errmess);
  385. return;
  386. }
  387. ro_gui_save_set_state(NULL, save_type, url, save_leafname, icon_buf);
  388. gui_current_drag_type = GUI_DRAG_SAVE;
  389. ro_gui_drag_icon(pointer.pos.x, pointer.pos.y, icon_buf);
  390. }
  391. /**
  392. * Start drag of icon under the pointer.
  393. */
  394. void ro_gui_drag_icon(int x, int y, const char *sprite)
  395. {
  396. os_error *error;
  397. wimp_drag drag;
  398. int r2;
  399. drag.initial.x0 = x - 34;
  400. drag.initial.y0 = y - 34;
  401. drag.initial.x1 = x + 34;
  402. drag.initial.y1 = y + 34;
  403. if (sprite && (xosbyte2(osbyte_READ_CMOS, 28, 0, &r2) || (r2 & 2))) {
  404. osspriteop_area *area = (osspriteop_area*)1;
  405. /* first try our local sprite area in case it's a thumbnail sprite */
  406. if (saveas_area) {
  407. error = xosspriteop_select_sprite(osspriteop_USER_AREA,
  408. saveas_area, (osspriteop_id)sprite, NULL);
  409. if (error) {
  410. if (error->errnum != error_SPRITE_OP_DOESNT_EXIST) {
  411. LOG(("xosspriteop_select_sprite: 0x%x: %s",
  412. error->errnum, error->errmess));
  413. warn_user("MiscError", error->errmess);
  414. }
  415. }
  416. else
  417. area = saveas_area;
  418. }
  419. error = xdragasprite_start(dragasprite_HPOS_CENTRE |
  420. dragasprite_VPOS_CENTRE |
  421. dragasprite_BOUND_POINTER |
  422. dragasprite_DROP_SHADOW,
  423. area, sprite, &drag.initial, 0);
  424. if (!error) {
  425. using_dragasprite = true;
  426. dragbox_active = true;
  427. return;
  428. }
  429. LOG(("xdragasprite_start: 0x%x: %s",
  430. error->errnum, error->errmess));
  431. }
  432. drag.type = wimp_DRAG_USER_FIXED;
  433. drag.bbox.x0 = -0x8000;
  434. drag.bbox.y0 = -0x8000;
  435. drag.bbox.x1 = 0x7fff;
  436. drag.bbox.y1 = 0x7fff;
  437. using_dragasprite = false;
  438. error = xwimp_drag_box(&drag);
  439. if (error) {
  440. LOG(("xwimp_drag_box: 0x%x: %s",
  441. error->errnum, error->errmess));
  442. warn_user("DragError", error->errmess);
  443. }
  444. else
  445. dragbox_active = true;
  446. }
  447. /**
  448. * Convert a ctrl-char terminated pathname possibly containing spaces
  449. * to a NUL-terminated one containing only hard spaces.
  450. *
  451. * \param dp destination buffer to receive pathname
  452. * \param len size of destination buffer
  453. * \param p source pathname, ctrl-char terminated
  454. */
  455. void ro_gui_convert_save_path(char *dp, size_t len, const char *p)
  456. {
  457. char *ep = dp + len - 1; /* leave room for NUL */
  458. assert(p <= dp || p > ep); /* in-situ conversion /is/ allowed */
  459. while (dp < ep && *p >= ' ') /* ctrl-char terminated */
  460. {
  461. *dp++ = (*p == ' ') ? 160 : *p;
  462. p++;
  463. }
  464. *dp = '\0';
  465. }
  466. void ro_gui_drag_box_cancel(void)
  467. {
  468. if (dragbox_active) {
  469. os_error *error;
  470. if (using_dragasprite) {
  471. error = xdragasprite_stop();
  472. if (error) {
  473. LOG(("xdragasprite_stop: 0x%x: %s",
  474. error->errnum, error->errmess));
  475. warn_user("WimpError", error->errmess);
  476. }
  477. }
  478. else {
  479. error = xwimp_drag_box(NULL);
  480. if (error) {
  481. LOG(("xwimp_drag_box: 0x%x: %s",
  482. error->errnum, error->errmess));
  483. warn_user("WimpError", error->errmess);
  484. }
  485. }
  486. dragbox_active = false;
  487. }
  488. }
  489. /**
  490. * Handle User_Drag_Box event for a drag from the save dialog or browser window.
  491. */
  492. void ro_gui_save_drag_end(wimp_dragged *drag)
  493. {
  494. const char *name;
  495. wimp_pointer pointer;
  496. wimp_message message;
  497. os_error *error;
  498. char *dp, *ep;
  499. char *local_name = NULL;
  500. utf8_convert_ret err;
  501. if (dragbox_active)
  502. ro_gui_drag_box_cancel();
  503. error = xwimp_get_pointer_info(&pointer);
  504. if (error) {
  505. LOG(("xwimp_get_pointer_info: 0x%x: %s",
  506. error->errnum, error->errmess));
  507. warn_user("WimpError", error->errmess);
  508. return;
  509. }
  510. /* perform hit-test if the destination is the same as the source window;
  511. we want to allow drag-saving from a page into the input fields within
  512. the page, but avoid accidental replacements of the current page */
  513. if (gui_save_sourcew != (wimp_w)-1 && pointer.w == gui_save_sourcew) {
  514. int dx = (drag->final.x1 + drag->final.x0)/2;
  515. int dy = (drag->final.y1 + drag->final.y0)/2;
  516. struct gui_window *g;
  517. bool dest_ok = false;
  518. os_coord pos;
  519. g = ro_gui_window_lookup(gui_save_sourcew);
  520. if (g && ro_gui_window_to_window_pos(g, dx, dy, &pos)) {
  521. hlcache_handle *h = g->bw->current_content;
  522. if (h && content_get_type(h) == CONTENT_HTML) {
  523. struct box *box = html_get_box_tree(h);
  524. int box_x, box_y;
  525. /* Consider the margins of the html page now */
  526. box_x = box->margin[LEFT];
  527. box_y = box->margin[TOP];
  528. while (!dest_ok && (box = box_at_point(box,
  529. pos.x, pos.y, &box_x, &box_y,
  530. &h))) {
  531. if (box->style &&
  532. css_computed_visibility(
  533. box->style) ==
  534. CSS_VISIBILITY_HIDDEN)
  535. continue;
  536. if (box->gadget) {
  537. switch (box->gadget->type) {
  538. case GADGET_FILE:
  539. case GADGET_TEXTBOX:
  540. case GADGET_TEXTAREA:
  541. case GADGET_PASSWORD:
  542. dest_ok = true;
  543. break;
  544. default: /* appease compiler */
  545. break;
  546. }
  547. }
  548. }
  549. }
  550. }
  551. if (!dest_ok) {
  552. /* cancel the drag operation */
  553. gui_current_drag_type = GUI_DRAG_NONE;
  554. return;
  555. }
  556. }
  557. if (!saving_from_dialog) {
  558. /* saving directly from browser window, choose a
  559. * name based upon the URL */
  560. err = utf8_to_local_encoding(save_leafname, 0, &local_name);
  561. if (err != UTF8_CONVERT_OK) {
  562. /* badenc should never happen */
  563. assert(err != UTF8_CONVERT_BADENC);
  564. local_name = NULL;
  565. }
  566. name = local_name ? local_name : save_leafname;
  567. }
  568. else {
  569. char *dot;
  570. /* saving from dialog, grab leafname from icon */
  571. name = ro_gui_get_icon_string(gui_save_sourcew, ICON_SAVE_PATH);
  572. dot = strrchr(name, '.');
  573. if (dot)
  574. name = dot + 1;
  575. }
  576. dp = message.data.data_xfer.file_name;
  577. ep = dp + sizeof message.data.data_xfer.file_name;
  578. if (gui_save_current_type == GUI_SAVE_COMPLETE) {
  579. message.data.data_xfer.file_type = 0x2000;
  580. if (*name != '!') *dp++ = '!';
  581. } else
  582. message.data.data_xfer.file_type = gui_save_filetype;
  583. ro_gui_convert_save_path(dp, ep - dp, name);
  584. /* \todo - we're supposed to set this if drag-n-drop used */
  585. message.your_ref = 0;
  586. message.action = message_DATA_SAVE;
  587. message.data.data_xfer.w = pointer.w;
  588. message.data.data_xfer.i = pointer.i;
  589. message.data.data_xfer.pos.x = pointer.pos.x;
  590. message.data.data_xfer.pos.y = pointer.pos.y;
  591. message.data.data_xfer.est_size = 1000;
  592. message.size = 44 + ((strlen(message.data.data_xfer.file_name) + 4) &
  593. (~3u));
  594. ro_message_send_message_to_window(wimp_USER_MESSAGE_RECORDED, &message,
  595. pointer.w, pointer.i, ro_gui_save_bounced, NULL);
  596. free(local_name);
  597. }
  598. /**
  599. * Send DataSave message on behalf of clipboard code and remember that it's the
  600. * clipboard contents we're being asked for when the DataSaveAck reply arrives
  601. */
  602. void ro_gui_send_datasave(gui_save_type save_type,
  603. wimp_full_message_data_xfer *message, wimp_t to)
  604. {
  605. /* Close the save window because otherwise we need two contexts
  606. */
  607. xwimp_create_menu(wimp_CLOSE_MENU, 0, 0);
  608. ro_gui_dialog_close(dialog_saveas);
  609. if (ro_message_send_message(wimp_USER_MESSAGE_RECORDED, (wimp_message*)message,
  610. to, ro_gui_save_bounced)) {
  611. gui_save_current_type = save_type;
  612. gui_save_sourcew = (wimp_w)-1;
  613. saving_from_dialog = false;
  614. gui_current_drag_type = GUI_DRAG_SAVE;
  615. }
  616. }
  617. /**
  618. * Handle lack of Message_DataSaveAck for drags, saveas dialogs and clipboard code
  619. */
  620. void ro_gui_save_bounced(wimp_message *message)
  621. {
  622. gui_current_drag_type = GUI_DRAG_NONE;
  623. }
  624. /**
  625. * Handle Message_DataSaveAck for a drag from the save dialog or browser window,
  626. * or Clipboard protocol.
  627. */
  628. void ro_gui_save_datasave_ack(wimp_message *message)
  629. {
  630. char *path = message->data.data_xfer.file_name;
  631. hlcache_handle *h = gui_save_content;
  632. bool force_overwrite;
  633. switch (gui_save_current_type) {
  634. case GUI_SAVE_LINK_URI:
  635. case GUI_SAVE_LINK_URL:
  636. case GUI_SAVE_LINK_TEXT:
  637. case GUI_SAVE_HOTLIST_EXPORT_HTML:
  638. case GUI_SAVE_HISTORY_EXPORT_HTML:
  639. case GUI_SAVE_TEXT_SELECTION:
  640. case GUI_SAVE_CLIPBOARD_CONTENTS:
  641. break;
  642. default:
  643. if (!gui_save_content) {
  644. LOG(("unexpected DataSaveAck: gui_save_content not set"));
  645. return;
  646. }
  647. break;
  648. }
  649. if (saving_from_dialog)
  650. ro_gui_set_icon_string(gui_save_sourcew, ICON_SAVE_PATH,
  651. path, true);
  652. gui_save_send_dataload = true;
  653. memcpy(&gui_save_message, message, sizeof(gui_save_message));
  654. /* if saving/pasting to another application, don't request user
  655. confirmation; a ScrapFile almost certainly exists already */
  656. if (message->data.data_xfer.est_size == -1)
  657. force_overwrite = true;
  658. else
  659. force_overwrite = !option_confirm_overwrite;
  660. if (ro_gui_save_content(h, path, force_overwrite))
  661. ro_gui_save_done();
  662. }
  663. /**
  664. * Does the actual saving
  665. *
  666. * \param c content to save (or NULL for other)
  667. * \param path path to save as
  668. * \param force_overwrite true iff required to overwrite without prompting
  669. * \return true on success,
  670. * false on (i) error and error reported
  671. * or (ii) deferred awaiting user confirmation
  672. */
  673. bool ro_gui_save_content(hlcache_handle *h, char *path, bool force_overwrite)
  674. {
  675. os_error *error;
  676. const char *source_data;
  677. unsigned long source_size;
  678. /* does the user want to check for collisions when saving? */
  679. if (!force_overwrite) {
  680. fileswitch_object_type obj_type;
  681. /* check whether the destination file/dir already exists */
  682. error = xosfile_read_stamped(path, &obj_type,
  683. NULL, NULL, NULL, NULL, NULL);
  684. if (error) {
  685. LOG(("xosfile_read_stamped: 0x%x:%s", error->errnum, error->errmess));
  686. warn_user("SaveError", error->errmess);
  687. return false;
  688. }
  689. switch (obj_type) {
  690. case osfile_NOT_FOUND:
  691. break;
  692. case osfile_IS_FILE:
  693. gui_save_query = query_user("OverwriteFile", NULL, &overwrite_funcs, NULL,
  694. messages_get("Replace"), messages_get("DontReplace"));
  695. // gui_save_query_rsn = QueryRsn_Overwrite;
  696. return false;
  697. default:
  698. error = xosfile_make_error(path, obj_type);
  699. assert(error);
  700. warn_user("SaveError", error->errmess);
  701. return false;
  702. }
  703. }
  704. switch (gui_save_current_type) {
  705. #ifdef WITH_DRAW_EXPORT
  706. case GUI_SAVE_DRAW:
  707. return save_as_draw(h, path);
  708. #endif
  709. #ifdef WITH_PDF_EXPORT
  710. case GUI_SAVE_PDF:
  711. return save_as_pdf(h, path);
  712. #endif
  713. case GUI_SAVE_TEXT:
  714. save_as_text(h, path);
  715. xosfile_set_type(path, 0xfff);
  716. break;
  717. case GUI_SAVE_COMPLETE:
  718. assert(h);
  719. if (content_get_type(h) == CONTENT_HTML) {
  720. if (strcmp(path, "<Wimp$Scrap>"))
  721. return ro_gui_save_complete(h, path);
  722. /* we can't send a whole directory to another
  723. * application, so just send the HTML source */
  724. gui_save_current_type = GUI_SAVE_SOURCE;
  725. }
  726. else
  727. gui_save_current_type = GUI_SAVE_OBJECT_ORIG; /* \todo do this earlier? */
  728. /* no break */
  729. case GUI_SAVE_SOURCE:
  730. case GUI_SAVE_OBJECT_ORIG:
  731. source_data = content_get_source_data(h, &source_size);
  732. error = xosfile_save_stamped(path,
  733. ro_content_filetype(h),
  734. (byte *) source_data,
  735. (byte *) source_data + source_size);
  736. if (error) {
  737. LOG(("xosfile_save_stamped: 0x%x: %s",
  738. error->errnum, error->errmess));
  739. warn_user("SaveError", error->errmess);
  740. return false;
  741. }
  742. break;
  743. case GUI_SAVE_OBJECT_NATIVE:
  744. return ro_gui_save_object_native(h, path);
  745. case GUI_SAVE_LINK_URI:
  746. return ro_gui_save_link(gui_save_url, gui_save_title,
  747. LINK_ACORN, path);
  748. case GUI_SAVE_LINK_URL:
  749. return ro_gui_save_link(gui_save_url, gui_save_title,
  750. LINK_ANT, path);
  751. case GUI_SAVE_LINK_TEXT:
  752. return ro_gui_save_link(gui_save_url, gui_save_title,
  753. LINK_TEXT, path);
  754. case GUI_SAVE_HOTLIST_EXPORT_HTML:
  755. if (!options_save_tree(hotlist_tree, path,
  756. "NetSurf hotlist"))
  757. return false;
  758. error = xosfile_set_type(path, 0xfaf);
  759. if (error)
  760. LOG(("xosfile_set_type: 0x%x: %s",
  761. error->errnum, error->errmess));
  762. break;
  763. case GUI_SAVE_HISTORY_EXPORT_HTML:
  764. if (!options_save_tree(global_history_tree, path,
  765. "NetSurf history"))
  766. return false;
  767. error = xosfile_set_type(path, 0xfaf);
  768. if (error)
  769. LOG(("xosfile_set_type: 0x%x: %s",
  770. error->errnum, error->errmess));
  771. break;
  772. case GUI_SAVE_TEXT_SELECTION:
  773. if (!selection_save_text(gui_save_selection, path))
  774. return false;
  775. xosfile_set_type(path, 0xfff);
  776. break;
  777. case GUI_SAVE_CLIPBOARD_CONTENTS:
  778. return ro_gui_save_clipboard(path);
  779. default:
  780. LOG(("Unexpected content type: %d, path %s",
  781. gui_save_current_type, path));
  782. return false;
  783. }
  784. return true;
  785. }
  786. /**
  787. * Save completed, inform recipient and close our 'save as' dialog.
  788. */
  789. void ro_gui_save_done(void)
  790. {
  791. os_error *error;
  792. if (gui_save_send_dataload) {
  793. /* Ack successful save with message_DATA_LOAD */
  794. wimp_message *message = &gui_save_message;
  795. message->action = message_DATA_LOAD;
  796. message->your_ref = message->my_ref;
  797. error = xwimp_send_message(wimp_USER_MESSAGE, message,
  798. message->sender);
  799. if (error) {
  800. LOG(("xwimp_send_message: 0x%x: %s",
  801. error->errnum, error->errmess));
  802. warn_user("SaveError", error->errmess);
  803. }
  804. }
  805. if (saving_from_dialog) {
  806. /* remember the save directory if saving to the Filer */
  807. if (!gui_save_send_dataload ||
  808. gui_save_message.data.data_xfer.est_size != -1) {
  809. char *sp = gui_save_message.data.data_xfer.file_name;
  810. char *ep = sp + sizeof(gui_save_message.data.data_xfer.file_name);
  811. char *lastdot = NULL;
  812. char *p = sp;
  813. while (p < ep && *p >= 0x20) {
  814. if (*p == '.') {
  815. /* don't remember the directory if it's a temporary file */
  816. if (!lastdot && p == sp + 12 &&
  817. !memcmp(sp, "<Wimp$Scrap>", 12)) break;
  818. lastdot = p;
  819. }
  820. p++;
  821. }
  822. if (lastdot) {
  823. /* remember the directory */
  824. char *new_dir = realloc(save_dir, (lastdot+1)-sp);
  825. if (new_dir) {
  826. save_dir_len = lastdot - sp;
  827. memcpy(new_dir, sp, save_dir_len);
  828. new_dir[save_dir_len] = '\0';
  829. save_dir = new_dir;
  830. }
  831. }
  832. }
  833. if (gui_save_close_after) {
  834. /* Close the save window */
  835. ro_gui_dialog_close(dialog_saveas);
  836. error = xwimp_create_menu(wimp_CLOSE_MENU, 0, 0);
  837. if (error) {
  838. LOG(("xwimp_create_menu: 0x%x: %s",
  839. error->errnum, error->errmess));
  840. warn_user("MenuError", error->errmess);
  841. }
  842. }
  843. }
  844. if (!saving_from_dialog || gui_save_close_after)
  845. gui_save_content = 0;
  846. }
  847. /**
  848. * conducts the filesystem save appropriate to the gui
  849. * \param path save path
  850. * \param filename name of file to save
  851. * \param len data length
  852. * \param sourcedata pointer to data to save, NULL when all data in c
  853. * \param type content type
  854. * \return true for success
  855. */
  856. bool save_complete_gui_save(const char *path, const char *filename, size_t len,
  857. const char *sourcedata, content_type type)
  858. {
  859. char *fullpath;
  860. os_error *error;
  861. int namelen = strlen(path) + strlen(filename) + 2;
  862. int rotype;
  863. fullpath = malloc(namelen);
  864. if (fullpath == NULL) {
  865. warn_user("NoMemory", 0);
  866. return false;
  867. }
  868. snprintf(fullpath, namelen, "%s.%s", path, filename);
  869. rotype = ro_content_filetype_from_type(type);
  870. error = xosfile_save_stamped(fullpath, rotype, (byte *) sourcedata,
  871. (byte *) sourcedata + len);
  872. free(fullpath);
  873. if (error) {
  874. LOG(("xosfile_save_stamped: 0x%x: %s",
  875. error->errnum, error->errmess));
  876. warn_user("SaveError", error->errmess);
  877. return false;
  878. }
  879. return true;
  880. }
  881. /**
  882. * wrapper for lib function htmlSaveFileFormat; front sets path from
  883. * path + filename in a filesystem-specific way
  884. */
  885. int save_complete_htmlSaveFileFormat(const char *path, const char *filename,
  886. xmlDocPtr cur, const char *encoding, int format)
  887. {
  888. os_error *error;
  889. int ret;
  890. int len = strlen(path) + strlen(filename) + 2;
  891. char *fullpath = malloc(len);
  892. if (fullpath == NULL){
  893. warn_user("NoMemory", 0);
  894. return -1;
  895. }
  896. snprintf(fullpath, len, "%s.%s", path, filename);
  897. ret = htmlSaveFileFormat(fullpath, cur, encoding, format);
  898. error = xosfile_set_type(fullpath, 0xFAF);
  899. if (error) {
  900. LOG(("xosfile_save_stamped: 0x%x: %s",
  901. error->errnum, error->errmess));
  902. /* Don't warn the user here -- they probably don't care */
  903. }
  904. free(fullpath);
  905. return ret;
  906. }
  907. /**
  908. * Prepare an application directory and save_complete() to it.
  909. *
  910. * \param h content of type CONTENT_HTML to save
  911. * \param path path to save as
  912. * \return true on success, false on error and error reported
  913. */
  914. #define WIDTH 64
  915. #define HEIGHT 64
  916. #define SPRITE_SIZE (16 + 44 + ((WIDTH / 2 + 3) & ~3) * HEIGHT / 2)
  917. bool ro_gui_save_complete(hlcache_handle *h, char *path)
  918. {
  919. void *spr = ((byte *) saveas_area) + saveas_area->first;
  920. osspriteop_header *sprite = (osspriteop_header *) spr;
  921. char name[12];
  922. char buf[256];
  923. FILE *fp;
  924. os_error *error;
  925. size_t len;
  926. char *dot;
  927. int i;
  928. /* Create dir */
  929. error = xosfile_create_dir(path, 0);
  930. if (error) {
  931. LOG(("xosfile_create_dir: 0x%x: %s",
  932. error->errnum, error->errmess));
  933. warn_user("SaveError", error->errmess);
  934. return false;
  935. }
  936. /* Save !Run file */
  937. snprintf(buf, sizeof buf, "%s.!Run", path);
  938. fp = fopen(buf, "w");
  939. if (!fp) {
  940. LOG(("fopen(): errno = %i", errno));
  941. warn_user("SaveError", strerror(errno));
  942. return false;
  943. }
  944. fprintf(fp, "IconSprites <Obey$Dir>.!Sprites\n");
  945. fprintf(fp, "Filer_Run <Obey$Dir>.index\n");
  946. fclose(fp);
  947. error = xosfile_set_type(buf, 0xfeb);
  948. if (error) {
  949. LOG(("xosfile_set_type: 0x%x: %s",
  950. error->errnum, error->errmess));
  951. warn_user("SaveError", error->errmess);
  952. return false;
  953. }
  954. /* Make sure the sprite name matches the directory name, because
  955. the user may have renamed the directory since we created the
  956. thumbnail sprite */
  957. dot = strrchr(path, '.');
  958. if (dot) dot++; else dot = path;
  959. len = strlen(dot);
  960. if (len >= 12) len = 12;
  961. memcpy(name, sprite->name, 12); /* remember original name */
  962. memcpy(sprite->name, dot, len);
  963. memset(sprite->name + len, 0, 12 - len);
  964. for (i = 0; i < 12; i++) /* convert to lower case */
  965. if (sprite->name[i] != '\0')
  966. sprite->name[i] = tolower(sprite->name[i]);
  967. /* Create !Sprites */
  968. snprintf(buf, sizeof buf, "%s.!Sprites", path);
  969. error = xosspriteop_save_sprite_file(osspriteop_NAME, saveas_area, buf);
  970. if (error) {
  971. LOG(("xosspriteop_save_sprite_file: 0x%x: %s",
  972. error->errnum, error->errmess));
  973. warn_user("SaveError", error->errmess);
  974. return false;
  975. }
  976. /* restore sprite name in case the save fails and we need to try again */
  977. memcpy(sprite->name, name, 12);
  978. /* save URL file with original URL */
  979. snprintf(buf, sizeof buf, "%s.URL", path);
  980. if (!ro_gui_save_link(content_get_url(h), content_get_title(h),
  981. LINK_ANT, buf))
  982. return false;
  983. return save_complete(h, path);
  984. }
  985. bool ro_gui_save_object_native(hlcache_handle *h, char *path)
  986. {
  987. const char *source_data;
  988. unsigned long source_size;
  989. switch (content_get_type(h)) {
  990. #ifdef WITH_JPEG
  991. case CONTENT_JPEG:
  992. #endif
  993. #if defined(WITH_MNG) || defined(WITH_PNG)
  994. case CONTENT_PNG:
  995. #endif
  996. #ifdef WITH_MNG
  997. case CONTENT_JNG:
  998. case CONTENT_MNG:
  999. #endif
  1000. #ifdef WITH_GIF
  1001. case CONTENT_GIF:
  1002. #endif
  1003. #ifdef WITH_BMP
  1004. case CONTENT_BMP:
  1005. case CONTENT_ICO:
  1006. #endif
  1007. {
  1008. unsigned flags = (os_version == 0xA9) ?
  1009. BITMAP_SAVE_FULL_ALPHA : 0;
  1010. bitmap_save(content_get_bitmap(h), path, flags);
  1011. return true;
  1012. }
  1013. break;
  1014. #ifdef WITH_SPRITE
  1015. case CONTENT_SPRITE:
  1016. #endif
  1017. #ifdef WITH_DRAW
  1018. case CONTENT_DRAW:
  1019. #endif
  1020. {
  1021. os_error *error;
  1022. source_data = content_get_source_data(h, &source_size);
  1023. error = xosfile_save_stamped(path,
  1024. ro_content_filetype(h),
  1025. (byte *) source_data,
  1026. (byte *) source_data + source_size);
  1027. if (error) {
  1028. LOG(("xosfile_save_stamped: 0x%x: %s",
  1029. error->errnum, error->errmess));
  1030. warn_user("SaveError", error->errmess);
  1031. return false;
  1032. }
  1033. return true;
  1034. }
  1035. break;
  1036. #if defined(WITH_NS_SVG) || defined(WITH_RSVG)
  1037. case CONTENT_SVG:
  1038. return save_as_draw(h, path);
  1039. #endif
  1040. default:
  1041. return false;
  1042. }
  1043. }
  1044. /**
  1045. * Save a link file.
  1046. *
  1047. * \param url url to be saved
  1048. * \param title corresponding title, if any
  1049. * \param format format of link file
  1050. * \param path pathname for link file
  1051. * \return true on success, false on failure and reports the error
  1052. */
  1053. bool ro_gui_save_link(const char *url, const char *title, link_format format,
  1054. char *path)
  1055. {
  1056. FILE *fp = fopen(path, "w");
  1057. if (!fp) {
  1058. warn_user("SaveError", strerror(errno));
  1059. return false;
  1060. }
  1061. switch (format) {
  1062. case LINK_ACORN: /* URI */
  1063. fprintf(fp, "%s\t%s\n", "URI", "100");
  1064. fprintf(fp, "\t# NetSurf %s\n\n", netsurf_version);
  1065. fprintf(fp, "\t%s\n", url);
  1066. if (title)
  1067. fprintf(fp, "\t%s\n", title);
  1068. else
  1069. fprintf(fp, "\t*\n");
  1070. break;
  1071. case LINK_ANT: /* URL */
  1072. case LINK_TEXT: /* Text */
  1073. fprintf(fp, "%s\n", url);
  1074. break;
  1075. }
  1076. fclose(fp);
  1077. switch (format) {
  1078. case LINK_ACORN: /* URI */
  1079. xosfile_set_type(path, 0xf91);
  1080. break;
  1081. case LINK_ANT: /* URL */
  1082. xosfile_set_type(path, 0xb28);
  1083. break;
  1084. case LINK_TEXT: /* Text */
  1085. xosfile_set_type(path, 0xfff);
  1086. break;
  1087. }
  1088. return true;
  1089. }
  1090. /**
  1091. * Suggest a leafname and sprite name for the given content.
  1092. *
  1093. * \param h content being saved
  1094. * \param save_type type of save operation being performed
  1095. * \param url used to determine leafname
  1096. * \param leaf_buf buffer to receive suggested leafname, length at least
  1097. * LEAFNAME_MAX
  1098. * \param icon_buf buffer to receive sprite name, length at least 13
  1099. */
  1100. void ro_gui_save_set_state(hlcache_handle *h, gui_save_type save_type,
  1101. const char *url, char *leaf_buf, char *icon_buf)
  1102. {
  1103. /* filename */
  1104. const char *name = gui_save_table[save_type].name;
  1105. bool done = false;
  1106. char *nice = NULL;
  1107. utf8_convert_ret err;
  1108. char *local_name;
  1109. size_t i;
  1110. /* parameters that we need to remember */
  1111. gui_save_current_type = save_type;
  1112. gui_save_content = h;
  1113. /* suggest a filetype based upon the content */
  1114. gui_save_filetype = gui_save_table[save_type].filetype;
  1115. if (!gui_save_filetype && h) {
  1116. if (save_type == GUI_SAVE_OBJECT_NATIVE) {
  1117. switch (content_get_type(h)) {
  1118. /* bitmap images */
  1119. #ifdef WITH_JPEG
  1120. case CONTENT_JPEG:
  1121. #endif
  1122. #if defined(WITH_MNG) || defined(WITH_PNG)
  1123. case CONTENT_PNG:
  1124. #endif
  1125. #ifdef WITH_MNG
  1126. case CONTENT_JNG:
  1127. case CONTENT_MNG:
  1128. #endif
  1129. #ifdef WITH_GIF
  1130. case CONTENT_GIF:
  1131. #endif
  1132. #ifdef WITH_BMP
  1133. case CONTENT_BMP:
  1134. case CONTENT_ICO:
  1135. #endif
  1136. gui_save_filetype = osfile_TYPE_SPRITE;
  1137. break;
  1138. /* vector formats */
  1139. #if defined(WITH_NS_SVG) || defined(WITH_RSVG)
  1140. case CONTENT_SVG:
  1141. gui_save_filetype = osfile_TYPE_DRAW;
  1142. break;
  1143. #endif
  1144. #ifdef WITH_DRAW
  1145. case CONTENT_DRAW:
  1146. gui_save_filetype = osfile_TYPE_DRAW;
  1147. break;
  1148. #endif
  1149. default:
  1150. break;
  1151. }
  1152. }
  1153. if (!gui_save_filetype)
  1154. gui_save_filetype = ro_content_filetype(h);
  1155. }
  1156. /* leafname */
  1157. if (url && url_nice(url, &nice, option_strip_extensions) ==
  1158. URL_FUNC_OK) {
  1159. for (i = 0; nice[i]; i++) {
  1160. if (nice[i] == '.')
  1161. nice[i] = '/';
  1162. else if (nice[i] <= ' ' ||
  1163. strchr(":*#$&@^%\\", nice[i]))
  1164. nice[i] = '_';
  1165. }
  1166. name = nice;
  1167. } else {
  1168. name = messages_get(name);
  1169. }
  1170. /* filename is utf8 */
  1171. strncpy(leaf_buf, name, LEAFNAME_MAX);
  1172. leaf_buf[LEAFNAME_MAX - 1] = 0;
  1173. err = utf8_to_local_encoding(name, 0, &local_name);
  1174. if (err != UTF8_CONVERT_OK) {
  1175. /* badenc should never happen */
  1176. assert(err != UTF8_CONVERT_BADENC);
  1177. local_name = NULL;
  1178. }
  1179. name = local_name ? local_name : name;
  1180. /* sprite name used for icon and dragging */
  1181. if (save_type == GUI_SAVE_COMPLETE) {
  1182. int index;
  1183. /* Paint gets confused with uppercase characters and we need to
  1184. convert spaces to hard spaces */
  1185. icon_buf[0] = '!';
  1186. for (index = 0; index < 11 && name[index]; ) {
  1187. char ch = name[index];
  1188. if (ch == ' ')
  1189. icon_buf[++index] = 0xa0;
  1190. else
  1191. icon_buf[++index] = tolower(ch);
  1192. }
  1193. memset(&icon_buf[index + 1], 0, 11 - index);
  1194. icon_buf[12] = '\0';
  1195. if (ro_gui_save_create_thumbnail(h, icon_buf))
  1196. done = true;
  1197. }
  1198. if (!done) {
  1199. osspriteop_header *sprite;
  1200. os_error *error;
  1201. sprintf(icon_buf, "file_%.3x", gui_save_filetype);
  1202. error = ro_gui_wimp_get_sprite(icon_buf, &sprite);
  1203. if (error && error->errnum == error_SPRITE_OP_DOESNT_EXIST) {
  1204. /* try the 'unknown' filetype sprite as a fallback */
  1205. memcpy(icon_buf, "file_xxx", 9);
  1206. error = ro_gui_wimp_get_sprite(icon_buf, &sprite);
  1207. }
  1208. if (error) {
  1209. LOG(("ro_gui_wimp_get_sprite: 0x%x: %s",
  1210. error->errnum, error->errmess));
  1211. warn_user("MiscError", error->errmess);
  1212. } else {
  1213. /* the sprite area should always be large enough for
  1214. * file_xxx sprites */
  1215. assert(sprite->size <= saveas_area->size -
  1216. saveas_area->first);
  1217. memcpy((byte*)saveas_area + saveas_area->first,
  1218. sprite,
  1219. sprite->size);
  1220. saveas_area->sprite_count = 1;
  1221. saveas_area->used = saveas_area->first + sprite->size;
  1222. }
  1223. }
  1224. free(local_name);
  1225. free(nice);
  1226. }
  1227. /**
  1228. * Create a thumbnail sprite for the page being saved.
  1229. *
  1230. * \param h content to be converted
  1231. * \param name sprite name to use
  1232. * \return true iff successful
  1233. */
  1234. bool ro_gui_save_create_thumbnail(hlcache_handle *h, const char *name)
  1235. {
  1236. osspriteop_header *sprite_header;
  1237. struct bitmap *bitmap;
  1238. osspriteop_area *area;
  1239. bitmap = bitmap_create(34, 34, BITMAP_NEW | BITMAP_OPAQUE | BITMAP_CLEAR_MEMORY);
  1240. if (!bitmap) {
  1241. LOG(("Thumbnail initialisation failed."));
  1242. return false;
  1243. }
  1244. thumbnail_create(h, bitmap, NULL);
  1245. area = thumbnail_convert_8bpp(bitmap);
  1246. bitmap_destroy(bitmap);
  1247. if (!area) {
  1248. LOG(("Thumbnail conversion failed."));
  1249. return false;
  1250. }
  1251. sprite_header = (osspriteop_header *)(area + 1);
  1252. strncpy(sprite_header->name, name, 12);
  1253. /* we can't resize the saveas sprite area because it may move and we have
  1254. no elegant way to update the window definition on all OS versions */
  1255. assert(sprite_header->size <= saveas_area->size - saveas_area->first);
  1256. memcpy((byte*)saveas_area + saveas_area->first,
  1257. sprite_header, sprite_header->size);
  1258. saveas_area->sprite_count = 1;
  1259. saveas_area->used = saveas_area->first + sprite_header->size;
  1260. free(area);
  1261. return true;
  1262. }
  1263. /**
  1264. * User has opted not to overwrite the existing file.
  1265. */
  1266. void ro_gui_save_overwrite_cancelled(query_id id, enum query_response res, void *p)
  1267. {
  1268. if (!saving_from_dialog) {
  1269. // ro_gui_save_prepare(gui_save_current_type, gui_save_content);
  1270. // ro_gui_dialog_open_persistent(g->window, dialog_saveas, true);
  1271. }
  1272. }
  1273. /**
  1274. * Overwrite of existing file confirmed, proceed with the save.
  1275. */
  1276. void ro_gui_save_overwrite_confirmed(query_id id, enum query_response res, void *p)
  1277. {
  1278. if (ro_gui_save_content(gui_save_content, gui_save_message.data.data_xfer.file_name, true))
  1279. ro_gui_save_done();
  1280. }