New wrap quotation implementation. Move gtkstext_strncmp() to gtkstext.c.
[claws.git] / src / gtkstext.c
index 9992ca72fe12b0197d3c7ee993bd993f01826ff8..751439b7964bceba0d8b0f0a16b6294bbe725e63 100644 (file)
@@ -27,7 +27,7 @@
 /*
  * Modified by the Sylpheed Team and others 2001. Interesting 
  * parts are marked using comment block following this one.
- * This modification is based on the GtkSText of GTK 1.2.8
+ * This modification is based on the GtkText of GTK 1.2.10
  */
 
 /* SYLPHEED: 
@@ -477,6 +477,13 @@ static void gtk_stext_show_props (GtkSText* test,
 #define TEXT_SHOW_ADJ(text,adj,msg)
 #endif
 
+#define AHX_DEBUG
+#if defined(AHX_DEBUG)
+#      define XDEBUG(args) g_message args
+#else
+#      define XDEBUG(args)
+#endif /* AHX_DEBUG */
+
 /* Memory Management. */
 static GMemChunk  *params_mem_chunk    = NULL;
 static GMemChunk  *text_property_chunk = NULL;
@@ -808,6 +815,13 @@ gtk_stext_new (GtkAdjustment *hadj,
                         "vadjustment", vadj,
                         NULL);
 
+  /* SYLPHEED:
+   * force widget name to be GtkText so it silently adapts
+   * the GtkText widget's style... 
+   */
+  gtk_widget_set_name(text, "GtkText");                        
+  gtk_widget_ensure_style(text);
+
   return text;
 }
 
@@ -992,6 +1006,15 @@ gtk_stext_thaw (GtkSText *text)
   draw_cursor (text, FALSE);
 }
 
