edit.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2009 Jiri Svoboda
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  *
00009  * - Redistributions of source code must retain the above copyright
00010  *   notice, this list of conditions and the following disclaimer.
00011  * - Redistributions in binary form must reproduce the above copyright
00012  *   notice, this list of conditions and the following disclaimer in the
00013  *   documentation and/or other materials provided with the distribution.
00014  * - The name of the author may not be used to endorse or promote products
00015  *   derived from this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00019  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00020  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00022  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00023  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00024  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00025  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00026  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  */
00028 
00037 #include <stdio.h>
00038 #include <stdlib.h>
00039 #include <sys/types.h>
00040 #include <vfs/vfs.h>
00041 #include <io/console.h>
00042 #include <io/style.h>
00043 #include <io/keycode.h>
00044 #include <errno.h>
00045 #include <align.h>
00046 #include <macros.h>
00047 #include <clipboard.h>
00048 #include <bool.h>
00049 
00050 #include "sheet.h"
00051 
00052 enum redraw_flags {
00053         REDRAW_TEXT     = (1 << 0),
00054         REDRAW_ROW      = (1 << 1),
00055         REDRAW_STATUS   = (1 << 2),
00056         REDRAW_CARET    = (1 << 3)
00057 };
00058 
00064 typedef struct {
00065         /* Pane dimensions */
00066         int rows, columns;
00067 
00068         /* Position of the visible area */
00069         int sh_row, sh_column;
00070 
00072         enum redraw_flags rflags;
00073 
00075         tag_t caret_pos;
00076 
00078         tag_t sel_start;
00079 
00084         int ideal_column;
00085 } pane_t;
00086 
00091 typedef struct {
00092         char *file_name;
00093         sheet_t sh;
00094 } doc_t;
00095 
00096 static int con;
00097 static doc_t doc;
00098 static bool done;
00099 static pane_t pane;
00100 static bool cursor_visible;
00101 
00102 static sysarg_t scr_rows;
00103 static sysarg_t scr_columns;
00104 
00105 #define ROW_BUF_SIZE 4096
00106 #define BUF_SIZE 64
00107 #define TAB_WIDTH 8
00108 #define ED_INFTY 65536
00109 
00111 #define INFNAME_MAX_LEN 128
00112 
00113 static void cursor_show(void);
00114 static void cursor_hide(void);
00115 static void cursor_setvis(bool visible);
00116 
00117 static void key_handle_unmod(console_event_t const *ev);
00118 static void key_handle_ctrl(console_event_t const *ev);
00119 static void key_handle_shift(console_event_t const *ev);
00120 static void key_handle_movement(unsigned int key, bool shift);
00121 
00122 static int file_save(char const *fname);
00123 static void file_save_as(void);
00124 static int file_insert(char *fname);
00125 static int file_save_range(char const *fname, spt_t const *spos,
00126     spt_t const *epos);
00127 static char *filename_prompt(char const *prompt, char const *init_value);
00128 static char *range_get_str(spt_t const *spos, spt_t const *epos);
00129 
00130 static void pane_text_display(void);
00131 static void pane_row_display(void);
00132 static void pane_row_range_display(int r0, int r1);
00133 static void pane_status_display(void);
00134 static void pane_caret_display(void);
00135 
00136 static void insert_char(wchar_t c);
00137 static void delete_char_before(void);
00138 static void delete_char_after(void);
00139 static void caret_update(void);
00140 static void caret_move(int drow, int dcolumn, enum dir_spec align_dir);
00141 
00142 static bool selection_active(void);
00143 static void selection_sel_all(void);
00144 static void selection_get_points(spt_t *pa, spt_t *pb);
00145 static void selection_delete(void);
00146 static void selection_copy(void);
00147 static void insert_clipboard_data(void);
00148 
00149 static void pt_get_sof(spt_t *pt);
00150 static void pt_get_eof(spt_t *pt);
00151 static int tag_cmp(tag_t const *a, tag_t const *b);
00152 static int spt_cmp(spt_t const *a, spt_t const *b);
00153 static int coord_cmp(coord_t const *a, coord_t const *b);
00154 
00155 static void status_display(char const *str);
00156 
00157 
00158 int main(int argc, char *argv[])
00159 {
00160         console_event_t ev;
00161         coord_t coord;
00162         bool new_file;
00163 
00164         spt_t pt;
00165 
00166         con = fphone(stdout);
00167         console_clear(con);
00168 
00169         console_get_size(con, &scr_columns, &scr_rows);
00170 
00171         pane.rows = scr_rows - 1;
00172         pane.columns = scr_columns;
00173         pane.sh_row = 1;
00174         pane.sh_column = 1;
00175 
00176         /* Start with an empty sheet. */
00177         sheet_init(&doc.sh);
00178 
00179         /* Place caret at the beginning of file. */
00180         coord.row = coord.column = 1;
00181         sheet_get_cell_pt(&doc.sh, &coord, dir_before, &pt);
00182         sheet_place_tag(&doc.sh, &pt, &pane.caret_pos);
00183         pane.ideal_column = coord.column;
00184 
00185         if (argc == 2) {
00186                 doc.file_name = str_dup(argv[1]);
00187         } else if (argc > 1) {
00188                 printf("Invalid arguments.\n");
00189                 return -2;
00190         } else {
00191                 doc.file_name = NULL;
00192         }
00193 
00194         new_file = false;
00195 
00196         if (doc.file_name == NULL || file_insert(doc.file_name) != EOK)
00197                 new_file = true;
00198 
00199         /* Move to beginning of file. */
00200         caret_move(-ED_INFTY, -ED_INFTY, dir_before);
00201 
00202         /* Place selection start tag. */
00203         tag_get_pt(&pane.caret_pos, &pt);
00204         sheet_place_tag(&doc.sh, &pt, &pane.sel_start);
00205 
00206         /* Initial display */
00207         cursor_visible = true;
00208 
00209         cursor_hide();
00210         console_clear(con);
00211         pane_text_display();
00212         pane_status_display();
00213         if (new_file && doc.file_name != NULL)
00214                 status_display("File not found. Starting empty file.");
00215         pane_caret_display();
00216         cursor_show();
00217 
00218         done = false;
00219 
00220         while (!done) {
00221                 console_get_event(con, &ev);
00222                 pane.rflags = 0;
00223 
00224                 if (ev.type == KEY_PRESS) {
00225                         /* Handle key press. */
00226                         if (((ev.mods & KM_ALT) == 0) &&
00227                             ((ev.mods & KM_SHIFT) == 0) &&
00228                              (ev.mods & KM_CTRL) != 0) {
00229                                 key_handle_ctrl(&ev);
00230                         } else if (((ev.mods & KM_ALT) == 0) &&
00231                             ((ev.mods & KM_CTRL) == 0) &&
00232                              (ev.mods & KM_SHIFT) != 0) {
00233                                 key_handle_shift(&ev);
00234                         } else if ((ev.mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
00235                                 key_handle_unmod(&ev);
00236                         }
00237                 }
00238 
00239                 /* Redraw as necessary. */
00240 
00241                 cursor_hide();
00242 
00243                 if (pane.rflags & REDRAW_TEXT)
00244                         pane_text_display();
00245                 if (pane.rflags & REDRAW_ROW)
00246                         pane_row_display();
00247                 if (pane.rflags & REDRAW_STATUS)
00248                         pane_status_display();
00249                 if (pane.rflags & REDRAW_CARET)
00250                         pane_caret_display();
00251 
00252                 cursor_show();
00253         }
00254 
00255         console_clear(con);
00256 
00257         return 0;
00258 }
00259 
00260 static void cursor_show(void)
00261 {
00262         cursor_setvis(true);
00263 }
00264 
00265 static void cursor_hide(void)
00266 {
00267         cursor_setvis(false);
00268 }
00269 
00270 static void cursor_setvis(bool visible)
00271 {
00272         if (cursor_visible != visible) {
00273                 console_cursor_visibility(con, visible);
00274                 cursor_visible = visible;
00275         }
00276 }
00277 
00279 static void key_handle_unmod(console_event_t const *ev)
00280 {
00281         switch (ev->key) {
00282         case KC_ENTER:
00283                 selection_delete();
00284                 insert_char('\n');
00285                 caret_update();
00286                 break;
00287         case KC_LEFT:
00288         case KC_RIGHT:
00289         case KC_UP:
00290         case KC_DOWN:
00291         case KC_HOME:
00292         case KC_END:
00293         case KC_PAGE_UP:
00294         case KC_PAGE_DOWN:
00295                 key_handle_movement(ev->key, false);
00296                 break;
00297         case KC_BACKSPACE:
00298                 if (selection_active())
00299                         selection_delete();
00300                 else
00301                         delete_char_before();
00302                 caret_update();
00303                 break;
00304         case KC_DELETE:
00305                 if (selection_active())
00306                         selection_delete();
00307                 else
00308                         delete_char_after();
00309                 caret_update();
00310                 break;
00311         default:
00312                 if (ev->c >= 32 || ev->c == '\t') {
00313                         selection_delete();
00314                         insert_char(ev->c);
00315                         caret_update();
00316                 }
00317                 break;
00318         }
00319 }
00320 
00322 static void key_handle_shift(console_event_t const *ev)
00323 {
00324         switch (ev->key) {
00325         case KC_LEFT:
00326         case KC_RIGHT:
00327         case KC_UP:
00328         case KC_DOWN:
00329         case KC_HOME:
00330         case KC_END:
00331         case KC_PAGE_UP:
00332         case KC_PAGE_DOWN:
00333                 key_handle_movement(ev->key, true);
00334                 break;
00335         default:
00336                 if (ev->c >= 32 || ev->c == '\t') {
00337                         selection_delete();
00338                         insert_char(ev->c);
00339                         caret_update();
00340                 }
00341                 break;
00342         }
00343 }
00344 
00346 static void key_handle_ctrl(console_event_t const *ev)
00347 {
00348         switch (ev->key) {
00349         case KC_Q:
00350                 done = true;
00351                 break;
00352         case KC_S:
00353                 if (doc.file_name != NULL)
00354                         file_save(doc.file_name);
00355                 else
00356                         file_save_as();
00357                 break;
00358         case KC_E:
00359                 file_save_as();
00360                 break;
00361         case KC_C:
00362                 selection_copy();
00363                 break;
00364         case KC_V:
00365                 selection_delete();
00366                 insert_clipboard_data();
00367                 pane.rflags |= REDRAW_TEXT;
00368                 caret_update();
00369                 break;
00370         case KC_X:
00371                 selection_copy();
00372                 selection_delete();
00373                 pane.rflags |= REDRAW_TEXT;
00374                 caret_update();
00375                 break;
00376         case KC_A:
00377                 selection_sel_all();
00378                 break;
00379         default:
00380                 break;
00381         }
00382 }
00383 
00384 static void key_handle_movement(unsigned int key, bool select)
00385 {
00386         spt_t pt;
00387         spt_t caret_pt;
00388         coord_t c_old, c_new;
00389         bool had_sel;
00390 
00391         /* Check if we had selection before. */
00392         tag_get_pt(&pane.caret_pos, &caret_pt);
00393         tag_get_pt(&pane.sel_start, &pt);
00394         had_sel = !spt_equal(&caret_pt, &pt);
00395 
00396         switch (key) {
00397         case KC_LEFT:
00398                 caret_move(0, -1, dir_before);
00399                 break;
00400         case KC_RIGHT:
00401                 caret_move(0, 0, dir_after);
00402                 break;
00403         case KC_UP:
00404                 caret_move(-1, 0, dir_before);
00405                 break;
00406         case KC_DOWN:
00407                 caret_move(+1, 0, dir_before);
00408                 break;
00409         case KC_HOME:
00410                 caret_move(0, -ED_INFTY, dir_before);
00411                 break;
00412         case KC_END:
00413                 caret_move(0, +ED_INFTY, dir_before);
00414                 break;
00415         case KC_PAGE_UP:
00416                 caret_move(-pane.rows, 0, dir_before);
00417                 break;
00418         case KC_PAGE_DOWN:
00419                 caret_move(+pane.rows, 0, dir_before);
00420                 break;
00421         default:
00422                 break;
00423         }
00424 
00425         if (select == false) {
00426                 /* Move sel_start to the same point as caret. */
00427                 sheet_remove_tag(&doc.sh, &pane.sel_start);
00428                 tag_get_pt(&pane.caret_pos, &pt);
00429                 sheet_place_tag(&doc.sh, &pt, &pane.sel_start);
00430         }
00431 
00432         if (select) {
00433                 tag_get_pt(&pane.caret_pos, &pt);
00434                 spt_get_coord(&caret_pt, &c_old);
00435                 spt_get_coord(&pt, &c_new);
00436 
00437                 if (c_old.row == c_new.row)
00438                         pane.rflags |= REDRAW_ROW;
00439                 else
00440                         pane.rflags |= REDRAW_TEXT;
00441 
00442         } else if (had_sel == true) {
00443                 /* Redraw because text was unselected. */
00444                 pane.rflags |= REDRAW_TEXT;
00445         }
00446 }
00447 
00449 static int file_save(char const *fname)
00450 {
00451         spt_t sp, ep;
00452         int rc;
00453 
00454         status_display("Saving...");
00455         pt_get_sof(&sp);
00456         pt_get_eof(&ep);
00457 
00458         rc = file_save_range(fname, &sp, &ep);
00459 
00460         switch (rc) {
00461         case EINVAL:
00462                 status_display("Error opening file!");
00463                 break;
00464         case EIO:
00465                 status_display("Error writing data!");
00466                 break;
00467         default:
00468                 status_display("File saved.");
00469                 break;
00470         }
00471 
00472         return rc;
00473 }
00474 
00476 static void file_save_as(void)
00477 {
00478         const char *old_fname = (doc.file_name != NULL) ? doc.file_name : "";
00479         char *fname;
00480         
00481         fname = filename_prompt("Save As", old_fname);
00482         if (fname == NULL) {
00483                 status_display("Save cancelled.");
00484                 return;
00485         }
00486 
00487         int rc = file_save(fname);
00488         if (rc != EOK)
00489                 return;
00490 
00491         if (doc.file_name != NULL)
00492                 free(doc.file_name);
00493         doc.file_name = fname;
00494 }
00495 
00497 static char *filename_prompt(char const *prompt, char const *init_value)
00498 {
00499         console_event_t ev;
00500         char *str;
00501         wchar_t buffer[INFNAME_MAX_LEN + 1];
00502         int max_len;
00503         int nc;
00504         bool done;
00505 
00506         asprintf(&str, "%s: %s", prompt, init_value);
00507         status_display(str);
00508         console_set_pos(con, 1 + str_length(str), scr_rows - 1);
00509         free(str);
00510 
00511         console_set_style(con, STYLE_INVERTED);
00512 
00513         max_len = min(INFNAME_MAX_LEN, scr_columns - 4 - str_length(prompt));
00514         str_to_wstr(buffer, max_len + 1, init_value);
00515         nc = wstr_length(buffer);
00516         done = false;
00517 
00518         while (!done) {
00519                 console_get_event(con, &ev);
00520 
00521                 if (ev.type == KEY_PRESS) {
00522                         /* Handle key press. */
00523                         if (((ev.mods & KM_ALT) == 0) &&
00524                              (ev.mods & KM_CTRL) != 0) {
00525                                 ;
00526                         } else if ((ev.mods & (KM_CTRL | KM_ALT)) == 0) {
00527                                 switch (ev.key) {
00528                                 case KC_ESCAPE:
00529                                         return NULL;
00530                                 case KC_BACKSPACE:
00531                                         if (nc > 0) {
00532                                                 putchar('\b');
00533                                                 fflush(stdout);
00534                                                 --nc;
00535                                         }
00536                                         break;
00537                                 case KC_ENTER:
00538                                         done = true;
00539                                         break;
00540                                 default:
00541                                         if (ev.c >= 32 && nc < max_len) {
00542                                                 putchar(ev.c);
00543                                                 fflush(stdout);
00544                                                 buffer[nc++] = ev.c;
00545                                         }
00546                                         break;
00547                                 }
00548                         }
00549                 }
00550         }
00551 
00552         buffer[nc] = '\0';
00553         str = wstr_to_astr(buffer);
00554 
00555         console_set_style(con, STYLE_NORMAL);
00556 
00557         return str;
00558 }
00559 
00565 static int file_insert(char *fname)
00566 {
00567         FILE *f;
00568         wchar_t c;
00569         char buf[BUF_SIZE];
00570         int bcnt;
00571         int n_read;
00572         size_t off;
00573 
00574         f = fopen(fname, "rt");
00575         if (f == NULL)
00576                 return EINVAL;
00577 
00578         bcnt = 0;
00579 
00580         while (true) {
00581                 if (bcnt < STR_BOUNDS(1)) {
00582                         n_read = fread(buf + bcnt, 1, BUF_SIZE - bcnt, f);
00583                         bcnt += n_read;
00584                 }
00585 
00586                 off = 0;
00587                 c = str_decode(buf, &off, bcnt);
00588                 if (c == '\0')
00589                         break;
00590 
00591                 bcnt -= off;
00592                 memcpy(buf, buf + off, bcnt);
00593 
00594                 insert_char(c);
00595         }
00596 
00597         fclose(f);
00598 
00599         return EOK;
00600 }
00601 
00603 static int file_save_range(char const *fname, spt_t const *spos,
00604     spt_t const *epos)
00605 {
00606         FILE *f;
00607         char buf[BUF_SIZE];
00608         spt_t sp, bep;
00609         size_t bytes, n_written;
00610 
00611         f = fopen(fname, "wt");
00612         if (f == NULL)
00613                 return EINVAL;
00614 
00615         sp = *spos;
00616 
00617         do {
00618                 sheet_copy_out(&doc.sh, &sp, epos, buf, BUF_SIZE, &bep);
00619                 bytes = str_size(buf);
00620 
00621                 n_written = fwrite(buf, 1, bytes, f);
00622                 if (n_written != bytes) {
00623                         return EIO;
00624                 }
00625 
00626                 sp = bep;
00627         } while (!spt_equal(&bep, epos));
00628 
00629         if (fclose(f) != EOK)
00630                 return EIO;
00631 
00632         return EOK;
00633 }
00634 
00636 static char *range_get_str(spt_t const *spos, spt_t const *epos)
00637 {
00638         char *buf;
00639         spt_t sp, bep;
00640         size_t bytes;
00641         size_t buf_size, bpos;
00642 
00643         buf_size = 1;
00644 
00645         buf = malloc(buf_size);
00646         if (buf == NULL)
00647                 return NULL;
00648 
00649         bpos = 0;
00650         sp = *spos;
00651 
00652         while (true) {
00653                 sheet_copy_out(&doc.sh, &sp, epos, &buf[bpos], buf_size - bpos,
00654                     &bep);
00655                 bytes = str_size(&buf[bpos]);
00656                 bpos += bytes;
00657                 sp = bep;
00658 
00659                 if (spt_equal(&bep, epos))
00660                         break;
00661 
00662                 buf_size *= 2;
00663                 buf = realloc(buf, buf_size);
00664                 if (buf == NULL)
00665                         return NULL;
00666         }
00667 
00668         return buf;
00669 }
00670 
00671 static void pane_text_display(void)
00672 {
00673         int sh_rows, rows;
00674 
00675         sheet_get_num_rows(&doc.sh, &sh_rows);
00676         rows = min(sh_rows - pane.sh_row + 1, pane.rows);
00677 
00678         /* Draw rows from the sheet. */
00679 
00680         console_set_pos(con, 0, 0);
00681         pane_row_range_display(0, rows);
00682 
00683         /* Clear the remaining rows if file is short. */
00684         
00685         int i;
00686         sysarg_t j;
00687         for (i = rows; i < pane.rows; ++i) {
00688                 console_set_pos(con, 0, i);
00689                 for (j = 0; j < scr_columns; ++j)
00690                         putchar(' ');
00691                 fflush(stdout);
00692         }
00693 
00694         pane.rflags |= (REDRAW_STATUS | REDRAW_CARET);
00695         pane.rflags &= ~REDRAW_ROW;
00696 }
00697 
00699 static void pane_row_display(void)
00700 {
00701         spt_t caret_pt;
00702         coord_t coord;
00703         int ridx;
00704 
00705         tag_get_pt(&pane.caret_pos, &caret_pt);
00706         spt_get_coord(&caret_pt, &coord);
00707 
00708         ridx = coord.row - pane.sh_row;
00709         pane_row_range_display(ridx, ridx + 1);
00710         pane.rflags |= (REDRAW_STATUS | REDRAW_CARET);
00711 }
00712 
00713 static void pane_row_range_display(int r0, int r1)
00714 {
00715         int i, j, fill;
00716         spt_t rb, re, dep, pt;
00717         coord_t rbc, rec;
00718         char row_buf[ROW_BUF_SIZE];
00719         wchar_t c;
00720         size_t pos, size;
00721         int s_column;
00722         coord_t csel_start, csel_end, ctmp;
00723 
00724         /* Determine selection start and end. */
00725 
00726         tag_get_pt(&pane.sel_start, &pt);
00727         spt_get_coord(&pt, &csel_start);
00728 
00729         tag_get_pt(&pane.caret_pos, &pt);
00730         spt_get_coord(&pt, &csel_end);
00731 
00732         if (coord_cmp(&csel_start, &csel_end) > 0) {
00733                 ctmp = csel_start;
00734                 csel_start = csel_end;
00735                 csel_end = ctmp;
00736         }
00737 
00738         /* Draw rows from the sheet. */
00739 
00740         console_set_pos(con, 0, 0);
00741         for (i = r0; i < r1; ++i) {
00742                 /* Starting point for row display */
00743                 rbc.row = pane.sh_row + i;
00744                 rbc.column = pane.sh_column;
00745                 sheet_get_cell_pt(&doc.sh, &rbc, dir_before, &rb);
00746 
00747                 /* Ending point for row display */
00748                 rec.row = pane.sh_row + i;
00749                 rec.column = pane.sh_column + pane.columns;
00750                 sheet_get_cell_pt(&doc.sh, &rec, dir_before, &re);
00751 
00752                 /* Copy the text of the row to the buffer. */
00753                 sheet_copy_out(&doc.sh, &rb, &re, row_buf, ROW_BUF_SIZE, &dep);
00754 
00755                 /* Display text from the buffer. */
00756 
00757                 if (coord_cmp(&csel_start, &rbc) <= 0 &&
00758                     coord_cmp(&rbc, &csel_end) < 0) {
00759                         fflush(stdout);
00760                         console_set_style(con, STYLE_SELECTED);
00761                         fflush(stdout);
00762                 }
00763 
00764                 console_set_pos(con, 0, i);
00765                 size = str_size(row_buf);
00766                 pos = 0;
00767                 s_column = pane.sh_column;
00768                 while (pos < size) {
00769                         if ((csel_start.row == rbc.row) && (csel_start.column == s_column)) {
00770                                 fflush(stdout);
00771                                 console_set_style(con, STYLE_SELECTED);
00772                                 fflush(stdout);
00773                         }
00774         
00775                         if ((csel_end.row == rbc.row) && (csel_end.column == s_column)) {
00776                                 fflush(stdout);
00777                                 console_set_style(con, STYLE_NORMAL);
00778                                 fflush(stdout);
00779                         }
00780         
00781                         c = str_decode(row_buf, &pos, size);
00782                         if (c != '\t') {
00783                                 printf("%lc", (wint_t) c);
00784                                 s_column += 1;
00785                         } else {
00786                                 fill = 1 + ALIGN_UP(s_column, TAB_WIDTH)
00787                                     - s_column;
00788 
00789                                 for (j = 0; j < fill; ++j)
00790                                         putchar(' ');
00791                                 s_column += fill;
00792                         }
00793                 }
00794 
00795                 if ((csel_end.row == rbc.row) && (csel_end.column == s_column)) {
00796                         fflush(stdout);
00797                         console_set_style(con, STYLE_NORMAL);
00798                         fflush(stdout);
00799                 }
00800 
00801                 /* Fill until the end of display area. */
00802 
00803                 if (str_length(row_buf) < (unsigned) scr_columns)
00804                         fill = scr_columns - str_length(row_buf);
00805                 else
00806                         fill = 0;
00807 
00808                 for (j = 0; j < fill; ++j)
00809                         putchar(' ');
00810                 fflush(stdout);
00811                 console_set_style(con, STYLE_NORMAL);
00812         }
00813 
00814         pane.rflags |= REDRAW_CARET;
00815 }
00816 
00818 static void pane_status_display(void)
00819 {
00820         spt_t caret_pt;
00821         coord_t coord;
00822 
00823         tag_get_pt(&pane.caret_pos, &caret_pt);
00824         spt_get_coord(&caret_pt, &coord);
00825 
00826         const char *fname = (doc.file_name != NULL) ? doc.file_name : "<unnamed>";
00827 
00828         console_set_pos(con, 0, scr_rows - 1);
00829         console_set_style(con, STYLE_INVERTED);
00830         int n = printf(" %d, %d: File '%s'. Ctrl-Q Quit  Ctrl-S Save  "
00831             "Ctrl-E Save As", coord.row, coord.column, fname);
00832         
00833         int pos = scr_columns - 1 - n;
00834         printf("%*s", pos, "");
00835         fflush(stdout);
00836         console_set_style(con, STYLE_NORMAL);
00837 
00838         pane.rflags |= REDRAW_CARET;
00839 }
00840 
00842 static void pane_caret_display(void)
00843 {
00844         spt_t caret_pt;
00845         coord_t coord;
00846 
00847         tag_get_pt(&pane.caret_pos, &caret_pt);
00848 
00849         spt_get_coord(&caret_pt, &coord);
00850         console_set_pos(con, coord.column - pane.sh_column,
00851             coord.row - pane.sh_row);
00852 }
00853 
00855 static void insert_char(wchar_t c)
00856 {
00857         spt_t pt;
00858         char cbuf[STR_BOUNDS(1) + 1];
00859         size_t offs;
00860 
00861         tag_get_pt(&pane.caret_pos, &pt);
00862 
00863         offs = 0;
00864         chr_encode(c, cbuf, &offs, STR_BOUNDS(1) + 1);
00865         cbuf[offs] = '\0';
00866 
00867         (void) sheet_insert(&doc.sh, &pt, dir_before, cbuf);
00868 
00869         pane.rflags |= REDRAW_ROW;
00870         if (c == '\n')
00871                 pane.rflags |= REDRAW_TEXT;
00872 }
00873 
00875 static void delete_char_before(void)
00876 {
00877         spt_t sp, ep;
00878         coord_t coord;
00879 
00880         tag_get_pt(&pane.caret_pos, &ep);
00881         spt_get_coord(&ep, &coord);
00882 
00883         coord.column -= 1;
00884         sheet_get_cell_pt(&doc.sh, &coord, dir_before, &sp);
00885 
00886         (void) sheet_delete(&doc.sh, &sp, &ep);
00887 
00888         pane.rflags |= REDRAW_ROW;
00889         if (coord.column < 1)
00890                 pane.rflags |= REDRAW_TEXT;
00891 }
00892 
00894 static void delete_char_after(void)
00895 {
00896         spt_t sp, ep;
00897         coord_t sc, ec;
00898 
00899         tag_get_pt(&pane.caret_pos, &sp);
00900         spt_get_coord(&sp, &sc);
00901 
00902         sheet_get_cell_pt(&doc.sh, &sc, dir_after, &ep);
00903         spt_get_coord(&ep, &ec);
00904 
00905         (void) sheet_delete(&doc.sh, &sp, &ep);
00906 
00907         pane.rflags |= REDRAW_ROW;
00908         if (ec.row != sc.row)
00909                 pane.rflags |= REDRAW_TEXT;
00910 }
00911 
00917 static void caret_update(void)
00918 {
00919         spt_t pt;
00920         coord_t coord;
00921 
00922         tag_get_pt(&pane.caret_pos, &pt);
00923         spt_get_coord(&pt, &coord);
00924 
00925         /* Scroll pane vertically. */
00926 
00927         if (coord.row < pane.sh_row) {
00928                 pane.sh_row = coord.row;
00929                 pane.rflags |= REDRAW_TEXT;
00930         }
00931 
00932         if (coord.row > pane.sh_row + pane.rows - 1) {
00933                 pane.sh_row = coord.row - pane.rows + 1;
00934                 pane.rflags |= REDRAW_TEXT;
00935         }
00936 
00937         /* Scroll pane horizontally. */
00938 
00939         if (coord.column < pane.sh_column) {
00940                 pane.sh_column = coord.column;
00941                 pane.rflags |= REDRAW_TEXT;
00942         }
00943 
00944         if (coord.column > pane.sh_column + pane.columns - 1) {
00945                 pane.sh_column = coord.column - pane.columns + 1;
00946                 pane.rflags |= REDRAW_TEXT;
00947         }
00948 
00949         pane.rflags |= (REDRAW_CARET | REDRAW_STATUS);
00950 }
00951 
00959 static void caret_move(int drow, int dcolumn, enum dir_spec align_dir)
00960 {
00961         spt_t pt;
00962         coord_t coord;
00963         int num_rows;
00964         bool pure_vertical;
00965 
00966         tag_get_pt(&pane.caret_pos, &pt);
00967         spt_get_coord(&pt, &coord);
00968         coord.row += drow; coord.column += dcolumn;
00969 
00970         /* Clamp coordinates. */
00971         if (drow < 0 && coord.row < 1) coord.row = 1;
00972         if (dcolumn < 0 && coord.column < 1) coord.column = 1;
00973         if (drow > 0) {
00974                 sheet_get_num_rows(&doc.sh, &num_rows);
00975                 if (coord.row > num_rows) coord.row = num_rows;
00976         }
00977 
00978         /* For purely vertical movement try attaining @c ideal_column. */
00979         pure_vertical = (dcolumn == 0 && align_dir == dir_before);
00980         if (pure_vertical)
00981                 coord.column = pane.ideal_column;
00982 
00983         /*
00984          * Select the point before or after the character at the designated
00985          * coordinates. The character can be wider than one cell (e.g. tab).
00986          */
00987         sheet_get_cell_pt(&doc.sh, &coord, align_dir, &pt);
00988         sheet_remove_tag(&doc.sh, &pane.caret_pos);
00989         sheet_place_tag(&doc.sh, &pt, &pane.caret_pos);
00990 
00991         /* For non-vertical movement set the new value for @c ideal_column. */
00992         if (!pure_vertical) {
00993                 spt_get_coord(&pt, &coord);
00994                 pane.ideal_column = coord.column;
00995         }
00996 
00997         caret_update();
00998 }
00999 
01001 static bool selection_active(void)
01002 {
01003         return (tag_cmp(&pane.caret_pos, &pane.sel_start) != 0);
01004 }
01005 
01006 static void selection_get_points(spt_t *pa, spt_t *pb)
01007 {
01008         spt_t pt;
01009 
01010         tag_get_pt(&pane.sel_start, pa);
01011         tag_get_pt(&pane.caret_pos, pb);
01012 
01013         if (spt_cmp(pa, pb) > 0) {
01014                 pt = *pa;
01015                 *pa = *pb;
01016                 *pb = pt;
01017         }
01018 }
01019 
01021 static void selection_delete(void)
01022 {
01023         spt_t pa, pb;
01024         coord_t ca, cb;
01025         int rel;
01026 
01027         tag_get_pt(&pane.sel_start, &pa);
01028         tag_get_pt(&pane.caret_pos, &pb);
01029         spt_get_coord(&pa, &ca);
01030         spt_get_coord(&pb, &cb);
01031         rel = coord_cmp(&ca, &cb);
01032 
01033         if (rel == 0)
01034                 return;
01035 
01036         if (rel < 0)
01037                 sheet_delete(&doc.sh, &pa, &pb);
01038         else
01039                 sheet_delete(&doc.sh, &pb, &pa);
01040 
01041         if (ca.row == cb.row)
01042                 pane.rflags |= REDRAW_ROW;
01043         else
01044                 pane.rflags |= REDRAW_TEXT;
01045 }
01046 
01047 static void selection_sel_all(void)
01048 {
01049         spt_t spt, ept;
01050 
01051         pt_get_sof(&spt);
01052         pt_get_eof(&ept);
01053         sheet_remove_tag(&doc.sh, &pane.sel_start);
01054         sheet_place_tag(&doc.sh, &spt, &pane.sel_start);
01055         sheet_remove_tag(&doc.sh, &pane.caret_pos);
01056         sheet_place_tag(&doc.sh, &ept, &pane.caret_pos);
01057 
01058         pane.rflags |= REDRAW_TEXT;
01059         caret_update();
01060 }
01061 
01062 static void selection_copy(void)
01063 {
01064         spt_t pa, pb;
01065         char *str;
01066 
01067         selection_get_points(&pa, &pb);
01068         str = range_get_str(&pa, &pb);
01069         if (str == NULL || clipboard_put_str(str) != EOK) {
01070                 status_display("Copying to clipboard failed!");
01071         }
01072         free(str);
01073 }
01074 
01075 static void insert_clipboard_data(void)
01076 {
01077         char *str;
01078         size_t off;
01079         wchar_t c;
01080         int rc;
01081 
01082         rc = clipboard_get_str(&str);
01083         if (rc != EOK || str == NULL)
01084                 return;
01085 
01086         off = 0;
01087 
01088         while (true) {
01089                 c = str_decode(str, &off, STR_NO_LIMIT);
01090                 if (c == '\0')
01091                         break;
01092 
01093                 insert_char(c);
01094         }
01095 
01096         free(str);
01097 }
01098 
01100 static void pt_get_sof(spt_t *pt)
01101 {
01102         coord_t coord;
01103 
01104         coord.row = coord.column = 1;
01105         sheet_get_cell_pt(&doc.sh, &coord, dir_before, pt);
01106 }
01107 
01109 static void pt_get_eof(spt_t *pt)
01110 {
01111         coord_t coord;
01112         int num_rows;
01113 
01114         sheet_get_num_rows(&doc.sh, &num_rows);
01115         coord.row = num_rows + 1;
01116         coord.column = 1;
01117 
01118         sheet_get_cell_pt(&doc.sh, &coord, dir_after, pt);
01119 }
01120 
01122 static int tag_cmp(tag_t const *a, tag_t const *b)
01123 {
01124         spt_t pa, pb;
01125 
01126         tag_get_pt(a, &pa);
01127         tag_get_pt(b, &pb);
01128 
01129         return spt_cmp(&pa, &pb);
01130 }
01131 
01133 static int spt_cmp(spt_t const *a, spt_t const *b)
01134 {
01135         coord_t ca, cb;
01136 
01137         spt_get_coord(a, &ca);
01138         spt_get_coord(b, &cb);
01139 
01140         return coord_cmp(&ca, &cb);
01141 }
01142 
01144 static int coord_cmp(coord_t const *a, coord_t const *b)
01145 {
01146         if (a->row - b->row != 0)
01147                 return a->row - b->row;
01148 
01149         return a->column - b->column;
01150 }
01151 
01153 static void status_display(char const *str)
01154 {
01155         console_set_pos(con, 0, scr_rows - 1);
01156         console_set_style(con, STYLE_INVERTED);
01157         
01158         int pos = -(scr_columns - 3);
01159         printf(" %*s ", pos, str);
01160         fflush(stdout);
01161         console_set_style(con, STYLE_NORMAL);
01162 
01163         pane.rflags |= REDRAW_CARET;
01164 }
01165 

Generated on Thu Jun 2 07:45:41 2011 for HelenOS/USB by  doxygen 1.4.7