00001
00007
00008
00009 #include "NMEGtk.h"
00010 #include <stdio.h>
00011 #include <stdlib.h>
00012 #include <string.h>
00013
00014 static GdkCursor *textCursor = NULL;
00015 static GdkCursor *linkCursor = NULL;
00016
00017 void NMEGtkInit(NMEGtk *nmegtk, GtkTextBuffer *textBuffer,
00018 int charSize)
00019 {
00020 int i;
00021
00022 nmegtk->textBuffer = textBuffer;
00023 nmegtk->linkCB = NULL;
00024
00025 nmegtk->plainTag = gtk_text_buffer_create_tag(textBuffer, "plain", NULL);
00026 g_object_set(G_OBJECT(nmegtk->plainTag), "font", "serif", NULL);
00027 nmegtk->boldTag = gtk_text_buffer_create_tag(textBuffer, "bold", NULL);
00028 g_object_set(G_OBJECT(nmegtk->boldTag), "weight", PANGO_WEIGHT_BOLD, NULL);
00029 nmegtk->italicTag = gtk_text_buffer_create_tag(textBuffer, "italic", NULL);
00030 g_object_set(G_OBJECT(nmegtk->italicTag), "style", PANGO_STYLE_ITALIC, NULL);
00031 nmegtk->underlineTag = gtk_text_buffer_create_tag(textBuffer, "underline", NULL);
00032 g_object_set(G_OBJECT(nmegtk->underlineTag), "underline", PANGO_UNDERLINE_SINGLE, NULL);
00033 nmegtk->superTag = gtk_text_buffer_create_tag(textBuffer, "superscript", NULL);
00034 g_object_set(G_OBJECT(nmegtk->superTag), "rise", charSize / 2, NULL);
00035 nmegtk->subTag = gtk_text_buffer_create_tag(textBuffer, "subscript", NULL);
00036 g_object_set(G_OBJECT(nmegtk->subTag), "rise", -charSize / 3, NULL);
00037 nmegtk->monoTag = gtk_text_buffer_create_tag(textBuffer, "monospace", NULL);
00038 g_object_set(G_OBJECT(nmegtk->monoTag), "font", "monospace", NULL);
00039 nmegtk->headingTag[0] = gtk_text_buffer_create_tag(textBuffer, "h1", NULL);
00040 g_object_set(G_OBJECT(nmegtk->headingTag[0]),
00041 "weight", PANGO_WEIGHT_BOLD,
00042 "scale", PANGO_SCALE_XX_LARGE,
00043 "pixels-above-lines", charSize / PANGO_SCALE,
00044 "justification", GTK_JUSTIFY_CENTER,
00045 NULL);
00046 nmegtk->headingTag[1] = gtk_text_buffer_create_tag(textBuffer, "h2", NULL);
00047 g_object_set(G_OBJECT(nmegtk->headingTag[1]),
00048 "weight", PANGO_WEIGHT_BOLD,
00049 "scale", PANGO_SCALE_X_LARGE,
00050 "pixels-above-lines", 2 * charSize / 3 / PANGO_SCALE,
00051 NULL);
00052 nmegtk->headingTag[2] = gtk_text_buffer_create_tag(textBuffer, "h3", NULL);
00053 g_object_set(G_OBJECT(nmegtk->headingTag[2]),
00054 "weight", PANGO_WEIGHT_BOLD,
00055 "scale", PANGO_SCALE_LARGE,
00056 "pixels-above-lines", charSize / 2 / PANGO_SCALE,
00057 NULL);
00058 nmegtk->headingTag[3] = gtk_text_buffer_create_tag(textBuffer, "h4", NULL);
00059 g_object_set(G_OBJECT(nmegtk->headingTag[3]), "weight", PANGO_WEIGHT_BOLD,
00060 "pixels-above-lines", charSize / 3 / PANGO_SCALE,
00061 NULL);
00062 nmegtk->parTag = gtk_text_buffer_create_tag(textBuffer, "plainpar", NULL);
00063 g_object_set(G_OBJECT(nmegtk->parTag), "indent", 2 * charSize / PANGO_SCALE, NULL);
00064 for (i = 0; i < kMaxListLevel; i++)
00065 {
00066 char name[16];
00067
00068 sprintf(name, "indent%d", i + 1);
00069 nmegtk->indentTag[i] = gtk_text_buffer_create_tag(textBuffer, name, NULL);
00070 g_object_set(G_OBJECT(nmegtk->indentTag[i]),
00071 "left-margin", (3 * i + 1) * charSize / PANGO_SCALE,
00072 "indent", -charSize / PANGO_SCALE,
00073 NULL);
00074 }
00075
00076
00077 if (!linkCursor)
00078 linkCursor = gdk_cursor_new(GDK_LEFT_PTR);
00079 if (!textCursor)
00080 textCursor = gdk_cursor_new(GDK_XTERM);
00081 }
00082
00083 void NMEGtkSetLinkFun(NMEGtk *nmegtk, NMEGtkLinkFun linkFun, void *linkFunData)
00084 {
00085 nmegtk->linkCB = g_malloc(sizeof(NMEGtkLinkCB));
00086 nmegtk->linkCB->fun = linkFun;
00087 nmegtk->linkCB->data = linkFunData;
00088 g_object_set_data_full(G_OBJECT(nmegtk->textBuffer), "link_cb",
00089 nmegtk->linkCB, g_free);
00090 }
00091
00099 static gboolean linkEvent(GtkTextTag *tag, GtkWidget *w,
00100 GdkEventButton *event, GtkTextIter *iter,
00101 gpointer d)
00102 {
00103 NMEGtkLinkCB *l = (NMEGtkLinkCB *)d;
00104
00105 switch (event->type)
00106 {
00107 case GDK_BUTTON_RELEASE:
00108 if (l)
00109 {
00110 l->fun(g_object_get_data(G_OBJECT(tag), "link_url"),
00111 l->data);
00112 return TRUE;
00113 }
00114 break;
00115
00116 case GDK_ENTER_NOTIFY:
00117 if (linkCursor)
00118 gdk_window_set_cursor(gtk_widget_get_parent_window(w), linkCursor);
00119 return TRUE;
00120 case GDK_LEAVE_NOTIFY:
00121 if (textCursor)
00122 gdk_window_set_cursor(gtk_widget_get_parent_window(w), textCursor);
00123 return TRUE;
00124 }
00125
00126 return TRUE;
00127 }
00128
00129 void NMEGtkApplyStyle(NMEGtk const *nmegtk,
00130 NMEStyleTable const *spanTable,
00131 NMEInt offset, NMEInt length,
00132 NMEConstText nmeTextForLinks)
00133 {
00134 GtkTextIter start, end;
00135 GtkTextTag *tag;
00136 int i;
00137
00138 gtk_text_buffer_get_iter_at_offset(nmegtk->textBuffer, &start,
00139 offset);
00140 gtk_text_buffer_get_iter_at_offset(nmegtk->textBuffer, &end,
00141 offset + length);
00142 gtk_text_buffer_apply_tag(nmegtk->textBuffer, nmegtk->plainTag, &start, &end);
00143
00144 for (i = 0; i < spanTable->n; i++)
00145 {
00146 switch (spanTable->span[i].style)
00147 {
00148 case kNMEStyleCharBold:
00149 case kNMEStyleCharDT:
00150 case kNMEStyleCharTH:
00151 tag = nmegtk->boldTag;
00152 break;
00153 case kNMEStyleCharItalic:
00154 tag = nmegtk->italicTag;
00155 break;
00156 case kNMEStyleCharUnderline:
00157 tag = nmegtk->underlineTag;
00158 break;
00159 case kNMEStyleCharSuperscript:
00160 tag = nmegtk->superTag;
00161 break;
00162 case kNMEStyleCharSubscript:
00163 tag = nmegtk->subTag;
00164 break;
00165 case kNMEStyleCharMonospace:
00166 tag = nmegtk->monoTag;
00167 break;
00168 case kNMEStyleParHeading:
00169 if (spanTable->span[i].level <= kMaxHeadingLevel)
00170 tag = nmegtk->headingTag[spanTable->span[i].level - 1];
00171 else
00172 tag = nmegtk->headingTag[kMaxHeadingLevel - 1];
00173 break;
00174 case kNMEStyleParPlain:
00175 tag = nmegtk->parTag;
00176 break;
00177 case kNMEStyleParUL:
00178 case kNMEStyleParOL:
00179 case kNMEStyleParDL:
00180 case kNMEStyleParDT:
00181 case kNMEStyleParIndentedPar:
00182 if (spanTable->span[i].level <= kMaxListLevel)
00183 tag = nmegtk->indentTag[spanTable->span[i].level - 1];
00184 else
00185 tag = nmegtk->indentTag[kMaxHeadingLevel - 1];
00186 break;
00187 case kNMEStyleCharLink:
00188 if (nmeTextForLinks)
00189 {
00190 tag = gtk_text_buffer_create_tag(nmegtk->textBuffer, NULL,
00191 "foreground", "blue",
00192 "underline", PANGO_UNDERLINE_SINGLE,
00193 NULL);
00194 g_object_set_data_full(G_OBJECT(tag),
00195 "link_url",
00196 g_strndup(nmeTextForLinks + spanTable->span[i].linkOffset,
00197 spanTable->span[i].linkLength),
00198 g_free);
00199 g_signal_connect(G_OBJECT(tag), "event",
00200 G_CALLBACK(linkEvent), (gpointer)nmegtk->linkCB);
00201 break;
00202 }
00203
00204 default:
00205 tag = NULL;
00206 break;
00207 }
00208
00209 if (tag)
00210 {
00211 gtk_text_buffer_get_iter_at_offset(nmegtk->textBuffer, &start,
00212 spanTable->span[i].begin + offset);
00213 gtk_text_buffer_get_iter_at_offset(nmegtk->textBuffer, &end,
00214 spanTable->span[i].end + offset);
00215 gtk_text_buffer_apply_tag(nmegtk->textBuffer, tag, &start, &end);
00216 }
00217 }
00218 }
00219
00220 NMEErr NMEGtkInsert(GtkTextBuffer *textBuffer,
00221 NMEGtk const *nmegtk,
00222 NMEConstText str, NMEInt len,
00223 NMEBoolean replaceSel,
00224 NMEBoolean links)
00225 {
00226 NMEText buf, dest;
00227 NMEInt bufSize, destLen, destLenUCS16;
00228 NMEOutputFormat f;
00229 NMEErr err;
00230 int length;
00231 GtkTextIter iter;
00232
00233 if (len < 0)
00234 len = strlen(str);
00235
00236 bufSize = 1024 + 2 * len;
00237 f = NMEOutputFormatBasicText;
00238 f.spanHookFun = NMEStyleSpanHook;
00239 f.parHookFun = NMEStyleSpanHook;
00240
00241 tryAgain:
00242 buf = malloc(bufSize);
00243 if (!buf)
00244 return kNMEErrNotEnoughMemory;
00245 f.hookData = malloc(bufSize);
00246 if (!f.hookData)
00247 {
00248 free((void *)buf);
00249 return kNMEErrNotEnoughMemory;
00250 }
00251 NMEStyleInit((NMEStyleTable *)f.hookData, bufSize, TRUE);
00252
00253 err = NMEProcess(str, len, buf, bufSize,
00254 kNMEProcessOptDefault, "\n", &f, 0,
00255 &dest, &destLen, &destLenUCS16);
00256 if (err == kNMEErrNotEnoughMemory || err == kNMEErrStyleTableTooSmall)
00257 {
00258 free((void *)buf);
00259 free((void *)f.hookData);
00260 if (bufSize < 65536 + 10 * len)
00261 {
00262 bufSize *= 2;
00263 goto tryAgain;
00264 }
00265 else
00266 return kNMEErrNotEnoughMemory;
00267 }
00268
00269 if (replaceSel)
00270 {
00271 length = gtk_text_buffer_get_char_count(textBuffer);
00272 gtk_text_buffer_get_iter_at_offset(textBuffer, &iter, length);
00273 gtk_text_buffer_insert(textBuffer, &iter, dest, destLen);
00274 NMEGtkApplyStyle(nmegtk, (NMEStyleTable *)f.hookData,
00275 length, destLenUCS16, links ? str : NULL);
00276 }
00277 else
00278 {
00279 gtk_text_buffer_set_text(textBuffer, dest, destLen);
00280 NMEGtkApplyStyle(nmegtk, (NMEStyleTable *)f.hookData,
00281 0, destLenUCS16, links ? str : NULL);
00282 }
00283
00284 free((void *)buf);
00285 free((void *)f.hookData);
00286
00287 return kNMEErrOk;
00288 }