+/* SYLPHEED */
+void
+gtk_stext_compact_buffer (GtkSText    *text)
+{
+  g_return_if_fail (text != NULL);
+  g_return_if_fail (GTK_IS_STEXT (text));
+  move_gap (text, gtk_stext_get_length(text));
+}
+
 void
 gtk_stext_insert (GtkSText    *text,
                 GdkFont    *font,
@@ -2182,8 +2205,17 @@ gtk_stext_key_press (GtkWidget   *widget,
       switch (event->keyval)
        {
        case GDK_Home:
-         if (event->state & GDK_CONTROL_MASK)
-           move_cursor_buffer_ver (text, -1);
+         if (event->state & GDK_CONTROL_MASK) {
+               if (text->wrap_rmargin == 0) {
+                       /* SYLPHEED: old behaviour */
+                       move_cursor_buffer_ver (text, -1);
+               }
+               else {
+                       /* SYLPHEED: contrived, but "trusty" */
+                       move_cursor_buffer_ver(text, -1);
+                       move_cursor_to_display_row_start(text);
+               }
+         }     
          else {
                if (text->wrap_rmargin > 0) {
                        /* SYLPHEED: line start */
@@ -2195,8 +2227,17 @@ gtk_stext_key_press (GtkWidget   *widget,
          }     
          break;
        case GDK_End:
-         if (event->state & GDK_CONTROL_MASK)
-           move_cursor_buffer_ver (text, +1);
+         if (event->state & GDK_CONTROL_MASK) {
+               /* SYLPHEED: a little bit contrived... */
+               if (text->wrap_rmargin == 0) {
+                       /* old behaviour */
+                       move_cursor_buffer_ver (text, +1);
+               }
+               else {
+                       move_cursor_buffer_ver(text, +1);
+                       move_cursor_to_display_row_end(text);
+               }
+         }             
          else {
                if (text->wrap_rmargin > 0) {
                        /* SYLPHEED: line end */
@@ -2224,7 +2265,7 @@ gtk_stext_key_press (GtkWidget   *widget,
                break;
        case GDK_Down:      
                move_cursor_to_display_row_down(text);
-//             move_cursor_ver (text, +1); 
+/*             move_cursor_ver (text, +1);  */
                break;
        case GDK_Left:
          if (event->state & GDK_CONTROL_MASK)
@@ -2264,15 +2305,19 @@ gtk_stext_key_press (GtkWidget   *widget,
            }
          break;
        case GDK_Delete:
-         if (event->state & GDK_CONTROL_MASK)
-           gtk_stext_delete_forward_word (text);
-         else if (event->state & GDK_SHIFT_MASK)
-           {
-             extend_selection = FALSE;
-             gtk_editable_cut_clipboard (editable);
-           }
-         else
-           gtk_stext_delete_forward_character (text);
+               {
+                       if (event->state & GDK_CONTROL_MASK) {
+                               gtk_stext_delete_forward_word (text);
+                       }       
+                       else if (event->state & GDK_SHIFT_MASK)
+                       {
+                               extend_selection = FALSE;
+                               gtk_editable_cut_clipboard (editable);
+                       }
+                       else {
+                               gtk_stext_delete_forward_character (text);
+                       }       
+               }               
          break;
        case GDK_Tab:
          position = text->point.index;
@@ -4182,6 +4227,10 @@ move_cursor_hor (GtkSText *text, int count)
  * move_cursor_to_display_row_down()           (line down)
  */
 
+/*
+ * SYLPHEED_TODO: for some reason the line fetcher also returns markers
+ * of just one character! should investigate this... -- alfons
+ */
 
 static void move_cursor_to_display_row_end(GtkSText *text)
 {
@@ -4203,6 +4252,16 @@ static void move_cursor_to_display_row_start(GtkSText *text)
        }
 }
 
+/* dumb */
+static gboolean range_intersect(guint x1, guint x2, guint y1, guint y2)
+{
+       guint tmp;
+       if (x1 > x2) { tmp = x1; x1 = x2; x2 = tmp; }
+       if (y1 > y2) { tmp = y1; y1 = y2; y1 = tmp; }
+       if (y1 < x1) { tmp = x1; x1 = y1; y1 = tmp; tmp = x2; x2 = y2; y2 = tmp; }
+       return y1 <= x2;
+}
+
 typedef struct {
        int                     start, end;
        gboolean        found;
@@ -4213,12 +4272,17 @@ static gint back_display_row_fetcher(GtkSText *text,
                                                                         LineParams *params,
                                                                         bdrf *data)
 {
-       if (data->start == params->start.index
-       &&  data->end   == params->end.index) {
+       if (range_intersect(data->start, data->end, params->start.index, params->end.index)) {
+               XDEBUG( ("%s(%d) - FOUND search (%d, %d), current (%d, %d)", __FILE__, __LINE__, 
+                         data->start, data->end,
+                                 params->start.index, params->end.index) );
                data->found = TRUE;
                return TRUE;
        }
        else {
+               XDEBUG( ("%s(%d) - NEXT search (%d, %d), current (%d, %d)", __FILE__, __LINE__, 
+                         data->start, data->end,
+                                 params->start.index, params->end.index) );
                data->lp = *params;
                return FALSE;
        }
@@ -4230,41 +4294,49 @@ static void move_cursor_to_display_row_up(GtkSText *text)
        bdrf              data = { 0 };
        int                       new_index;
        int                   col;
-       GtkSPropertyMark  mark = find_this_line_start_mark(text, text->cursor_mark.index, &text->cursor_mark);
+       GtkSPropertyMark  mark;
+
+       mark = find_this_line_start_mark(text, text->cursor_mark.index, &text->cursor_mark);
 
        /* top of buffer */
        if (mark.index == 0) {
-               return;
+               XDEBUG ( ("%s(%d) top of buffer", __FILE__, __LINE__) );
        }
 
-       /* we need to previous DISPLAY row not the previous BUFFER line, so we go one line back,
-        * and iterate over the lines until we have a LineParams that matches the original */
-
+       /* we need previous DISPLAY row not the previous BUFFER line, so we go to start
+        * of paragraph, and iterate over the lines until we have a LineParams that matches 
+        * the original */
        lp  = CACHE_DATA(text->current_line);
        col = (text->cursor_mark.index - lp.start.index);
        data.start = lp.start.index;
        data.end   = lp.end.index;
 
        /* get the previous line */
-       if (mark.index - 1 > 0) {
+       if (mark.index != 0) {
                decrement_mark(&mark);
-               if (mark.index - 1 > 0) {
-                       mark = find_this_line_start_mark(text, mark.index -1,  &mark);
+               if (mark.index != 0) {
+                       GtkSPropertyMark smark = mark;
+                       XDEBUG( ("%s(%d) finding line start mark", __FILE__, __LINE__) );
+                       mark = find_this_line_start_mark(text, smark.index -1,  &smark);
                }                       
        }               
        
        /* let's get the previous display line */
+       XDEBUG( ("%s(%d) iterating to get display lines", __FILE__, __LINE__) );
        line_params_iterate(text, &mark, NULL, FALSE, &data, 
                                                (LineIteratorFunction)back_display_row_fetcher);        
+       XDEBUG( ("%s(%d) done iterating. found = %d", __FILE__, __LINE__, data.found) );                                        
                
        if (data.found) {
                if (col < text->persist_column) col = text->persist_column; 
                else text->persist_column = col;
                new_index = data.lp.start.index + col;
+               XDEBUG( ("%s(%d) - new index = %d", __FILE__, __LINE__, new_index) );
                if (new_index > data.lp.end.index) {
                        new_index = data.lp.end.index;
                }
                /* and move the cursor */
+               XDEBUG( ("%s(%d) - setting index", __FILE__, __LINE__) );
                gtk_stext_set_position_X(GTK_EDITABLE(text), new_index);                
        }
 }
@@ -4276,16 +4348,34 @@ typedef struct {
        LineParams   lp;
 } fdrf; 
 
+#if defined(AHX_DEBUG)
+static void print_line(GtkSText *text, guint start, guint end)
+{
+       gchar *buf = alloca(2048), *walk = buf;
+
+       memset(buf, 0, 2048);
+       for (; start <= end; start++) {
+               *walk++ = GTK_STEXT_INDEX(text, start);
+       }               
+       XDEBUG( ("%s", buf) );  
+}
+#endif
+
 static gint forward_display_row_fetcher(GtkSText *text,
                                                                                LineParams *lp,
                                                                                fdrf       *data)
 {
        if (data->found) {
+               XDEBUG( ("%s(%d) - FW RETURNS. search (%d, %d),  current (%d, %d)",
+                               __FILE__, __LINE__, data->start, data->end, lp->start.index, lp->end.index) );
                data->lp = *lp;
                data->completed = TRUE;
+               print_line(text, lp->start.index, lp->end.index);
                return TRUE;
        }
-       else if (data->start == lp->start.index && data->end == lp->end.index) {
+       else if (range_intersect(data->start, data->end, lp->start.index, lp->end.index)) {
+               XDEBUG( ("%s(%d) - FW FOUND IT. search (%d, %d),  current (%d, %d)",
+                               __FILE__, __LINE__, data->start, data->end, lp->start.index, lp->end.index) );
                data->found = TRUE;
                return FALSE;
        }
@@ -4299,18 +4389,21 @@ static void move_cursor_to_display_row_down     (GtkSText *text)
        LineParams              lp;
        int                             new_index;
        int                             col;
-       GtkSPropertyMark  mark = find_this_line_start_mark(text, text->cursor_mark.index, &text->cursor_mark);
+       GtkSPropertyMark  mark;
        fdrf                    data = { FALSE, FALSE };
 
+       mark = find_this_line_start_mark(text, text->cursor_mark.index, &text->cursor_mark);
        lp  = CACHE_DATA(text->current_line);
        col = (text->cursor_mark.index - lp.start.index);
 
        data.start = lp.start.index;
        data.end   = lp.end.index;
 
-       /* find the next DISPLAY line */ 
+       /* find the next DISPLAY line */
+       XDEBUG( ("%s(%d) - FW iterating", __FILE__, __LINE__) ) ;
        line_params_iterate(text, &mark, NULL, FALSE, &data, 
                                                (LineIteratorFunction)forward_display_row_fetcher);     
+       XDEBUG( ("%s(%d) - FW done iterating", __FILE__, __LINE__) );                                           
        
        if (data.completed) {
                if (col < text->persist_column) col = text->persist_column; 
@@ -4320,6 +4413,7 @@ static void move_cursor_to_display_row_down       (GtkSText *text)
                        new_index = data.lp.end.index;
                }
                /* and move the cursor */
+               XDEBUG( ("%s(%d) - FW set pos %d", __FILE__, __LINE__, new_index) );
                gtk_stext_set_position_X(GTK_EDITABLE(text), new_index);                
        }
 }
@@ -4563,8 +4657,9 @@ static void
 gtk_stext_kill_word (GtkEditable *editable,
                    gint         direction)
 {
-  if (editable->selection_start_pos != editable->selection_end_pos)
+  if (editable->selection_start_pos != editable->selection_end_pos) {
     gtk_editable_delete_selection (editable);
+  }    
   else
     {
       gint old_pos = editable->current_pos;
@@ -4977,6 +5072,65 @@ scroll_up (GtkSText* text, gint diff0)
     process_exposes (text);
 }
 
+/* compare gtkstext string at pos1 with string at pos2 for equality
+   (max. len chars) - we treat characters as single byte */
+guint gtkstext_strncmp(GtkSText *text, guint pos1, guint pos2, guint len,
+                      guint tlen)
+{
+       guint i = 0;
+       gchar ch1, ch2;
+
+       for (; (i < len) && (pos1 + i < tlen) && (pos2 + i < tlen); i++) {
+               ch1 = GTK_STEXT_INDEX(text, pos1 + i);
+               ch2 = GTK_STEXT_INDEX(text, pos2 + i);
+               if (ch1 != ch2)
+                       break;
+       }
+
+       return i;
+}
+
+/* return str length if text at start_pos matches str else return zero */
+guint gtkstext_str_strcmp(GtkSText *text, guint start_pos,
+                         guint text_len, gchar *str) {
+       guint is_str, i, str_len;
+       gchar str_ch;
+
+       is_str = 0;
+       if (str) {
+               str_len = strlen(str);
+               is_str = 1;
+               for (i = 0; (i < str_len) && (start_pos + i < text_len); i++) {
+                       str_ch = GTK_STEXT_INDEX(text, start_pos + i);
+                       if (*(str + i) != str_ch) {
+                               break;
+                       }
+               }
+               if (i == 0 || i < str_len)
+                       is_str = 0;
+       }
+
+       return is_str ? str_len : 0;
+}
+
+/* return true if text at pos is URL */
+guint is_url_string(GtkSText *text, guint start_pos, guint text_len)
+{
+       guint len;
+
+       len = gtkstext_str_strcmp(text, start_pos, text_len, "ftp://");
+       if (len == 6)
+               return 1;
+       len = gtkstext_str_strcmp(text, start_pos, text_len, "http://");
+       if (len == 7)
+               return 1;
+       len = gtkstext_str_strcmp(text, start_pos, text_len, "https://");
+       if (len == 8)
+               return 1;
+
+       return 0;
+}
+
 /**********************************************************************/
 /*                           Display Code                            */
 /**********************************************************************/
@@ -5017,7 +5171,10 @@ find_line_params (GtkSText* text,
        max_display_pixels = text->wrap_rmargin * ch_width; 
   }    
   
-  if (GTK_EDITABLE (text)->editable || !text->word_wrap)
+  /* SYLPHEED - we don't draw ugly word wrapping thing 
+   * if our wrap margin is set */
+  if (!text->wrap_rmargin &&
+      ((GTK_EDITABLE (text)->editable || !text->word_wrap)))
     max_display_pixels -= LINE_WRAP_ROOM;
   
   lp.wraps             = 0;
@@ -5117,6 +5274,16 @@ find_line_params (GtkSText* text,
                      /* If whole line is one word, revert to char wrapping */
                      if (lp.end.index == lp.start.index)
                        {
+                         /* SYLPHEED: don't wrap URLs */
+                          if (is_url_string(text, lp.end.index,
+                                        gtk_stext_get_length(text)))
+                            {
+                             lp.end = saved_mark;
+                             lp.displayable_chars = saved_characters + 1;
+                              lp.wraps = 0;
+                              goto no_url_wrap;
+                            }
+
                          lp.end = saved_mark;
                          lp.displayable_chars = saved_characters;
                          decrement_mark (&lp.end);
@@ -5139,6 +5306,7 @@ find_line_params (GtkSText* text,
          lp.displayable_chars += 1;
        }
       
+no_url_wrap:
       lp.font_ascent = MAX (font->ascent, lp.font_ascent);
       lp.font_descent = MAX (font->descent, lp.font_descent);
       lp.pixel_width  += ch_width;
@@ -5541,7 +5709,7 @@ undraw_cursor (GtkSText* text, gint absolute)
              draw_bg_rect (text, &text->cursor_mark,
                                text->cursor_pos_x - 1,
                                            text->cursor_pos_y - text->cursor_char_offset - font->ascent,
-                                               2, font->ascent + 1, FALSE);
+                                               2, font->descent + font->ascent + 1, FALSE);
                                                                                          
         }
       
@@ -5552,12 +5720,23 @@ undraw_cursor (GtkSText* text, gint absolute)
 
          gdk_gc_set_foreground (text->gc, MARK_CURRENT_FORE (text, &text->cursor_mark));
 
-         gdk_draw_text_wc (text->text_area, font,
-                        text->gc,
-                        text->cursor_pos_x,
-                        text->cursor_pos_y - text->cursor_char_offset,
-                        &text->cursor_char,
-                        1);
+          if (text->use_wchar)
+           gdk_draw_text_wc (text->text_area, font,
+                             text->gc,
+                             text->cursor_pos_x,
+                             text->cursor_pos_y - text->cursor_char_offset,
+                             &text->cursor_char,
+                             1);
+         else
+           {
+             guchar ch = text->cursor_char;
+             gdk_draw_text (text->text_area, font,
+                            text->gc,
+                            text->cursor_pos_x,
+                            text->cursor_pos_y - text->cursor_char_offset,
+                            (gchar *)&ch,
+                            1);         
+           }
        }
 
        gdk_gc_copy(text->gc, gc);
@@ -5639,9 +5818,9 @@ draw_cursor (GtkSText* text, gint absolute)
                gdk_gc_set_line_attributes(text->gc, 2, GDK_LINE_SOLID, GDK_CAP_NOT_LAST, GDK_JOIN_MITER);
                gdk_gc_set_foreground(text->gc, &GTK_WIDGET (text)->style->text[GTK_STATE_NORMAL]);
            gdk_draw_line(text->text_area, text->gc, text->cursor_pos_x,
-                             text->cursor_pos_y - text->cursor_char_offset,
+                             text->cursor_pos_y + font->descent - text->cursor_char_offset,
                                          text->cursor_pos_x,
-                                         text->cursor_pos_y - text->cursor_char_offset - font->ascent);
+                                         text->cursor_pos_y - text->cursor_char_offset - font->ascent );
                                                                                                   
          }
          gdk_gc_copy(text->gc, gc);